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 "magick/studio.h"
45 #include "magick/attribute.h"
46 #include "magick/blob.h"
47 #include "magick/cache-view.h"
48 #include "magick/cache.h"
49 #include "magick/color.h"
50 #include "magick/color-private.h"
51 #include "magick/colormap.h"
52 #include "magick/colormap-private.h"
53 #include "magick/client.h"
54 #include "magick/configure.h"
55 #include "magick/exception.h"
56 #include "magick/exception-private.h"
57 #include "magick/gem.h"
58 #include "magick/geometry.h"
59 #include "magick/image-private.h"
60 #include "magick/memory_.h"
61 #include "magick/monitor.h"
62 #include "magick/monitor-private.h"
63 #include "magick/option.h"
64 #include "magick/pixel-accessor.h"
65 #include "magick/pixel-private.h"
66 #include "magick/quantize.h"
67 #include "magick/quantum.h"
68 #include "magick/semaphore.h"
69 #include "magick/resource_.h"
70 #include "magick/string_.h"
71 #include "magick/thread-private.h"
72 #include "magick/token.h"
73 #include "magick/utility.h"
74 #include "magick/xml-tree.h"
75
76 /*
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 % %
79 % %
80 % %
81 % A c q u i r e I m a g e C o l o r m a p %
82 % %
83 % %
84 % %
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 %
87 % AcquireImageColormap() allocates an image colormap and initializes
88 % it to a linear gray colorspace. If the image already has a colormap,
89 % it is replaced. AcquireImageColormap() returns MagickTrue if successful,
90 % otherwise MagickFalse if there is not enough memory.
91 %
92 % The format of the AcquireImageColormap method is:
93 %
94 % MagickBooleanType AcquireImageColormap(Image *image,const size_t colors)
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 */
AcquireImageColormap(Image * image,const size_t colors)103 MagickExport MagickBooleanType AcquireImageColormap(Image *image,
104 const size_t colors)
105 {
106 ssize_t
107 i;
108
109 /*
110 Allocate image colormap.
111 */
112 assert(image != (Image *) NULL);
113 assert(image->signature == MagickCoreSignature);
114 if (image->debug != MagickFalse)
115 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
116 if (colors > MaxColormapSize)
117 {
118 image->colors=0;
119 image->storage_class=DirectClass;
120 ThrowBinaryImageException(ResourceLimitError,"MemoryAllocationFailed",
121 image->filename);
122 }
123 image->colors=MagickMax(colors,1);
124 if (image->colormap == (PixelPacket *) NULL)
125 image->colormap=(PixelPacket *) AcquireQuantumMemory(image->colors+256,
126 sizeof(*image->colormap));
127 else
128 image->colormap=(PixelPacket *) ResizeQuantumMemory(image->colormap,
129 image->colors+256,sizeof(*image->colormap));
130 if (image->colormap == (PixelPacket *) NULL)
131 {
132 image->colors=0;
133 image->storage_class=DirectClass;
134 ThrowBinaryImageException(ResourceLimitError,"MemoryAllocationFailed",
135 image->filename);
136 }
137 for (i=0; i < (ssize_t) image->colors; i++)
138 {
139 size_t
140 pixel;
141
142 pixel=(size_t) (i*(QuantumRange/MagickMax(colors-1,1)));
143 image->colormap[i].red=(Quantum) pixel;
144 image->colormap[i].green=(Quantum) pixel;
145 image->colormap[i].blue=(Quantum) pixel;
146 image->colormap[i].opacity=OpaqueOpacity;
147 }
148 return(SetImageStorageClass(image,PseudoClass));
149 }
150
151 /*
152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 % %
154 % %
155 % %
156 % C y c l e C o l o r m a p I m a g e %
157 % %
158 % %
159 % %
160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
161 %
162 % CycleColormap() displaces an image's colormap by a given number of
163 % positions. If you cycle the colormap a number of times you can produce
164 % a psychodelic effect.
165 %
166 % The format of the CycleColormapImage method is:
167 %
168 % MagickBooleanType CycleColormapImage(Image *image,const ssize_t displace)
169 %
170 % A description of each parameter follows:
171 %
172 % o image: the image.
173 %
174 % o displace: displace the colormap this amount.
175 %
176 */
CycleColormapImage(Image * image,const ssize_t displace)177 MagickExport MagickBooleanType CycleColormapImage(Image *image,
178 const ssize_t displace)
179 {
180 CacheView
181 *image_view;
182
183 ExceptionInfo
184 *exception;
185
186 MagickBooleanType
187 status;
188
189 ssize_t
190 y;
191
192 assert(image != (Image *) NULL);
193 assert(image->signature == MagickCoreSignature);
194 if (image->debug != MagickFalse)
195 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
196 if (image->storage_class == DirectClass)
197 (void) SetImageType(image,PaletteType);
198 status=MagickTrue;
199 exception=(&image->exception);
200 image_view=AcquireAuthenticCacheView(image,exception);
201 #if defined(MAGICKCORE_OPENMP_SUPPORT)
202 #pragma omp parallel for schedule(static) shared(status) \
203 magick_number_threads(image,image,image->rows,1)
204 #endif
205 for (y=0; y < (ssize_t) image->rows; y++)
206 {
207 IndexPacket
208 *magick_restrict indexes;
209
210 ssize_t
211 x;
212
213 PixelPacket
214 *magick_restrict q;
215
216 ssize_t
217 index;
218
219 if (status == MagickFalse)
220 continue;
221 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
222 if (q == (PixelPacket *) NULL)
223 {
224 status=MagickFalse;
225 continue;
226 }
227 indexes=GetCacheViewAuthenticIndexQueue(image_view);
228 for (x=0; x < (ssize_t) image->columns; x++)
229 {
230 index=(ssize_t) (GetPixelIndex(indexes+x)+displace) %
231 image->colors;
232 if (index < 0)
233 index+=(ssize_t) image->colors;
234 SetPixelIndex(indexes+x,index);
235 SetPixelRGBO(q,image->colormap+(ssize_t) index);
236 q++;
237 }
238 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
239 status=MagickFalse;
240 }
241 image_view=DestroyCacheView(image_view);
242 return(status);
243 }
244
245 /*
246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
247 % %
248 % %
249 % %
250 + S o r t C o l o r m a p B y I n t e n s i t y %
251 % %
252 % %
253 % %
254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
255 %
256 % SortColormapByIntensity() sorts the colormap of a PseudoClass image by
257 % decreasing color intensity.
258 %
259 % The format of the SortColormapByIntensity method is:
260 %
261 % MagickBooleanType SortColormapByIntensity(Image *image)
262 %
263 % A description of each parameter follows:
264 %
265 % o image: A pointer to an Image structure.
266 %
267 */
268
269 #if defined(__cplusplus) || defined(c_plusplus)
270 extern "C" {
271 #endif
272
IntensityCompare(const void * x,const void * y)273 static int IntensityCompare(const void *x,const void *y)
274 {
275 const PixelPacket
276 *color_1,
277 *color_2;
278
279 int
280 intensity;
281
282 color_1=(const PixelPacket *) x;
283 color_2=(const PixelPacket *) y;
284 intensity=PixelPacketIntensity(color_2)-(int) PixelPacketIntensity(color_1);
285 return(intensity);
286 }
287
288 #if defined(__cplusplus) || defined(c_plusplus)
289 }
290 #endif
291
SortColormapByIntensity(Image * image)292 MagickExport MagickBooleanType SortColormapByIntensity(Image *image)
293 {
294 CacheView
295 *image_view;
296
297 ExceptionInfo
298 *exception;
299
300 MagickBooleanType
301 status;
302
303 ssize_t
304 i;
305
306 ssize_t
307 y;
308
309 unsigned short
310 *pixels;
311
312 assert(image != (Image *) NULL);
313 if (image->debug != MagickFalse)
314 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
315 assert(image->signature == MagickCoreSignature);
316 if (image->storage_class != PseudoClass)
317 return(MagickTrue);
318 exception=(&image->exception);
319 /*
320 Allocate memory for pixel indexes.
321 */
322 pixels=(unsigned short *) AcquireQuantumMemory((size_t) image->colors,
323 sizeof(*pixels));
324 if (pixels == (unsigned short *) NULL)
325 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
326 image->filename);
327 /*
328 Assign index values to colormap entries.
329 */
330 for (i=0; i < (ssize_t) image->colors; i++)
331 image->colormap[i].opacity=(IndexPacket) i;
332 /*
333 Sort image colormap by decreasing color popularity.
334 */
335 qsort((void *) image->colormap,(size_t) image->colors,
336 sizeof(*image->colormap),IntensityCompare);
337 /*
338 Update image colormap indexes to sorted colormap order.
339 */
340 for (i=0; i < (ssize_t) image->colors; i++)
341 pixels[(ssize_t) image->colormap[i].opacity]=(unsigned short) i;
342 status=MagickTrue;
343 image_view=AcquireAuthenticCacheView(image,exception);
344 for (y=0; y < (ssize_t) image->rows; y++)
345 {
346 IndexPacket
347 index;
348
349 ssize_t
350 x;
351
352 IndexPacket
353 *magick_restrict indexes;
354
355 PixelPacket
356 *magick_restrict q;
357
358 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
359 if (q == (PixelPacket *) NULL)
360 {
361 status=MagickFalse;
362 continue;
363 }
364 indexes=GetCacheViewAuthenticIndexQueue(image_view);
365 for (x=0; x < (ssize_t) image->columns; x++)
366 {
367 i=ConstrainColormapIndex(image,GetPixelIndex(indexes+x));
368 index=(IndexPacket) pixels[i];
369 SetPixelIndex(indexes+x,index);
370 SetPixelRGBO(q,image->colormap+(ssize_t) index);
371 q++;
372 }
373 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
374 status=MagickFalse;
375 if (status == MagickFalse)
376 break;
377 }
378 image_view=DestroyCacheView(image_view);
379 pixels=(unsigned short *) RelinquishMagickMemory(pixels);
380 return(status);
381 }
382