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