/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% 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-2019 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 "MagickCore/studio.h"
|
#include "MagickCore/attribute.h"
|
#include "MagickCore/blob.h"
|
#include "MagickCore/cache-view.h"
|
#include "MagickCore/cache.h"
|
#include "MagickCore/color.h"
|
#include "MagickCore/color-private.h"
|
#include "MagickCore/colormap.h"
|
#include "MagickCore/client.h"
|
#include "MagickCore/configure.h"
|
#include "MagickCore/exception.h"
|
#include "MagickCore/exception-private.h"
|
#include "MagickCore/gem.h"
|
#include "MagickCore/geometry.h"
|
#include "MagickCore/image-private.h"
|
#include "MagickCore/memory_.h"
|
#include "MagickCore/monitor.h"
|
#include "MagickCore/monitor-private.h"
|
#include "MagickCore/option.h"
|
#include "MagickCore/pixel-accessor.h"
|
#include "MagickCore/quantize.h"
|
#include "MagickCore/quantum.h"
|
#include "MagickCore/resource_.h"
|
#include "MagickCore/semaphore.h"
|
#include "MagickCore/string_.h"
|
#include "MagickCore/thread-private.h"
|
#include "MagickCore/token.h"
|
#include "MagickCore/utility.h"
|
#include "MagickCore/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,
|
% ExceptionInfo *exception)
|
%
|
% A description of each parameter follows:
|
%
|
% o image: the image.
|
%
|
% o colors: the number of colors in the image colormap.
|
%
|
% o exception: return any errors or warnings in this structure.
|
%
|
*/
|
MagickExport MagickBooleanType AcquireImageColormap(Image *image,
|
const size_t colors,ExceptionInfo *exception)
|
{
|
register 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);
|
image->colors=MagickMax(colors,1);
|
if (image->colormap == (PixelInfo *) NULL)
|
image->colormap=(PixelInfo *) AcquireQuantumMemory(image->colors+1,
|
sizeof(*image->colormap));
|
else
|
image->colormap=(PixelInfo *) ResizeQuantumMemory(image->colormap,
|
image->colors+1,sizeof(*image->colormap));
|
if (image->colormap == (PixelInfo *) NULL)
|
{
|
image->colors=0;
|
image->storage_class=DirectClass;
|
ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
|
image->filename);
|
}
|
for (i=0; i < (ssize_t) image->colors; i++)
|
{
|
double
|
pixel;
|
|
GetPixelInfo(image,image->colormap+i);
|
pixel=(double) (i*(QuantumRange/MagickMax(colors-1,1)));
|
image->colormap[i].red=pixel;
|
image->colormap[i].green=pixel;
|
image->colormap[i].blue=pixel;
|
image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
|
image->colormap[i].alpha_trait=BlendPixelTrait;
|
}
|
return(SetImageStorageClass(image,PseudoClass,exception));
|
}
|
|
/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% 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.
|
%
|
% WARNING: this assumes an images colormap is in a well know and defined
|
% order. Currently Imagemagick has no way of setting that order.
|
%
|
% The format of the CycleColormapImage method is:
|
%
|
% MagickBooleanType CycleColormapImage(Image *image,const ssize_t displace,
|
% ExceptionInfo *exception)
|
%
|
% A description of each parameter follows:
|
%
|
% o image: the image.
|
%
|
% o displace: displace the colormap this amount.
|
%
|
% o exception: return any errors or warnings in this structure.
|
%
|
*/
|
MagickExport MagickBooleanType CycleColormapImage(Image *image,
|
const ssize_t displace,ExceptionInfo *exception)
|
{
|
CacheView
|
*image_view;
|
|
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,exception);
|
status=MagickTrue;
|
image_view=AcquireAuthenticCacheView(image,exception);
|
#if defined(MAGICKCORE_OPENMP_SUPPORT)
|
#pragma omp parallel for schedule(static) \
|
magick_number_threads(image,image,image->rows,1)
|
#endif
|
for (y=0; y < (ssize_t) image->rows; y++)
|
{
|
register ssize_t
|
x;
|
|
register Quantum
|
*magick_restrict q;
|
|
ssize_t
|
index;
|
|
if (status == MagickFalse)
|
continue;
|
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
|
if (q == (Quantum *) NULL)
|
{
|
status=MagickFalse;
|
continue;
|
}
|
for (x=0; x < (ssize_t) image->columns; x++)
|
{
|
index=(ssize_t) (GetPixelIndex(image,q)+displace) % image->colors;
|
if (index < 0)
|
index+=(ssize_t) image->colors;
|
SetPixelIndex(image,(Quantum) index,q);
|
SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
|
q+=GetPixelChannels(image);
|
}
|
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,
|
% ExceptionInfo *exception)
|
%
|
% A description of each parameter follows:
|
%
|
% o image: A pointer to an Image structure.
|
%
|
% o exception: return any errors or warnings in this structure.
|
%
|
*/
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
extern "C" {
|
#endif
|
|
static int IntensityCompare(const void *x,const void *y)
|
{
|
const PixelInfo
|
*color_1,
|
*color_2;
|
|
int
|
intensity;
|
|
color_1=(const PixelInfo *) x;
|
color_2=(const PixelInfo *) y;
|
intensity=(int) GetPixelInfoIntensity((const Image *) NULL,color_2)-(int)
|
GetPixelInfoIntensity((const Image *) NULL,color_1);
|
return(intensity);
|
}
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
}
|
#endif
|
|
MagickExport MagickBooleanType SortColormapByIntensity(Image *image,
|
ExceptionInfo *exception)
|
{
|
CacheView
|
*image_view;
|
|
MagickBooleanType
|
status;
|
|
register 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);
|
/*
|
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].alpha=(double) 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].alpha]=(unsigned short) i;
|
status=MagickTrue;
|
image_view=AcquireAuthenticCacheView(image,exception);
|
for (y=0; y < (ssize_t) image->rows; y++)
|
{
|
Quantum
|
index;
|
|
register ssize_t
|
x;
|
|
register Quantum
|
*magick_restrict q;
|
|
q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
|
if (q == (Quantum *) NULL)
|
{
|
status=MagickFalse;
|
break;
|
}
|
for (x=0; x < (ssize_t) image->columns; x++)
|
{
|
index=(Quantum) pixels[(ssize_t) GetPixelIndex(image,q)];
|
SetPixelIndex(image,index,q);
|
SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
|
q+=GetPixelChannels(image);
|
}
|
if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
|
status=MagickFalse;
|
if (status == MagickFalse)
|
break;
|
}
|
image_view=DestroyCacheView(image_view);
|
pixels=(unsigned short *) RelinquishMagickMemory(pixels);
|
return(status);
|
}
|