1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % CCCC OOO L OOO RRRR M M AAA PPPP %
7 % C O O L O O R R MM MM A A P P %
8 % C O O L O O RRRR M M M AAAAA PPPP %
9 % C O O L O O R R M M A A P %
10 % CCCC OOO LLLLL OOO R R M M A A P %
11 % %
12 % %
13 % MagickCore Colormap Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % We use linked-lists because splay-trees do not currently support duplicate
37 % key / value pairs (.e.g X11 green compliance and SVG green compliance).
38 %
39 */
40
41 /*
42 Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/attribute.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/cache.h"
49 #include "MagickCore/color.h"
50 #include "MagickCore/color-private.h"
51 #include "MagickCore/colormap.h"
52 #include "MagickCore/colormap-private.h"
53 #include "MagickCore/client.h"
54 #include "MagickCore/configure.h"
55 #include "MagickCore/exception.h"
56 #include "MagickCore/exception-private.h"
57 #include "MagickCore/gem.h"
58 #include "MagickCore/geometry.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/option.h"
64 #include "MagickCore/pixel-accessor.h"
65 #include "MagickCore/quantize.h"
66 #include "MagickCore/quantum.h"
67 #include "MagickCore/resource_.h"
68 #include "MagickCore/semaphore.h"
69 #include "MagickCore/string_.h"
70 #include "MagickCore/thread-private.h"
71 #include "MagickCore/token.h"
72 #include "MagickCore/utility.h"
73 #include "MagickCore/xml-tree.h"
74
75 /*
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 % %
78 % %
79 % %
80 % A c q u i r e I m a g e C o l o r m a p %
81 % %
82 % %
83 % %
84 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85 %
86 % AcquireImageColormap() allocates an image colormap and initializes
87 % it to a linear gray colorspace. If the image already has a colormap,
88 % it is replaced. AcquireImageColormap() returns MagickTrue if successful,
89 % otherwise MagickFalse if there is not enough memory.
90 %
91 % The format of the AcquireImageColormap method is:
92 %
93 % MagickBooleanType AcquireImageColormap(Image *image,const size_t colors,
94 % ExceptionInfo *exception)
95 %
96 % A description of each parameter follows:
97 %
98 % o image: the image.
99 %
100 % o colors: the number of colors in the image colormap.
101 %
102 % o exception: return any errors or warnings in this structure.
103 %
104 */
AcquireImageColormap(Image * image,const size_t colors,ExceptionInfo * exception)105 MagickExport MagickBooleanType AcquireImageColormap(Image *image,
106 const size_t colors,ExceptionInfo *exception)
107 {
108 ssize_t
109 i;
110
111 /*
112 Allocate image colormap.
113 */
114 assert(image != (Image *) NULL);
115 assert(image->signature == MagickCoreSignature);
116 if (image->debug != MagickFalse)
117 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
118 if (colors > MaxColormapSize)
119 {
120 image->colors=0;
121 image->storage_class=DirectClass;
122 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
123 image->filename);
124 }
125 image->colors=MagickMax(colors,1);
126 if (image->colormap == (PixelInfo *) NULL)
127 image->colormap=(PixelInfo *) AcquireQuantumMemory(image->colors+1,
128 sizeof(*image->colormap));
129 else
130 image->colormap=(PixelInfo *) ResizeQuantumMemory(image->colormap,
131 image->colors+1,sizeof(*image->colormap));
132 if (image->colormap == (PixelInfo *) NULL)
133 {
134 image->colors=0;
135 image->storage_class=DirectClass;
136 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
137 image->filename);
138 }
139 for (i=0; i < (ssize_t) image->colors; i++)
140 {
141 double
142 pixel;
143
144 GetPixelInfo(image,image->colormap+i);
145 pixel=(double) (i*(QuantumRange/MagickMax(colors-1,1)));
146 image->colormap[i].red=pixel;
147 image->colormap[i].green=pixel;
148 image->colormap[i].blue=pixel;
149 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
150 image->colormap[i].alpha_trait=BlendPixelTrait;
151 }
152 return(SetImageStorageClass(image,PseudoClass,exception));
153 }
154
155 /*
156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
157 % %
158 % %
159 % %
160 % C y c l e C o l o r m a p I m a g e %
161 % %
162 % %
163 % %
164 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
165 %
166 % CycleColormap() displaces an image's colormap by a given number of
167 % positions. If you cycle the colormap a number of times you can produce
168 % a psychodelic effect.
169 %
170 % WARNING: this assumes an images colormap is in a well know and defined
171 % order. Currently Imagemagick has no way of setting that order.
172 %
173 % The format of the CycleColormapImage method is:
174 %
175 % MagickBooleanType CycleColormapImage(Image *image,const ssize_t displace,
176 % ExceptionInfo *exception)
177 %
178 % A description of each parameter follows:
179 %
180 % o image: the image.
181 %
182 % o displace: displace the colormap this amount.
183 %
184 % o exception: return any errors or warnings in this structure.
185 %
186 */
CycleColormapImage(Image * image,const ssize_t displace,ExceptionInfo * exception)187 MagickExport MagickBooleanType CycleColormapImage(Image *image,
188 const ssize_t displace,ExceptionInfo *exception)
189 {
190 CacheView
191 *image_view;
192
193 MagickBooleanType
194 status;
195
196 ssize_t
197 y;
198
199 assert(image != (Image *) NULL);
200 assert(image->signature == MagickCoreSignature);
201 if (image->debug != MagickFalse)
202 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
203 if (image->storage_class == DirectClass)
204 (void) SetImageType(image,PaletteType,exception);
205 status=MagickTrue;
206 image_view=AcquireAuthenticCacheView(image,exception);
207 #if defined(MAGICKCORE_OPENMP_SUPPORT)
208 #pragma omp parallel for schedule(static) \
209 magick_number_threads(image,image,image->rows,1)
210 #endif
211 for (y=0; y < (ssize_t) image->rows; y++)
212 {
213 ssize_t
214 x;
215
216 Quantum
217 *magick_restrict q;
218
219 ssize_t
220 index;
221
222 if (status == MagickFalse)
223 continue;
224 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
225 if (q == (Quantum *) NULL)
226 {
227 status=MagickFalse;
228 continue;
229 }
230 for (x=0; x < (ssize_t) image->columns; x++)
231 {
232 index=(ssize_t) (GetPixelIndex(image,q)+displace) % image->colors;
233 if (index < 0)
234 index+=(ssize_t) image->colors;
235 SetPixelIndex(image,(Quantum) index,q);
236 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
237 q+=GetPixelChannels(image);
238 }
239 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
240 status=MagickFalse;
241 }
242 image_view=DestroyCacheView(image_view);
243 return(status);
244 }
245
246 /*
247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
248 % %
249 % %
250 % %
251 + S o r t C o l o r m a p B y I n t e n s i t y %
252 % %
253 % %
254 % %
255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
256 %
257 % SortColormapByIntensity() sorts the colormap of a PseudoClass image by
258 % decreasing color intensity.
259 %
260 % The format of the SortColormapByIntensity method is:
261 %
262 % MagickBooleanType SortColormapByIntensity(Image *image,
263 % ExceptionInfo *exception)
264 %
265 % A description of each parameter follows:
266 %
267 % o image: A pointer to an Image structure.
268 %
269 % o exception: return any errors or warnings in this structure.
270 %
271 */
272
273 #if defined(__cplusplus) || defined(c_plusplus)
274 extern "C" {
275 #endif
276
IntensityCompare(const void * x,const void * y)277 static int IntensityCompare(const void *x,const void *y)
278 {
279 const PixelInfo
280 *color_1,
281 *color_2;
282
283 int
284 intensity;
285
286 color_1=(const PixelInfo *) x;
287 color_2=(const PixelInfo *) y;
288 intensity=(int) GetPixelInfoIntensity((const Image *) NULL,color_2)-(int)
289 GetPixelInfoIntensity((const Image *) NULL,color_1);
290 return(intensity);
291 }
292
293 #if defined(__cplusplus) || defined(c_plusplus)
294 }
295 #endif
296
SortColormapByIntensity(Image * image,ExceptionInfo * exception)297 MagickExport MagickBooleanType SortColormapByIntensity(Image *image,
298 ExceptionInfo *exception)
299 {
300 CacheView
301 *image_view;
302
303 MagickBooleanType
304 status;
305
306 ssize_t
307 i;
308
309 ssize_t
310 y;
311
312 unsigned short
313 *pixels;
314
315 assert(image != (Image *) NULL);
316 if (image->debug != MagickFalse)
317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
318 assert(image->signature == MagickCoreSignature);
319 if (image->storage_class != PseudoClass)
320 return(MagickTrue);
321 /*
322 Allocate memory for pixel indexes.
323 */
324 pixels=(unsigned short *) AcquireQuantumMemory((size_t) image->colors,
325 sizeof(*pixels));
326 if (pixels == (unsigned short *) NULL)
327 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
328 image->filename);
329 /*
330 Assign index values to colormap entries.
331 */
332 for (i=0; i < (ssize_t) image->colors; i++)
333 image->colormap[i].alpha=(double) i;
334 /*
335 Sort image colormap by decreasing color popularity.
336 */
337 qsort((void *) image->colormap,(size_t) image->colors,
338 sizeof(*image->colormap),IntensityCompare);
339 /*
340 Update image colormap indexes to sorted colormap order.
341 */
342 for (i=0; i < (ssize_t) image->colors; i++)
343 pixels[(ssize_t) image->colormap[i].alpha]=(unsigned short) i;
344 status=MagickTrue;
345 image_view=AcquireAuthenticCacheView(image,exception);
346 for (y=0; y < (ssize_t) image->rows; y++)
347 {
348 Quantum
349 index;
350
351 ssize_t
352 x;
353
354 Quantum
355 *magick_restrict q;
356
357 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
358 if (q == (Quantum *) NULL)
359 {
360 status=MagickFalse;
361 break;
362 }
363 for (x=0; x < (ssize_t) image->columns; x++)
364 {
365 i=ConstrainColormapIndex(image,GetPixelIndex(image,q),exception);
366 index=(Quantum) pixels[i];
367 SetPixelIndex(image,index,q);
368 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
369 q+=GetPixelChannels(image);
370 }
371 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
372 status=MagickFalse;
373 if (status == MagickFalse)
374 break;
375 }
376 image_view=DestroyCacheView(image_view);
377 pixels=(unsigned short *) RelinquishMagickMemory(pixels);
378 return(status);
379 }
380