382 lines
13 KiB
C
382 lines
13 KiB
C
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% CCCC OOO L OOO RRRR M M AAA PPPP %
|
||
% C O O L O O R R MM MM A A P P %
|
||
% C O O L O O RRRR M M M AAAAA PPPP %
|
||
% C O O L O O R R M M A A P %
|
||
% CCCC OOO LLLLL OOO R R M M A A P %
|
||
% %
|
||
% %
|
||
% MagickCore Colormap Methods %
|
||
% %
|
||
% Software Design %
|
||
% Cristy %
|
||
% July 1992 %
|
||
% %
|
||
% %
|
||
% Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
|
||
% dedicated to making software imaging solutions freely available. %
|
||
% %
|
||
% You may not use this file except in compliance with the License. You may %
|
||
% obtain a copy of the License at %
|
||
% %
|
||
% https://imagemagick.org/script/license.php %
|
||
% %
|
||
% Unless required by applicable law or agreed to in writing, software %
|
||
% distributed under the License is distributed on an "AS IS" BASIS, %
|
||
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
|
||
% See the License for the specific language governing permissions and %
|
||
% limitations under the License. %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% We use linked-lists because splay-trees do not currently support duplicate
|
||
% key / value pairs (.e.g X11 green compliance and SVG green compliance).
|
||
%
|
||
*/
|
||
|
||
/*
|
||
Include declarations.
|
||
*/
|
||
#include "magick/studio.h"
|
||
#include "magick/attribute.h"
|
||
#include "magick/blob.h"
|
||
#include "magick/cache-view.h"
|
||
#include "magick/cache.h"
|
||
#include "magick/color.h"
|
||
#include "magick/color-private.h"
|
||
#include "magick/colormap.h"
|
||
#include "magick/colormap-private.h"
|
||
#include "magick/client.h"
|
||
#include "magick/configure.h"
|
||
#include "magick/exception.h"
|
||
#include "magick/exception-private.h"
|
||
#include "magick/gem.h"
|
||
#include "magick/geometry.h"
|
||
#include "magick/image-private.h"
|
||
#include "magick/memory_.h"
|
||
#include "magick/monitor.h"
|
||
#include "magick/monitor-private.h"
|
||
#include "magick/option.h"
|
||
#include "magick/pixel-accessor.h"
|
||
#include "magick/pixel-private.h"
|
||
#include "magick/quantize.h"
|
||
#include "magick/quantum.h"
|
||
#include "magick/semaphore.h"
|
||
#include "magick/resource_.h"
|
||
#include "magick/string_.h"
|
||
#include "magick/thread-private.h"
|
||
#include "magick/token.h"
|
||
#include "magick/utility.h"
|
||
#include "magick/xml-tree.h"
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% A c q u i r e I m a g e C o l o r m a p %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% AcquireImageColormap() allocates an image colormap and initializes
|
||
% it to a linear gray colorspace. If the image already has a colormap,
|
||
% it is replaced. AcquireImageColormap() returns MagickTrue if successful,
|
||
% otherwise MagickFalse if there is not enough memory.
|
||
%
|
||
% The format of the AcquireImageColormap method is:
|
||
%
|
||
% MagickBooleanType AcquireImageColormap(Image *image,const size_t colors)
|
||
%
|
||
% A description of each parameter follows:
|
||
%
|
||
% o image: the image.
|
||
%
|
||
% o colors: the number of colors in the image colormap.
|
||
%
|
||
*/
|
||
MagickExport MagickBooleanType AcquireImageColormap(Image *image,
|
||
const size_t colors)
|
||
{
|
||
ssize_t
|
||
i;
|
||
|
||
/*
|
||
Allocate image colormap.
|
||
*/
|
||
assert(image != (Image *) NULL);
|
||
assert(image->signature == MagickCoreSignature);
|
||
if (image->debug != MagickFalse)
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||
if (colors > MaxColormapSize)
|
||
{
|
||
image->colors=0;
|
||
image->storage_class=DirectClass;
|
||
ThrowBinaryImageException(ResourceLimitError,"MemoryAllocationFailed",
|
||
image->filename);
|
||
}
|
||
image->colors=MagickMax(colors,1);
|
||
if (image->colormap == (PixelPacket *) NULL)
|
||
image->colormap=(PixelPacket *) AcquireQuantumMemory(image->colors+256,
|
||
sizeof(*image->colormap));
|
||
else
|
||
image->colormap=(PixelPacket *) ResizeQuantumMemory(image->colormap,
|
||
image->colors+256,sizeof(*image->colormap));
|
||
if (image->colormap == (PixelPacket *) NULL)
|
||
{
|
||
image->colors=0;
|
||
image->storage_class=DirectClass;
|
||
ThrowBinaryImageException(ResourceLimitError,"MemoryAllocationFailed",
|
||
image->filename);
|
||
}
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
{
|
||
size_t
|
||
pixel;
|
||
|
||
pixel=(size_t) (i*(QuantumRange/MagickMax(colors-1,1)));
|
||
image->colormap[i].red=(Quantum) pixel;
|
||
image->colormap[i].green=(Quantum) pixel;
|
||
image->colormap[i].blue=(Quantum) pixel;
|
||
image->colormap[i].opacity=OpaqueOpacity;
|
||
}
|
||
return(SetImageStorageClass(image,PseudoClass));
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
% C y c l e C o l o r m a p I m a g e %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% CycleColormap() displaces an image's colormap by a given number of
|
||
% positions. If you cycle the colormap a number of times you can produce
|
||
% a psychodelic effect.
|
||
%
|
||
% The format of the CycleColormapImage method is:
|
||
%
|
||
% MagickBooleanType CycleColormapImage(Image *image,const ssize_t displace)
|
||
%
|
||
% A description of each parameter follows:
|
||
%
|
||
% o image: the image.
|
||
%
|
||
% o displace: displace the colormap this amount.
|
||
%
|
||
*/
|
||
MagickExport MagickBooleanType CycleColormapImage(Image *image,
|
||
const ssize_t displace)
|
||
{
|
||
CacheView
|
||
*image_view;
|
||
|
||
ExceptionInfo
|
||
*exception;
|
||
|
||
MagickBooleanType
|
||
status;
|
||
|
||
ssize_t
|
||
y;
|
||
|
||
assert(image != (Image *) NULL);
|
||
assert(image->signature == MagickCoreSignature);
|
||
if (image->debug != MagickFalse)
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
|
||
if (image->storage_class == DirectClass)
|
||
(void) SetImageType(image,PaletteType);
|
||
status=MagickTrue;
|
||
exception=(&image->exception);
|
||
image_view=AcquireAuthenticCacheView(image,exception);
|
||
#if defined(MAGICKCORE_OPENMP_SUPPORT)
|
||
#pragma omp parallel for schedule(static) shared(status) \
|
||
magick_number_threads(image,image,image->rows,1)
|
||
#endif
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
IndexPacket
|
||
*magick_restrict indexes;
|
||
|
||
ssize_t
|
||
x;
|
||
|
||
PixelPacket
|
||
*magick_restrict q;
|
||
|
||
ssize_t
|
||
index;
|
||
|
||
if (status == MagickFalse)
|
||
continue;
|
||
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
|
||
if (q == (PixelPacket *) NULL)
|
||
{
|
||
status=MagickFalse;
|
||
continue;
|
||
}
|
||
indexes=GetCacheViewAuthenticIndexQueue(image_view);
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
index=(ssize_t) (GetPixelIndex(indexes+x)+displace) %
|
||
image->colors;
|
||
if (index < 0)
|
||
index+=(ssize_t) image->colors;
|
||
SetPixelIndex(indexes+x,index);
|
||
SetPixelRGBO(q,image->colormap+(ssize_t) index);
|
||
q++;
|
||
}
|
||
if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
|
||
status=MagickFalse;
|
||
}
|
||
image_view=DestroyCacheView(image_view);
|
||
return(status);
|
||
}
|
||
|
||
/*
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
% %
|
||
% %
|
||
% %
|
||
+ S o r t C o l o r m a p B y I n t e n s i t y %
|
||
% %
|
||
% %
|
||
% %
|
||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||
%
|
||
% SortColormapByIntensity() sorts the colormap of a PseudoClass image by
|
||
% decreasing color intensity.
|
||
%
|
||
% The format of the SortColormapByIntensity method is:
|
||
%
|
||
% MagickBooleanType SortColormapByIntensity(Image *image)
|
||
%
|
||
% A description of each parameter follows:
|
||
%
|
||
% o image: A pointer to an Image structure.
|
||
%
|
||
*/
|
||
|
||
#if defined(__cplusplus) || defined(c_plusplus)
|
||
extern "C" {
|
||
#endif
|
||
|
||
static int IntensityCompare(const void *x,const void *y)
|
||
{
|
||
const PixelPacket
|
||
*color_1,
|
||
*color_2;
|
||
|
||
int
|
||
intensity;
|
||
|
||
color_1=(const PixelPacket *) x;
|
||
color_2=(const PixelPacket *) y;
|
||
intensity=PixelPacketIntensity(color_2)-(int) PixelPacketIntensity(color_1);
|
||
return(intensity);
|
||
}
|
||
|
||
#if defined(__cplusplus) || defined(c_plusplus)
|
||
}
|
||
#endif
|
||
|
||
MagickExport MagickBooleanType SortColormapByIntensity(Image *image)
|
||
{
|
||
CacheView
|
||
*image_view;
|
||
|
||
ExceptionInfo
|
||
*exception;
|
||
|
||
MagickBooleanType
|
||
status;
|
||
|
||
ssize_t
|
||
i;
|
||
|
||
ssize_t
|
||
y;
|
||
|
||
unsigned short
|
||
*pixels;
|
||
|
||
assert(image != (Image *) NULL);
|
||
if (image->debug != MagickFalse)
|
||
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
|
||
assert(image->signature == MagickCoreSignature);
|
||
if (image->storage_class != PseudoClass)
|
||
return(MagickTrue);
|
||
exception=(&image->exception);
|
||
/*
|
||
Allocate memory for pixel indexes.
|
||
*/
|
||
pixels=(unsigned short *) AcquireQuantumMemory((size_t) image->colors,
|
||
sizeof(*pixels));
|
||
if (pixels == (unsigned short *) NULL)
|
||
ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
|
||
image->filename);
|
||
/*
|
||
Assign index values to colormap entries.
|
||
*/
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
image->colormap[i].opacity=(IndexPacket) i;
|
||
/*
|
||
Sort image colormap by decreasing color popularity.
|
||
*/
|
||
qsort((void *) image->colormap,(size_t) image->colors,
|
||
sizeof(*image->colormap),IntensityCompare);
|
||
/*
|
||
Update image colormap indexes to sorted colormap order.
|
||
*/
|
||
for (i=0; i < (ssize_t) image->colors; i++)
|
||
pixels[(ssize_t) image->colormap[i].opacity]=(unsigned short) i;
|
||
status=MagickTrue;
|
||
image_view=AcquireAuthenticCacheView(image,exception);
|
||
for (y=0; y < (ssize_t) image->rows; y++)
|
||
{
|
||
IndexPacket
|
||
index;
|
||
|
||
ssize_t
|
||
x;
|
||
|
||
IndexPacket
|
||
*magick_restrict indexes;
|
||
|
||
PixelPacket
|
||
*magick_restrict q;
|
||
|
||
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
|
||
if (q == (PixelPacket *) NULL)
|
||
{
|
||
status=MagickFalse;
|
||
continue;
|
||
}
|
||
indexes=GetCacheViewAuthenticIndexQueue(image_view);
|
||
for (x=0; x < (ssize_t) image->columns; x++)
|
||
{
|
||
i=ConstrainColormapIndex(image,GetPixelIndex(indexes+x));
|
||
index=(IndexPacket) pixels[i];
|
||
SetPixelIndex(indexes+x,index);
|
||
SetPixelRGBO(q,image->colormap+(ssize_t) index);
|
||
q++;
|
||
}
|
||
if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
|
||
status=MagickFalse;
|
||
if (status == MagickFalse)
|
||
break;
|
||
}
|
||
image_view=DestroyCacheView(image_view);
|
||
pixels=(unsigned short *) RelinquishMagickMemory(pixels);
|
||
return(status);
|
||
}
|