1 /*
2 % Copyright (C) 2009 GraphicsMagick Group
3 % Copyright (C) 2005 Eskil Steenberg. All rights reserved.
4 %
5 % This program is covered by multiple licenses, which are described in
6 % Copyright.txt. You should have received a copy of Copyright.txt with this
7 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8 %
9 */
10
11 /*
12 Include declarations.
13 */
14 #include "magick/studio.h"
15 #include "magick/hclut.h"
16 #include "magick/pixel_cache.h"
17 #include "magick/pixel_iterator.h"
18 #include "magick/monitor.h"
19 #include "magick/utility.h"
20
21 /*
22 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23 % %
24 % %
25 % %
26 % H a l d C l u t I m a g e %
27 % %
28 % %
29 % %
30 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31 %
32 % The HaldClutImage() method apply a color lookup table (Hald CLUT) to the
33 % image. The fundamental principle of the Hald CLUT algorithm is that
34 % application of an identity CLUT causes no change to the input image,
35 % but an identity CLUT image which has had its colors transformed in
36 % some way (e.g. in Adobe Photoshop) may be used to implement an identical
37 % transform on any other image.
38 %
39 % The minimum CLUT level is 2, and the maximum depends on available memory
40 % (largest successfully tested is 24). A CLUT image is required to have equal
41 % width and height. A CLUT of level 8 is an image of dimension 512x512, a CLUT
42 % of level 16 is an image of dimension 4096x4096. Interpolation is used so
43 % extremely large CLUT images are not required.
44 %
45 % GraphicsMagick provides an 'identity' coder which may be used to generate
46 % identity HLUTs. For example, reading from "identity:8" creates an identity
47 % CLUT of order 8.
48 %
49 % The Hald CLUT algorithm has been developed by Eskil Steenberg as described
50 % at http://www.quelsolaar.com/technology/clut.html, and was adapted for
51 % GraphicsMagick by Clément Follet with support from Cédric Lejeune of
52 % Workflowers.
53 %
54 % The format of the HaldClutImage method is:
55 %
56 % MagickPassFail HaldClutImage(Image *image,const Image *clut)
57 %
58 % A description of each parameter follows:
59 %
60 % o image: The image.
61 %
62 % o clut: The color lookup table image
63 %
64 %
65 */
66
67 typedef struct _HaldClutImageParameters_t
68 {
69 unsigned int
70 level;
71
72 const PixelPacket
73 *ppcl;
74
75 } HaldClutImageParameters_t;
76
77 static MagickPassFail
HaldClutImagePixels(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)78 HaldClutImagePixels(void *mutable_data, /* User provided mutable data */
79 const void *immutable_data, /* User provided immutable data */
80 Image * restrict image, /* Modify image */
81 PixelPacket * restrict pixels, /* Pixel row */
82 IndexPacket * restrict indexes, /* Pixel row indexes */
83 const long npixels, /* Number of pixels in row */
84 ExceptionInfo *exception) /* Exception report */
85 {
86
87 const HaldClutImageParameters_t *
88 param = (const HaldClutImageParameters_t *) immutable_data;
89
90 unsigned int
91 level = param->level;
92
93 const PixelPacket
94 * restrict clut = param->ppcl;
95
96 unsigned int
97 color;
98
99 unsigned int
100 blueaxis,
101 greenaxis,
102 redaxis;
103
104 register long
105 i,
106 k;
107
108 double
109 sums[9],
110 r,
111 g,
112 b,
113 value;
114
115 ARG_NOT_USED(mutable_data);
116 ARG_NOT_USED(image);
117 ARG_NOT_USED(indexes);
118 ARG_NOT_USED(exception);
119
120 level *= level;
121
122 for(k = 0; k < npixels ; k++)
123 {
124 /*
125 Calculate the position of each 3D axis pixel level.
126 */
127 redaxis = (unsigned int) (((double) pixels[k].red/MaxRGBDouble) * (level-1));
128 if (redaxis > level - 2)
129 redaxis = level - 2;
130 greenaxis = (unsigned int) (((double) pixels[k].green/MaxRGBDouble) * (level-1));
131 if(greenaxis > level - 2)
132 greenaxis = level - 2;
133 blueaxis = (unsigned int) (((double) pixels[k].blue/MaxRGBDouble) * (level-1));
134 if(blueaxis > level - 2)
135 blueaxis = level - 2;
136
137 /*
138 Convert between the value and the equivalent value position.
139 */
140 r = ((double) pixels[k].red/MaxRGBDouble) * (level - 1) - redaxis;
141 g = ((double) pixels[k].green/MaxRGBDouble) * (level - 1) - greenaxis;
142 b = ((double) pixels[k].blue/MaxRGBDouble) * (level - 1) - blueaxis;
143
144 color = redaxis + greenaxis * level + blueaxis * level * level;
145
146 i = color;
147 sums[0] = ((double) clut[i].red) * (1 - r);
148 sums[1] = ((double) clut[i].green) * (1 - r);
149 sums[2] = ((double) clut[i].blue) * (1 - r);
150 i++;
151 sums[0] += ((double) clut[i].red) * r;
152 sums[1] += ((double) clut[i].green) * r;
153 sums[2] += ((double) clut[i].blue) * r;
154
155 i = (color + level);
156 sums[3] = ((double) clut[i].red) * (1 - r);
157 sums[4] = ((double) clut[i].green) * (1 - r);
158 sums[5] = ((double) clut[i].blue) * (1 - r);
159 i++;
160 sums[3] += ((double) clut[i].red) * r;
161 sums[4] += ((double) clut[i].green) * r;
162 sums[5] += ((double) clut[i].blue) * r;
163
164 sums[6] = sums[0] * (1 - g) + sums[3] * g;
165 sums[7] = sums[1] * (1 - g) + sums[4] * g;
166 sums[8] = sums[2] * (1 - g) + sums[5] * g;
167
168 i = (color + level * level);
169 sums[0] = ((double) clut[i].red) * (1 - r);
170 sums[1] = ((double) clut[i].green) * (1 - r);
171 sums[2] = ((double) clut[i].blue) * (1 - r);
172 i++;
173 sums[0] += ((double) clut[i].red) * r;
174 sums[1] += ((double) clut[i].green) * r;
175 sums[2] += ((double) clut[i].blue) * r;
176
177 i = (color + level * level + level);
178 sums[3] = ((double) clut[i].red) * (1 - r);
179 sums[4] = ((double) clut[i].green) * (1 - r);
180 sums[5] = ((double) clut[i].blue) * (1 - r);
181 i++;
182 sums[3] += ((double) clut[i].red) * r;
183 sums[4] += ((double) clut[i].green) * r;
184 sums[5] += ((double) clut[i].blue) * r;
185
186 sums[0] = sums[0] * (1 - g) + sums[3] * g;
187 sums[1] = sums[1] * (1 - g) + sums[4] * g;
188 sums[2] = sums[2] * (1 - g) + sums[5] * g;
189
190 value=(sums[6] * (1 - b) + sums[0] * b);
191 pixels[k].red = RoundDoubleToQuantum(value);
192
193 value=(sums[7] * (1 - b) + sums[1] * b);
194 pixels[k].green = RoundDoubleToQuantum(value);
195
196 value=(sums[8] * (1 - b) + sums[2] * b);
197 pixels[k].blue = RoundDoubleToQuantum(value);
198 }
199
200 return MagickPass;
201 }
202
203
204 MagickExport MagickPassFail
HaldClutImage(Image * image,const Image * clut)205 HaldClutImage(Image *image, const Image *clut)
206 {
207 unsigned int
208 level;
209
210 char
211 progress_message[MaxTextExtent];
212
213 HaldClutImageParameters_t
214 param;
215
216 MagickPassFail
217 status=MagickPass;
218
219 assert(image != (Image *) NULL);
220 assert(image->signature == MagickSignature);
221
222 /*
223 Hald CLUT images are square.
224 */
225 if(clut->rows != clut->columns)
226 {
227 ThrowBinaryException(OptionError,HaldClutImageDimensionsInvalid,
228 clut->filename);
229 }
230
231 /*
232 Calculate the level of the Hald CLUT
233 */
234 for(level = 1; level * level * level < clut->rows; level++);
235 if((level * level * level > clut->rows) || (level < 2))
236 {
237 ThrowBinaryException(OptionError,HaldClutImageDimensionsInvalid,
238 clut->filename);
239 }
240
241 param.level = level;
242
243 /*
244 We acquire all of the pixels at once, which is the limiting factor
245 on maximum Hald CLUT size.
246 */
247 param.ppcl=AcquireImagePixels(clut,0,0,clut->columns,clut->rows,&image->exception);
248 if (param.ppcl == (const PixelPacket *) NULL)
249 return MagickFail;
250
251 FormatString(progress_message,
252 "[%%s] Applying Hald CLUT level %u (%lux%lu) ...",
253 param.level,clut->columns,clut->rows);
254
255 if (!IsRGBCompatibleColorspace(image->colorspace))
256 TransformColorspace(image,RGBColorspace);
257 if (image->storage_class == PseudoClass)
258 {
259 (void) HaldClutImagePixels(NULL,¶m,image,image->colormap,
260 (IndexPacket *) NULL,image->colors,
261 &image->exception);
262 status=SyncImage(image);
263 }
264 else
265 {
266 status=PixelIterateMonoModify(HaldClutImagePixels,NULL,progress_message,
267 NULL,¶m,0,0,image->columns,image->rows,
268 image,&image->exception);
269 }
270
271 return(status);
272 }
273