diff --git a/magick/ImageMagick.h b/magick/ImageMagick.h
new file mode 100644
index 0000000..cc54f18
--- /dev/null
+++ b/magick/ImageMagick.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ Deprecated as of ImageMagick 6.2.3.
+
+ MagickCore Application Programming Interface declarations.
+*/
+
+#ifndef _MAGICKCORE_IMAGEMAGICK_DEPRECATED_H
+#define _MAGICKCORE_IMAGEMAGICK_DEPRECATED_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/MagickCore.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/ImageMagick.ico b/magick/ImageMagick.ico
new file mode 100644
index 0000000..09def8c
--- /dev/null
+++ b/magick/ImageMagick.ico
Binary files differ
diff --git a/magick/ImageMagick.pc b/magick/ImageMagick.pc
new file mode 100644
index 0000000..72ce5d5
--- /dev/null
+++ b/magick/ImageMagick.pc
@@ -0,0 +1,10 @@
+prefix=/usr/local
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/ImageMagick
+
+Name: ImageMagick
+Description: ImageMagick - Convert, Edit, and Compose Images
+Version: 6.5.5
+Libs: -L${libdir} -lMagickCore
+Cflags: -I${includedir} -fopenmp
diff --git a/magick/ImageMagick.pc.in b/magick/ImageMagick.pc.in
new file mode 100644
index 0000000..f584ef5
--- /dev/null
+++ b/magick/ImageMagick.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/ImageMagick
+
+Name: ImageMagick
+Description: ImageMagick - Convert, Edit, and Compose Images
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lMagickCore
+Cflags: -I${includedir} @MAGICK_PCFLAGS@
diff --git a/magick/ImageMagick.rc b/magick/ImageMagick.rc
new file mode 100644
index 0000000..b1cfaf3
--- /dev/null
+++ b/magick/ImageMagick.rc
@@ -0,0 +1,71 @@
+#include "winver.h"
+#define __WINDOWS__
+#include "magick-config.h"
+#include "version.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+/////////////////////////////////////////////////////////////////////////////
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MagickLibVersionNumber
+ PRODUCTVERSION MagickLibVersionNumber
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "ProductName", "ImageMagick\0"
+ VALUE "FileDescription", "ImageMagick Studio library and utility programs\0"
+ VALUE "OriginalFilename", "ImageMagick\0"
+ VALUE "InternalName", "ImageMagick\0"
+ VALUE "FileVersion", MagickLibVersionText "\0"
+ VALUE "ProductVersion", MagickLibVersionText "\0"
+ VALUE "CompanyName", "ImageMagick Studio\0"
+ VALUE "LegalCopyright", MagickCopyright "\0"
+ VALUE "Comments", MagickVersion "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// IMAGEMAGICK
+//
+/////////////////////////////////////////////////////////////////////////////
+
+CODER.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\coder.xml"
+COLORS.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\colors.xml"
+CONFIGURE.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\configure.xml"
+DELEGATES.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\delegates.xml"
+ENGLISH.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\english.xml"
+LOCALE.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\locale.xml"
+LOG.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\log.xml"
+MAGIC.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\magic.xml"
+THRESHOLDS.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\thresholds.xml"
+TYPE.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\type.xml"
+TYPE-GHOSTSCRIPT.XML IMAGEMAGICK DISCARDABLE "..\\VisualMagick\\bin\\type-ghostscript.xml"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+/////////////////////////////////////////////////////////////////////////////
+
+IDR_MAGICKICON ICON DISCARDABLE "ImageMagick.ico"
diff --git a/magick/Magick-config b/magick/Magick-config
new file mode 100755
index 0000000..9b859e0
--- /dev/null
+++ b/magick/Magick-config
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Configure options script for re-calling MagickCore compilation options
+# required to use the MagickCore library.
+#
+
+prefix=/usr/local
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/ImageMagick
+
+usage="\
+Usage: Magick-config [--cflags] [--cppflags] [--exec-prefix] [--ldflags] [--libs] [--prefix] [--version]"
+
+if test $# -eq 0; then
+ echo "${usage}" 1>&2
+ echo "Example: gcc \`Magick-config --cflags --cppflags\` -o core core.c \`Magick-config --ldflags --libs\`" 1>&2
+ exit 1
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+ case $1 in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+ --prefix)
+ echo $prefix
+ ;;
+ --exec-prefix=*)
+ exec_prefix=$optarg
+ ;;
+ --exec-prefix)
+ echo $exec_prefix
+ ;;
+ --version)
+ echo '6.5.5 Q16 '
+ ;;
+ --cflags)
+ echo "-I${includedir} -fopenmp"
+ ;;
+ --cxxflags)
+ echo '-g -O2 -Wall -W -pthread'
+ ;;
+ --cppflags)
+ echo '-I/usr/local/include/ImageMagick'
+ ;;
+ --ldflags)
+ echo '-L/usr/local/lib -lfreetype'
+ ;;
+ --libs)
+ echo "-L${libdir} -lMagickCore -llcms -ltiff -lfreetype -ljpeg -lfftw3 -lfontconfig -lXext -lSM -lICE -lX11 -lXt -lbz2 -lz -lm -lgomp -lpthread -lltdl"
+ ;;
+ *)
+ echo "${usage}" 1>&2
+ exit 1
+ ;;
+ esac
+ shift
+done
diff --git a/magick/Magick-config.1 b/magick/Magick-config.1
new file mode 100644
index 0000000..5c2bc2a
--- /dev/null
+++ b/magick/Magick-config.1
@@ -0,0 +1,69 @@
+.ad l
+.nh
+.TH Magick-Config 1 "2 May 2002" "ImageMagick"
+.SH NAME
+Magick-config \- get information about the installed version of ImageMagick
+.SH SYNOPSIS
+.B Magick-config
+.B [--cflags]
+.B [--cppflags]
+.B [--exec-prefix]
+.B [--ldflags]
+.B [--libs]
+.B [--prefix]
+.B [--version]
+.SH DESCRIPTION
+.B Magick-config
+prints the compiler and linker flags required to compile and link programs
+that use the
+.BR ImageMagick
+Application Programmer Interface.
+.SH EXAMPLES
+To print the version of the installed distribution of
+.BR ImageMagick ,
+use:
+
+.nf
+ Magick-config \-\-version
+.fi
+
+To compile a program that calls the
+.BR ImageMagick
+Application Programmer Interface, use:
+
+.nf
+ cc `Magick-config \-\-cflags \-\-cppflags \-\-ldflags \-\-libs` program.c
+.fi
+
+.SH OPTIONS
+.TP
+.B \-\-cflags
+Print the compiler flags that were used to compile
+.BR libMagick .
+.TP
+.B \-\-cppflags
+Print the preprocessor flags that are needed to find the
+.B ImageMagick
+C include files and defines to ensure that the ImageMagick data structures match between
+your program and the installed libraries.
+.TP
+.B \-\-exec-prefix
+Print the directory under which target specific binaries and executables are installed.
+.TP
+.B \-\-ldflags
+Print the linker flags that are needed to link with the
+.B ImageMagick
+library.
+.TP
+.B \-\-libs
+Print the linker flags that are needed to link a program with
+.BR libMagick .
+.TP
+.B \-\-version
+Print the version of the
+.B ImageMagick
+distribution to standard output.
+.SH LICENSE
+See http://www.imagemagick.org/script/license.php.
+.SH AUTHORS
+John Cristy, ImageMagick Studio LLC
diff --git a/magick/Magick-config.in b/magick/Magick-config.in
new file mode 100644
index 0000000..8c08bf0
--- /dev/null
+++ b/magick/Magick-config.in
@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# Configure options script for re-calling MagickCore compilation options
+# required to use the MagickCore library.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/ImageMagick
+
+usage="\
+Usage: Magick-config [--cflags] [--cppflags] [--exec-prefix] [--ldflags] [--libs] [--prefix] [--version]"
+
+if test $# -eq 0; then
+ echo "${usage}" 1>&2
+ echo "Example: gcc \`Magick-config --cflags --cppflags\` -o core core.c \`Magick-config --ldflags --libs\`" 1>&2
+ exit 1
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+ case $1 in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+ --prefix)
+ echo $prefix
+ ;;
+ --exec-prefix=*)
+ exec_prefix=$optarg
+ ;;
+ --exec-prefix)
+ echo $exec_prefix
+ ;;
+ --version)
+ echo '@PACKAGE_VERSION@ Q@QUANTUM_DEPTH@ @MAGICK_HDRI@'
+ ;;
+ --cflags)
+ echo "-I${includedir} @MAGICK_PCFLAGS@"
+ ;;
+ --cxxflags)
+ echo '@MAGICK_CXXFLAGS@'
+ ;;
+ --cppflags)
+ echo '@MAGICK_CPPFLAGS@'
+ ;;
+ --ldflags)
+ echo '@MAGICK_LDFLAGS@'
+ ;;
+ --libs)
+ echo "-L${libdir} @MAGICK_LIBS@"
+ ;;
+ *)
+ echo "${usage}" 1>&2
+ exit 1
+ ;;
+ esac
+ shift
+done
diff --git a/magick/MagickCore-config.1 b/magick/MagickCore-config.1
new file mode 100644
index 0000000..f58e957
--- /dev/null
+++ b/magick/MagickCore-config.1
@@ -0,0 +1,69 @@
+.ad l
+.nh
+.TH MagickCore-Config 1 "2 May 2002" "ImageMagick"
+.SH NAME
+MagickCore-config \- get information about the installed version of ImageMagick
+.SH SYNOPSIS
+.B MagickCore-config
+.B [--cflags]
+.B [--cppflags]
+.B [--exec-prefix]
+.B [--ldflags]
+.B [--libs]
+.B [--prefix]
+.B [--version]
+.SH DESCRIPTION
+.B MagickCore-config
+prints the compiler and linker flags required to compile and link programs
+that use the
+.BR ImageMagick
+Application Programmer Interface.
+.SH EXAMPLES
+To print the version of the installed distribution of
+.BR ImageMagick ,
+use:
+
+.nf
+ MagickCore-config \-\-version
+.fi
+
+To compile a program that calls the
+.BR ImageMagick
+Application Programmer Interface, use:
+
+.nf
+ cc `MagickCore-config \-\-cflags \-\-cppflags \-\-ldflags \-\-libs` program.c
+.fi
+
+.SH OPTIONS
+.TP
+.B \-\-cflags
+Print the compiler flags that were used to compile
+.BR libMagick .
+.TP
+.B \-\-cppflags
+Print the preprocessor flags that are needed to find the
+.B ImageMagick
+C include files and defines to ensure that the ImageMagick data structures match between
+your program and the installed libraries.
+.TP
+.B \-\-exec-prefix
+Print the directory under which target specific binaries and executables are installed.
+.TP
+.B \-\-ldflags
+Print the linker flags that are needed to link with the
+.B ImageMagick
+library.
+.TP
+.B \-\-libs
+Print the linker flags that are needed to link a program with
+.BR libMagick .
+.TP
+.B \-\-version
+Print the version of the
+.B ImageMagick
+distribution to standard output.
+.SH LICENSE
+See http://www.imagemagick.org/script/license.php.
+.SH AUTHORS
+John Cristy, ImageMagick Studio LLC
diff --git a/magick/MagickCore-config.in b/magick/MagickCore-config.in
new file mode 100644
index 0000000..6020f8b
--- /dev/null
+++ b/magick/MagickCore-config.in
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# Configure options script for re-calling MagickCore compilation options
+# required to use the MagickCore library.
+#
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/ImageMagick
+
+usage="\
+Usage: MagickCore-config [--cflags] [--cppflags] [--exec-prefix] [--ldflags] [--libs] [--prefix] [--version]"
+
+if test $# -eq 0; then
+ echo "${usage}" 1>&2
+ echo "Example: gcc \`MagickCore-config --cflags --cppflags\` -o core core.c \`Magick-config --ldflags --libs\`" 1>&2
+ exit 1
+fi
+
+while test $# -gt 0; do
+ case "$1" in
+ -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) optarg= ;;
+ esac
+ case $1 in
+ --prefix=*)
+ prefix=$optarg
+ ;;
+ --prefix)
+ echo $prefix
+ ;;
+ --exec-prefix=*)
+ exec_prefix=$optarg
+ ;;
+ --exec-prefix)
+ echo $exec_prefix
+ ;;
+ --version)
+ echo '@PACKAGE_VERSION@ Q@QUANTUM_DEPTH@ @MAGICK_HDRI@'
+ ;;
+ --cflags)
+ echo "-I${includedir} @MAGICK_PCFLAGS@"
+ ;;
+ --cxxflags)
+ echo '@MAGICK_CXXFLAGS@'
+ ;;
+ --cppflags)
+ echo '@MAGICK_CPPFLAGS@'
+ ;;
+ --ldflags)
+ echo '@MAGICK_LDFLAGS@'
+ ;;
+ --libs)
+ echo "-L${libdir} @MAGICK_LIBS@"
+ ;;
+ --coder-path)
+ echo "@CODER_PATH@"
+ ;;
+ --filter-path)
+ echo "@FILTER_PATH@"
+ ;;
+ *)
+ echo "${usage}" 1>&2
+ exit 1
+ ;;
+ esac
+ shift
+done
diff --git a/magick/MagickCore.h b/magick/MagickCore.h
new file mode 100644
index 0000000..618db2f
--- /dev/null
+++ b/magick/MagickCore.h
@@ -0,0 +1,229 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore Application Programming Interface declarations.
+*/
+
+#ifndef _MAGICKCORE_CORE_H
+#define _MAGICKCORE_CORE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if !defined(_MAGICKCORE_CONFIG_H)
+# define _MAGICKCORE_CONFIG_H
+# if !defined(vms) && !defined(macintosh)
+# include "magick/magick-config.h"
+# else
+# include "magick-config.h"
+# endif
+#if defined(_magickcore_const) && !defined(const)
+# define const _magickcore_const
+#endif
+#if defined(_magickcore_inline) && !defined(inline)
+# define inline _magickcore_inline
+#endif
+# if defined(__cplusplus) || defined(c_plusplus)
+# undef inline
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#if defined(__CYGWIN32__)
+# if !defined(__CYGWIN__)
+# define __CYGWIN__ __CYGWIN32__
+# endif
+#endif
+
+#if defined(_WIN32) || defined(WIN32)
+# if !defined(__WINDOWS__)
+# if defined(_WIN32)
+# define __WINDOWS__ _WIN32
+# else
+# if defined(WIN32)
+# define __WINDOWS__ WIN32
+# endif
+# endif
+# endif
+#endif
+
+#if defined(_WIN64) || defined(WIN64)
+# if !defined(__WINDOWS__)
+# if defined(_WIN64)
+# define __WINDOWS__ _WIN64
+# else
+# if !defined(WIN64)
+# define __WINDOWS__ WIN64
+# endif
+# endif
+# endif
+#endif
+
+#if defined(__WINDOWS__)
+# if defined(_MT) && defined(_DLL) && !defined(_MAGICKDLL_) && !defined(_LIB) && !defined(MAGICK_STATIC_LINK)
+# define _MAGICKDLL_
+# endif
+# if defined(_MAGICKDLL_)
+# if defined(_VISUALC_)
+# pragma warning( disable: 4273 ) /* Disable the dll linkage warnings */
+# endif
+# if !defined(_MAGICKLIB_)
+# define MagickExport __declspec(dllimport)
+# if defined(_VISUALC_)
+# pragma message( "Magick lib DLL import interface" )
+# endif
+# else
+# define MagickExport __declspec(dllexport)
+# if defined(_VISUALC_)
+# pragma message( "Magick lib DLL export interface" )
+# endif
+# endif
+# else
+# define MagickExport
+# if defined(_VISUALC_)
+# pragma message( "Magick lib static interface" )
+# endif
+# endif
+
+# if defined(_DLL) && !defined(_LIB)
+# define ModuleExport __declspec(dllexport)
+# if defined(_VISUALC_)
+# pragma message( "Magick module DLL export interface" )
+# endif
+# else
+# define ModuleExport
+# if defined(_VISUALC_)
+# pragma message( "Magick module static interface" )
+# endif
+
+# endif
+# define MagickGlobal __declspec(thread)
+# if defined(_VISUALC_)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4142)
+# pragma warning(disable : 4800)
+# pragma warning(disable : 4786)
+# pragma warning(disable : 4996)
+# endif
+#else
+# define MagickExport
+# define ModuleExport
+# define MagickGlobal
+#endif
+
+#if !defined(MaxTextExtent)
+# define MaxTextExtent 4096
+#endif
+#define MagickSignature 0xabacadabUL
+
+#if !defined(magick_attribute)
+# if !defined(__GNUC__)
+# define magick_attribute(x) /* nothing */
+# else
+# define magick_attribute __attribute__
+# endif
+#endif
+
+#if defined(MAGICKCORE_NAMESPACE_PREFIX)
+# include "magick/methods.h"
+#endif
+#include "magick/magick-type.h"
+#include "magick/animate.h"
+#include "magick/annotate.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/cipher.h"
+#include "magick/client.h"
+#include "magick/coder.h"
+#include "magick/color.h"
+#include "magick/colorspace.h"
+#include "magick/compare.h"
+#include "magick/composite.h"
+#include "magick/compress.h"
+#include "magick/configure.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/delegate.h"
+#include "magick/deprecate.h"
+#include "magick/display.h"
+#include "magick/distort.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/fourier.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/hashmap.h"
+#include "magick/histogram.h"
+#include "magick/identify.h"
+#include "magick/image.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/matrix.h"
+#include "magick/memory_.h"
+#include "magick/module.h"
+#include "magick/mime.h"
+#include "magick/monitor.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/paint.h"
+#include "magick/pixel.h"
+#include "magick/policy.h"
+#include "magick/prepress.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/registry.h"
+#include "magick/random_.h"
+#include "magick/resample.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/segment.h"
+#include "magick/shear.h"
+#include "magick/signature.h"
+#include "magick/splay-tree.h"
+#include "magick/stream.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/timer.h"
+#include "magick/token.h"
+#include "magick/transform.h"
+#include "magick/threshold.h"
+#include "magick/type.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#include "magick/xml-tree.h"
+#include "magick/xwindow.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/MagickCore.pc.in b/magick/MagickCore.pc.in
new file mode 100644
index 0000000..6b062d7
--- /dev/null
+++ b/magick/MagickCore.pc.in
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/ImageMagick
+
+Name: MagickCore
+Description: MagickCore - C API for ImageMagick
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lMagickCore
+Cflags: -I${includedir} @MAGICK_PCFLAGS@
diff --git a/magick/Make.com b/magick/Make.com
new file mode 100644
index 0000000..c13c1e4
--- /dev/null
+++ b/magick/Make.com
@@ -0,0 +1,173 @@
+$!
+$! Make ImageMagick image utilities for VMS.
+$!
+$ define/nolog MAGICK [-.magick]
+$ define/nolog WAND [-.wand]
+$ copy config.h_vms magick-config.h
+$ copy xwdfile.h_vms xwdfile.h
+$
+$if (f$trnlnm("X11") .eqs. "") then define/nolog X11 decw$include:
+$compile_options="/nodebug/optimize"
+$if (f$search("sys$system:decc$compiler.exe") .nes. "")
+$then ! VAX with DEC C
+$ compile_options="/decc/nodebug/optimize/warning=(disable=rightshiftovr)"
+$else ! VAX with VAX C
+$define/nolog lnk$library sys$library:vaxcrtl
+$define/nolog sys sys$share
+$endif
+$if (f$getsyi("HW_MODEL") .gt. 1023)
+$then ! Alpha with DEC C
+$ define/nolog sys decc$library_include
+$ compile_options="/nodebug/optimize/prefix=all/warning=(disable=rightshiftovr)/name=(as_is,short)/float=ieee
+$endif
+$
+$write sys$output "Making Magick..."
+$call Make animate.c
+$call Make annotate.c
+$call Make artifact.c
+$call Make blob.c
+$call Make cache.c
+$call Make cache-view.c
+$call Make cipher.c
+$call Make client.c
+$call Make coder.c
+$call Make color.c
+$call Make colorspace.c
+$call Make compare.c
+$call Make composite.c
+$call Make compress.c
+$call Make configure.c
+$call Make constitute.c
+$call Make decorate.c
+$call Make delegate.c
+$call Make deprecate.c
+$call Make display.c
+$call Make distort.c
+$call Make draw.c
+$call Make effect.c
+$call Make enhance.c
+$call Make exception.c
+$call Make fx.c
+$call Make gem.c
+$call Make geometry.c
+$call Make hashmap.c
+$call Make histogram.c
+$call Make identify.c
+$call Make image.c
+$call Make layer.c
+$call Make list.c
+$call Make locale.c
+$call Make log.c
+$call Make magic.c
+$call Make magick.c
+$call Make matrix.c
+$call Make memory.c
+$call Make mime.c
+$call Make module.c
+$call Make monitor.c
+$call Make montage.c
+$call Make option.c
+$call Make paint.c
+$call Make pixel.c
+$call Make prepress.c
+$call Make property.c
+$call Make PreRvIcccm.c
+$call Make profile.c
+$call Make quantize.c
+$call Make quantum.c
+$call Make quantum-export.c
+$call Make quantum-import.c
+$call Make random.c
+$call Make registry.c
+$call Make resample.c
+$call Make resize.c
+$call Make resource.c
+$call Make segment.c
+$call Make semaphore.c
+$call Make shear.c
+$call Make signature.c
+$call Make splay-tree.c
+$call Make static.c
+$call Make statistic.c
+$call Make stream.c
+$call Make string.c
+$call Make thread.c
+$call Make timer.c
+$call Make token.c
+$call Make transform.c
+$call Make threshold.c
+$call Make type.c
+$call Make utility.c
+$call Make version.c
+$call Make vms.c
+$call Make widget.c
+$call Make xml-tree.c
+$call Make xwindow.c
+$ set default [-.filters]
+$ call Make analyze.c
+$ set default [-.wand]
+$ call Make drawing-wand.c
+$ call Make pixel-wand.c
+$ call Make pixel-view.c
+$ call Make conjure.c
+$ call Make convert.c
+$ call Make import.c
+$ call Make mogrify.c
+$ copy animate.c animate-wand.c
+$ call make animate-wand.c
+$ copy compare.c compare-wand.c
+$ call make compare-wand.c
+$ copy composite.c composite-wand.c
+$ call make composite-wand.c
+$ copy display.c display-wand.c
+$ call make display-wand.c
+$ copy identify.c identify-wand.c
+$ call make identify-wand.c
+$ copy montage.c montage-wand.c
+$ call make montage-wand.c
+$ set default [-.magick]
+$ deass magick
+$ deass wand
+$library/create libMagick.olb -
+ animate, annotate, artifact, blob, cache, cache-view, cipher, client, coder, -
+ color, colorspace, compare, composite, compress, configure, -
+ constitute, decorate, delegate, deprecate, display, distort, draw, -
+ effect, enhance, exception, fx, gem, geometry, hashmap, histogram, identify, -
+ image, layer, list, locale, log, magic, magick, matrix, memory, mime, -
+ module, monitor, montage, option, paint, pixel, PreRvIcccm, profile, -
+ quantize, quantum,quantum-export,quantum-import,random, registry, resample, -
+ resize, resource, segment, semaphore, -
+ shear, signature, splay-tree, static, stream, string, thread, timer, token, -
+ transform, threshold, type, utility, version, vms, widget, xwindow, -
+ statistic, prepress, property, xml-tree, -
+ [-.filters]analyze,[-.wand]drawing-wand, pixel-wand, pixel-view, conjure, -
+ convert,import, mogrify, animate-wand, compare-wand, composite-wand, -
+ display-wand,identify-wand,montage-wand
+$exit
+$
+$Make: subroutine
+$!
+$! Primitive MMS hack for DCL.
+$!
+$if (p1 .eqs. "") then exit
+$source_file=f$search(f$parse(p1,".c"))
+$if (source_file .nes. "")
+$then
+$ object_file=f$parse(source_file,,,"name")+".obj"
+$ object_file=f$search( object_file )
+$ if (object_file .nes. "")
+$ then
+$ object_time=f$file_attribute(object_file,"cdt")
+$ source_time=f$file_attribute(source_file,"cdt")
+$ if (f$cvtime(object_time) .lts. f$cvtime(source_time)) then -
+$ object_file=""
+$ endif
+$ if (object_file .eqs. "")
+$ then
+$ write sys$output "Compiling ",p1
+$ cc'compile_options'/include_directory=([-],[-.magick],[-.jpeg],[-.png], -
+ [-.tiff],[-.ttf],[-.zlib]) 'source_file'
+$ endif
+$endif
+$exit
+$endsubroutine
diff --git a/magick/Makefile.am b/magick/Makefile.am
new file mode 100644
index 0000000..9bc4080
--- /dev/null
+++ b/magick/Makefile.am
@@ -0,0 +1,412 @@
+# Copyright 1999-2009 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
+#
+# http://www.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.
+#
+# Copyright (C) 2003 - 2008 GraphicsMagick Group
+#
+# Makefile for building the MagickCore API.
+#
+
+magickincdir = $(topincludedir)/magick
+
+# Headers which are installed
+magickinc_HEADERS = \
+ $(MAGICK_INCLUDE_HDRS)
+
+MAGICK_BIN_SCRPTS = \
+ magick/Magick-config \
+ magick/MagickCore-config
+
+MAGICK_PKGCONFIG = \
+ magick/ImageMagick.pc \
+ magick/MagickCore.pc
+
+OSX_GCOV_LDFLAG = @OSX_GCOV_LDFLAG@
+
+MAGICK_MANS = \
+ magick/Magick-config.1 \
+ magick/MagickCore-config.1
+
+MAGICKCORE_LIBS = magick/libMagickCore.la
+
+if WITH_MODULES
+magick_libMagickCore_la_SOURCES = $(MAGICK_BASE_SRCS) $(MAGICK_PLATFORM_SRCS)
+magick_libMagickCore_la_LIBADD = $(MAGICK_DEP_LIBS) $(MAGICK_LIBLTDL)
+else
+magick_libMagickCore_la_SOURCES = $(MAGICK_BASE_SRCS) $(MAGICK_PLATFORM_SRCS) $(MAGICK_CODER_SRCS) $(MAGICK_FILTER_SRCS)
+magick_libMagickCore_la_LIBADD = $(MAGICK_DEP_LIBS) $(MAGICK_LIBLTDL)
+endif # WITH_MODULES
+magick_libMagickCore_la_CPPFLAGS = -I$(top_builddir)/ltdl -I$(top_srcdir)/ltdl $(LIBRARY_EXTRA_CPPFLAGS)
+magick_libMagickCore_la_LDFLAGS = -no-undefined -export-symbols-regex ".*" \
+ $(OSX_GCOV_LDFLAG) -version-info \
+ $(MAGICK_LIBRARY_CURRENT):$(MAGICK_LIBRARY_REVISION):$(MAGICK_LIBRARY_AGE)
+magick_libMagickCore_la_DEPENDENCIES = $(MAGICK_LTDLDEPS)
+
+# Library base sources
+MAGICK_BASE_SRCS = \
+ magick/ImageMagick.h \
+ magick/MagickCore.h \
+ magick/animate.c \
+ magick/animate.h \
+ magick/animate-private.h \
+ magick/annotate.c \
+ magick/annotate.h \
+ magick/api.h \
+ magick/artifact.c \
+ magick/artifact.h \
+ magick/blob.c \
+ magick/blob.h \
+ magick/blob-private.h \
+ magick/cache.c \
+ magick/cache.h \
+ magick/cache-private.h \
+ magick/cache-view.c \
+ magick/cache-view.h \
+ magick/cipher.c \
+ magick/cipher.h \
+ magick/client.c \
+ magick/client.h \
+ magick/coder.c \
+ magick/coder.h \
+ magick/color.c \
+ magick/color.h \
+ magick/color-private.h \
+ magick/colorspace.c \
+ magick/colorspace.h \
+ magick/colorspace-private.h \
+ magick/compare.c \
+ magick/compare.h \
+ magick/composite.c \
+ magick/composite.h \
+ magick/composite-private.h \
+ magick/compress.c \
+ magick/compress.h \
+ magick/configure.c \
+ magick/configure.h \
+ magick/constitute.c \
+ magick/constitute.h \
+ magick/decorate.c \
+ magick/decorate.h \
+ magick/delegate.c \
+ magick/delegate.h \
+ magick/delegate-private.h \
+ magick/deprecate.c \
+ magick/deprecate.h \
+ magick/display.c \
+ magick/display.h \
+ magick/display-private.h \
+ magick/distort.c \
+ magick/distort.h \
+ magick/draw.c \
+ magick/draw.h \
+ magick/draw-private.h \
+ magick/effect.c \
+ magick/effect.h \
+ magick/enhance.c \
+ magick/enhance.h \
+ magick/exception.c \
+ magick/exception.h \
+ magick/exception-private.h \
+ magick/fourier.c \
+ magick/fourier.h \
+ magick/fx.c \
+ magick/fx.h \
+ magick/fx-private.h \
+ magick/gem.c \
+ magick/gem.h \
+ magick/geometry.c \
+ magick/geometry.h \
+ magick/hashmap.c \
+ magick/hashmap.h \
+ magick/histogram.c \
+ magick/histogram.h \
+ magick/identify.c \
+ magick/identify.h \
+ magick/image.c \
+ magick/image.h \
+ magick/image-private.h \
+ magick/layer.c \
+ magick/layer.h \
+ magick/list.c \
+ magick/list.h \
+ magick/locale.c \
+ magick/locale_.h \
+ magick/log.c \
+ magick/log.h \
+ magick/mac.h \
+ magick/magic.c \
+ magick/magic.h \
+ magick/magick.c \
+ magick/magick-config.h \
+ magick/magick-type.h \
+ magick/magick.h \
+ magick/matrix.c \
+ magick/matrix.h \
+ magick/memory.c \
+ magick/memory_.h \
+ magick/methods.h \
+ magick/mime.c \
+ magick/mime.h \
+ magick/module.c \
+ magick/module.h \
+ magick/monitor.c \
+ magick/monitor.h \
+ magick/monitor-private.h \
+ magick/montage.c \
+ magick/montage.h \
+ magick/nt-base.h \
+ magick/nt-feature.h \
+ magick/option.c \
+ magick/option.h \
+ magick/paint.c \
+ magick/paint.h \
+ magick/pixel.c \
+ magick/pixel.h \
+ magick/pixel-private.h \
+ magick/policy.c \
+ magick/policy.h \
+ magick/PreRvIcccm.c \
+ magick/PreRvIcccm.h \
+ magick/prepress.c \
+ magick/prepress.h \
+ magick/property.c \
+ magick/property.h \
+ magick/profile.c \
+ magick/profile.h \
+ magick/quantize.c \
+ magick/quantize.h \
+ magick/quantum.c \
+ magick/quantum.h \
+ magick/quantum-export.c \
+ magick/quantum-import.c \
+ magick/quantum-private.h \
+ magick/random.c \
+ magick/random_.h \
+ magick/random-private.h \
+ magick/registry.c \
+ magick/registry.h \
+ magick/resample.c \
+ magick/resample.h \
+ magick/resample-private.h \
+ magick/resize.c \
+ magick/resize.h \
+ magick/resize-private.h \
+ magick/resource.c \
+ magick/resource_.h \
+ magick/segment.c \
+ magick/segment.h \
+ magick/semaphore.c \
+ magick/semaphore.h \
+ magick/shear.c \
+ magick/shear.h \
+ magick/signature.c \
+ magick/signature.h \
+ magick/signature-private.h \
+ magick/splay-tree.c \
+ magick/splay-tree.h \
+ magick/static.c \
+ magick/static.h \
+ magick/statistic.c \
+ magick/statistic.h \
+ magick/stream.c \
+ magick/stream.h \
+ magick/stream-private.h \
+ magick/string.c \
+ magick/string_.h \
+ magick/studio.h \
+ magick/thread.c \
+ magick/thread_.h \
+ magick/thread-private.h \
+ magick/timer.c \
+ magick/timer.h \
+ magick/token.c \
+ magick/token.h \
+ magick/token-private.h \
+ magick/transform.c \
+ magick/transform.h \
+ magick/threshold.c \
+ magick/threshold.h \
+ magick/type.c \
+ magick/type.h \
+ magick/utility.c \
+ magick/utility.h \
+ magick/version.c \
+ magick/version.h \
+ magick/vms.h \
+ magick/widget.c \
+ magick/widget.h \
+ magick/xml-tree.c \
+ magick/xml-tree.h \
+ magick/xwindow.c \
+ magick/xwindow.h
+
+if WIN32_NATIVE_BUILD
+MAGICK_PLATFORM_SRCS = \
+ magick/nt-base.c \
+ magick/nt-base.h \
+ magick/nt-feature.c \
+ magick/nt-feature.h
+else
+if CYGWIN_BUILD
+MAGICK_PLATFORM_SRCS = \
+ magick/nt-feature.c \
+ magick/nt-feature.h
+else
+MAGICK_PLATFORM_SRCS =
+endif # if CYGWIN_BUILD
+endif # if WIN32_NATIVE_BUILD
+
+MAGICK_INCLUDE_HDRS = \
+ magick/ImageMagick.h \
+ magick/MagickCore.h \
+ magick/PreRvIcccm.h \
+ magick/animate.h \
+ magick/annotate.h \
+ magick/api.h \
+ magick/artifact.h \
+ magick/blob.h \
+ magick/cache.h \
+ magick/cache-view.h \
+ magick/cipher.h \
+ magick/client.h \
+ magick/coder.h \
+ magick/color.h \
+ magick/colorspace.h \
+ magick/compare.h \
+ magick/composite.h \
+ magick/compress.h \
+ magick/configure.h \
+ magick/constitute.h \
+ magick/decorate.h \
+ magick/delegate.h \
+ magick/deprecate.h \
+ magick/display.h \
+ magick/distort.h \
+ magick/draw.h \
+ magick/effect.h \
+ magick/enhance.h \
+ magick/exception.h \
+ magick/fourier.h \
+ magick/fx.h \
+ magick/gem.h \
+ magick/geometry.h \
+ magick/hashmap.h \
+ magick/histogram.h \
+ magick/identify.h \
+ magick/image.h \
+ magick/layer.h \
+ magick/list.h \
+ magick/locale_.h \
+ magick/log.h \
+ magick/magic.h \
+ magick/magick.h \
+ magick/magick-config.h \
+ magick/magick-type.h \
+ magick/matrix.h \
+ magick/memory_.h \
+ magick/methods.h \
+ magick/mime.h \
+ magick/module.h \
+ magick/monitor.h \
+ magick/montage.h \
+ magick/option.h \
+ magick/paint.h \
+ magick/pixel.h \
+ magick/policy.h \
+ magick/prepress.h \
+ magick/profile.h \
+ magick/property.h \
+ magick/quantize.h \
+ magick/quantum.h \
+ magick/random_.h \
+ magick/registry.h \
+ magick/resample.h \
+ magick/resize.h \
+ magick/resource_.h \
+ magick/segment.h \
+ magick/semaphore.h \
+ magick/shear.h \
+ magick/signature.h \
+ magick/splay-tree.h \
+ magick/statistic.h \
+ magick/stream.h \
+ magick/string_.h \
+ magick/timer.h \
+ magick/token.h \
+ magick/transform.h \
+ magick/threshold.h \
+ magick/type.h \
+ magick/utility.h \
+ magick/version.h \
+ magick/widget.h \
+ magick/xml-tree.h \
+ magick/xwindow.h
+
+MAGICK_NOINST_HDRS = \
+ magick/animate-private.h \
+ magick/blob-private.h \
+ magick/cache-private.h \
+ magick/color-private.h \
+ magick/colorspace-private.h \
+ magick/composite-private.h \
+ magick/delegate-private.h \
+ magick/display-private.h \
+ magick/draw-private.h \
+ magick/exception-private.h \
+ magick/fx-private.h \
+ magick/image-private.h \
+ magick/mac.h \
+ magick/mime-private.h \
+ magick/monitor-private.h \
+ magick/nt-base.h \
+ magick/nt-feature.h \
+ magick/pixel-private.h \
+ magick/quantum-private.h \
+ magick/random-private.h \
+ magick/resample-private.h \
+ magick/resize-private.h \
+ magick/signature-private.h \
+ magick/static.h \
+ magick/stream-private.h \
+ magick/studio.h \
+ magick/thread_.h \
+ magick/thread-private.h \
+ magick/token-private.h \
+ magick/xwindow-private.h \
+ magick/vms.h
+
+MAGICK_EXTRA_DIST = \
+ magick/Magick-config.in \
+ magick/MagickCore-config.in \
+ $(MAGICK_MANS) \
+ magick/ImageMagick.pc.in \
+ magick/MagickCore.pc.in \
+ magick/Make.com \
+ magick/config.h_vms \
+ magick/mac.c \
+ magick/nt-base.c \
+ magick/nt-feature.c \
+ magick/vms.c \
+ magick/xwdfile.h_vms
+
+# Install magick-config.h
+MAGICK_INSTALL_DATA_LOCAL_TARGETS = magick-install-data-local
+magick-install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(magickincdir)
+ $(INSTALL_HEADER) magick/magick-config.h $(DESTDIR)$(magickincdir)/magick-config.h
+
+# Uninstall magick-config.h
+MAGICK_UNINSTALL_LOCAL_TARGETS = magick-uninstall-local
+magick-uninstall-local:
+ rm -f $(DESTDIR)$(magickincdir)/magick-config.h
+
diff --git a/magick/PreRvIcccm.c b/magick/PreRvIcccm.c
new file mode 100644
index 0000000..7bce9cc
--- /dev/null
+++ b/magick/PreRvIcccm.c
@@ -0,0 +1,347 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% %
+% IIIII CCCC CCCC CCCC M M %
+% I C C C MM MM %
+% I C C C M M M %
+% I C C C M M %
+% IIIII CCCC CCCC CCCC M M %
+% %
+% MagickCore X11 Compatibility Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 1994 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+#include "magick/studio.h"
+#if defined(MAGICKCORE_X11_DELEGATE)
+#include "magick/xwindow-private.h"
+
+#if defined(PRE_R6_ICCCM)
+/*
+ Compatibility methods for pre X11R6 ICCCM.
+*/
+Status XInitImage(XImage *ximage)
+{
+ Display
+ display;
+
+ ScreenFormat
+ screen_format;
+
+ XImage
+ *created_ximage,
+ target_ximage;
+
+ /*
+ Initialize the X image.
+ */
+ screen_format.depth=ximage->depth;
+ screen_format.bits_per_pixel=(int) ximage->bits_per_pixel;
+ display.byte_order=ximage->byte_order;
+ display.bitmap_unit=ximage->bitmap_unit;
+ display.bitmap_bit_order=ximage->bitmap_bit_order;
+ display.pixmap_format=(&screen_format);
+ display.nformats=1;
+ created_ximage=XCreateImage(&display,(Visual *) NULL,ximage->depth,
+ ximage->format,ximage->xoffset,(char *) NULL,ximage->width,ximage->height,
+ ximage->bitmap_pad,ximage->bytes_per_line);
+ if (created_ximage == (XImage *) NULL)
+ return(0);
+ target_ximage=(*ximage);
+ *ximage=(*created_ximage);
+ created_ximage->data=(char *) NULL;
+ XDestroyImage(created_ximage);
+ ximage->red_mask=target_ximage.red_mask;
+ ximage->green_mask=target_ximage.green_mask;
+ ximage->blue_mask=target_ximage.blue_mask;
+ return(1);
+}
+#endif
+
+#if defined(PRE_R5_ICCCM)
+/*
+ Compatibility methods for pre X11R5 ICCCM.
+*/
+void XrmCombineDatabase(XrmDatabase source,XrmDatabase *target,
+ Bool override)
+{
+ XrmMergeDatabases(source,target);
+}
+
+Status XrmCombineFileDatabase(const char *filename,XrmDatabase *target,
+ Bool override)
+{
+ XrmDatabase
+ *combined_database,
+ source;
+
+ source=XrmGetFileDatabase(filename);
+ if (override == MagickFalse)
+ XrmMergeDatabases(source,target);
+ return(1);
+}
+
+XrmDatabase XrmGetDatabase(Display *display)
+{
+ return(display->db);
+}
+
+char *XSetLocaleModifiers(char *modifiers)
+{
+ return((char *) NULL);
+}
+
+Bool XSupportsLocale()
+{
+ return(0);
+}
+#endif
+
+#if defined(PRE_R4_ICCCM)
+/*
+ Compatibility methods for pre X11R4 ICCCM.
+*/
+XClassHint *XAllocClassHint)
+{
+ return((XClassHint *) AcquireMagickMemory(sizeof(XClassHint)));
+}
+
+XIconSize *XAllocIconSize)
+{
+ return((XIconSize *) AcquireMagickMemory(sizeof(XIconSize)));
+}
+
+XSizeHints *XAllocSizeHints)
+{
+ return((XSizeHints *) AcquireMagickMemory(sizeof(XSizeHints)));
+}
+
+Status XReconfigureWMWindow(Display *display,Window window,int screen_number,
+ unsigned int value_mask,XWindowChanges *values)
+{
+ return(XConfigureWindow(display,window,value_mask,values));
+}
+
+XStandardColormap *XAllocStandardColormap)
+{
+ return((XStandardColormap *) AcquireMagickMemory(sizeof(XStandardColormap)));
+}
+
+XWMHints *XAllocWMHints)
+{
+ return((XWMHints *) AcquireMagickMemory(sizeof(XWMHints)));
+}
+
+Status XGetGCValues(Display *display,GC gc,unsigned long mask,
+ XGCValues *values)
+{
+ return(MagickTrue);
+}
+
+Status XGetRGBColormaps(Display *display,Window window,
+ XStandardColormap **colormap,int *count,Atom property)
+{
+ *count=1;
+ return(XGetStandardColormap(display,window,*colormap,property));
+}
+
+Status XGetWMColormapWindows(Display *display,Window window,
+ Window **colormap_windows,int *number_windows)
+{
+ Atom
+ actual_type,
+ *data,
+ property;
+
+ int
+ actual_format,
+ status;
+
+ unsigned long
+ leftover,
+ number_items;
+
+ property=XInternAtom(display,"WM_COLORMAP_WINDOWS",MagickFalse);
+ if (property == None)
+ return(MagickFalse);
+ /*
+ Get the window property.
+ */
+ *data=(Atom) NULL;
+ status=XGetWindowProperty(display,window,property,0L,1000000L,MagickFalse,
+ XA_WINDOW,&actual_type,&actual_format,&number_items,&leftover,
+ (unsigned char **) &data);
+ if (status != Success)
+ return(MagickFalse);
+ if ((actual_type != XA_WINDOW) || (actual_format != 32))
+ {
+ if (data != (Atom *) NULL)
+ XFree((char *) data);
+ return(MagickFalse);
+ }
+ *colormap_windows=(Window *) data;
+ *number_windows=(int) number_items;
+ return(MagickTrue);
+}
+
+Status XGetWMName(Display *display,Window window,XTextProperty *text_property)
+{
+ char
+ *window_name;
+
+ if (XFetchName(display,window,&window_name) == 0)
+ return(MagickFalse);
+ text_property->value=(unsigned char *) window_name;
+ text_property->encoding=XA_STRING;
+ text_property->format=8;
+ text_property->nitems=strlen(window_name);
+ return(MagickTrue);
+}
+
+char *XResourceManagerString(Display *display)
+{
+ return(display->xdefaults);
+}
+
+void XrmDestroyDatabase(XrmDatabase database)
+{
+}
+
+void XSetWMIconName(Display *display,Window window,XTextProperty *property)
+{
+ XSetIconName(display,window,property->value);
+}
+
+void XSetWMName(Display *display,Window window,XTextProperty *property)
+{
+ XStoreName(display,window,property->value);
+}
+
+void XSetWMProperties(Display *display,Window window,
+ XTextProperty *window_name,XTextProperty *icon_name,char **argv,
+ int argc,XSizeHints *size_hints,XWMHints *manager_hints,
+ XClassHint *class_hint)
+{
+ XSetStandardProperties(display,window,window_name->value,icon_name->value,
+ None,argv,argc,size_hints);
+ XSetWMHints(display,window,manager_hints);
+ XSetClassHint(display,window,class_hint);
+}
+
+Status XSetWMProtocols(Display *display,Window window,Atom *protocols,
+ int count)
+{
+ Atom
+ wm_protocols;
+
+ wm_protocols=XInternAtom(display,"WM_PROTOCOLS",MagickFalse);
+ XChangeProperty(display,window,wm_protocols,XA_ATOM,32,PropModeReplace,
+ (unsigned char *) protocols,count);
+ return(MagickTrue);
+}
+
+int XStringListToTextProperty(char **argv,int argc,XTextProperty *property)
+{
+ register int
+ i;
+
+ register unsigned int
+ number_bytes;
+
+ XTextProperty
+ protocol;
+
+ number_bytes=0;
+ for (i=0; i < (long) argc; i++)
+ number_bytes+=(unsigned int) ((argv[i] ? strlen(argv[i]) : 0)+1);
+ protocol.encoding=XA_STRING;
+ protocol.format=8;
+ protocol.nitems=0;
+ if (number_bytes)
+ protocol.nitems=number_bytes-1;
+ protocol.value=NULL;
+ if (number_bytes <= 0)
+ {
+ protocol.value=(unsigned char *) AcquireQuantumMemory(1UL,
+ sizeof(*protocol.value));
+ if (protocol.value == MagickFalse)
+ return(MagickFalse);
+ *protocol.value='\0';
+ }
+ else
+ {
+ register char
+ *buffer;
+
+ buffer=(char *) AcquireQuantumMemory(number_bytes,sizeof(*buffer));
+ if (buffer == (char *) NULL)
+ return(MagickFalse);
+ protocol.value=(unsigned char *) buffer;
+ for (i=0; i < (long) argc; i++)
+ {
+ char
+ *argument;
+
+ argument=argv[i];
+ if (argument == MagickFalse)
+ *buffer++='\0';
+ else
+ {
+ (void) CopyMagickString(buffer,argument,MaxTextExtent);
+ buffer+=(strlen(argument)+1);
+ }
+ }
+ }
+ *property=protocol;
+ return(MagickTrue);
+}
+
+VisualID XVisualIDFromVisual(Visual *visual)
+{
+ return(visual->visualid);
+}
+
+Status XWithdrawWindow(Display *display,Window window,int screen)
+{
+ return(XUnmapWindow(display,window));
+}
+
+int XWMGeometry(Display *display,int screen,char *user_geometry,
+ char *default_geometry,unsigned int border_width,XSizeHints *size_hints,
+ int *x,int *y,int *width,int *height,int *gravity)
+{
+ int
+ status;
+
+ status=XGeometry(display,screen,user_geometry,default_geometry,border_width,
+ 0,0,0,0,x,y,width,height);
+ *gravity=NorthWestGravity;
+ return(status);
+}
+#endif
+
+#endif
diff --git a/magick/PreRvIcccm.h b/magick/PreRvIcccm.h
new file mode 100644
index 0000000..0c2b4df
--- /dev/null
+++ b/magick/PreRvIcccm.h
@@ -0,0 +1,115 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore X11 compatibility methods.
+*/
+#ifndef _MAGICKCORE_PRER5ICCCM_H
+#define _MAGICKCORE_PRER5ICCCM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(PRE_R6_ICCCM)
+/*
+ Compatability defines for pre X11R6 ICCCM.
+*/
+#define XK_KP_Home 0xFF95
+#define XK_KP_Left 0xFF96
+#define XK_KP_Up 0xFF97
+#define XK_KP_Right 0xFF98
+#define XK_KP_Down 0xFF99
+#define XK_KP_Prior 0xFF9A
+#define XK_KP_Page_Up 0xFF9A
+#define XK_KP_Next 0xFF9B
+#define XK_KP_Page_Down 0xFF9B
+#define XK_KP_End 0xFF9C
+#define XK_KP_Delete 0xFF9F
+
+extern MagickExport Status
+ XInitImage(XImage *ximage);
+#endif
+
+#if defined(PRE_R5_ICCCM)
+extern MagickExport XrmDatabase
+ XrmGetDatabase();
+#endif
+
+#if defined(PRE_R4_ICCCM)
+#if defined(vms)
+#define XMaxRequestSize(display) 16384
+#endif
+
+#define WithdrawnState 0
+
+typedef struct _XTextProperty
+{
+ unsigned char
+ *value;
+
+ Atom
+ encoding;
+
+ int
+ format;
+
+ unsigned long
+ nitems;
+} XTextProperty;
+
+char
+ *XResourceManagerString();
+
+extern MagickExport int
+ XWMGeometry();
+
+extern MagickExport Status
+ XGetRGBColormaps(),
+ XGetWMName(),
+ XReconfigureWMWindow(),
+ XSetWMProtocols(),
+ XWithdrawWindow();
+
+extern MagickExport XClassHint
+ *XAllocClassHint();
+
+extern MagickExport XIconSize
+ *XAllocIconSize();
+
+extern MagickExport XSizeHints
+ *XAllocSizeHints();
+
+extern MagickExport XStandardColormap
+ *XAllocStandardColormap();
+
+extern MagickExport XWMHints
+ *XAllocWMHints();
+
+extern MagickExport VisualID
+ XVisualIDFromVisual();
+
+extern MagickExport void
+ XrmDestroyDatabase(),
+ XSetWMIconName(),
+ XSetWMName(),
+ XSetWMProperties();
+#else
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/animate-private.h b/magick/animate-private.h
new file mode 100644
index 0000000..67ccb5c
--- /dev/null
+++ b/magick/animate-private.h
@@ -0,0 +1,39 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private methods to interactively animate an image sequence.
+*/
+#ifndef _MAGICKCORE_ANIMATE_PRIVATE_H
+#define _MAGICKCORE_ANIMATE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+#include "magick/xwindow-private.h"
+
+extern MagickExport Image
+ *XAnimateImages(Display *,XResourceInfo *,char **,const int,Image *);
+
+extern MagickExport void
+ XAnimateBackgroundImage(Display *,XResourceInfo *,Image *);
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/animate.c b/magick/animate.c
new file mode 100644
index 0000000..850eba3
--- /dev/null
+++ b/magick/animate.c
@@ -0,0 +1,3031 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% AAA N N IIIII M M AAA TTTTT EEEEE %
+% A A NN N I MM MM A A T E %
+% AAAAA N N N I M M M AAAAA T EEE %
+% A A N NN I M M A A T E %
+% A A N N IIIII M M A A T EEEEE %
+% %
+% %
+% Methods to Interactively Animate an Image Sequence %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/animate.h"
+#include "magick/animate-private.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/image-private.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/property.h"
+#include "magick/resource_.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#include "magick/widget.h"
+#include "magick/xwindow-private.h"
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+/*
+ Animate state declarations.
+*/
+#define AutoReverseAnimationState 0x0004
+#define ForwardAnimationState 0x0008
+#define HighlightState 0x0010
+#define PlayAnimationState 0x0020
+#define RepeatAnimationState 0x0040
+#define StepAnimationState 0x0080
+
+/*
+ Static declarations.
+*/
+static const char
+ *AnimateHelp[]=
+ {
+ "BUTTONS",
+ "",
+ " Press any button to map or unmap the Command widget.",
+ "",
+ "COMMAND WIDGET",
+ " The Command widget lists a number of sub-menus and commands.",
+ " They are",
+ "",
+ " Animate",
+ " Open...",
+ " Save...",
+ " Play",
+ " Step",
+ " Repeat",
+ " Auto Reverse",
+ " Speed",
+ " Slower",
+ " Faster",
+ " Direction",
+ " Forward",
+ " Reverse",
+ " Help",
+ " Overview",
+ " Browse Documentation",
+ " About Animate",
+ " Image Info",
+ " Quit",
+ "",
+ " Menu items with a indented triangle have a sub-menu. They",
+ " are represented above as the indented items. To access a",
+ " sub-menu item, move the pointer to the appropriate menu and",
+ " press a button and drag. When you find the desired sub-menu",
+ " item, release the button and the command is executed. Move",
+ " the pointer away from the sub-menu if you decide not to",
+ " execute a particular command.",
+ "",
+ "KEYBOARD ACCELERATORS",
+ " Accelerators are one or two key presses that effect a",
+ " particular command. The keyboard accelerators that",
+ " animate(1) understands is:",
+ "",
+ " Ctl+O Press to open an image from a file.",
+ "",
+ " space Press to display the next image in the sequence.",
+ "",
+ " < Press to speed-up the display of the images. Refer to",
+ " -delay for more information.",
+ "",
+ " > Press to slow the display of the images. Refer to",
+ " -delay for more information.",
+ "",
+ " F1 Press to display helpful information about animate(1).",
+ "",
+ " Find Press to browse documentation about ImageMagick.",
+ "",
+ " ? Press to display information about the image. Press",
+ " any key or button to erase the information.",
+ "",
+ " This information is printed: image name; image size;",
+ " and the total number of unique colors in the image.",
+ "",
+ " Ctl-q Press to discard all images and exit program.",
+ (char *) NULL
+ };
+
+/*
+ Constant declarations.
+*/
+static const char
+ *PageSizes[]=
+ {
+ "Letter",
+ "Tabloid",
+ "Ledger",
+ "Legal",
+ "Statement",
+ "Executive",
+ "A3",
+ "A4",
+ "A5",
+ "B4",
+ "B5",
+ "Folio",
+ "Quarto",
+ "10x14",
+ (char *) NULL
+ };
+
+static const unsigned char
+ HighlightBitmap[8] =
+ {
+ (unsigned char) 0xaa,
+ (unsigned char) 0x55,
+ (unsigned char) 0xaa,
+ (unsigned char) 0x55,
+ (unsigned char) 0xaa,
+ (unsigned char) 0x55,
+ (unsigned char) 0xaa,
+ (unsigned char) 0x55
+ },
+ ShadowBitmap[8] =
+ {
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00,
+ (unsigned char) 0x00
+ };
+
+/*
+ Enumeration declarations.
+*/
+typedef enum
+{
+ OpenCommand,
+ SaveCommand,
+ PlayCommand,
+ StepCommand,
+ RepeatCommand,
+ AutoReverseCommand,
+ SlowerCommand,
+ FasterCommand,
+ ForwardCommand,
+ ReverseCommand,
+ HelpCommand,
+ BrowseDocumentationCommand,
+ VersionCommand,
+ InfoCommand,
+ QuitCommand,
+ StepBackwardCommand,
+ StepForwardCommand,
+ NullCommand
+} CommandType;
+
+/*
+ Stipples.
+*/
+#define HighlightWidth 8
+#define HighlightHeight 8
+#define ShadowWidth 8
+#define ShadowHeight 8
+
+/*
+ Forward declarations.
+*/
+static Image
+ *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
+ Image **,MagickStatusType *);
+
+static MagickBooleanType
+ XSaveImage(Display *,XResourceInfo *,XWindows *,Image *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A n i m a t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AnimateImages() repeatedly displays an image sequence to any X window
+% screen. It returns a value other than 0 if successful. Check the
+% exception member of image to determine the reason for any failure.
+%
+% The format of the AnimateImages method is:
+%
+% MagickBooleanType AnimateImages(const ImageInfo *image_info,
+% Image *images)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
+ Image *images)
+{
+ char
+ *argv[1];
+
+ Display
+ *display;
+
+ MagickStatusType
+ status;
+
+ XrmDatabase
+ resource_database;
+
+ XResourceInfo
+ resource_info;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ display=XOpenDisplay(image_info->server_name);
+ if (display == (Display *) NULL)
+ {
+ (void) ThrowMagickException(&images->exception,GetMagickModule(),
+ XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
+ image_info->server_name));
+ return(MagickFalse);
+ }
+ if (images->exception.severity != UndefinedException)
+ CatchException(&images->exception);
+ (void) XSetErrorHandler(XError);
+ resource_database=XGetResourceDatabase(display,GetClientName());
+ (void) ResetMagickMemory(&resource_info,0,sizeof(XResourceInfo));
+ XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
+ if (image_info->page != (char *) NULL)
+ resource_info.image_geometry=AcquireString(image_info->page);
+ resource_info.immutable=MagickTrue;
+ argv[0]=AcquireString(GetClientName());
+ (void) XAnimateImages(display,&resource_info,argv,1,images);
+ argv[0]=DestroyString(argv[0]);
+ (void) XCloseDisplay(display);
+ XDestroyResourceInfo(&resource_info);
+ status=images->exception.severity == UndefinedException ?
+ MagickTrue : MagickFalse;
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a g i c k C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMagickCommand() makes a transform to the image or Image window as specified
+% by a user menu button or keyboard command.
+%
+% The format of the XMagickCommand method is:
+%
+% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,const CommandType command_type,Image **image,
+% MagickStatusType *state)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; XMagickCommand
+% may transform the image and return a new image pointer.
+%
+% o state: Specifies a MagickStatusType; XMagickCommand may return a
+% modified state.
+%
+%
+*/
+static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,const CommandType command_type,Image **image,
+ MagickStatusType *state)
+{
+ Image
+ *nexus;
+
+ MagickBooleanType
+ proceed;
+
+ MagickStatusType
+ status;
+
+ XTextProperty
+ window_name;
+
+ /*
+ Process user command.
+ */
+ nexus=NewImageList();
+ switch (command_type)
+ {
+ case OpenCommand:
+ {
+ char
+ **filelist;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *images,
+ *next;
+
+ ImageInfo
+ *read_info;
+
+ int
+ number_files;
+
+ register int
+ i;
+
+ static char
+ filenames[MaxTextExtent] = "*";
+
+ if (resource_info->immutable != MagickFalse)
+ break;
+ /*
+ Request file name from user.
+ */
+ XFileBrowserWidget(display,windows,"Animate",filenames);
+ if (*filenames == '\0')
+ return((Image *) NULL);
+ /*
+ Expand the filenames.
+ */
+ filelist=(char **) AcquireMagickMemory(sizeof(char *));
+ if (filelist == (char **) NULL)
+ {
+ ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
+ filenames);
+ return((Image *) NULL);
+ }
+ number_files=1;
+ filelist[0]=filenames;
+ status=ExpandFilenames(&number_files,&filelist);
+ if ((status == MagickFalse) || (number_files == 0))
+ {
+ if (number_files == 0)
+ {
+ ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
+ return((Image *) NULL);
+ }
+ ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
+ filenames);
+ return((Image *) NULL);
+ }
+ read_info=CloneImageInfo(resource_info->image_info);
+ exception=AcquireExceptionInfo();
+ images=NewImageList();
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ for (i=0; i < number_files; i++)
+ {
+ (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
+ filelist[i]=DestroyString(filelist[i]);
+ *read_info->magick='\0';
+ next=ReadImage(read_info,exception);
+ CatchException(exception);
+ if (next != (Image *) NULL)
+ AppendImageToList(&images,next);
+ if (number_files <= 5)
+ continue;
+ proceed=SetImageProgress(images,LoadImageTag,i,(MagickSizeType)
+ number_files);
+ if (proceed == MagickFalse)
+ break;
+ }
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ exception=DestroyExceptionInfo(exception);
+ read_info=DestroyImageInfo(read_info);
+ if (images == (Image *) NULL)
+ {
+ XSetCursorState(display,windows,MagickFalse);
+ ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
+ return((Image *) NULL);
+ }
+ nexus=GetFirstImageInList(images);
+ *state|=ExitState;
+ break;
+ }
+ case PlayCommand:
+ {
+ char
+ basename[MaxTextExtent];
+
+ int
+ status;
+
+ /*
+ Window name is the base of the filename.
+ */
+ *state|=PlayAnimationState;
+ *state&=(~AutoReverseAnimationState);
+ GetPathComponent((*image)->magick_filename,BasePath,basename);
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s",basename);
+ if (resource_info->title != (char *) NULL)
+ {
+ char
+ *title;
+
+ title=InterpretImageProperties(resource_info->image_info,*image,
+ resource_info->title);
+ (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
+ title=DestroyString(title);
+ }
+ status=XStringListToTextProperty(&windows->image.name,1,&window_name);
+ if (status == 0)
+ break;
+ XSetWMName(display,windows->image.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ break;
+ }
+ case StepCommand:
+ case StepBackwardCommand:
+ case StepForwardCommand:
+ {
+ *state|=StepAnimationState;
+ *state&=(~PlayAnimationState);
+ if (command_type == StepBackwardCommand)
+ *state&=(~ForwardAnimationState);
+ if (command_type == StepForwardCommand)
+ *state|=ForwardAnimationState;
+ if (resource_info->title != (char *) NULL)
+ break;
+ break;
+ }
+ case RepeatCommand:
+ {
+ *state|=RepeatAnimationState;
+ *state&=(~AutoReverseAnimationState);
+ *state|=PlayAnimationState;
+ break;
+ }
+ case AutoReverseCommand:
+ {
+ *state|=AutoReverseAnimationState;
+ *state&=(~RepeatAnimationState);
+ *state|=PlayAnimationState;
+ break;
+ }
+ case SaveCommand:
+ {
+ /*
+ Save image.
+ */
+ status=XSaveImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to write X image:",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case SlowerCommand:
+ {
+ resource_info->delay++;
+ break;
+ }
+ case FasterCommand:
+ {
+ if (resource_info->delay == 0)
+ break;
+ resource_info->delay--;
+ break;
+ }
+ case ForwardCommand:
+ {
+ *state=ForwardAnimationState;
+ *state&=(~AutoReverseAnimationState);
+ break;
+ }
+ case ReverseCommand:
+ {
+ *state&=(~ForwardAnimationState);
+ *state&=(~AutoReverseAnimationState);
+ break;
+ }
+ case InfoCommand:
+ {
+ XDisplayImageInfo(display,resource_info,windows,(Image *) NULL,*image);
+ break;
+ }
+ case HelpCommand:
+ {
+ /*
+ User requested help.
+ */
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Animate",AnimateHelp);
+ break;
+ }
+ case BrowseDocumentationCommand:
+ {
+ Atom
+ mozilla_atom;
+
+ Window
+ mozilla_window,
+ root_window;
+
+ /*
+ Browse the ImageMagick documentation.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
+ mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
+ if (mozilla_window != (Window) NULL)
+ {
+ char
+ command[MaxTextExtent],
+ *url;
+
+ /*
+ Display documentation using Netscape remote control.
+ */
+ url=GetMagickHomeURL();
+ (void) FormatMagickString(command,MaxTextExtent,
+ "openurl(%s,new-tab)",url);
+ url=DestroyString(url);
+ mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
+ (void) XChangeProperty(display,mozilla_window,mozilla_atom,
+ XA_STRING,8,PropModeReplace,(unsigned char *) command,
+ (int) strlen(command));
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ status=InvokeDelegate(resource_info->image_info,*image,"browse",
+ (char *) NULL,&(*image)->exception);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to browse documentation",
+ (char *) NULL);
+ XDelay(display,1500);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case VersionCommand:
+ {
+ XNoticeWidget(display,windows,GetMagickVersion((unsigned long *) NULL),
+ GetMagickCopyright());
+ break;
+ }
+ case QuitCommand:
+ {
+ /*
+ exit program
+ */
+ if (resource_info->confirm_exit == MagickFalse)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ else
+ {
+ int
+ status;
+
+ /*
+ Confirm program exit.
+ */
+ status=XConfirmWidget(display,windows,"Do you really want to exit",
+ resource_info->client_name);
+ if (status != 0)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return(nexus);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X A n i m a t e B a c k g r o u n d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XAnimateBackgroundImage() animates an image sequence in the background of
+% a window.
+%
+% The format of the XAnimateBackgroundImage method is:
+%
+% void XAnimateBackgroundImage(Display *display,
+% XResourceInfo *resource_info,Image *images)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o images: the image list.
+%
+*/
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int SceneCompare(const void *x,const void *y)
+{
+ const Image
+ **image_1,
+ **image_2;
+
+ image_1=(const Image **) x;
+ image_2=(const Image **) y;
+ return((int) ((*image_1)->scene-(*image_2)->scene));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport void XAnimateBackgroundImage(Display *display,
+ XResourceInfo *resource_info,Image *images)
+{
+ char
+ geometry[MaxTextExtent],
+ visual_type[MaxTextExtent];
+
+ Image
+ *coalesce_image,
+ *display_image,
+ **image_list;
+
+ int
+ scene;
+
+ MagickStatusType
+ status;
+
+ RectangleInfo
+ geometry_info;
+
+ register long
+ i;
+
+ size_t
+ number_scenes;
+
+ static XPixelInfo
+ pixel;
+
+ static XStandardColormap
+ *map_info;
+
+ static XVisualInfo
+ *visual_info = (XVisualInfo *) NULL;
+
+ static XWindowInfo
+ window_info;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ delay;
+
+ Window
+ root_window;
+
+ XEvent
+ event;
+
+ XGCValues
+ context_values;
+
+ XResourceInfo
+ resources;
+
+ XWindowAttributes
+ window_attributes;
+
+ /*
+ Determine target window.
+ */
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ resources=(*resource_info);
+ window_info.id=(Window) NULL;
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ if (LocaleCompare(resources.window_id,"root") == 0)
+ window_info.id=root_window;
+ else
+ {
+ if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
+ window_info.id=XWindowByID(display,root_window,
+ (Window) strtol((char *) resources.window_id,(char **) NULL,0));
+ if (window_info.id == (Window) NULL)
+ window_info.id=
+ XWindowByName(display,root_window,resources.window_id);
+ }
+ if (window_info.id == (Window) NULL)
+ {
+ ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
+ resources.window_id);
+ return;
+ }
+ /*
+ Determine window visual id.
+ */
+ window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
+ window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
+ (void) CopyMagickString(visual_type,"default",MaxTextExtent);
+ status=XGetWindowAttributes(display,window_info.id,&window_attributes) != 0 ?
+ MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
+ XVisualIDFromVisual(window_attributes.visual));
+ if (visual_info == (XVisualInfo *) NULL)
+ {
+ /*
+ Allocate standard colormap.
+ */
+ map_info=XAllocStandardColormap();
+ if (map_info == (XStandardColormap *) NULL)
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ images->filename);
+ map_info->colormap=(Colormap) NULL;
+ pixel.pixels=(unsigned long *) NULL;
+ /*
+ Initialize visual info.
+ */
+ resources.map_type=(char *) NULL;
+ resources.visual_type=visual_type;
+ visual_info=XBestVisualInfo(display,map_info,&resources);
+ if (visual_info == (XVisualInfo *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
+ images->filename);
+ /*
+ Initialize window info.
+ */
+ window_info.ximage=(XImage *) NULL;
+ window_info.matte_image=(XImage *) NULL;
+ window_info.pixmap=(Pixmap) NULL;
+ window_info.matte_pixmap=(Pixmap) NULL;
+ }
+ /*
+ Free previous root colors.
+ */
+ if (window_info.id == root_window)
+ XDestroyWindowColors(display,root_window);
+ coalesce_image=CoalesceImages(images,&images->exception);
+ if (coalesce_image == (Image *) NULL)
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ images->filename);
+ images=coalesce_image;
+ if (resources.map_type == (char *) NULL)
+ if ((visual_info->klass != TrueColor) &&
+ (visual_info->klass != DirectColor))
+ {
+ Image
+ *next;
+
+ /*
+ Determine if the sequence of images has the identical colormap.
+ */
+ for (next=images; next != (Image *) NULL; )
+ {
+ next->matte=MagickFalse;
+ if ((next->storage_class == DirectClass) ||
+ (next->colors != images->colors) ||
+ (next->colors > (unsigned long) visual_info->colormap_size))
+ break;
+ for (i=0; i < (long) images->colors; i++)
+ if (IsColorEqual(next->colormap+i,images->colormap+i) == MagickFalse)
+ break;
+ if (i < (long) images->colors)
+ break;
+ next=GetNextImageInList(next);
+ }
+ if (next != (Image *) NULL)
+ (void) RemapImages(resources.quantize_info,images,(Image *) NULL);
+ }
+ /*
+ Sort images by increasing scene number.
+ */
+ number_scenes=GetImageListLength(images);
+ image_list=ImageListToArray(images,&images->exception);
+ if (image_list == (Image **) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed",images->filename);
+ for (i=0; i < (long) number_scenes; i++)
+ if (image_list[i]->scene == 0)
+ break;
+ if (i == (long) number_scenes)
+ qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
+ /*
+ Initialize Standard Colormap.
+ */
+ resources.colormap=SharedColormap;
+ display_image=image_list[0];
+ for (scene=0; scene < (int) number_scenes; scene++)
+ {
+ if ((resource_info->map_type != (char *) NULL) ||
+ (visual_info->klass == TrueColor) ||
+ (visual_info->klass == DirectColor))
+ (void) SetImageType(image_list[scene],image_list[scene]->matte ==
+ MagickFalse ? TrueColorType : TrueColorMatteType);
+ if ((display_image->columns < image_list[scene]->columns) &&
+ (display_image->rows < image_list[scene]->rows))
+ display_image=image_list[scene];
+ }
+ if ((resource_info->map_type != (char *) NULL) ||
+ (visual_info->klass == TrueColor) || (visual_info->klass == DirectColor))
+ (void) SetImageType(display_image,display_image->matte == MagickFalse ?
+ TrueColorType : TrueColorMatteType);
+ XMakeStandardColormap(display,visual_info,&resources,display_image,map_info,
+ &pixel);
+ /*
+ Graphic context superclass.
+ */
+ context_values.background=pixel.background_color.pixel;
+ context_values.foreground=pixel.foreground_color.pixel;
+ pixel.annotate_context=XCreateGC(display,window_info.id,(unsigned long)
+ GCBackground | GCForeground,&context_values);
+ if (pixel.annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ images->filename);
+ /*
+ Initialize Image window attributes.
+ */
+ XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
+ &resources,&window_info);
+ /*
+ Create the X image.
+ */
+ window_info.width=(unsigned int) image_list[0]->columns;
+ window_info.height=(unsigned int) image_list[0]->rows;
+ if ((image_list[0]->columns != window_info.width) ||
+ (image_list[0]->rows != window_info.height))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ image_list[0]->filename);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
+ window_attributes.width,window_attributes.height);
+ geometry_info.width=window_info.width;
+ geometry_info.height=window_info.height;
+ geometry_info.x=window_info.x;
+ geometry_info.y=window_info.y;
+ (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
+ &geometry_info.width,&geometry_info.height);
+ window_info.width=(unsigned int) geometry_info.width;
+ window_info.height=(unsigned int) geometry_info.height;
+ window_info.x=(int) geometry_info.x;
+ window_info.y=(int) geometry_info.y;
+ status=XMakeImage(display,&resources,&window_info,image_list[0],
+ window_info.width,window_info.height);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ images->filename);
+ window_info.x=0;
+ window_info.y=0;
+ if (display_image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: %s[%lu] %lux%lu ",image_list[0]->filename,
+ image_list[0]->scene,image_list[0]->columns,image_list[0]->rows);
+ if (image_list[0]->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
+ image_list[0]->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
+ image_list[0]->magick);
+ }
+ /*
+ Adjust image dimensions as specified by backdrop or geometry options.
+ */
+ width=window_info.width;
+ height=window_info.height;
+ if (resources.backdrop != MagickFalse)
+ {
+ /*
+ Center image on window.
+ */
+ window_info.x=(int) (window_attributes.width/2)-
+ (window_info.ximage->width/2);
+ window_info.y=(int) (window_attributes.height/2)-
+ (window_info.ximage->height/2);
+ width=(unsigned int) window_attributes.width;
+ height=(unsigned int) window_attributes.height;
+ }
+ if (resources.image_geometry != (char *) NULL)
+ {
+ char
+ default_geometry[MaxTextExtent];
+
+ int
+ flags,
+ gravity;
+
+ XSizeHints
+ *size_hints;
+
+ /*
+ User specified geometry.
+ */
+ size_hints=XAllocSizeHints();
+ if (size_hints == (XSizeHints *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed",images->filename);
+ size_hints->flags=0L;
+ (void) FormatMagickString(default_geometry,MaxTextExtent,"%ux%u",width,
+ height);
+ flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
+ default_geometry,window_info.border_width,size_hints,&window_info.x,
+ &window_info.y,(int *) &width,(int *) &height,&gravity);
+ if (((flags & (XValue | YValue))) != 0)
+ {
+ width=(unsigned int) window_attributes.width;
+ height=(unsigned int) window_attributes.height;
+ }
+ (void) XFree((void *) size_hints);
+ }
+ /*
+ Create the X pixmap.
+ */
+ window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
+ (unsigned int) height,window_info.depth);
+ if (window_info.pixmap == (Pixmap) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
+ images->filename);
+ /*
+ Display pixmap on the window.
+ */
+ if (((unsigned int) width > window_info.width) ||
+ ((unsigned int) height > window_info.height))
+ (void) XFillRectangle(display,window_info.pixmap,
+ window_info.annotate_context,0,0,(unsigned int) width,
+ (unsigned int) height);
+ (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
+ window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
+ window_info.height);
+ (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
+ (void) XClearWindow(display,window_info.id);
+ /*
+ Initialize image pixmaps structure.
+ */
+ window_info.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
+ sizeof(*window_info.pixmaps));
+ window_info.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
+ sizeof(*window_info.matte_pixmaps));
+ if ((window_info.pixmaps == (Pixmap *) NULL) ||
+ (window_info.matte_pixmaps == (Pixmap *) NULL))
+ ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
+ images->filename);
+ window_info.pixmaps[0]=window_info.pixmap;
+ window_info.matte_pixmaps[0]=window_info.pixmap;
+ for (scene=1; scene < (int) number_scenes; scene++)
+ {
+ unsigned int
+ columns,
+ rows;
+
+ /*
+ Create X image.
+ */
+ window_info.pixmap=(Pixmap) NULL;
+ window_info.matte_pixmap=(Pixmap) NULL;
+ if ((resources.map_type != (char *) NULL) ||
+ (visual_info->klass == TrueColor) ||
+ (visual_info->klass == DirectColor))
+ if (image_list[scene]->storage_class == PseudoClass)
+ XGetPixelPacket(display,visual_info,map_info,&resources,
+ image_list[scene],window_info.pixel_info);
+ columns=(unsigned int) image_list[scene]->columns;
+ rows=(unsigned int) image_list[scene]->rows;
+ if ((image_list[scene]->columns != columns) ||
+ (image_list[scene]->rows != rows))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ image_list[scene]->filename);
+ status=XMakeImage(display,&resources,&window_info,image_list[scene],
+ columns,rows);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ images->filename);
+ if (display_image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: [%lu] %s %ux%u ",image_list[scene]->scene,
+ image_list[scene]->filename,columns,rows);
+ if (image_list[scene]->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
+ image_list[scene]->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
+ image_list[scene]->magick);
+ }
+ /*
+ Create the X pixmap.
+ */
+ window_info.pixmap=XCreatePixmap(display,window_info.id,width,height,
+ window_info.depth);
+ if (window_info.pixmap == (Pixmap) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
+ images->filename);
+ /*
+ Display pixmap on the window.
+ */
+ if ((width > window_info.width) || (height > window_info.height))
+ (void) XFillRectangle(display,window_info.pixmap,
+ window_info.annotate_context,0,0,width,height);
+ (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
+ window_info.ximage,0,0,window_info.x,window_info.y,window_info.width,
+ window_info.height);
+ (void) XSetWindowBackgroundPixmap(display,window_info.id,
+ window_info.pixmap);
+ (void) XClearWindow(display,window_info.id);
+ window_info.pixmaps[scene]=window_info.pixmap;
+ window_info.matte_pixmaps[scene]=window_info.matte_pixmap;
+ if (image_list[scene]->matte)
+ (void) XClearWindow(display,window_info.id);
+ delay=1000*image_list[scene]->delay/MagickMax(
+ image_list[scene]->ticks_per_second,1L);
+ XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
+ }
+ window_info.pixel_info=(&pixel);
+ /*
+ Display pixmap on the window.
+ */
+ (void) XSelectInput(display,window_info.id,SubstructureNotifyMask);
+ event.type=Expose;
+ do
+ {
+ for (scene=0; scene < (int) number_scenes; scene++)
+ {
+ if (XEventsQueued(display,QueuedAfterFlush) > 0)
+ {
+ (void) XNextEvent(display,&event);
+ if (event.type == DestroyNotify)
+ break;
+ }
+ window_info.pixmap=window_info.pixmaps[scene];
+ window_info.matte_pixmap=window_info.matte_pixmaps[scene];
+ (void) XSetWindowBackgroundPixmap(display,window_info.id,
+ window_info.pixmap);
+ (void) XClearWindow(display,window_info.id);
+ (void) XSync(display,MagickFalse);
+ delay=1000*image_list[scene]->delay/MagickMax(
+ image_list[scene]->ticks_per_second,1L);
+ XDelay(display,resources.delay*(delay == 0 ? 10 : delay));
+ }
+ } while (event.type != DestroyNotify);
+ (void) XSync(display,MagickFalse);
+ image_list=(Image **) RelinquishMagickMemory(image_list);
+ images=DestroyImageList(images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X A n i m a t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XAnimateImages() displays an image via X11.
+%
+% The format of the XAnimateImages method is:
+%
+% Image *XAnimateImages(Display *display,XResourceInfo *resource_info,
+% char **argv,const int argc,Image *images)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o argv: Specifies the application's argument list.
+%
+% o argc: Specifies the number of arguments.
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *XAnimateImages(Display *display,
+ XResourceInfo *resource_info,char **argv,const int argc,Image *images)
+{
+#define MagickMenus 4
+#define MaXWindows 8
+#define MagickTitle "Commands"
+
+ static const char
+ *CommandMenu[]=
+ {
+ "Animate",
+ "Speed",
+ "Direction",
+ "Help",
+ "Image Info",
+ "Quit",
+ (char *) NULL
+ },
+ *AnimateMenu[]=
+ {
+ "Open...",
+ "Play",
+ "Step",
+ "Repeat",
+ "Auto Reverse",
+ "Save...",
+ (char *) NULL
+ },
+ *SpeedMenu[]=
+ {
+ "Faster",
+ "Slower",
+ (char *) NULL
+ },
+ *DirectionMenu[]=
+ {
+ "Forward",
+ "Reverse",
+ (char *) NULL
+ },
+ *HelpMenu[]=
+ {
+ "Overview",
+ "Browse Documentation",
+ "About Animate",
+ (char *) NULL
+ };
+
+ static const char
+ **Menus[MagickMenus]=
+ {
+ AnimateMenu,
+ SpeedMenu,
+ DirectionMenu,
+ HelpMenu
+ };
+
+ static const CommandType
+ CommandMenus[]=
+ {
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ InfoCommand,
+ QuitCommand
+ },
+ CommandTypes[]=
+ {
+ OpenCommand,
+ PlayCommand,
+ StepCommand,
+ RepeatCommand,
+ AutoReverseCommand,
+ SaveCommand
+ },
+ SpeedCommands[]=
+ {
+ FasterCommand,
+ SlowerCommand
+ },
+ DirectionCommands[]=
+ {
+ ForwardCommand,
+ ReverseCommand
+ },
+ HelpCommands[]=
+ {
+ HelpCommand,
+ BrowseDocumentationCommand,
+ VersionCommand
+ };
+
+ static const CommandType
+ *Commands[MagickMenus]=
+ {
+ CommandTypes,
+ SpeedCommands,
+ DirectionCommands,
+ HelpCommands
+ };
+
+ char
+ command[MaxTextExtent],
+ *cwd,
+ geometry[MaxTextExtent],
+ resource_name[MaxTextExtent];
+
+ CommandType
+ command_type;
+
+ Image
+ *coalesce_image,
+ *display_image,
+ *image,
+ **image_list,
+ *nexus;
+
+ int
+ status;
+
+ long
+ first_scene,
+ iterations,
+ scene;
+
+ KeySym
+ key_symbol;
+
+ MagickStatusType
+ context_mask,
+ state;
+
+ RectangleInfo
+ geometry_info;
+
+ register char
+ *p;
+
+ register long
+ i;
+
+ static char
+ working_directory[MaxTextExtent];
+
+ static unsigned long
+ number_windows;
+
+ static XWindowInfo
+ *magick_windows[MaXWindows];
+
+ time_t
+ timestamp;
+
+ unsigned long
+ delay,
+ number_scenes;
+
+ WarningHandler
+ warning_handler;
+
+ Window
+ root_window;
+
+ XClassHint
+ *class_hints;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XGCValues
+ context_values;
+
+ XPixelInfo
+ *icon_pixel,
+ *pixel;
+
+ XResourceInfo
+ *icon_resources;
+
+ XStandardColormap
+ *icon_map,
+ *map_info;
+
+ XTextProperty
+ window_name;
+
+ XVisualInfo
+ *icon_visual,
+ *visual_info;
+
+ XWindowChanges
+ window_changes;
+
+ XWindows
+ *windows;
+
+ XWMHints
+ *manager_hints;
+
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ warning_handler=(WarningHandler) NULL;
+ windows=XSetWindows((XWindows *) ~0);
+ if (windows != (XWindows *) NULL)
+ {
+ int
+ status;
+
+ status=chdir(working_directory);
+ if (status == -1)
+ (void) ThrowMagickException(&images->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",working_directory);
+ warning_handler=resource_info->display_warnings ?
+ SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
+ warning_handler=resource_info->display_warnings ?
+ SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
+ }
+ else
+ {
+ register Image
+ *p;
+
+ /*
+ Initialize window structure.
+ */
+ for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
+ {
+ if (p->storage_class == DirectClass)
+ {
+ resource_info->colors=0;
+ break;
+ }
+ if (p->colors > resource_info->colors)
+ resource_info->colors=p->colors;
+ }
+ windows=XSetWindows(XInitializeWindows(display,resource_info));
+ if (windows == (XWindows *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
+ images->filename);
+ /*
+ Initialize window id's.
+ */
+ number_windows=0;
+ magick_windows[number_windows++]=(&windows->icon);
+ magick_windows[number_windows++]=(&windows->backdrop);
+ magick_windows[number_windows++]=(&windows->image);
+ magick_windows[number_windows++]=(&windows->info);
+ magick_windows[number_windows++]=(&windows->command);
+ magick_windows[number_windows++]=(&windows->widget);
+ magick_windows[number_windows++]=(&windows->popup);
+ for (i=0; i < (long) number_windows; i++)
+ magick_windows[i]->id=(Window) NULL;
+ }
+ /*
+ Initialize font info.
+ */
+ if (windows->font_info != (XFontStruct *) NULL)
+ (void) XFreeFont(display,windows->font_info);
+ windows->font_info=XBestFont(display,resource_info,MagickFalse);
+ if (windows->font_info == (XFontStruct *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
+ resource_info->font);
+ /*
+ Initialize Standard Colormap.
+ */
+ map_info=windows->map_info;
+ icon_map=windows->icon_map;
+ visual_info=windows->visual_info;
+ icon_visual=windows->icon_visual;
+ pixel=windows->pixel_info;
+ icon_pixel=windows->icon_pixel;
+ font_info=windows->font_info;
+ icon_resources=windows->icon_resources;
+ class_hints=windows->class_hints;
+ manager_hints=windows->manager_hints;
+ root_window=XRootWindow(display,visual_info->screen);
+ coalesce_image=CoalesceImages(images,&images->exception);
+ if (coalesce_image == (Image *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
+ images->filename);
+ images=coalesce_image;
+ if (resource_info->map_type == (char *) NULL)
+ if ((visual_info->klass != TrueColor) &&
+ (visual_info->klass != DirectColor))
+ {
+ Image
+ *next;
+
+ /*
+ Determine if the sequence of images has the identical colormap.
+ */
+ for (next=images; next != (Image *) NULL; )
+ {
+ next->matte=MagickFalse;
+ if ((next->storage_class == DirectClass) ||
+ (next->colors != images->colors) ||
+ (next->colors > (unsigned long) visual_info->colormap_size))
+ break;
+ for (i=0; i < (long) images->colors; i++)
+ if (IsColorEqual(next->colormap+i,images->colormap+i) == MagickFalse)
+ break;
+ if (i < (long) images->colors)
+ break;
+ next=GetNextImageInList(next);
+ }
+ if (next != (Image *) NULL)
+ (void) RemapImages(resource_info->quantize_info,images,
+ (Image *) NULL);
+ }
+ /*
+ Sort images by increasing scene number.
+ */
+ number_scenes=GetImageListLength(images);
+ image_list=ImageListToArray(images,&images->exception);
+ if (image_list == (Image **) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
+ images->filename);
+ for (scene=0; scene < (long) number_scenes; scene++)
+ if (image_list[scene]->scene == 0)
+ break;
+ if (scene == (long) number_scenes)
+ qsort((void *) image_list,number_scenes,sizeof(Image *),SceneCompare);
+ /*
+ Initialize Standard Colormap.
+ */
+ nexus=NewImageList();
+ display_image=image_list[0];
+ for (scene=0; scene < (long) number_scenes; scene++)
+ {
+ if ((resource_info->map_type != (char *) NULL) ||
+ (visual_info->klass == TrueColor) ||
+ (visual_info->klass == DirectColor))
+ (void) SetImageType(image_list[scene],image_list[scene]->matte ==
+ MagickFalse ? TrueColorType : TrueColorMatteType);
+ if ((display_image->columns < image_list[scene]->columns) &&
+ (display_image->rows < image_list[scene]->rows))
+ display_image=image_list[scene];
+ }
+ if (display_image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: %s[%lu] %lux%lu ",display_image->filename,
+ display_image->scene,display_image->columns,display_image->rows);
+ if (display_image->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
+ display_image->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
+ display_image->magick);
+ }
+ XMakeStandardColormap(display,visual_info,resource_info,display_image,
+ map_info,pixel);
+ /*
+ Initialize graphic context.
+ */
+ windows->context.id=(Window) NULL;
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->context);
+ (void) CloneString(&class_hints->res_name,resource_info->client_name);
+ (void) CloneString(&class_hints->res_class,resource_info->client_name);
+ class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
+ manager_hints->flags=InputHint | StateHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=WithdrawnState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->context);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (context)",windows->context.id);
+ context_values.background=pixel->background_color.pixel;
+ context_values.font=font_info->fid;
+ context_values.foreground=pixel->foreground_color.pixel;
+ context_values.graphics_exposures=MagickFalse;
+ context_mask=(MagickStatusType)
+ (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
+ if (pixel->annotate_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->annotate_context);
+ pixel->annotate_context=
+ XCreateGC(display,windows->context.id,context_mask,&context_values);
+ if (pixel->annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ images->filename);
+ context_values.background=pixel->depth_color.pixel;
+ if (pixel->widget_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->widget_context);
+ pixel->widget_context=
+ XCreateGC(display,windows->context.id,context_mask,&context_values);
+ if (pixel->widget_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ images->filename);
+ context_values.background=pixel->foreground_color.pixel;
+ context_values.foreground=pixel->background_color.pixel;
+ context_values.plane_mask=
+ context_values.background ^ context_values.foreground;
+ if (pixel->highlight_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->highlight_context);
+ pixel->highlight_context=XCreateGC(display,windows->context.id,
+ (unsigned long) (context_mask | GCPlaneMask),&context_values);
+ if (pixel->highlight_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ images->filename);
+ (void) XDestroyWindow(display,windows->context.id);
+ /*
+ Initialize icon window.
+ */
+ XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
+ icon_resources,&windows->icon);
+ windows->icon.geometry=resource_info->icon_geometry;
+ XBestIconSize(display,&windows->icon,display_image);
+ windows->icon.attributes.colormap=
+ XDefaultColormap(display,icon_visual->screen);
+ windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=IconicState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->icon);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
+ windows->icon.id);
+ /*
+ Initialize graphic context for icon window.
+ */
+ if (icon_pixel->annotate_context != (GC) NULL)
+ (void) XFreeGC(display,icon_pixel->annotate_context);
+ context_values.background=icon_pixel->background_color.pixel;
+ context_values.foreground=icon_pixel->foreground_color.pixel;
+ icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
+ (unsigned long) (GCBackground | GCForeground),&context_values);
+ if (icon_pixel->annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ images->filename);
+ windows->icon.annotate_context=icon_pixel->annotate_context;
+ /*
+ Initialize Image window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->image);
+ windows->image.shape=MagickTrue; /* non-rectangular shape hint */
+ if (resource_info->use_shared_memory == MagickFalse)
+ windows->image.shared_memory=MagickFalse;
+ if (resource_info->title != (char *) NULL)
+ {
+ char
+ *title;
+
+ title=InterpretImageProperties(resource_info->image_info,display_image,
+ resource_info->title);
+ (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
+ (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
+ title=DestroyString(title);
+ }
+ else
+ {
+ char
+ filename[MaxTextExtent];
+
+ /*
+ Window name is the base of the filename.
+ */
+ GetPathComponent(display_image->magick_filename,TailPath,filename);
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s[%lu of %lu]",filename,display_image->scene,
+ number_scenes);
+ (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
+ }
+ if (resource_info->immutable != MagickFalse)
+ windows->image.immutable=MagickTrue;
+ windows->image.shape=MagickTrue;
+ windows->image.geometry=resource_info->image_geometry;
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
+ XDisplayWidth(display,visual_info->screen),
+ XDisplayHeight(display,visual_info->screen));
+ geometry_info.width=display_image->columns;
+ geometry_info.height=display_image->rows;
+ geometry_info.x=0;
+ geometry_info.y=0;
+ (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
+ &geometry_info.width,&geometry_info.height);
+ windows->image.width=(unsigned int) geometry_info.width;
+ windows->image.height=(unsigned int) geometry_info.height;
+ windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
+ PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->backdrop);
+ if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
+ {
+ /*
+ Initialize backdrop window.
+ */
+ windows->backdrop.x=0;
+ windows->backdrop.y=0;
+ (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
+ windows->backdrop.flags=(unsigned long) (USSize | USPosition);
+ windows->backdrop.width=(unsigned int)
+ XDisplayWidth(display,visual_info->screen);
+ windows->backdrop.height=(unsigned int)
+ XDisplayHeight(display,visual_info->screen);
+ windows->backdrop.border_width=0;
+ windows->backdrop.immutable=MagickTrue;
+ windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
+ ButtonReleaseMask;
+ windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
+ StructureNotifyMask;
+ manager_hints->flags=IconWindowHint | InputHint | StateHint;
+ manager_hints->icon_window=windows->icon.id;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=
+ resource_info->iconic ? IconicState : NormalState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->backdrop);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (backdrop)",windows->backdrop.id);
+ (void) XMapWindow(display,windows->backdrop.id);
+ (void) XClearWindow(display,windows->backdrop.id);
+ if (windows->image.id != (Window) NULL)
+ {
+ (void) XDestroyWindow(display,windows->image.id);
+ windows->image.id=(Window) NULL;
+ }
+ /*
+ Position image in the center the backdrop.
+ */
+ windows->image.flags|=USPosition;
+ windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
+ (windows->image.width/2);
+ windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
+ (windows->image.height/2);
+ }
+ manager_hints->flags=IconWindowHint | InputHint | StateHint;
+ manager_hints->icon_window=windows->icon.id;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=
+ resource_info->iconic ? IconicState : NormalState;
+ if (windows->group_leader.id != (Window) NULL)
+ {
+ /*
+ Follow the leader.
+ */
+ manager_hints->flags|=(MagickStatusType) WindowGroupHint;
+ manager_hints->window_group=windows->group_leader.id;
+ (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (group leader)",windows->group_leader.id);
+ }
+ XMakeWindow(display,
+ (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
+ argv,argc,class_hints,manager_hints,&windows->image);
+ (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
+ XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
+ if (windows->group_leader.id != (Window) NULL)
+ (void) XSetTransientForHint(display,windows->image.id,
+ windows->group_leader.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
+ windows->image.id);
+ /*
+ Initialize Info widget.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->info);
+ (void) CloneString(&windows->info.name,"Info");
+ (void) CloneString(&windows->info.icon_name,"Info");
+ windows->info.border_width=1;
+ windows->info.x=2;
+ windows->info.y=2;
+ windows->info.flags|=PPosition;
+ windows->info.attributes.win_gravity=UnmapGravity;
+ windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
+ &windows->info);
+ windows->info.highlight_stipple=XCreateBitmapFromData(display,
+ windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->info.shadow_stipple=XCreateBitmapFromData(display,
+ windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
+ if (windows->image.mapped)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
+ windows->info.id);
+ /*
+ Initialize Command widget.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->command);
+ windows->command.data=MagickMenus;
+ (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
+ resource_info->client_name);
+ windows->command.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ (void) CloneString(&windows->command.name,MagickTitle);
+ windows->command.border_width=0;
+ windows->command.flags|=PPosition;
+ windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
+ OwnerGrabButtonMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->command);
+ windows->command.highlight_stipple=XCreateBitmapFromData(display,
+ windows->command.id,(char *) HighlightBitmap,HighlightWidth,
+ HighlightHeight);
+ windows->command.shadow_stipple=XCreateBitmapFromData(display,
+ windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (command)",windows->command.id);
+ /*
+ Initialize Widget window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->widget);
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
+ resource_info->client_name);
+ windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ windows->widget.border_width=0;
+ windows->widget.flags|=PPosition;
+ windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->widget);
+ windows->widget.highlight_stipple=XCreateBitmapFromData(display,
+ windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->widget.shadow_stipple=XCreateBitmapFromData(display,
+ windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (widget)",windows->widget.id);
+ /*
+ Initialize popup window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->popup);
+ windows->popup.border_width=0;
+ windows->popup.flags|=PPosition;
+ windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->popup);
+ windows->popup.highlight_stipple=XCreateBitmapFromData(display,
+ windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->popup.shadow_stipple=XCreateBitmapFromData(display,
+ windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (pop up)",windows->popup.id);
+ if ((windows->image.mapped == MagickFalse) ||
+ (windows->backdrop.id != (Window) NULL))
+ (void) XMapWindow(display,windows->image.id);
+ /*
+ Set out progress and warning handlers.
+ */
+ if (warning_handler == (WarningHandler) NULL)
+ {
+ warning_handler=resource_info->display_warnings ?
+ SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
+ warning_handler=resource_info->display_warnings ?
+ SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
+ }
+ /*
+ Initialize X image structure.
+ */
+ windows->image.x=0;
+ windows->image.y=0;
+ status=XMakeImage(display,resource_info,&windows->image,display_image,
+ (unsigned int) display_image->columns,(unsigned int) display_image->rows);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ images->filename);
+ if (windows->image.mapped)
+ XRefreshWindow(display,&windows->image,(XEvent *) NULL);
+ /*
+ Initialize image pixmaps structure.
+ */
+ window_changes.width=(int) windows->image.width;
+ window_changes.height=(int) windows->image.height;
+ (void) XReconfigureWMWindow(display,windows->image.id,windows->command.screen,
+ (unsigned int) (CWWidth | CWHeight),&window_changes);
+ (void) XMapWindow(display,windows->image.id);
+ windows->image.pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
+ sizeof(*windows->image.pixmaps));
+ windows->image.matte_pixmaps=(Pixmap *) AcquireQuantumMemory(number_scenes,
+ sizeof(*windows->image.pixmaps));
+ if ((windows->image.pixmaps == (Pixmap *) NULL) ||
+ (windows->image.matte_pixmaps == (Pixmap *) NULL))
+ ThrowXWindowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed",
+ images->filename);
+ windows->image.pixmaps[0]=windows->image.pixmap;
+ windows->image.matte_pixmaps[0]=windows->image.matte_pixmap;
+ for (scene=1; scene < (long) number_scenes; scene++)
+ {
+ unsigned int
+ columns,
+ rows;
+
+ /*
+ Create X image.
+ */
+ (void) TransformImageColorspace(image_list[scene],RGBColorspace);
+ windows->image.pixmap=(Pixmap) NULL;
+ windows->image.matte_pixmap=(Pixmap) NULL;
+ if ((resource_info->map_type != (char *) NULL) ||
+ (visual_info->klass == TrueColor) ||
+ (visual_info->klass == DirectColor))
+ if (image_list[scene]->storage_class == PseudoClass)
+ XGetPixelPacket(display,visual_info,map_info,resource_info,
+ image_list[scene],windows->image.pixel_info);
+ columns=(unsigned int) image_list[scene]->columns;
+ rows=(unsigned int) image_list[scene]->rows;
+ if ((image_list[scene]->columns != columns) ||
+ (image_list[scene]->rows != rows))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ image_list[scene]->filename);
+ status=XMakeImage(display,resource_info,&windows->image,image_list[scene],
+ columns,rows);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ images->filename);
+ if (image_list[scene]->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: [%lu] %s %ux%u ",image_list[scene]->scene,
+ image_list[scene]->filename,columns,rows);
+ if (image_list[scene]->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
+ image_list[scene]->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
+ image_list[scene]->magick);
+ }
+ /*
+ Window name is the base of the filename.
+ */
+ if (resource_info->title != (char *) NULL)
+ {
+ char
+ *title;
+
+ title=InterpretImageProperties(resource_info->image_info,
+ image_list[scene],resource_info->title);
+ (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
+ title=DestroyString(title);
+ }
+ else
+ {
+ p=image_list[scene]->magick_filename+
+ strlen(image_list[scene]->magick_filename)-1;
+ while ((p > image_list[scene]->magick_filename) && (*(p-1) != '/'))
+ p--;
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s[%lu of %lu]",p,scene+1,number_scenes);
+ }
+ status=XStringListToTextProperty(&windows->image.name,1,&window_name);
+ if (status != Success)
+ {
+ XSetWMName(display,windows->image.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ windows->image.pixmaps[scene]=windows->image.pixmap;
+ windows->image.matte_pixmaps[scene]=windows->image.matte_pixmap;
+ event.xexpose.x=0;
+ event.xexpose.y=0;
+ event.xexpose.width=(int) image_list[scene]->columns;
+ event.xexpose.height=(int) image_list[scene]->rows;
+ XRefreshWindow(display,&windows->image,&event);
+ (void) XSync(display,MagickFalse);
+ delay=1000*image_list[scene]->delay/MagickMax(images->ticks_per_second,1L);
+ XDelay(display,resource_info->delay*(delay == 0 ? 10 : delay));
+ if (XCheckTypedWindowEvent(display,windows->image.id,KeyPress,&event) != 0)
+ {
+ int
+ length;
+
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
+ {
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ break;
+ }
+ }
+ }
+ if (windows->command.mapped)
+ (void) XMapRaised(display,windows->command.id);
+ /*
+ Respond to events.
+ */
+ nexus=NewImageList();
+ scene=0;
+ first_scene=0;
+ iterations=0;
+ image=image_list[0];
+ state=(MagickStatusType) (ForwardAnimationState | RepeatAnimationState);
+ (void) XMagickCommand(display,resource_info,windows,PlayCommand,&images,
+ &state);
+ do
+ {
+ if (XEventsQueued(display,QueuedAfterFlush) == 0)
+ if ((state & PlayAnimationState) || (state & StepAnimationState))
+ {
+ MagickBooleanType
+ pause;
+
+ pause=MagickFalse;
+ delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
+ XDelay(display,resource_info->delay*(delay == 0 ? 10 : delay));
+ if (state & ForwardAnimationState)
+ {
+ /*
+ Forward animation: increment scene number.
+ */
+ if (scene < ((long) number_scenes-1))
+ scene++;
+ else
+ {
+ iterations++;
+ if (iterations == (long) image_list[0]->iterations)
+ {
+ iterations=0;
+ state|=ExitState;
+ }
+ if (state & AutoReverseAnimationState)
+ {
+ state&=(~ForwardAnimationState);
+ scene--;
+ }
+ else
+ {
+ if ((state & RepeatAnimationState) == MagickFalse)
+ state&=(~PlayAnimationState);
+ scene=first_scene;
+ pause=MagickTrue;
+ }
+ }
+ }
+ else
+ {
+ /*
+ Reverse animation: decrement scene number.
+ */
+ if (scene > first_scene)
+ scene--;
+ else
+ {
+ iterations++;
+ if (iterations == (long) image_list[0]->iterations)
+ {
+ iterations=0;
+ state&=(~RepeatAnimationState);
+ }
+ if (state & AutoReverseAnimationState)
+ {
+ state|=ForwardAnimationState;
+ scene=first_scene;
+ pause=MagickTrue;
+ }
+ else
+ {
+ if ((state & RepeatAnimationState) == MagickFalse)
+ state&=(~PlayAnimationState);
+ scene=(long) number_scenes-1;
+ }
+ }
+ }
+ scene=MagickMax(scene,0);
+ image=image_list[scene];
+ if ((image != (Image *) NULL) && (image->start_loop != 0))
+ first_scene=scene;
+ if ((state & StepAnimationState) ||
+ (resource_info->title != (char *) NULL))
+ {
+ /*
+ Update window title.
+ */
+ p=image_list[scene]->filename+
+ strlen(image_list[scene]->filename)-1;
+ while ((p > image_list[scene]->filename) && (*(p-1) != '/'))
+ p--;
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s[%lu of %lu]",p,scene+1,number_scenes);
+ if (resource_info->title != (char *) NULL)
+ {
+ char
+ *title;
+
+ title=InterpretImageProperties(resource_info->image_info,
+ image,resource_info->title);
+ (void) CopyMagickString(windows->image.name,title,
+ MaxTextExtent);
+ title=DestroyString(title);
+ }
+ status=XStringListToTextProperty(&windows->image.name,1,
+ &window_name);
+ if (status != Success)
+ {
+ XSetWMName(display,windows->image.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ }
+ /*
+ Copy X pixmap to Image window.
+ */
+ XGetPixelPacket(display,visual_info,map_info,resource_info,
+ image_list[scene],windows->image.pixel_info);
+ windows->image.ximage->width=(int) image->columns;
+ windows->image.ximage->height=(int) image->rows;
+ windows->image.pixmap=windows->image.pixmaps[scene];
+ windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
+ event.xexpose.x=0;
+ event.xexpose.y=0;
+ event.xexpose.width=(int) image->columns;
+ event.xexpose.height=(int) image->rows;
+ XRefreshWindow(display,&windows->image,&event);
+ (void) XSync(display,MagickFalse);
+ state&=(~StepAnimationState);
+ if (pause != MagickFalse)
+ for (i=0; i < (long) resource_info->pause; i++)
+ {
+ int
+ status;
+
+ status=XCheckTypedWindowEvent(display,windows->image.id,KeyPress,
+ &event);
+ if (status != 0)
+ {
+ int
+ length;
+
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
+ {
+ XClientMessage(display,windows->image.id,
+ windows->im_protocols,windows->im_exit,CurrentTime);
+ break;
+ }
+ }
+ (void) sleep(1);
+ }
+ continue;
+ }
+ /*
+ Handle a window event.
+ */
+ timestamp=time((time_t *) NULL);
+ (void) XNextEvent(display,&event);
+ if (windows->image.stasis == MagickFalse)
+ windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
+ MagickTrue : MagickFalse;
+ if (event.xany.window == windows->command.id)
+ {
+ int
+ id;
+
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,CommandMenu,&event);
+ if (id < 0)
+ continue;
+ (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
+ command_type=CommandMenus[id];
+ if (id < MagickMenus)
+ {
+ int
+ entry;
+
+ /*
+ Select a command from a pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
+ command);
+ if (entry < 0)
+ continue;
+ (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
+ command_type=Commands[id][entry];
+ }
+ if (command_type != NullCommand)
+ nexus=XMagickCommand(display,resource_info,windows,
+ command_type,&image,&state);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if ((event.xbutton.button == Button3) &&
+ (event.xbutton.state & Mod1Mask))
+ {
+ /*
+ Convert Alt-Button3 to Button2.
+ */
+ event.xbutton.button=Button2;
+ event.xbutton.state&=(~Mod1Mask);
+ }
+ if (event.xbutton.window == windows->backdrop.id)
+ {
+ (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
+ event.xbutton.time);
+ break;
+ }
+ if (event.xbutton.window == windows->image.id)
+ {
+ if (resource_info->immutable != MagickFalse)
+ {
+ state|=ExitState;
+ break;
+ }
+ /*
+ Map/unmap Command widget.
+ */
+ if (windows->command.mapped)
+ (void) XWithdrawWindow(display,windows->command.id,
+ windows->command.screen);
+ else
+ {
+ (void) XCommandWidget(display,windows,CommandMenu,
+ (XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ }
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ break;
+ }
+ case ClientMessage:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
+ event.xclient.message_type,event.xclient.format,(unsigned long)
+ event.xclient.data.l[0]);
+ if (event.xclient.message_type == windows->im_protocols)
+ {
+ if (*event.xclient.data.l == (long) windows->im_update_colormap)
+ {
+ /*
+ Update graphic context and window colormap.
+ */
+ for (i=0; i < (long) number_windows; i++)
+ {
+ if (magick_windows[i]->id == windows->icon.id)
+ continue;
+ context_values.background=pixel->background_color.pixel;
+ context_values.foreground=pixel->foreground_color.pixel;
+ (void) XChangeGC(display,magick_windows[i]->annotate_context,
+ context_mask,&context_values);
+ (void) XChangeGC(display,magick_windows[i]->widget_context,
+ context_mask,&context_values);
+ context_values.background=pixel->foreground_color.pixel;
+ context_values.foreground=pixel->background_color.pixel;
+ context_values.plane_mask=
+ context_values.background ^ context_values.foreground;
+ (void) XChangeGC(display,magick_windows[i]->highlight_context,
+ (unsigned long) (context_mask | GCPlaneMask),
+ &context_values);
+ magick_windows[i]->attributes.background_pixel=
+ pixel->background_color.pixel;
+ magick_windows[i]->attributes.border_pixel=
+ pixel->border_color.pixel;
+ magick_windows[i]->attributes.colormap=map_info->colormap;
+ (void) XChangeWindowAttributes(display,magick_windows[i]->id,
+ magick_windows[i]->mask,&magick_windows[i]->attributes);
+ }
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XInstallColormap(display,map_info->colormap);
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_exit)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ if (event.xclient.message_type == windows->dnd_protocols)
+ {
+ Atom
+ selection,
+ type;
+
+ int
+ format,
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Display image named by the Drag-and-Drop selection.
+ */
+ if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
+ break;
+ selection=XInternAtom(display,"DndSelection",MagickFalse);
+ status=XGetWindowProperty(display,root_window,selection,0L,2047L,
+ MagickFalse,(Atom) AnyPropertyType,&type,&format,&length,&after,
+ &data);
+ if ((status != Success) || (length == 0))
+ break;
+ if (*event.xclient.data.l == 2)
+ {
+ /*
+ Offix DND.
+ */
+ (void) CopyMagickString(resource_info->image_info->filename,
+ (char *) data,MaxTextExtent);
+ }
+ else
+ {
+ /*
+ XDND.
+ */
+ if (LocaleNCompare((char *) data,"file:",5) != 0)
+ {
+ (void) XFree((void *) data);
+ break;
+ }
+ (void) CopyMagickString(resource_info->image_info->filename,
+ ((char *) data)+5,MaxTextExtent);
+ }
+ nexus=ReadImage(resource_info->image_info,&image->exception);
+ CatchException(&image->exception);
+ if (nexus != (Image *) NULL)
+ state|=ExitState;
+ (void) XFree((void *) data);
+ break;
+ }
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (long) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (long) windows->wm_delete_window)
+ break;
+ (void) XWithdrawWindow(display,event.xclient.window,
+ visual_info->screen);
+ if (event.xclient.window == windows->image.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
+ event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
+ event.xconfigure.y,event.xconfigure.send_event);
+ if (event.xconfigure.window == windows->image.id)
+ {
+ if (event.xconfigure.send_event != 0)
+ {
+ XWindowChanges
+ window_changes;
+
+ /*
+ Position the transient windows relative of the Image window.
+ */
+ if (windows->command.geometry == (char *) NULL)
+ if (windows->command.mapped == MagickFalse)
+ {
+ windows->command.x=
+ event.xconfigure.x-windows->command.width-25;
+ windows->command.y=event.xconfigure.y;
+ XConstrainWindowPosition(display,&windows->command);
+ window_changes.x=windows->command.x;
+ window_changes.y=windows->command.y;
+ (void) XReconfigureWMWindow(display,windows->command.id,
+ windows->command.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ if (windows->widget.geometry == (char *) NULL)
+ if (windows->widget.mapped == MagickFalse)
+ {
+ windows->widget.x=
+ event.xconfigure.x+event.xconfigure.width/10;
+ windows->widget.y=
+ event.xconfigure.y+event.xconfigure.height/10;
+ XConstrainWindowPosition(display,&windows->widget);
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,
+ windows->widget.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ }
+ /*
+ Image window has a new configuration.
+ */
+ windows->image.width=(unsigned int) event.xconfigure.width;
+ windows->image.height=(unsigned int) event.xconfigure.height;
+ break;
+ }
+ if (event.xconfigure.window == windows->icon.id)
+ {
+ /*
+ Icon window has a new configuration.
+ */
+ windows->icon.width=(unsigned int) event.xconfigure.width;
+ windows->icon.height=(unsigned int) event.xconfigure.height;
+ break;
+ }
+ break;
+ }
+ case DestroyNotify:
+ {
+ /*
+ Group leader has exited.
+ */
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Destroy Notify: 0x%lx",event.xdestroywindow.window);
+ if (event.xdestroywindow.window == windows->group_leader.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case EnterNotify:
+ {
+ /*
+ Selectively install colormap.
+ */
+ if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
+ if (event.xcrossing.mode != NotifyUngrab)
+ XInstallColormap(display,map_info->colormap);
+ break;
+ }
+ case Expose:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
+ event.xexpose.width,event.xexpose.height,event.xexpose.x,
+ event.xexpose.y);
+ /*
+ Repaint windows that are now exposed.
+ */
+ if (event.xexpose.window == windows->image.id)
+ {
+ windows->image.pixmap=windows->image.pixmaps[scene];
+ windows->image.matte_pixmap=windows->image.matte_pixmaps[scene];
+ XRefreshWindow(display,&windows->image,&event);
+ break;
+ }
+ if (event.xexpose.window == windows->icon.id)
+ if (event.xexpose.count == 0)
+ {
+ XRefreshWindow(display,&windows->icon,&event);
+ break;
+ }
+ break;
+ }
+ case KeyPress:
+ {
+ static int
+ length;
+
+ /*
+ Respond to a user key press.
+ */
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key press: 0x%lx (%c)",(unsigned long) key_symbol,*command);
+ command_type=NullCommand;
+ switch (key_symbol)
+ {
+ case XK_o:
+ {
+ if ((event.xkey.state & ControlMask) == MagickFalse)
+ break;
+ command_type=OpenCommand;
+ break;
+ }
+ case XK_BackSpace:
+ {
+ command_type=StepBackwardCommand;
+ break;
+ }
+ case XK_space:
+ {
+ command_type=StepForwardCommand;
+ break;
+ }
+ case XK_less:
+ {
+ command_type=FasterCommand;
+ break;
+ }
+ case XK_greater:
+ {
+ command_type=SlowerCommand;
+ break;
+ }
+ case XK_F1:
+ {
+ command_type=HelpCommand;
+ break;
+ }
+ case XK_Find:
+ {
+ command_type=BrowseDocumentationCommand;
+ break;
+ }
+ case XK_question:
+ {
+ command_type=InfoCommand;
+ break;
+ }
+ case XK_q:
+ case XK_Escape:
+ {
+ command_type=QuitCommand;
+ break;
+ }
+ default:
+ break;
+ }
+ if (command_type != NullCommand)
+ nexus=XMagickCommand(display,resource_info,windows,
+ command_type,&image,&state);
+ break;
+ }
+ case KeyRelease:
+ {
+ /*
+ Respond to a user key release.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
+ break;
+ }
+ case LeaveNotify:
+ {
+ /*
+ Selectively uninstall colormap.
+ */
+ if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
+ if (event.xcrossing.mode != NotifyUngrab)
+ XUninstallColormap(display,map_info->colormap);
+ break;
+ }
+ case MapNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
+ event.xmap.window);
+ if (event.xmap.window == windows->backdrop.id)
+ {
+ (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
+ CurrentTime);
+ windows->backdrop.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->image.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XInstallColormap(display,map_info->colormap);
+ if (LocaleCompare(image_list[0]->magick,"LOGO") == 0)
+ {
+ if (LocaleCompare(display_image->filename,"LOGO") == 0)
+ nexus=XMagickCommand(display,resource_info,windows,
+ OpenCommand,&image,&state);
+ else
+ state|=ExitState;
+ }
+ windows->image.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->icon.id)
+ {
+ /*
+ Create an icon image.
+ */
+ XMakeStandardColormap(display,icon_visual,icon_resources,
+ display_image,icon_map,icon_pixel);
+ (void) XMakeImage(display,icon_resources,&windows->icon,
+ display_image,windows->icon.width,windows->icon.height);
+ (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
+ windows->icon.pixmap);
+ (void) XClearWindow(display,windows->icon.id);
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ windows->icon.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->command.id)
+ {
+ windows->command.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->popup.id)
+ {
+ windows->popup.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->widget.id)
+ {
+ windows->widget.mapped=MagickTrue;
+ break;
+ }
+ break;
+ }
+ case MappingNotify:
+ {
+ (void) XRefreshKeyboardMapping(&event.xmapping);
+ break;
+ }
+ case NoExpose:
+ break;
+ case PropertyNotify:
+ {
+ Atom
+ type;
+
+ int
+ format,
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
+ event.xproperty.atom,event.xproperty.state);
+ if (event.xproperty.atom != windows->im_remote_command)
+ break;
+ /*
+ Display image named by the remote command protocol.
+ */
+ status=XGetWindowProperty(display,event.xproperty.window,
+ event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
+ AnyPropertyType,&type,&format,&length,&after,&data);
+ if ((status != Success) || (length == 0))
+ break;
+ (void) CopyMagickString(resource_info->image_info->filename,
+ (char *) data,MaxTextExtent);
+ nexus=ReadImage(resource_info->image_info,&image->exception);
+ CatchException(&image->exception);
+ if (nexus != (Image *) NULL)
+ state|=ExitState;
+ (void) XFree((void *) data);
+ break;
+ }
+ case ReparentNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
+ event.xreparent.window);
+ break;
+ }
+ case UnmapNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Unmap Notify: 0x%lx",event.xunmap.window);
+ if (event.xunmap.window == windows->backdrop.id)
+ {
+ windows->backdrop.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->image.id)
+ {
+ windows->image.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->icon.id)
+ {
+ if (map_info->colormap == icon_map->colormap)
+ XConfigureImageColormap(display,resource_info,windows,
+ display_image);
+ (void) XFreeStandardColormap(display,icon_visual,icon_map,
+ icon_pixel);
+ windows->icon.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->command.id)
+ {
+ windows->command.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->popup.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XSetInputFocus(display,windows->image.id,RevertToParent,
+ CurrentTime);
+ windows->popup.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->widget.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XSetInputFocus(display,windows->image.id,RevertToParent,
+ CurrentTime);
+ windows->widget.mapped=MagickFalse;
+ break;
+ }
+ break;
+ }
+ default:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
+ event.type);
+ break;
+ }
+ }
+ }
+ while (!(state & ExitState));
+ image_list=(Image **) RelinquishMagickMemory(image_list);
+ images=DestroyImageList(images);
+ if ((windows->visual_info->klass == GrayScale) ||
+ (windows->visual_info->klass == PseudoColor) ||
+ (windows->visual_info->klass == DirectColor))
+ {
+ /*
+ Withdraw windows.
+ */
+ if (windows->info.mapped)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (windows->command.mapped)
+ (void) XWithdrawWindow(display,windows->command.id,
+ windows->command.screen);
+ }
+ if (resource_info->backdrop == MagickFalse)
+ if (windows->backdrop.mapped)
+ {
+ (void) XWithdrawWindow(display,windows->backdrop.id,\
+ windows->backdrop.screen);
+ (void) XDestroyWindow(display,windows->backdrop.id);
+ windows->backdrop.id=(Window) NULL;
+ (void) XWithdrawWindow(display,windows->image.id,windows->image.screen);
+ (void) XDestroyWindow(display,windows->image.id);
+ windows->image.id=(Window) NULL;
+ }
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ for (scene=1; scene < (long) number_scenes; scene++)
+ {
+ if (windows->image.pixmaps[scene] != (Pixmap) NULL)
+ (void) XFreePixmap(display,windows->image.pixmaps[scene]);
+ windows->image.pixmaps[scene]=(Pixmap) NULL;
+ if (windows->image.matte_pixmaps[scene] != (Pixmap) NULL)
+ (void) XFreePixmap(display,windows->image.matte_pixmaps[scene]);
+ windows->image.matte_pixmaps[scene]=(Pixmap) NULL;
+ }
+ windows->image.pixmaps=(Pixmap *)
+ RelinquishMagickMemory(windows->image.pixmaps);
+ windows->image.matte_pixmaps=(Pixmap *)
+ RelinquishMagickMemory(windows->image.matte_pixmaps);
+ if (nexus == (Image *) NULL)
+ {
+ /*
+ Free X resources.
+ */
+ if (windows->image.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->image.id,windows->image.screen); XDelay(display,SuspendTime);
+ (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
+ if (resource_info->map_type == (char *) NULL)
+ (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
+ DestroyXResources();
+ }
+ (void) XSync(display,MagickFalse);
+ /*
+ Restore our progress monitor and warning handlers.
+ */
+ (void) SetErrorHandler(warning_handler);
+ (void) SetWarningHandler(warning_handler);
+ /*
+ Change to home directory.
+ */
+ cwd=getcwd(working_directory,MaxTextExtent);
+ status=chdir(resource_info->home_directory);
+ if (status == -1)
+ (void) ThrowMagickException(&images->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
+ return(nexus);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S a v e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSaveImage() saves an image to a file.
+%
+% The format of the XSaveImage method is:
+%
+% MagickBooleanType XSaveImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o status: Method XSaveImage return True if the image is
+% written. False is returned is there is a memory shortage or if the
+% image fails to write.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XSaveImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ char
+ filename[MaxTextExtent];
+
+ ImageInfo
+ *image_info;
+
+ MagickStatusType
+ status;
+
+ /*
+ Request file name from user.
+ */
+ if (resource_info->write_filename != (char *) NULL)
+ (void) CopyMagickString(filename,resource_info->write_filename,
+ MaxTextExtent);
+ else
+ {
+ char
+ path[MaxTextExtent];
+
+ int
+ status;
+
+ GetPathComponent(image->filename,HeadPath,path);
+ GetPathComponent(image->filename,TailPath,filename);
+ status=chdir(path);
+ if (status == -1)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",path);
+ }
+ XFileBrowserWidget(display,windows,"Save",filename);
+ if (*filename == '\0')
+ return(MagickTrue);
+ if (IsPathAccessible(filename) != MagickFalse)
+ {
+ int
+ status;
+
+ /*
+ File exists-- seek user's permission before overwriting.
+ */
+ status=XConfirmWidget(display,windows,"Overwrite",filename);
+ if (status == 0)
+ return(MagickTrue);
+ }
+ image_info=CloneImageInfo(resource_info->image_info);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ (void) SetImageInfo(image_info,MagickFalse,&image->exception);
+ if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
+ (LocaleCompare(image_info->magick,"JPG") == 0))
+ {
+ char
+ quality[MaxTextExtent];
+
+ int
+ status;
+
+ /*
+ Request JPEG quality from user.
+ */
+ (void) FormatMagickString(quality,MaxTextExtent,"%lu",
+ image_info->quality);
+ status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
+ quality);
+ if (*quality == '\0')
+ return(MagickTrue);
+ image->quality=(unsigned long) atol(quality);
+ image_info->interlace=status != MagickFalse ? NoInterlace :
+ PlaneInterlace;
+ }
+ if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
+ (LocaleCompare(image_info->magick,"PDF") == 0) ||
+ (LocaleCompare(image_info->magick,"PS") == 0) ||
+ (LocaleCompare(image_info->magick,"PS2") == 0))
+ {
+ char
+ geometry[MaxTextExtent];
+
+ /*
+ Request page geometry from user.
+ */
+ (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
+ if (LocaleCompare(image_info->magick,"PDF") == 0)
+ (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
+ if (image_info->page != (char *) NULL)
+ (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
+ XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
+ "Select page geometry:",geometry);
+ if (*geometry != '\0')
+ image_info->page=GetPageGeometry(geometry);
+ }
+ /*
+ Write image.
+ */
+ image=GetFirstImageInList(image);
+ status=WriteImages(image_info,image,filename,&image->exception);
+ if (status != MagickFalse)
+ image->taint=MagickFalse;
+ image_info=DestroyImageInfo(image_info);
+ XSetCursorState(display,windows,MagickFalse);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+#else
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A n i m a t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AnimateImages() repeatedly displays an image sequence to any X window
+% screen. It returns a value other than 0 if successful. Check the
+% exception member of image to determine the reason for any failure.
+%
+% The format of the AnimateImages method is:
+%
+% MagickBooleanType AnimateImages(const ImageInfo *image_info,
+% Image *images)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType AnimateImages(const ImageInfo *image_info,
+ Image *image)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
+ image->filename);
+ return(MagickFalse);
+}
+#endif
diff --git a/magick/animate.h b/magick/animate.h
new file mode 100644
index 0000000..9f007c0
--- /dev/null
+++ b/magick/animate.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore methods to interactively animate an image sequence.
+*/
+#ifndef _MAGICKCORE_ANIMATE_H
+#define _MAGICKCORE_ANIMATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ AnimateImages(const ImageInfo *,Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/annotate.c b/magick/annotate.c
new file mode 100644
index 0000000..bb0933e
--- /dev/null
+++ b/magick/annotate.c
@@ -0,0 +1,1950 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% AAA N N N N OOO TTTTT AAA TTTTT EEEEE %
+% A A NN N NN N O O T A A T E %
+% AAAAA N N N N N N O O T AAAAA T EEE %
+% A A N NN N NN O O T A A T E %
+% A A N N N N OOO T A A T EEEEE %
+% %
+% %
+% MagickCore Image Annotation Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Digital Applications (www.digapp.com) contributed the stroked text algorithm.
+% It was written by Leonard Rosenthol.
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/annotate.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/draw-private.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/log.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/property.h"
+#include "magick/resource_.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/token-private.h"
+#include "magick/transform.h"
+#include "magick/type.h"
+#include "magick/utility.h"
+#include "magick/xwindow-private.h"
+#if defined(MAGICKCORE_FREETYPE_DELEGATE)
+#if defined(__MINGW32__)
+# undef interface
+#endif
+#if defined(MAGICKCORE_HAVE_FT2BUILD_H)
+# include <ft2build.h>
+#endif
+#if defined(FT_FREETYPE_H)
+# include FT_FREETYPE_H
+#else
+# include <freetype/freetype.h>
+#endif
+#if defined(FT_GLYPH_H)
+# include FT_GLYPH_H
+#else
+# include <freetype/ftglyph.h>
+#endif
+#if defined(FT_OUTLINE_H)
+# include FT_OUTLINE_H
+#else
+# include <freetype/ftoutln.h>
+#endif
+#if defined(FT_BBOX_H)
+# include FT_BBOX_H
+#else
+# include <freetype/ftbbox.h>
+#endif /* defined(FT_BBOX_H) */
+#endif
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ RenderType(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
+ RenderPostscript(Image *,const DrawInfo *,const PointInfo *,TypeMetric *),
+ RenderFreetype(Image *,const DrawInfo *,const char *,const PointInfo *,
+ TypeMetric *),
+ RenderX11(Image *,const DrawInfo *,const PointInfo *,TypeMetric *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A n n o t a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AnnotateImage() annotates an image with text. Optionally you can include
+% any of the following bits of information about the image by embedding
+% the appropriate special characters:
+%
+% %b file size in bytes.
+% %c comment.
+% %d directory in which the image resides.
+% %e extension of the image file.
+% %f original filename of the image.
+% %h height of image.
+% %i filename of the image.
+% %k number of unique colors.
+% %l image label.
+% %m image file format.
+% %n number of images in a image sequence.
+% %o output image filename.
+% %p page number of the image.
+% %q image depth (8 or 16).
+% %q image depth (8 or 16).
+% %s image scene number.
+% %t image filename without any extension.
+% %u a unique temporary filename.
+% %w image width.
+% %x x resolution of the image.
+% %y y resolution of the image.
+%
+% The format of the AnnotateImage method is:
+%
+% MagickBooleanType AnnotateImage(Image *image,DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+*/
+MagickExport MagickBooleanType AnnotateImage(Image *image,
+ const DrawInfo *draw_info)
+{
+ char
+ primitive[MaxTextExtent],
+ **textlist;
+
+ DrawInfo
+ *annotate,
+ *annotate_info;
+
+ GeometryInfo
+ geometry_info;
+
+ MagickBooleanType
+ status;
+
+ PointInfo
+ offset;
+
+ RectangleInfo
+ geometry;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ TypeMetric
+ metrics;
+
+ unsigned long
+ height,
+ number_lines;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if (draw_info->text == (char *) NULL)
+ return(MagickFalse);
+ if (*draw_info->text == '\0')
+ return(MagickTrue);
+ textlist=StringToList(draw_info->text);
+ if (textlist == (char **) NULL)
+ return(MagickFalse);
+ length=strlen(textlist[0]);
+ for (i=1; textlist[i] != (char *) NULL; i++)
+ if (strlen(textlist[i]) > length)
+ length=strlen(textlist[i]);
+ number_lines=(unsigned long) i;
+ annotate=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ SetGeometry(image,&geometry);
+ SetGeometryInfo(&geometry_info);
+ if (annotate_info->geometry != (char *) NULL)
+ {
+ (void) ParsePageGeometry(image,annotate_info->geometry,&geometry,
+ &image->exception);
+ (void) ParseGeometry(annotate_info->geometry,&geometry_info);
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ status=MagickTrue;
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ {
+ /*
+ Position text relative to image.
+ */
+ annotate_info->affine.tx=geometry_info.xi-image->page.x;
+ annotate_info->affine.ty=geometry_info.psi-image->page.y;
+ (void) CloneString(&annotate->text,textlist[i]);
+ (void) GetTypeMetrics(image,annotate,&metrics);
+ height=(unsigned long) (metrics.ascent-metrics.descent+0.5);
+ switch (annotate->gravity)
+ {
+ case UndefinedGravity:
+ default:
+ {
+ offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
+ offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
+ break;
+ }
+ case NorthWestGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
+ annotate_info->affine.ry*height+annotate_info->affine.ry*
+ (metrics.ascent+metrics.descent);
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
+ annotate_info->affine.sy*height+annotate_info->affine.sy*
+ metrics.ascent;
+ break;
+ }
+ case NorthGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
+ geometry.width/2.0+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
+ annotate_info->affine.ry*(metrics.ascent+metrics.descent);
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
+ annotate_info->affine.sy*height+annotate_info->affine.sy*
+ metrics.ascent-annotate_info->affine.rx*(metrics.width-
+ metrics.bounds.x1)/2.0;
+ break;
+ }
+ case NorthEastGravity:
+ {
+ offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
+ geometry.width+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
+ annotate_info->affine.ry*(metrics.ascent+metrics.descent)-1.0;
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+i*
+ annotate_info->affine.sy*height+annotate_info->affine.sy*
+ metrics.ascent-annotate_info->affine.rx*(metrics.width-
+ metrics.bounds.x1);
+ break;
+ }
+ case WestGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
+ annotate_info->affine.ry*height+annotate_info->affine.ry*
+ (metrics.ascent+metrics.descent-(number_lines-1.0)*height)/2.0;
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
+ geometry.height/2.0+i*annotate_info->affine.sy*height+
+ annotate_info->affine.sy*(metrics.ascent+metrics.descent-
+ (number_lines-1.0)*height)/2.0;
+ break;
+ }
+ case StaticGravity:
+ case CenterGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
+ geometry.width/2.0+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0+
+ annotate_info->affine.ry*(metrics.ascent+metrics.descent-
+ (number_lines-1)*height)/2.0;
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
+ geometry.height/2.0+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0+
+ annotate_info->affine.sy*(metrics.ascent+metrics.descent-
+ (number_lines-1.0)*height)/2.0;
+ break;
+ }
+ case EastGravity:
+ {
+ offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
+ geometry.width+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)+
+ annotate_info->affine.ry*(metrics.ascent+metrics.descent-
+ (number_lines-1.0)*height)/2.0-1.0;
+ offset.y=(geometry.height == 0 ? -1.0 : 1.0)*annotate_info->affine.ty+
+ geometry.height/2.0+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)+
+ annotate_info->affine.sy*(metrics.ascent+metrics.descent-
+ (number_lines-1.0)*height)/2.0;
+ break;
+ }
+ case SouthWestGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+i*
+ annotate_info->affine.ry*height-annotate_info->affine.ry*
+ (number_lines-1.0)*height;
+ offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
+ geometry.height+i*annotate_info->affine.sy*height-
+ annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
+ break;
+ }
+ case SouthGravity:
+ {
+ offset.x=(geometry.width == 0 ? -1.0 : 1.0)*annotate_info->affine.tx+
+ geometry.width/2.0+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0-
+ annotate_info->affine.ry*(number_lines-1.0)*height/2.0;
+ offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
+ geometry.height+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0-
+ annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
+ break;
+ }
+ case SouthEastGravity:
+ {
+ offset.x=(geometry.width == 0 ? 1.0 : -1.0)*annotate_info->affine.tx+
+ geometry.width+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)-
+ annotate_info->affine.ry*(number_lines-1.0)*height-1.0;
+ offset.y=(geometry.height == 0 ? 1.0 : -1.0)*annotate_info->affine.ty+
+ geometry.height+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)-
+ annotate_info->affine.sy*(number_lines-1.0)*height+metrics.descent;
+ break;
+ }
+ }
+ switch (annotate->align)
+ {
+ case LeftAlign:
+ {
+ offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height;
+ offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height;
+ break;
+ }
+ case CenterAlign:
+ {
+ offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1)/2.0;
+ offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1)/2.0;
+ break;
+ }
+ case RightAlign:
+ {
+ offset.x=annotate_info->affine.tx+i*annotate_info->affine.ry*height-
+ annotate_info->affine.sx*(metrics.width+metrics.bounds.x1);
+ offset.y=annotate_info->affine.ty+i*annotate_info->affine.sy*height-
+ annotate_info->affine.rx*(metrics.width+metrics.bounds.x1);
+ break;
+ }
+ default:
+ break;
+ }
+ if (draw_info->undercolor.opacity != TransparentOpacity)
+ {
+ DrawInfo
+ *undercolor_info;
+
+ /*
+ Text box.
+ */
+ undercolor_info=CloneDrawInfo((ImageInfo *) NULL,(DrawInfo *) NULL);
+ undercolor_info->fill=draw_info->undercolor;
+ undercolor_info->affine=draw_info->affine;
+ undercolor_info->affine.tx=offset.x-draw_info->affine.ry*metrics.ascent;
+ undercolor_info->affine.ty=offset.y-draw_info->affine.sy*metrics.ascent;
+ (void) FormatMagickString(primitive,MaxTextExtent,
+ "rectangle 0,0 %g,%ld",metrics.origin.x,height);
+ (void) CloneString(&undercolor_info->primitive,primitive);
+ (void) DrawImage(image,undercolor_info);
+ (void) DestroyDrawInfo(undercolor_info);
+ }
+ annotate_info->affine.tx=offset.x;
+ annotate_info->affine.ty=offset.y;
+ (void) FormatMagickString(primitive,MaxTextExtent,"stroke-width %g "
+ "line 0,0 %g,0",metrics.underline_thickness,metrics.width);
+ if (annotate->decorate == OverlineDecoration)
+ {
+ annotate_info->affine.ty-=(draw_info->affine.sy*(metrics.ascent+
+ metrics.descent-metrics.underline_position));
+ (void) CloneString(&annotate_info->primitive,primitive);
+ (void) DrawImage(image,annotate_info);
+ }
+ else
+ if (annotate->decorate == UnderlineDecoration)
+ {
+ annotate_info->affine.ty-=(draw_info->affine.sy*
+ metrics.underline_position);
+ (void) CloneString(&annotate_info->primitive,primitive);
+ (void) DrawImage(image,annotate_info);
+ }
+ /*
+ Annotate image with text.
+ */
+ status=RenderType(image,annotate,&offset,&metrics);
+ if (status == MagickFalse)
+ break;
+ if (annotate->decorate == LineThroughDecoration)
+ {
+ annotate_info->affine.ty-=(draw_info->affine.sy*(height+
+ metrics.underline_position+metrics.descent)/2.0);
+ (void) CloneString(&annotate_info->primitive,primitive);
+ (void) DrawImage(image,annotate_info);
+ }
+ }
+ /*
+ Relinquish resources.
+ */
+ annotate_info=DestroyDrawInfo(annotate_info);
+ annotate=DestroyDrawInfo(annotate);
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ textlist[i]=DestroyString(textlist[i]);
+ textlist=(char **) RelinquishMagickMemory(textlist);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t M a g i c k C a p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatMagickCaption() formats a caption so that it fits within the image
+% width. It returns the number of lines in the formatted caption.
+%
+% The format of the FormatMagickCaption method is:
+%
+% long FormatMagickCaption(Image *image,DrawInfo *draw_info,
+% TypeMetric *metrics,char **caption)
+%
+% A description of each parameter follows.
+%
+% o image: The image.
+%
+% o caption: the caption.
+%
+% o draw_info: the draw info.
+%
+% o metrics: Return the font metrics in this structure.
+%
+*/
+MagickExport long FormatMagickCaption(Image *image,DrawInfo *draw_info,
+ TypeMetric *metrics,char **caption)
+{
+ MagickBooleanType
+ status;
+
+ register char
+ *p,
+ *q,
+ *s;
+
+ register long
+ i;
+
+ unsigned long
+ width;
+
+ q=draw_info->text;
+ s=(char *) NULL;
+ for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
+ {
+ if (IsUTFSpace(GetUTFCode(p)) != MagickFalse)
+ s=p;
+ for (i=0; i < (long) GetUTFOctets(p); i++)
+ *q++=(*(p+i));
+ *q='\0';
+ status=GetTypeMetrics(image,draw_info,metrics);
+ if (status == MagickFalse)
+ break;
+ width=(unsigned long) (metrics->width+0.5);
+ if (GetUTFCode(p) != '\n')
+ if (width <= image->columns)
+ continue;
+ if (s == (char *) NULL)
+ {
+ s=p;
+ while ((IsUTFSpace(GetUTFCode(s)) == MagickFalse) &&
+ (GetUTFCode(s) != 0))
+ s+=GetUTFOctets(s);
+ }
+ if (GetUTFCode(s) != 0)
+ {
+ *s='\n';
+ p=s;
+ }
+ else
+ {
+ char
+ *target;
+
+ long
+ n;
+
+ /*
+ No convenient line breaks-- insert newline.
+ */
+ target=AcquireString(*caption);
+ n=p-(*caption);
+ CopyMagickString(target,*caption,n+1);
+ ConcatenateMagickString(target,"\n",strlen(*caption)+1);
+ ConcatenateMagickString(target,p,strlen(*caption)+2);
+ (void) DestroyString(*caption);
+ *caption=target;
+ p=(*caption)+n;
+ }
+ s=(char *) NULL;
+ q=draw_info->text;
+ }
+ i=0;
+ for (p=(*caption); GetUTFCode(p) != 0; p+=GetUTFOctets(p))
+ if (GetUTFCode(p) == '\n')
+ i++;
+ return(i);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M u l t i l i n e T y p e M e t r i c s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMultilineTypeMetrics() returns the following information for the
+% specified font and text:
+%
+% character width
+% character height
+% ascender
+% descender
+% text width
+% text height
+% maximum horizontal advance
+% bounds: x1
+% bounds: y1
+% bounds: x2
+% bounds: y2
+% origin: x
+% origin: y
+% underline position
+% underline thickness
+%
+% This method is like GetTypeMetrics() but it returns the maximum text width
+% and height for multiple lines of text.
+%
+% The format of the GetMultilineTypeMetrics method is:
+%
+% MagickBooleanType GetMultilineTypeMetrics(Image *image,
+% const DrawInfo *draw_info,TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o metrics: Return the font metrics in this structure.
+%
+*/
+MagickExport MagickBooleanType GetMultilineTypeMetrics(Image *image,
+ const DrawInfo *draw_info,TypeMetric *metrics)
+{
+ char
+ **textlist;
+
+ DrawInfo
+ *annotate_info;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ TypeMetric
+ extent;
+
+ unsigned long
+ number_lines;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->text != (char *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if (*draw_info->text == '\0')
+ return(MagickFalse);
+ annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ annotate_info->text=DestroyString(annotate_info->text);
+ /*
+ Convert newlines to multiple lines of text.
+ */
+ textlist=StringToList(draw_info->text);
+ if (textlist == (char **) NULL)
+ return(MagickFalse);
+ annotate_info->render=MagickFalse;
+ (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
+ (void) ResetMagickMemory(&extent,0,sizeof(extent));
+ /*
+ Find the widest of the text lines.
+ */
+ annotate_info->text=textlist[0];
+ status=GetTypeMetrics(image,annotate_info,&extent);
+ *metrics=extent;
+ for (i=1; textlist[i] != (char *) NULL; i++)
+ {
+ annotate_info->text=textlist[i];
+ status=GetTypeMetrics(image,annotate_info,&extent);
+ if (extent.width > metrics->width)
+ *metrics=extent;
+ }
+ number_lines=(unsigned long) i;
+ metrics->height=(double) number_lines*(long) (metrics->ascent-
+ metrics->descent+0.5);
+ /*
+ Relinquish resources.
+ */
+ annotate_info->text=(char *) NULL;
+ annotate_info=DestroyDrawInfo(annotate_info);
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ textlist[i]=DestroyString(textlist[i]);
+ textlist=(char **) RelinquishMagickMemory(textlist);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t T y p e M e t r i c s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTypeMetrics() returns the following information for the specified font
+% and text:
+%
+% character width
+% character height
+% ascender
+% descender
+% text width
+% text height
+% maximum horizontal advance
+% bounds: x1
+% bounds: y1
+% bounds: x2
+% bounds: y2
+% origin: x
+% origin: y
+% underline position
+% underline thickness
+%
+% The format of the GetTypeMetrics method is:
+%
+% MagickBooleanType GetTypeMetrics(Image *image,const DrawInfo *draw_info,
+% TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o metrics: Return the font metrics in this structure.
+%
+*/
+MagickExport MagickBooleanType GetTypeMetrics(Image *image,
+ const DrawInfo *draw_info,TypeMetric *metrics)
+{
+ DrawInfo
+ *annotate_info;
+
+ MagickBooleanType
+ status;
+
+ PointInfo
+ offset;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->text != (char *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ annotate_info->render=MagickFalse;
+ (void) ResetMagickMemory(metrics,0,sizeof(*metrics));
+ offset.x=0.0;
+ offset.y=0.0;
+ status=RenderType(image,annotate_info,&offset,metrics);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Metrics: text: %s; "
+ "width: %g; height: %g; ascent: %g; descent: %g; max advance: %g; "
+ "bounds: %g,%g %g,%g; origin: %g,%g; pixels per em: %g,%g; "
+ "underline position: %g; underline thickness: %g",annotate_info->text,
+ metrics->width,metrics->height,metrics->ascent,metrics->descent,
+ metrics->max_advance,metrics->bounds.x1,metrics->bounds.y1,
+ metrics->bounds.x2,metrics->bounds.y2,metrics->origin.x,metrics->origin.y,
+ metrics->pixels_per_em.x,metrics->pixels_per_em.y,
+ metrics->underline_position,metrics->underline_thickness);
+ annotate_info=DestroyDrawInfo(annotate_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e n d e r T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RenderType() renders text on the image. It also returns the bounding box of
+% the text relative to the image.
+%
+% The format of the RenderType method is:
+%
+% MagickBooleanType RenderType(Image *image,DrawInfo *draw_info,
+% const PointInfo *offset,TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o offset: (x,y) location of text relative to image.
+%
+% o metrics: bounding box of text.
+%
+*/
+static MagickBooleanType RenderType(Image *image,const DrawInfo *draw_info,
+ const PointInfo *offset,TypeMetric *metrics)
+{
+ const TypeInfo
+ *type_info;
+
+ DrawInfo
+ *annotate_info;
+
+ MagickBooleanType
+ status;
+
+ type_info=(const TypeInfo *) NULL;
+ if (draw_info->font != (char *) NULL)
+ {
+ if (*draw_info->font == '@')
+ {
+ status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
+ metrics);
+ return(status);
+ }
+ if (*draw_info->font == '-')
+ return(RenderX11(image,draw_info,offset,metrics));
+ if (IsPathAccessible(draw_info->font) != MagickFalse)
+ {
+ status=RenderFreetype(image,draw_info,draw_info->encoding,offset,
+ metrics);
+ return(status);
+ }
+ type_info=GetTypeInfo(draw_info->font,&image->exception);
+ if (type_info == (const TypeInfo *) NULL)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ TypeWarning,"UnableToReadFont","`%s'",draw_info->font);
+ }
+ if ((type_info == (const TypeInfo *) NULL) &&
+ (draw_info->family != (const char *) NULL))
+ {
+ type_info=GetTypeInfoByFamily(draw_info->family,draw_info->style,
+ draw_info->stretch,draw_info->weight,&image->exception);
+ if (type_info == (const TypeInfo *) NULL)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ TypeWarning,"UnableToReadFont","`%s'",draw_info->family);
+ }
+ if (type_info == (const TypeInfo *) NULL)
+ type_info=GetTypeInfoByFamily("arial",draw_info->style,
+ draw_info->stretch,draw_info->weight,&image->exception);
+ if (type_info == (const TypeInfo *) NULL)
+ type_info=GetTypeInfoByFamily("helvetica",draw_info->style,
+ draw_info->stretch,draw_info->weight,&image->exception);
+ if (type_info == (const TypeInfo *) NULL)
+ type_info=GetTypeInfoByFamily((const char *) NULL,draw_info->style,
+ draw_info->stretch,draw_info->weight,&image->exception);
+ if (type_info == (const TypeInfo *) NULL)
+ {
+ status=RenderFreetype(image,draw_info,draw_info->encoding,offset,metrics);
+ return(status);
+ }
+ annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ annotate_info->face=type_info->face;
+ if (type_info->metrics != (char *) NULL)
+ (void) CloneString(&annotate_info->metrics,type_info->metrics);
+ if (type_info->glyphs != (char *) NULL)
+ (void) CloneString(&annotate_info->font,type_info->glyphs);
+ status=RenderFreetype(image,annotate_info,type_info->encoding,offset,metrics);
+ annotate_info=DestroyDrawInfo(annotate_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e n d e r F r e e t y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RenderFreetype() renders text on the image with a Truetype font. It also
+% returns the bounding box of the text relative to the image.
+%
+% The format of the RenderFreetype method is:
+%
+% MagickBooleanType RenderFreetype(Image *image,DrawInfo *draw_info,
+% const char *encoding,const PointInfo *offset,TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o encoding: the font encoding.
+%
+% o offset: (x,y) location of text relative to image.
+%
+% o metrics: bounding box of text.
+%
+*/
+
+#if defined(MAGICKCORE_FREETYPE_DELEGATE)
+static int TraceCubicBezier(FT_Vector *p,FT_Vector *q,FT_Vector *to,
+ DrawInfo *draw_info)
+{
+ AffineMatrix
+ affine;
+
+ char
+ path[MaxTextExtent];
+
+ affine=draw_info->affine;
+ (void) FormatMagickString(path,MaxTextExtent,"C%g,%g %g,%g %g,%g",
+ affine.tx+p->x/64.0,affine.ty-p->y/64.0,affine.tx+q->x/64.0,
+ affine.ty-q->y/64.0,affine.tx+to->x/64.0,affine.ty-to->y/64.0);
+ (void) ConcatenateString(&draw_info->primitive,path);
+ return(0);
+}
+
+static int TraceLineTo(FT_Vector *to,DrawInfo *draw_info)
+{
+ AffineMatrix
+ affine;
+
+ char
+ path[MaxTextExtent];
+
+ affine=draw_info->affine;
+ (void) FormatMagickString(path,MaxTextExtent,"L%g,%g",affine.tx+to->x/64.0,
+ affine.ty-to->y/64.0);
+ (void) ConcatenateString(&draw_info->primitive,path);
+ return(0);
+}
+
+static int TraceMoveTo(FT_Vector *to,DrawInfo *draw_info)
+{
+ AffineMatrix
+ affine;
+
+ char
+ path[MaxTextExtent];
+
+ affine=draw_info->affine;
+ (void) FormatMagickString(path,MaxTextExtent,"M%g,%g",affine.tx+to->x/64.0,
+ affine.ty-to->y/64.0);
+ (void) ConcatenateString(&draw_info->primitive,path);
+ return(0);
+}
+
+static int TraceQuadraticBezier(FT_Vector *control,FT_Vector *to,
+ DrawInfo *draw_info)
+{
+ AffineMatrix
+ affine;
+
+ char
+ path[MaxTextExtent];
+
+ affine=draw_info->affine;
+ (void) FormatMagickString(path,MaxTextExtent,"Q%g,%g %g,%g",
+ affine.tx+control->x/64.0,affine.ty-control->y/64.0,affine.tx+to->x/64.0,
+ affine.ty-to->y/64.0);
+ (void) ConcatenateString(&draw_info->primitive,path);
+ return(0);
+}
+
+static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
+ const char *encoding,const PointInfo *offset,TypeMetric *metrics)
+{
+#if !defined(FT_OPEN_PATHNAME)
+#define FT_OPEN_PATHNAME ft_open_pathname
+#endif
+
+ typedef struct _GlyphInfo
+ {
+ FT_UInt
+ id;
+
+ FT_Vector
+ origin;
+
+ FT_Glyph
+ image;
+ } GlyphInfo;
+
+ const char
+ *value;
+
+ DrawInfo
+ *annotate_info;
+
+ FT_BBox
+ bounds;
+
+ FT_BitmapGlyph
+ bitmap;
+
+ FT_Encoding
+ encoding_type;
+
+ FT_Error
+ status;
+
+ FT_Face
+ face;
+
+ FT_Int32
+ flags;
+
+ FT_Library
+ library;
+
+ FT_Matrix
+ affine;
+
+ FT_Open_Args
+ args;
+
+ FT_Vector
+ origin;
+
+ GlyphInfo
+ glyph,
+ last_glyph;
+
+ long
+ code,
+ y;
+
+ PointInfo
+ point,
+ resolution;
+
+ register char
+ *p;
+
+ static FT_Outline_Funcs
+ OutlineMethods =
+ {
+ (FT_Outline_MoveTo_Func) TraceMoveTo,
+ (FT_Outline_LineTo_Func) TraceLineTo,
+ (FT_Outline_ConicTo_Func) TraceQuadraticBezier,
+ (FT_Outline_CubicTo_Func) TraceCubicBezier,
+ 0, 0
+ };
+
+ /*
+ Initialize Truetype library.
+ */
+ status=FT_Init_FreeType(&library);
+ if (status != 0)
+ ThrowBinaryException(TypeError,"UnableToInitializeFreetypeLibrary",
+ image->filename);
+ args.flags=FT_OPEN_PATHNAME;
+ if (draw_info->font == (char *) NULL)
+ args.pathname=ConstantString("helvetica");
+ else
+ if (*draw_info->font != '@')
+ args.pathname=ConstantString(draw_info->font);
+ else
+ args.pathname=ConstantString(draw_info->font+1);
+ face=(FT_Face) NULL;
+ status=FT_Open_Face(library,&args,draw_info->face,&face);
+ args.pathname=DestroyString(args.pathname);
+ if (status != 0)
+ {
+ (void) FT_Done_FreeType(library);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ TypeError,"UnableToReadFont","`%s'",draw_info->font);
+ return(RenderPostscript(image,draw_info,offset,metrics));
+ }
+ if ((draw_info->metrics != (char *) NULL) &&
+ (IsPathAccessible(draw_info->metrics) != MagickFalse))
+ (void) FT_Attach_File(face,draw_info->metrics);
+ encoding_type=ft_encoding_unicode;
+ status=FT_Select_Charmap(face,encoding_type);
+ if ((status != 0) && (face->num_charmaps != 0))
+ status=FT_Set_Charmap(face,face->charmaps[0]);
+ if (encoding != (const char *) NULL)
+ {
+ if (LocaleCompare(encoding,"AdobeCustom") == 0)
+ encoding_type=ft_encoding_adobe_custom;
+ if (LocaleCompare(encoding,"AdobeExpert") == 0)
+ encoding_type=ft_encoding_adobe_expert;
+ if (LocaleCompare(encoding,"AdobeStandard") == 0)
+ encoding_type=ft_encoding_adobe_standard;
+ if (LocaleCompare(encoding,"AppleRoman") == 0)
+ encoding_type=ft_encoding_apple_roman;
+ if (LocaleCompare(encoding,"BIG5") == 0)
+ encoding_type=ft_encoding_big5;
+ if (LocaleCompare(encoding,"GB2312") == 0)
+ encoding_type=ft_encoding_gb2312;
+ if (LocaleCompare(encoding,"Johab") == 0)
+ encoding_type=ft_encoding_johab;
+#if defined(ft_encoding_latin_1)
+ if (LocaleCompare(encoding,"Latin-1") == 0)
+ encoding_type=ft_encoding_latin_1;
+#endif
+ if (LocaleCompare(encoding,"Latin-2") == 0)
+ encoding_type=ft_encoding_latin_2;
+ if (LocaleCompare(encoding,"None") == 0)
+ encoding_type=ft_encoding_none;
+ if (LocaleCompare(encoding,"SJIScode") == 0)
+ encoding_type=ft_encoding_sjis;
+ if (LocaleCompare(encoding,"Symbol") == 0)
+ encoding_type=ft_encoding_symbol;
+ if (LocaleCompare(encoding,"Unicode") == 0)
+ encoding_type=ft_encoding_unicode;
+ if (LocaleCompare(encoding,"Wansung") == 0)
+ encoding_type=ft_encoding_wansung;
+ status=FT_Select_Charmap(face,encoding_type);
+ if (status != 0)
+ ThrowBinaryException(TypeError,"UnrecognizedFontEncoding",encoding);
+ }
+ /*
+ Set text size.
+ */
+ resolution.x=DefaultResolution;
+ resolution.y=DefaultResolution;
+ if (draw_info->density != (char *) NULL)
+ {
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ flags=ParseGeometry(draw_info->density,&geometry_info);
+ resolution.x=geometry_info.rho;
+ resolution.y=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ resolution.y=resolution.x;
+ }
+ status=FT_Set_Char_Size(face,(FT_F26Dot6) (64.0*draw_info->pointsize),
+ (FT_F26Dot6) (64.0*draw_info->pointsize),(FT_UInt) resolution.x,
+ (FT_UInt) resolution.y);
+ metrics->pixels_per_em.x=face->size->metrics.x_ppem;
+ metrics->pixels_per_em.y=face->size->metrics.y_ppem;
+ metrics->ascent=(double) face->size->metrics.ascender/64.0;
+ metrics->descent=(double) face->size->metrics.descender/64.0;
+ metrics->width=0;
+ metrics->origin.x=0;
+ metrics->origin.y=0;
+ metrics->height=(double) face->size->metrics.height/64.0;
+ metrics->max_advance=0.0;
+ if (face->size->metrics.max_advance > MagickEpsilon)
+ metrics->max_advance=(double) face->size->metrics.max_advance/64.0;
+ metrics->bounds.x1=0.0;
+ metrics->bounds.y1=metrics->descent;
+ metrics->bounds.x2=metrics->ascent+metrics->descent;
+ metrics->bounds.y2=metrics->ascent+metrics->descent;
+ metrics->underline_position=face->underline_position/64.0;
+ metrics->underline_thickness=face->underline_thickness/64.0;
+ if (*draw_info->text == '\0')
+ {
+ (void) FT_Done_Face(face);
+ (void) FT_Done_FreeType(library);
+ return(MagickTrue);
+ }
+ /*
+ Compute bounding box.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),"Font %s; "
+ "font-encoding %s; text-encoding %s; pointsize %g",
+ draw_info->font != (char *) NULL ? draw_info->font : "none",
+ encoding != (char *) NULL ? encoding : "none",
+ draw_info->encoding != (char *) NULL ? draw_info->encoding : "none",
+ draw_info->pointsize);
+ flags=FT_LOAD_NO_BITMAP;
+ value=GetImageProperty(image,"type:hinting");
+ if (LocaleCompare(value,"off") == 0)
+ flags|=FT_LOAD_NO_HINTING;
+ glyph.id=0;
+ glyph.image=NULL;
+ last_glyph.id=0;
+ last_glyph.image=NULL;
+ origin.x=0;
+ origin.y=0;
+ affine.xx=65536L;
+ affine.yx=0L;
+ affine.xy=0L;
+ affine.yy=65536L;
+ if (draw_info->render != MagickFalse)
+ {
+ affine.xx=(FT_Fixed) (65536L*draw_info->affine.sx+0.5);
+ affine.yx=(FT_Fixed) (-65536L*draw_info->affine.rx+0.5);
+ affine.xy=(FT_Fixed) (-65536L*draw_info->affine.ry+0.5);
+ affine.yy=(FT_Fixed) (65536L*draw_info->affine.sy+0.5);
+ }
+ annotate_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ (void) CloneString(&annotate_info->primitive,"path '");
+ if (draw_info->render != MagickFalse)
+ {
+ if (image->storage_class != DirectClass)
+ (void) SetImageStorageClass(image,DirectClass);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ }
+ point.x=0.0;
+ point.y=0.0;
+ code=0;
+ for (p=draw_info->text; GetUTFCode(p) != 0; p+=GetUTFOctets(p))
+ {
+ glyph.id=FT_Get_Char_Index(face,GetUTFCode(p));
+ if (glyph.id == 0)
+ glyph.id=FT_Get_Char_Index(face,'?');
+ if ((glyph.id != 0) && (last_glyph.id != 0))
+ {
+ if (draw_info->kerning != 0.0)
+ origin.x+=64.0*draw_info->kerning;
+ else
+ if (FT_HAS_KERNING(face))
+ {
+ FT_Vector
+ kerning;
+
+ status=FT_Get_Kerning(face,last_glyph.id,glyph.id,
+ ft_kerning_default,&kerning);
+ if (status == 0)
+ origin.x+=kerning.x;
+ }
+ }
+ glyph.origin=origin;
+ status=FT_Load_Glyph(face,glyph.id,flags);
+ if (status != 0)
+ continue;
+ status=FT_Get_Glyph(face->glyph,&glyph.image);
+ if (status != 0)
+ continue;
+ status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->outline,
+ &bounds);
+ if (status != 0)
+ continue;
+ if ((p == draw_info->text) || (bounds.xMin < metrics->bounds.x1))
+ metrics->bounds.x1=bounds.xMin;
+ if ((p == draw_info->text) || (bounds.yMin < metrics->bounds.y1))
+ metrics->bounds.y1=bounds.yMin;
+ if ((p == draw_info->text) || (bounds.xMax > metrics->bounds.x2))
+ metrics->bounds.x2=bounds.xMax;
+ if ((p == draw_info->text) || (bounds.yMax > metrics->bounds.y2))
+ metrics->bounds.y2=bounds.yMax;
+ if (draw_info->render != MagickFalse)
+ if ((draw_info->stroke.opacity != TransparentOpacity) ||
+ (draw_info->stroke_pattern != (Image *) NULL))
+ {
+ /*
+ Trace the glyph.
+ */
+ annotate_info->affine.tx=glyph.origin.x/64.0;
+ annotate_info->affine.ty=glyph.origin.y/64.0;
+ (void) FT_Outline_Decompose(&((FT_OutlineGlyph) glyph.image)->outline,
+ &OutlineMethods,annotate_info);
+ }
+ FT_Vector_Transform(&glyph.origin,&affine);
+ (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
+ status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
+ (FT_Vector *) NULL,MagickTrue);
+ if (status != 0)
+ continue;
+ bitmap=(FT_BitmapGlyph) glyph.image;
+ point.x=offset->x+bitmap->left;
+ point.y=offset->y-bitmap->top;
+ if (draw_info->render != MagickFalse)
+ {
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Rasterize the glyph.
+ */
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) bitmap->bitmap.rows; y++)
+ {
+ long
+ x_offset,
+ y_offset;
+
+ MagickBooleanType
+ active,
+ sync;
+
+ MagickRealType
+ fill_opacity;
+
+ PixelPacket
+ fill_color;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ register unsigned char
+ *p;
+
+ if (status == MagickFalse)
+ continue;
+ x_offset=(long) (point.x+0.5);
+ y_offset=(long) (point.y+y+0.5);
+ if ((y_offset < 0) || (y_offset >= (long) image->rows))
+ continue;
+ q=(PixelPacket *) NULL;
+ if ((x_offset < 0) || (x_offset >= (long) image->columns))
+ active=MagickFalse;
+ else
+ {
+ q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,
+ bitmap->bitmap.width,1,exception);
+ active=q != (PixelPacket *) NULL ? MagickTrue : MagickFalse;
+ }
+ p=bitmap->bitmap.buffer+y*bitmap->bitmap.width;
+ for (x=0; x < (long) bitmap->bitmap.width; x++)
+ {
+ x_offset++;
+ if ((*p == 0) || (x_offset < 0) ||
+ (x_offset >= (long) image->columns))
+ {
+ p++;
+ q++;
+ continue;
+ }
+ fill_opacity=(MagickRealType) (*p)/(bitmap->bitmap.num_grays-1);
+ if (draw_info->text_antialias == MagickFalse)
+ fill_opacity=fill_opacity >= 0.5 ? 1.0 : 0.0;
+ if (active == MagickFalse)
+ q=GetCacheViewAuthenticPixels(image_view,x_offset,y_offset,1,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ p++;
+ q++;
+ continue;
+ }
+ (void) GetFillColor(draw_info,x_offset,y_offset,&fill_color);
+ fill_opacity=QuantumRange-fill_opacity*(QuantumRange-
+ fill_color.opacity);
+ MagickCompositeOver(&fill_color,fill_opacity,q,q->opacity,q);
+ if (active == MagickFalse)
+ {
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ }
+ if ((bitmap->left+bitmap->bitmap.width) > metrics->width)
+ metrics->width=bitmap->left+bitmap->bitmap.width;
+ if ((draw_info->interword_spacing != 0.0) &&
+ (IsUTFSpace(GetUTFCode(p)) != MagickFalse) &&
+ (IsUTFSpace(code) == MagickFalse))
+ origin.x+=64.0*draw_info->interword_spacing;
+ else
+ origin.x+=face->glyph->advance.x;
+ metrics->origin.x=origin.x;
+ metrics->origin.y=origin.y;
+ if (last_glyph.id != 0)
+ FT_Done_Glyph(last_glyph.image);
+ last_glyph=glyph;
+ code=GetUTFCode(p);
+ }
+ if (last_glyph.id != 0)
+ FT_Done_Glyph(last_glyph.image);
+ if ((draw_info->stroke.opacity != TransparentOpacity) ||
+ (draw_info->stroke_pattern != (Image *) NULL))
+ {
+ if (draw_info->render != MagickFalse)
+ {
+ /*
+ Draw text stroke.
+ */
+ annotate_info->linejoin=RoundJoin;
+ annotate_info->affine.tx=offset->x;
+ annotate_info->affine.ty=offset->y;
+ (void) ConcatenateString(&annotate_info->primitive,"'");
+ (void) DrawImage(image,annotate_info);
+ }
+ }
+ /*
+ Determine font metrics.
+ */
+ glyph.id=FT_Get_Char_Index(face,'_');
+ glyph.origin=origin;
+ status=FT_Load_Glyph(face,glyph.id,flags);
+ if (status == 0)
+ {
+ status=FT_Get_Glyph(face->glyph,&glyph.image);
+ if (status == 0)
+ {
+ status=FT_Outline_Get_BBox(&((FT_OutlineGlyph) glyph.image)->
+ outline,&bounds);
+ if (status == 0)
+ {
+ FT_Vector_Transform(&glyph.origin,&affine);
+ (void) FT_Glyph_Transform(glyph.image,&affine,&glyph.origin);
+ status=FT_Glyph_To_Bitmap(&glyph.image,ft_render_mode_normal,
+ (FT_Vector *) NULL,MagickTrue);
+ bitmap=(FT_BitmapGlyph) glyph.image;
+ if (bitmap->left > metrics->width)
+ metrics->width=bitmap->left;
+ }
+ }
+ if (glyph.id != 0)
+ FT_Done_Glyph(glyph.image);
+ }
+ metrics->width-=metrics->bounds.x1/64.0;
+ metrics->bounds.x1/=64.0;
+ metrics->bounds.y1/=64.0;
+ metrics->bounds.x2/=64.0;
+ metrics->bounds.y2/=64.0;
+ metrics->origin.x/=64.0;
+ metrics->origin.y/=64.0;
+ /*
+ Relinquish resources.
+ */
+ annotate_info=DestroyDrawInfo(annotate_info);
+ (void) FT_Done_Face(face);
+ (void) FT_Done_FreeType(library);
+ return(MagickTrue);
+}
+#else
+static MagickBooleanType RenderFreetype(Image *image,const DrawInfo *draw_info,
+ const char *magick_unused(encoding),const PointInfo *offset,
+ TypeMetric *metrics)
+{
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (Freetype)",
+ draw_info->font);
+ return(RenderPostscript(image,draw_info,offset,metrics));
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e n d e r P o s t s c r i p t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RenderPostscript() renders text on the image with a Postscript font. It
+% also returns the bounding box of the text relative to the image.
+%
+% The format of the RenderPostscript method is:
+%
+% MagickBooleanType RenderPostscript(Image *image,DrawInfo *draw_info,
+% const PointInfo *offset,TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o offset: (x,y) location of text relative to image.
+%
+% o metrics: bounding box of text.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static char *EscapeParenthesis(const char *text)
+{
+ char
+ *buffer;
+
+ register char
+ *p;
+
+ register long
+ i;
+
+ size_t
+ escapes;
+
+ escapes=0;
+ buffer=AcquireString(text);
+ p=buffer;
+ for (i=0; i < (long) MagickMin(strlen(text),MaxTextExtent-escapes-1); i++)
+ {
+ if ((text[i] == '(') || (text[i] == ')'))
+ {
+ *p++='\\';
+ escapes++;
+ }
+ *p++=text[i];
+ }
+ *p='\0';
+ return(buffer);
+}
+
+static MagickBooleanType RenderPostscript(Image *image,
+ const DrawInfo *draw_info,const PointInfo *offset,TypeMetric *metrics)
+{
+ char
+ filename[MaxTextExtent],
+ geometry[MaxTextExtent],
+ *text;
+
+ FILE
+ *file;
+
+ Image
+ *annotate_image;
+
+ ImageInfo
+ *annotate_info;
+
+ int
+ unique_file;
+
+ long
+ y;
+
+ MagickBooleanType
+ identity;
+
+ PointInfo
+ extent,
+ point,
+ resolution;
+
+ register long
+ i;
+
+ /*
+ Render label with a Postscript font.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
+ "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
+ draw_info->font : "none",draw_info->pointsize);
+ file=(FILE *) NULL;
+ unique_file=AcquireUniqueFileResource(filename);
+ if (unique_file != -1)
+ file=fdopen(unique_file,"wb");
+ if ((unique_file == -1) || (file == (FILE *) NULL))
+ {
+ ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
+ filename);
+ return(MagickFalse);
+ }
+ (void) fprintf(file,"%%!PS-Adobe-3.0\n");
+ (void) fprintf(file,"/ReencodeType\n");
+ (void) fprintf(file,"{\n");
+ (void) fprintf(file," findfont dup length\n");
+ (void) fprintf(file,
+ " dict begin { 1 index /FID ne {def} {pop pop} ifelse } forall\n");
+ (void) fprintf(file,
+ " /Encoding ISOLatin1Encoding def currentdict end definefont pop\n");
+ (void) fprintf(file,"} bind def\n");
+ /*
+ Sample to compute bounding box.
+ */
+ identity=(draw_info->affine.sx == draw_info->affine.sy) &&
+ (draw_info->affine.rx == 0.0) && (draw_info->affine.ry == 0.0) ?
+ MagickTrue : MagickFalse;
+ extent.x=0.0;
+ extent.y=0.0;
+ for (i=0; i <= (long) (strlen(draw_info->text)+2); i++)
+ {
+ point.x=fabs(draw_info->affine.sx*i*draw_info->pointsize+
+ draw_info->affine.ry*2.0*draw_info->pointsize);
+ point.y=fabs(draw_info->affine.rx*i*draw_info->pointsize+
+ draw_info->affine.sy*2.0*draw_info->pointsize);
+ if (point.x > extent.x)
+ extent.x=point.x;
+ if (point.y > extent.y)
+ extent.y=point.y;
+ }
+ (void) fprintf(file,"%g %g moveto\n",identity != MagickFalse ? 0.0 :
+ extent.x/2.0,extent.y/2.0);
+ (void) fprintf(file,"%g %g scale\n",draw_info->pointsize,
+ draw_info->pointsize);
+ if ((draw_info->font == (char *) NULL) || (*draw_info->font == '\0') ||
+ (strchr(draw_info->font,'/') != (char *) NULL))
+ (void) fprintf(file,
+ "/Times-Roman-ISO dup /Times-Roman ReencodeType findfont setfont\n");
+ else
+ (void) fprintf(file,"/%s-ISO dup /%s ReencodeType findfont setfont\n",
+ draw_info->font,draw_info->font);
+ (void) fprintf(file,"[%g %g %g %g 0 0] concat\n",draw_info->affine.sx,
+ -draw_info->affine.rx,-draw_info->affine.ry,draw_info->affine.sy);
+ text=EscapeParenthesis(draw_info->text);
+ if (identity == MagickFalse)
+ (void) fprintf(file,"(%s) stringwidth pop -0.5 mul -0.5 rmoveto\n",text);
+ (void) fprintf(file,"(%s) show\n",text);
+ text=DestroyString(text);
+ (void) fprintf(file,"showpage\n");
+ (void) fclose(file);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ldx%ld+0+0!",(long)
+ (extent.x+0.5),(long) (extent.y+0.5));
+ annotate_info=AcquireImageInfo();
+ (void) FormatMagickString(annotate_info->filename,MaxTextExtent,"ps:%s",
+ filename);
+ (void) CloneString(&annotate_info->page,geometry);
+ if (draw_info->density != (char *) NULL)
+ (void) CloneString(&annotate_info->density,draw_info->density);
+ annotate_info->antialias=draw_info->text_antialias;
+ annotate_image=ReadImage(annotate_info,&image->exception);
+ CatchException(&image->exception);
+ annotate_info=DestroyImageInfo(annotate_info);
+ (void) RelinquishUniqueFileResource(filename);
+ if (annotate_image == (Image *) NULL)
+ return(MagickFalse);
+ resolution.x=DefaultResolution;
+ resolution.y=DefaultResolution;
+ if (draw_info->density != (char *) NULL)
+ {
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ flags=ParseGeometry(draw_info->density,&geometry_info);
+ resolution.x=geometry_info.rho;
+ resolution.y=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ resolution.y=resolution.x;
+ }
+ if (identity == MagickFalse)
+ (void) TransformImage(&annotate_image,"0x0",(char *) NULL);
+ else
+ {
+ RectangleInfo
+ crop_info;
+
+ crop_info=GetImageBoundingBox(annotate_image,&annotate_image->exception);
+ crop_info.height=(unsigned long) ((resolution.y/DefaultResolution)*
+ ExpandAffine(&draw_info->affine)*draw_info->pointsize+0.5);
+ crop_info.y=(long) ((resolution.y/DefaultResolution)*extent.y/8.0+0.5);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
+ crop_info.width,crop_info.height,crop_info.x,crop_info.y);
+ (void) TransformImage(&annotate_image,geometry,(char *) NULL);
+ }
+ metrics->pixels_per_em.x=(resolution.y/DefaultResolution)*
+ ExpandAffine(&draw_info->affine)*draw_info->pointsize;
+ metrics->pixels_per_em.y=metrics->pixels_per_em.x;
+ metrics->ascent=metrics->pixels_per_em.x;
+ metrics->descent=metrics->pixels_per_em.y/-5.0;
+ metrics->width=(double) annotate_image->columns/
+ ExpandAffine(&draw_info->affine);
+ metrics->height=1.152*metrics->pixels_per_em.x;
+ metrics->max_advance=metrics->pixels_per_em.x;
+ metrics->bounds.x1=0.0;
+ metrics->bounds.y1=metrics->descent;
+ metrics->bounds.x2=metrics->ascent+metrics->descent;
+ metrics->bounds.y2=metrics->ascent+metrics->descent;
+ metrics->underline_position=(-2.0);
+ metrics->underline_thickness=1.0;
+ if (draw_info->render == MagickFalse)
+ {
+ annotate_image=DestroyImage(annotate_image);
+ return(MagickTrue);
+ }
+ if (draw_info->fill.opacity != TransparentOpacity)
+ {
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ sync;
+
+ PixelPacket
+ fill_color;
+
+ CacheView
+ *annotate_view;
+
+ /*
+ Render fill color.
+ */
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ if (annotate_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(annotate_image,OpaqueAlphaChannel);
+ fill_color=draw_info->fill;
+ exception=(&image->exception);
+ annotate_view=AcquireCacheView(annotate_image);
+ for (y=0; y < (long) annotate_image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(annotate_view,0,y,annotate_image->columns,
+ 1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) annotate_image->columns; x++)
+ {
+ (void) GetFillColor(draw_info,x,y,&fill_color);
+ q->opacity=RoundToQuantum(QuantumRange-(((QuantumRange-
+ (MagickRealType) PixelIntensityToQuantum(q))*(QuantumRange-
+ fill_color.opacity))/QuantumRange));
+ q->red=fill_color.red;
+ q->green=fill_color.green;
+ q->blue=fill_color.blue;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(annotate_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ annotate_view=DestroyCacheView(annotate_view);
+ (void) CompositeImage(image,OverCompositeOp,annotate_image,
+ (long) (offset->x+0.5),(long) (offset->y-(metrics->ascent+
+ metrics->descent)+0.5));
+ }
+ annotate_image=DestroyImage(annotate_image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e n d e r X 1 1 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RenderX11() renders text on the image with an X11 font. It also returns the
+% bounding box of the text relative to the image.
+%
+% The format of the RenderX11 method is:
+%
+% MagickBooleanType RenderX11(Image *image,DrawInfo *draw_info,
+% const PointInfo *offset,TypeMetric *metrics)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o offset: (x,y) location of text relative to image.
+%
+% o metrics: bounding box of text.
+%
+*/
+#if defined(MAGICKCORE_X11_DELEGATE)
+static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
+ const PointInfo *offset,TypeMetric *metrics)
+{
+ MagickBooleanType
+ status;
+
+ static DrawInfo
+ cache_info;
+
+ static Display
+ *display = (Display *) NULL;
+
+ static XAnnotateInfo
+ annotate_info;
+
+ static XFontStruct
+ *font_info;
+
+ static XPixelInfo
+ pixel;
+
+ static XResourceInfo
+ resource_info;
+
+ static XrmDatabase
+ resource_database;
+
+ static XStandardColormap
+ *map_info;
+
+ static XVisualInfo
+ *visual_info;
+
+ unsigned long
+ height,
+ width;
+
+ if (display == (Display *) NULL)
+ {
+ ImageInfo
+ *image_info;
+
+ /*
+ Open X server connection.
+ */
+ display=XOpenDisplay(draw_info->server_name);
+ if (display == (Display *) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToOpenXServer",
+ draw_info->server_name);
+ return(MagickFalse);
+ }
+ /*
+ Get user defaults from X resource database.
+ */
+ (void) XSetErrorHandler(XError);
+ image_info=AcquireImageInfo();
+ resource_database=XGetResourceDatabase(display,GetClientName());
+ XGetResourceInfo(image_info,resource_database,GetClientName(),
+ &resource_info);
+ resource_info.close_server=MagickFalse;
+ resource_info.colormap=PrivateColormap;
+ resource_info.font=AcquireString(draw_info->font);
+ resource_info.background_color=AcquireString("#ffffffffffff");
+ resource_info.foreground_color=AcquireString("#000000000000");
+ map_info=XAllocStandardColormap();
+ if (map_info == (XStandardColormap *) NULL)
+ {
+ ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ return(MagickFalse);
+ }
+ /*
+ Initialize visual info.
+ */
+ visual_info=XBestVisualInfo(display,map_info,&resource_info);
+ if (visual_info == (XVisualInfo *) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToGetVisual",
+ image->filename);
+ return(MagickFalse);
+ }
+ map_info->colormap=(Colormap) NULL;
+ pixel.pixels=(unsigned long *) NULL;
+ /*
+ Initialize Standard Colormap info.
+ */
+ XGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen),
+ map_info);
+ XGetPixelPacket(display,visual_info,map_info,&resource_info,
+ (Image *) NULL,&pixel);
+ pixel.annotate_context=XDefaultGC(display,visual_info->screen);
+ /*
+ Initialize font info.
+ */
+ font_info=XBestFont(display,&resource_info,MagickFalse);
+ if (font_info == (XFontStruct *) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToLoadFont",
+ draw_info->font);
+ return(MagickFalse);
+ }
+ if ((map_info == (XStandardColormap *) NULL) ||
+ (visual_info == (XVisualInfo *) NULL) ||
+ (font_info == (XFontStruct *) NULL))
+ {
+ XFreeResources(display,visual_info,map_info,&pixel,font_info,
+ &resource_info,(XWindowInfo *) NULL);
+ ThrowXWindowException(XServerError,"UnableToLoadFont",
+ image->filename);
+ return(MagickFalse);
+ }
+ cache_info=(*draw_info);
+ }
+ /*
+ Initialize annotate info.
+ */
+ XGetAnnotateInfo(&annotate_info);
+ annotate_info.stencil=ForegroundStencil;
+ if (cache_info.font != draw_info->font)
+ {
+ /*
+ Type name has changed.
+ */
+ (void) XFreeFont(display,font_info);
+ (void) CloneString(&resource_info.font,draw_info->font);
+ font_info=XBestFont(display,&resource_info,MagickFalse);
+ if (font_info == (XFontStruct *) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToLoadFont",
+ draw_info->font);
+ return(MagickFalse);
+ }
+ }
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(AnnotateEvent,GetMagickModule(),
+ "Font %s; pointsize %g",draw_info->font != (char *) NULL ?
+ draw_info->font : "none",draw_info->pointsize);
+ cache_info=(*draw_info);
+ annotate_info.font_info=font_info;
+ annotate_info.text=(char *) draw_info->text;
+ annotate_info.width=(unsigned int) XTextWidth(font_info,draw_info->text,
+ (int) strlen(draw_info->text));
+ annotate_info.height=(unsigned int) font_info->ascent+font_info->descent;
+ metrics->pixels_per_em.x=(double) font_info->max_bounds.width;
+ metrics->pixels_per_em.y=(double) font_info->ascent+font_info->descent;
+ metrics->ascent=(double) font_info->ascent+4;
+ metrics->descent=(double) (-font_info->descent);
+ metrics->width=annotate_info.width/ExpandAffine(&draw_info->affine);
+ metrics->height=font_info->ascent+font_info->descent;
+ metrics->max_advance=(double) font_info->max_bounds.width;
+ metrics->bounds.x1=0.0;
+ metrics->bounds.y1=metrics->descent;
+ metrics->bounds.x2=metrics->ascent+metrics->descent;
+ metrics->bounds.y2=metrics->ascent+metrics->descent;
+ metrics->underline_position=(-2.0);
+ metrics->underline_thickness=1.0;
+ if (draw_info->render == MagickFalse)
+ return(MagickTrue);
+ if (draw_info->fill.opacity == TransparentOpacity)
+ return(MagickTrue);
+ /*
+ Render fill color.
+ */
+ width=annotate_info.width;
+ height=annotate_info.height;
+ if ((draw_info->affine.rx != 0.0) || (draw_info->affine.ry != 0.0))
+ {
+ if (((draw_info->affine.sx-draw_info->affine.sy) == 0.0) &&
+ ((draw_info->affine.rx+draw_info->affine.ry) == 0.0))
+ annotate_info.degrees=(180.0/MagickPI)*
+ atan2(draw_info->affine.rx,draw_info->affine.sx);
+ }
+ (void) FormatMagickString(annotate_info.geometry,MaxTextExtent,
+ "%lux%lu+%ld+%ld",width,height,(long) (offset->x+0.5),
+ (long) (offset->y-metrics->ascent-metrics->descent+0.5));
+ pixel.pen_color.red=ScaleQuantumToShort(draw_info->fill.red);
+ pixel.pen_color.green=ScaleQuantumToShort(draw_info->fill.green);
+ pixel.pen_color.blue=ScaleQuantumToShort(draw_info->fill.blue);
+ status=XAnnotateImage(display,&pixel,&annotate_info,image);
+ if (status == 0)
+ {
+ ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+#else
+static MagickBooleanType RenderX11(Image *image,const DrawInfo *draw_info,
+ const PointInfo *offset,TypeMetric *metrics)
+{
+ (void) draw_info;
+ (void) offset;
+ (void) metrics;
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
+ image->filename);
+ return(MagickFalse);
+}
+#endif
diff --git a/magick/annotate.h b/magick/annotate.h
new file mode 100644
index 0000000..d7db220
--- /dev/null
+++ b/magick/annotate.h
@@ -0,0 +1,39 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image annotation methods.
+*/
+#ifndef _MAGICKCORE_ANNOTATE_H
+#define _MAGICKCORE_ANNOTATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/draw.h"
+
+extern MagickExport long
+ FormatMagickCaption(Image *,DrawInfo *,TypeMetric *,char **);
+
+extern MagickExport MagickBooleanType
+ AnnotateImage(Image *,const DrawInfo *),
+ GetMultilineTypeMetrics(Image *,const DrawInfo *,TypeMetric *),
+ GetTypeMetrics(Image *,const DrawInfo *,TypeMetric *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/api.h b/magick/api.h
new file mode 100644
index 0000000..c3f3dc3
--- /dev/null
+++ b/magick/api.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ Deprecated as of ImageMagick 6.2.3.
+
+ MagickCore Application Programming Interface declarations.
+*/
+
+#ifndef _MAGICKCORE_API_DEPRECATED_H
+#define _MAGICKCORE_API_DEPRECATED_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/MagickCore.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/artifact.c b/magick/artifact.c
new file mode 100644
index 0000000..0b0f5f4
--- /dev/null
+++ b/magick/artifact.c
@@ -0,0 +1,448 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% AAA RRRR TTTTT IIIII FFFFF AAA CCCC TTTTT %
+% A A R R T I F A A C T %
+% AAAAA RRRRR T I FFF AAAAA C T %
+% A A R R T I F A A C T %
+% A A R R T IIIII F A A CCCCC T %
+% %
+% %
+% MagickCore Artifact Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/compare.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/fx-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/profile.h"
+#include "magick/quantum.h"
+#include "magick/resource_.h"
+#include "magick/splay-tree.h"
+#include "magick/signature-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e A r t i f a c t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageArtifacts() clones one or more image artifacts.
+%
+% The format of the CloneImageArtifacts method is:
+%
+% MagickBooleanType CloneImageArtifacts(Image *image,
+% const Image *clone_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clone_image: the clone image.
+%
+*/
+MagickExport MagickBooleanType CloneImageArtifacts(Image *image,
+ const Image *clone_image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(clone_image != (const Image *) NULL);
+ assert(clone_image->signature == MagickSignature);
+ if (clone_image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ clone_image->filename);
+ if (clone_image->artifacts != (void *) NULL)
+ image->artifacts=CloneSplayTree((SplayTreeInfo *) clone_image->artifacts,
+ (void *(*)(void *)) ConstantString,(void *(*)(void *)) ConstantString);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e f i n e I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageArtifact() associates a key/value pair with an image artifact.
+%
+% The format of the DefineImageArtifact method is:
+%
+% MagickBooleanType DefineImageArtifact(Image *image,
+% const char *artifact)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o artifact: the image artifact.
+%
+*/
+MagickExport MagickBooleanType DefineImageArtifact(Image *image,
+ const char *artifact)
+{
+ char
+ key[MaxTextExtent],
+ value[MaxTextExtent];
+
+ register char
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(artifact != (const char *) NULL);
+ (void) CopyMagickString(key,artifact,MaxTextExtent-1);
+ for (p=key; *p != '\0'; p++)
+ if (*p == '=')
+ break;
+ *value='\0';
+ if (*p == '=')
+ (void) CopyMagickString(value,p+1,MaxTextExtent);
+ *p='\0';
+ return(SetImageArtifact(image,key,value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageArtifact() deletes an image artifact.
+%
+% The format of the DeleteImageArtifact method is:
+%
+% MagickBooleanType DeleteImageArtifact(Image *image,const char *artifact)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o artifact: the image artifact.
+%
+*/
+MagickExport MagickBooleanType DeleteImageArtifact(Image *image,
+ const char *artifact)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts == (void *) NULL)
+ return(MagickFalse);
+ return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->artifacts,artifact));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e A r t i f a c t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageArtifacts() releases memory associated with image artifact
+% values.
+%
+% The format of the DestroyDefines method is:
+%
+% void DestroyImageArtifacts(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyImageArtifacts(Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts != (void *) NULL)
+ image->artifacts=(void *) DestroySplayTree((SplayTreeInfo *)
+ image->artifacts);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageArtifact() gets a value associated with an image artifact.
+%
+% The format of the GetImageArtifact method is:
+%
+% const char *GetImageArtifact(const Image *image,const char *key)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o key: the key.
+%
+*/
+MagickExport const char *GetImageArtifact(const Image *image,
+ const char *artifact)
+{
+ register const char
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ p=(const char *) NULL;
+ if (artifact == (const char *) NULL)
+ {
+ ResetSplayTreeIterator((SplayTreeInfo *) image->artifacts);
+ p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
+ image->artifacts);
+ return(p);
+ }
+ if (image->artifacts != (void *) NULL)
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->artifacts,artifact);
+ if (p != (const char *) NULL)
+ return(p);
+ }
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageArtifact() gets the next image artifact value.
+%
+% The format of the GetNextImageArtifact method is:
+%
+% char *GetNextImageArtifact(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport char *GetNextImageArtifact(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts == (void *) NULL)
+ return((char *) NULL);
+ return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->artifacts));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageArtifact() removes an artifact from the image and returns its
+% value.
+%
+% The format of the RemoveImageArtifact method is:
+%
+% char *RemoveImageArtifact(Image *image,const char *artifact)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o artifact: the image artifact.
+%
+*/
+MagickExport char *RemoveImageArtifact(Image *image,const char *artifact)
+{
+ char
+ *value;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts == (void *) NULL)
+ return((char *) NULL);
+ value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->artifacts,
+ artifact);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e A r t i f a c t I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageArtifactIterator() resets the image artifact iterator. Use it
+% in conjunction with GetNextImageArtifact() to iterate over all the values
+% associated with an image artifact.
+%
+% The format of the ResetImageArtifactIterator method is:
+%
+% ResetImageArtifactIterator(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void ResetImageArtifactIterator(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts == (void *) NULL)
+ return;
+ ResetSplayTreeIterator((SplayTreeInfo *) image->artifacts);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e A r t i f a c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageArtifact() associates a value with an image artifact.
+%
+% The format of the SetImageArtifact method is:
+%
+% MagickBooleanType SetImageArtifact(Image *image,const char *artifact,
+% const char *value)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o artifact: the image artifact.
+%
+% o values: the image artifact values.
+%
+*/
+MagickExport MagickBooleanType SetImageArtifact(Image *image,
+ const char *artifact,const char *value)
+{
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->artifacts == (void *) NULL)
+ image->artifacts=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,RelinquishMagickMemory);
+ if ((value == (const char *) NULL) || (*value == '\0'))
+ return(DeleteImageArtifact(image,artifact));
+ status=AddValueToSplayTree((SplayTreeInfo *) image->artifacts,
+ ConstantString(artifact),ConstantString(value));
+ return(status);
+}
diff --git a/magick/artifact.h b/magick/artifact.h
new file mode 100644
index 0000000..b8d8ac2
--- /dev/null
+++ b/magick/artifact.h
@@ -0,0 +1,46 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore artifact methods.
+*/
+#ifndef _MAGICKCORE_ARTIFACT_H
+#define _MAGICKCORE_ARTIFACT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport char
+ *GetNextImageArtifact(const Image *),
+ *RemoveImageArtifact(Image *,const char *);
+
+extern MagickExport const char
+ *GetImageArtifact(const Image *,const char *);
+
+extern MagickExport MagickBooleanType
+ CloneImageArtifacts(Image *,const Image *),
+ DefineImageArtifact(Image *,const char *),
+ DeleteImageArtifact(Image *,const char *),
+ SetImageArtifact(Image *,const char *,const char *);
+
+extern MagickExport void
+ DestroyImageArtifacts(Image *),
+ ResetImageArtifactIterator(const Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/blob-private.h b/magick/blob-private.h
new file mode 100644
index 0000000..64bf419
--- /dev/null
+++ b/magick/blob-private.h
@@ -0,0 +1,126 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore Binary Large OBjects private methods.
+*/
+#ifndef _MAGICKCORE_BLOB_PRIVATE_H
+#define _MAGICKCORE_BLOB_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/image.h"
+#include "magick/stream.h"
+
+#define MagickMinBlobExtent 32767L
+
+typedef enum
+{
+ UndefinedBlobMode,
+ ReadBlobMode,
+ ReadBinaryBlobMode,
+ WriteBlobMode,
+ WriteBinaryBlobMode,
+ AppendBlobMode,
+ AppendBinaryBlobMode
+} BlobMode;
+
+typedef enum
+{
+ UndefinedStream,
+ FileStream,
+ StandardStream,
+ PipeStream,
+ ZipStream,
+ BZipStream,
+ FifoStream,
+ BlobStream
+} StreamType;
+
+typedef int
+ *(*BlobFifo)(const Image *,const void *,const size_t);
+
+extern MagickExport BlobInfo
+ *CloneBlobInfo(const BlobInfo *),
+ *ReferenceBlob(BlobInfo *);
+
+extern MagickExport char
+ *ReadBlobString(Image *,char *);
+
+extern MagickExport const struct stat
+ *GetBlobProperties(const Image *);
+
+extern MagickExport double
+ ReadBlobDouble(Image *);
+
+extern MagickExport float
+ ReadBlobFloat(Image *);
+
+extern MagickExport int
+ EOFBlob(const Image *),
+ ReadBlobByte(Image *);
+
+extern MagickExport MagickBooleanType
+ CloseBlob(Image *),
+ OpenBlob(const ImageInfo *,Image *,const BlobMode,ExceptionInfo *),
+ SetBlobExtent(Image *,const MagickSizeType),
+ UnmapBlob(void *,const size_t);
+
+extern MagickExport MagickOffsetType
+ SeekBlob(Image *,const MagickOffsetType,const int),
+ TellBlob(const Image *);
+
+extern MagickExport MagickSizeType
+ ReadBlobLongLong(Image *);
+
+extern MagickExport ssize_t
+ ReadBlob(Image *,const size_t,unsigned char *),
+ WriteBlob(Image *,const size_t,const unsigned char *),
+ WriteBlobByte(Image *,const unsigned char),
+ WriteBlobFloat(Image *,const float),
+ WriteBlobLong(Image *,const unsigned int),
+ WriteBlobShort(Image *,const unsigned short),
+ WriteBlobLSBLong(Image *,const unsigned int),
+ WriteBlobLSBShort(Image *,const unsigned short),
+ WriteBlobMSBLong(Image *,const unsigned int),
+ WriteBlobMSBShort(Image *,const unsigned short),
+ WriteBlobString(Image *,const char *);
+
+extern MagickExport unsigned char
+ *DetachBlob(BlobInfo *),
+ *MapBlob(int,const MapMode,const MagickOffsetType,const size_t);
+
+extern MagickExport unsigned int
+ ReadBlobLong(Image *),
+ ReadBlobLSBLong(Image *),
+ ReadBlobMSBLong(Image *);
+
+extern MagickExport unsigned short
+ ReadBlobShort(Image *),
+ ReadBlobLSBShort(Image *),
+ ReadBlobMSBShort(Image *);
+
+extern MagickExport void
+ AttachBlob(BlobInfo *,const void *,const size_t),
+ GetBlobInfo(BlobInfo *),
+ MSBOrderLong(unsigned char *,const size_t),
+ MSBOrderShort(unsigned char *,const size_t);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/blob.c b/magick/blob.c
new file mode 100644
index 0000000..9fd4baf
--- /dev/null
+++ b/magick/blob.c
@@ -0,0 +1,4268 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% BBBB L OOO BBBB %
+% B B L O O B B %
+% BBBB L O O BBBB %
+% B B L O O B B %
+% BBBB LLLLL OOO BBBB %
+% %
+% %
+% MagickCore Binary Large OBjectS Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1999 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/client.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/policy.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#if defined(MAGICKCORE_HAVE_MMAP_FILEIO) && !defined(__WINDOWS__)
+# include <sys/mman.h>
+#endif
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+#include "zlib.h"
+#endif
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+#include "bzlib.h"
+#endif
+
+/*
+ Define declarations.
+*/
+#define MagickMaxBlobExtent 65541
+#if defined(MAGICKCORE_HAVE_FSEEKO)
+# define fseek fseeko
+# define ftell ftello
+#endif
+#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+#if !defined(MAP_FAILED)
+#define MAP_FAILED ((void *) -1)
+#endif
+#if !defined(MS_SYNC)
+#define MS_SYNC 0x04
+#endif
+#if defined(__OS2__)
+#include <io.h>
+#define _O_BINARY O_BINARY
+#endif
+
+/*
+ Typedef declarations.
+*/
+struct _BlobInfo
+{
+ size_t
+ length,
+ extent,
+ quantum;
+
+ MagickBooleanType
+ mapped,
+ eof;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ size;
+
+ MagickBooleanType
+ exempt,
+ synchronize,
+ status,
+ temporary;
+
+ StreamType
+ type;
+
+ FILE
+ *file;
+
+ struct stat
+ properties;
+
+ StreamHandler
+ stream;
+
+ unsigned char
+ *data;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ long
+ reference_count;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Forward declarations.
+*/
+static int
+ SyncBlob(Image *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A t t a c h B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AttachBlob() attaches a blob to the BlobInfo structure.
+%
+% The format of the AttachBlob method is:
+%
+% void AttachBlob(BlobInfo *blob_info,const void *blob,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o blob_info: Specifies a pointer to a BlobInfo structure.
+%
+% o blob: the address of a character stream in one of the image formats
+% understood by ImageMagick.
+%
+% o length: This size_t integer reflects the length in bytes of the blob.
+%
+*/
+MagickExport void AttachBlob(BlobInfo *blob_info,const void *blob,
+ const size_t length)
+{
+ assert(blob_info != (BlobInfo *) NULL);
+ if (blob_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ blob_info->length=length;
+ blob_info->extent=length;
+ blob_info->quantum=(size_t) MagickMaxBlobExtent;
+ blob_info->offset=0;
+ blob_info->type=BlobStream;
+ blob_info->file=(FILE *) NULL;
+ blob_info->data=(unsigned char *) blob;
+ blob_info->mapped=MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ B l o b T o F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BlobToFile() writes a blob to a file. It returns MagickFalse if an error
+% occurs otherwise MagickTrue.
+%
+% The format of the BlobToFile method is:
+%
+% MagickBooleanType BlobToFile(char *filename,const void *blob,
+% const size_t length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: Write the blob to this file.
+%
+% o blob: the address of a blob.
+%
+% o length: This length in bytes of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType BlobToFile(char *filename,const void *blob,
+ const size_t length,ExceptionInfo *exception)
+{
+ int
+ file;
+
+ register size_t
+ i;
+
+ ssize_t
+ count;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(blob != (const void *) NULL);
+ if (*filename == '\0')
+ file=AcquireUniqueFileResource(filename);
+ else
+ file=open(filename,O_RDWR | O_CREAT | O_EXCL | O_BINARY,S_MODE);
+ if (file == -1)
+ {
+ ThrowFileException(exception,BlobError,"UnableToWriteBlob",filename);
+ return(MagickFalse);
+ }
+ for (i=0; i < length; i+=count)
+ {
+ count=(ssize_t) write(file,(const char *) blob+i,MagickMin(length-i,(size_t)
+ SSIZE_MAX));
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ file=close(file)-1;
+ if (i < length)
+ {
+ ThrowFileException(exception,BlobError,"UnableToWriteBlob",filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B l o b T o I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BlobToImage() implements direct to memory image formats. It returns the
+% blob as an image.
+%
+% The format of the BlobToImage method is:
+%
+% Image *BlobToImage(const ImageInfo *image_info,const void *blob,
+% const size_t length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o blob: the address of a character stream in one of the image formats
+% understood by ImageMagick.
+%
+% o length: This size_t integer reflects the length in bytes of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *BlobToImage(const ImageInfo *image_info,const void *blob,
+ const size_t length,ExceptionInfo *exception)
+{
+ const MagickInfo
+ *magick_info;
+
+ Image
+ *image;
+
+ ImageInfo
+ *blob_info,
+ *clone_info;
+
+ MagickBooleanType
+ status;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((blob == (const void *) NULL) || (length == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),BlobError,
+ "ZeroLengthBlobNotPermitted","`%s'",image_info->filename);
+ return((Image *) NULL);
+ }
+ blob_info=CloneImageInfo(image_info);
+ blob_info->blob=(void *) blob;
+ blob_info->length=length;
+ if (*blob_info->magick == '\0')
+ (void) SetImageInfo(blob_info,MagickFalse,exception);
+ magick_info=GetMagickInfo(blob_info->magick,exception);
+ if (magick_info == (const MagickInfo *) NULL)
+ {
+ blob_info=DestroyImageInfo(blob_info);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
+ image_info->filename);
+ return((Image *) NULL);
+ }
+ if (GetMagickBlobSupport(magick_info) != MagickFalse)
+ {
+ /*
+ Native blob support for this image format.
+ */
+ (void) CopyMagickString(blob_info->filename,image_info->filename,
+ MaxTextExtent);
+ (void) CopyMagickString(blob_info->magick,image_info->magick,
+ MaxTextExtent);
+ image=ReadImage(blob_info,exception);
+ if (image != (Image *) NULL)
+ (void) DetachBlob(image->blob);
+ blob_info=DestroyImageInfo(blob_info);
+ return(image);
+ }
+ /*
+ Write blob to a temporary file on disk.
+ */
+ blob_info->blob=(void *) NULL;
+ blob_info->length=0;
+ *blob_info->filename='\0';
+ status=BlobToFile(blob_info->filename,blob,length,exception);
+ if (status == MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(blob_info->filename);
+ blob_info=DestroyImageInfo(blob_info);
+ return((Image *) NULL);
+ }
+ clone_info=CloneImageInfo(blob_info);
+ (void) FormatMagickString(clone_info->filename,MaxTextExtent,"%s:%s",
+ blob_info->magick,blob_info->filename);
+ image=ReadImage(clone_info,exception);
+ clone_info=DestroyImageInfo(clone_info);
+ (void) RelinquishUniqueFileResource(blob_info->filename);
+ blob_info=DestroyImageInfo(blob_info);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o n e B l o b I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneBlobInfo() makes a duplicate of the given blob info structure, or if
+% blob info is NULL, a new one.
+%
+% The format of the CloneBlobInfo method is:
+%
+% BlobInfo *CloneBlobInfo(const BlobInfo *blob_info)
+%
+% A description of each parameter follows:
+%
+% o blob_info: the blob info.
+%
+*/
+MagickExport BlobInfo *CloneBlobInfo(const BlobInfo *blob_info)
+{
+ BlobInfo
+ *clone_info;
+
+ clone_info=(BlobInfo *) AcquireMagickMemory(sizeof(*clone_info));
+ if (clone_info == (BlobInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetBlobInfo(clone_info);
+ if (blob_info == (BlobInfo *) NULL)
+ return(clone_info);
+ clone_info->length=blob_info->length;
+ clone_info->extent=blob_info->extent;
+ clone_info->synchronize=blob_info->synchronize;
+ clone_info->quantum=blob_info->quantum;
+ clone_info->mapped=blob_info->mapped;
+ clone_info->eof=blob_info->eof;
+ clone_info->offset=blob_info->offset;
+ clone_info->size=blob_info->size;
+ clone_info->exempt=blob_info->exempt;
+ clone_info->status=blob_info->status;
+ clone_info->temporary=blob_info->temporary;
+ clone_info->type=blob_info->type;
+ clone_info->file=blob_info->file;
+ clone_info->properties=blob_info->properties;
+ clone_info->stream=blob_info->stream;
+ clone_info->data=blob_info->data;
+ clone_info->debug=IsEventLogging();
+ clone_info->reference_count=1;
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o s e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloseBlob() closes a stream associated with the image.
+%
+% The format of the CloseBlob method is:
+%
+% MagickBooleanType CloseBlob(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType CloseBlob(Image *image)
+{
+ int
+ status;
+
+ /*
+ Close image file.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ if (image->blob->type == UndefinedStream)
+ return(MagickTrue);
+ if (image->blob->synchronize != MagickFalse)
+ SyncBlob(image);
+ image->blob->size=GetBlobSize(image);
+ image->blob->eof=MagickFalse;
+ if (image->blob->exempt != MagickFalse)
+ {
+ image->blob->type=UndefinedStream;
+ return(MagickTrue);
+ }
+ status=0;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ case PipeStream:
+ {
+ status=ferror(image->blob->file);
+ break;
+ }
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ (void) gzerror(image->blob->file,&status);
+#endif
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ (void) BZ2_bzerror((BZFILE *) image->blob->file,&status);
+#endif
+ break;
+ }
+ case FifoStream:
+ case BlobStream:
+ break;
+ }
+ image->blob->status=status < 0 ? MagickTrue : MagickFalse;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ {
+ if (image->blob->synchronize != MagickFalse)
+ status=fsync(fileno(image->blob->file));
+ status=fclose(image->blob->file);
+ break;
+ }
+ case PipeStream:
+ {
+#if defined(MAGICKCORE_HAVE_PCLOSE)
+ status=pclose(image->blob->file);
+#endif
+ break;
+ }
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ status=gzclose(image->blob->file);
+#endif
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ BZ2_bzclose((BZFILE *) image->blob->file);
+#endif
+ break;
+ }
+ case FifoStream:
+ case BlobStream:
+ break;
+ }
+ (void) DetachBlob(image->blob);
+ image->blob->status=status < 0 ? MagickTrue : MagickFalse;
+ return(image->blob->status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyBlob() deallocates memory associated with a blob.
+%
+% The format of the DestroyBlob method is:
+%
+% void DestroyBlob(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyBlob(Image *image)
+{
+ MagickBooleanType
+ destroy;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->signature == MagickSignature);
+ destroy=MagickFalse;
+ (void) LockSemaphoreInfo(image->blob->semaphore);
+ image->blob->reference_count--;
+ assert(image->blob->reference_count >= 0);
+ if (image->blob->reference_count == 0)
+ destroy=MagickTrue;
+ (void) UnlockSemaphoreInfo(image->blob->semaphore);
+ if (destroy == MagickFalse)
+ return;
+ (void) CloseBlob(image);
+ if (image->blob->mapped != MagickFalse)
+ (void) UnmapBlob(image->blob->data,image->blob->length);
+ if (image->blob->semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&image->blob->semaphore);
+ image->blob->signature=(~MagickSignature);
+ image->blob=(BlobInfo *) RelinquishMagickMemory(image->blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e t a c h B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DetachBlob() detaches a blob from the BlobInfo structure.
+%
+% The format of the DetachBlob method is:
+%
+% unsigned char *DetachBlob(BlobInfo *blob_info)
+%
+% A description of each parameter follows:
+%
+% o blob_info: Specifies a pointer to a BlobInfo structure.
+%
+*/
+MagickExport unsigned char *DetachBlob(BlobInfo *blob_info)
+{
+ unsigned char
+ *data;
+
+ assert(blob_info != (BlobInfo *) NULL);
+ if (blob_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (blob_info->mapped != MagickFalse)
+ (void) UnmapBlob(blob_info->data,blob_info->length);
+ blob_info->mapped=MagickFalse;
+ blob_info->length=0;
+ blob_info->offset=0;
+ blob_info->eof=MagickFalse;
+ blob_info->exempt=MagickFalse;
+ blob_info->type=UndefinedStream;
+ blob_info->file=(FILE *) NULL;
+ data=blob_info->data;
+ blob_info->data=(unsigned char *) NULL;
+ blob_info->stream=(StreamHandler) NULL;
+ return(data);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D u p l i c a t e s B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DuplicateBlob() duplicates a blob descriptor.
+%
+% The format of the DuplicateBlob method is:
+%
+% void DuplicateBlob(Image *image,const Image *duplicate)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o duplicate: the duplicate image.
+%
+*/
+MagickExport void DuplicateBlob(Image *image,const Image *duplicate)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(duplicate != (Image *) NULL);
+ assert(duplicate->signature == MagickSignature);
+ DestroyBlob(image);
+ image->blob=ReferenceBlob(duplicate->blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ E O F B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EOFBlob() returns a non-zero value when EOF has been detected reading from
+% a blob or file.
+%
+% The format of the EOFBlob method is:
+%
+% int EOFBlob(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport int EOFBlob(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ case PipeStream:
+ {
+ image->blob->eof=feof(image->blob->file) != 0 ? MagickTrue : MagickFalse;
+ break;
+ }
+ case ZipStream:
+ {
+ image->blob->eof=MagickFalse;
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ int
+ status;
+
+ status=0;
+ (void) BZ2_bzerror((BZFILE *) image->blob->file,&status);
+ image->blob->eof=status == BZ_UNEXPECTED_EOF ? MagickTrue : MagickFalse;
+#endif
+ break;
+ }
+ case FifoStream:
+ {
+ image->blob->eof=MagickFalse;
+ break;
+ }
+ case BlobStream:
+ break;
+ }
+ return((int) image->blob->eof);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F i l e T o B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FileToBlob() returns the contents of a file as a blob. It returns the
+% file as a blob and its length. If an error occurs, NULL is returned.
+%
+% The format of the FileToBlob method is:
+%
+% unsigned char *FileToBlob(const char *filename,const size_t extent,
+% size_t *length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o blob: FileToBlob() returns the contents of a file as a blob. If
+% an error occurs NULL is returned.
+%
+% o filename: the filename.
+%
+% o extent: The maximum length of the blob.
+%
+% o length: On return, this reflects the actual length of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned char *FileToBlob(const char *filename,const size_t extent,
+ size_t *length,ExceptionInfo *exception)
+{
+ int
+ file;
+
+ MagickOffsetType
+ offset;
+
+ register size_t
+ i;
+
+ ssize_t
+ count;
+
+ unsigned char
+ *blob;
+
+ void
+ *map;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ *length=0;
+ file=fileno(stdin);
+ if (LocaleCompare(filename,"-") != 0)
+ file=open(filename,O_RDONLY | O_BINARY);
+ if (file == -1)
+ {
+ ThrowFileException(exception,BlobError,"UnableToOpenFile",filename);
+ return((unsigned char *) NULL);
+ }
+ offset=(MagickOffsetType) MagickSeek(file,0,SEEK_END);
+ count=0;
+ if ((offset < 0) || (offset != (MagickOffsetType) ((ssize_t) offset)))
+ {
+ size_t
+ quantum;
+
+ struct stat
+ file_info;
+
+ /*
+ Stream is not seekable.
+ */
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(file,&file_info) == 0) && (file_info.st_size != 0))
+ quantum=MagickMin((size_t) file_info.st_size,MagickMaxBufferExtent);
+ blob=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*blob));
+ for (i=0; blob != (unsigned char *) NULL; i+=count)
+ {
+ count=(ssize_t) read(file,blob+i,quantum);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ if (~(1UL*i) < (quantum+1))
+ {
+ blob=(unsigned char *) RelinquishMagickMemory(blob);
+ break;
+ }
+ blob=(unsigned char *) ResizeQuantumMemory(blob,i+quantum+1,
+ sizeof(*blob));
+ if ((size_t) (i+count) >= extent)
+ break;
+ }
+ file=close(file)-1;
+ if (blob == (unsigned char *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
+ return((unsigned char *) NULL);
+ }
+ *length=MagickMin(i+count,extent);
+ blob[*length]='\0';
+ return(blob);
+ }
+ *length=MagickMin((size_t) offset,extent);
+ blob=(unsigned char *) NULL;
+ if (~(*length) >= MaxTextExtent)
+ blob=(unsigned char *) AcquireQuantumMemory(*length+MaxTextExtent,
+ sizeof(*blob));
+ if (blob == (unsigned char *) NULL)
+ {
+ file=close(file)-1;
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
+ return((unsigned char *) NULL);
+ }
+ map=MapBlob(file,ReadMode,0,*length);
+ if (map != (unsigned char *) NULL)
+ {
+ (void) CopyMagickMemory(blob,map,*length);
+ (void) UnmapBlob(map,*length);
+ }
+ else
+ {
+ (void) MagickSeek(file,0,SEEK_SET);
+ for (i=0; i < *length; i+=count)
+ {
+ count=(ssize_t) read(file,blob+i,MagickMin(*length-i,(size_t)
+ SSIZE_MAX));
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ if (i < *length)
+ {
+ file=close(file)-1;
+ blob=(unsigned char *) RelinquishMagickMemory(blob);
+ ThrowFileException(exception,BlobError,"UnableToReadBlob",filename);
+ return((unsigned char *) NULL);
+ }
+ }
+ file=close(file)-1;
+ blob[*length]='\0';
+ return(blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F i l e T o I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FileToImage() write the contents of a file to an image.
+%
+% The format of the FileToImage method is:
+%
+% MagickBooleanType FileToImage(Image *,const char *filename)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o filename: the filename.
+%
+*/
+
+static inline ssize_t WriteBlobStream(Image *image,const size_t length,
+ const unsigned char *data)
+{
+ MagickSizeType
+ extent;
+
+ register unsigned char
+ *q;
+
+ assert(image->blob != (BlobInfo *) NULL);
+ if (image->blob->type != BlobStream)
+ return(WriteBlob(image,length,data));
+ assert(image->blob->type != UndefinedStream);
+ assert(data != (void *) NULL);
+ extent=(MagickSizeType) (image->blob->offset+(MagickOffsetType) length);
+ if (extent >= image->blob->extent)
+ {
+ image->blob->quantum<<=1;
+ extent=image->blob->extent+image->blob->quantum+length;
+ if (SetBlobExtent(image,extent) == MagickFalse)
+ return(0);
+ }
+ q=image->blob->data+image->blob->offset;
+ (void) CopyMagickMemory(q,data,length);
+ image->blob->offset+=length;
+ if (image->blob->offset >= (MagickOffsetType) image->blob->length)
+ image->blob->length=(size_t) image->blob->offset;
+ return((ssize_t) length);
+}
+
+MagickExport MagickBooleanType FileToImage(Image *image,const char *filename)
+{
+ int
+ file;
+
+ size_t
+ length,
+ quantum;
+
+ ssize_t
+ count;
+
+ struct stat
+ file_info;
+
+ unsigned char
+ *blob;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ file=open(filename,O_RDONLY | O_BINARY);
+ if (file == -1)
+ {
+ ThrowFileException(&image->exception,BlobError,"UnableToOpenBlob",
+ filename);
+ return(MagickFalse);
+ }
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(file,&file_info) == 0) && (file_info.st_size != 0))
+ quantum=MagickMin((size_t) file_info.st_size,MagickMaxBufferExtent);
+ blob=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*blob));
+ if (blob == (unsigned char *) NULL)
+ {
+ ThrowFileException(&image->exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ for ( ; ; )
+ {
+ count=(ssize_t) read(file,blob,quantum);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ length=(size_t) count;
+ count=WriteBlobStream(image,length,blob);
+ if (count != (ssize_t) length)
+ {
+ ThrowFileException(&image->exception,BlobError,"UnableToWriteBlob",
+ filename);
+ break;
+ }
+ }
+ file=close(file)-1;
+ blob=(unsigned char *) RelinquishMagickMemory(blob);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobError() returns MagickTrue if the blob associated with the specified
+% image encountered an error.
+%
+% The format of the GetBlobError method is:
+%
+% MagickBooleanType GetBlobError(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType GetBlobError(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(image->blob->status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b F i l e H a n d l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobFileHandle() returns the file handle associated with the image blob.
+%
+% The format of the GetBlobFile method is:
+%
+% FILE *GetBlobFileHandle(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport FILE *GetBlobFileHandle(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ return(image->blob->file);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobInfo() initializes the BlobInfo structure.
+%
+% The format of the GetBlobInfo method is:
+%
+% void GetBlobInfo(BlobInfo *blob_info)
+%
+% A description of each parameter follows:
+%
+% o blob_info: Specifies a pointer to a BlobInfo structure.
+%
+*/
+MagickExport void GetBlobInfo(BlobInfo *blob_info)
+{
+ assert(blob_info != (BlobInfo *) NULL);
+ (void) ResetMagickMemory(blob_info,0,sizeof(*blob_info));
+ blob_info->type=UndefinedStream;
+ blob_info->quantum=(size_t) MagickMaxBlobExtent;
+ blob_info->properties.st_mtime=time((time_t *) NULL);
+ blob_info->properties.st_ctime=time((time_t *) NULL);
+ blob_info->debug=IsEventLogging();
+ blob_info->reference_count=1;
+ blob_info->semaphore=AllocateSemaphoreInfo();
+ blob_info->signature=MagickSignature;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t B l o b P r o p e r t i e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobProperties() returns information about an image blob.
+%
+% The format of the GetBlobProperties method is:
+%
+% const struct stat *GetBlobProperties(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport const struct stat *GetBlobProperties(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(&image->blob->properties);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b S i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobSize() returns the current length of the image file or blob; zero is
+% returned if the size cannot be determined.
+%
+% The format of the GetBlobSize method is:
+%
+% MagickSizeType GetBlobSize(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickSizeType GetBlobSize(const Image *image)
+{
+ MagickSizeType
+ length;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ length=0;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ {
+ length=image->blob->size;
+ break;
+ }
+ case FileStream:
+ {
+ if (fstat(fileno(image->blob->file),&image->blob->properties) == 0)
+ length=(MagickSizeType) image->blob->properties.st_size;
+ break;
+ }
+ case StandardStream:
+ case PipeStream:
+ {
+ length=image->blob->size;
+ break;
+ }
+ case ZipStream:
+ case BZipStream:
+ {
+ MagickBooleanType
+ status;
+
+ status=GetPathAttributes(image->filename,&image->blob->properties);
+ if (status != MagickFalse)
+ length=(MagickSizeType) image->blob->properties.st_size;
+ break;
+ }
+ case FifoStream:
+ break;
+ case BlobStream:
+ {
+ length=(MagickSizeType) image->blob->length;
+ break;
+ }
+ }
+ return(length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b S t r e a m D a t a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobStreamData() returns the stream data for the image.
+%
+% The format of the GetBlobStreamData method is:
+%
+% unsigned char *GetBlobStreamData(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned char *GetBlobStreamData(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ return(image->blob->data);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t B l o b S t r e a m H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetBlobStreamHandler() returns the stream handler for the image.
+%
+% The format of the GetBlobStreamHandler method is:
+%
+% StreamHandler GetBlobStreamHandler(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport StreamHandler GetBlobStreamHandler(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(image->blob->stream);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m a g e T o B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImageToBlob() implements direct to memory image formats. It returns the
+% image as a blob and its length. The magick member of the ImageInfo structure
+% determines the format of the returned blob (GIF, JPEG, PNG, etc.)
+%
+% The format of the ImageToBlob method is:
+%
+% unsigned char *ImageToBlob(const ImageInfo *image_info,Image *image,
+% size_t *length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o length: This pointer to a size_t integer sets the initial length of the
+% blob. On return, it reflects the actual length of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned char *ImageToBlob(const ImageInfo *image_info,
+ Image *image,size_t *length,ExceptionInfo *exception)
+{
+ const MagickInfo
+ *magick_info;
+
+ ImageInfo
+ *blob_info;
+
+ MagickBooleanType
+ status;
+
+ unsigned char
+ *blob;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ *length=0;
+ blob=(unsigned char *) NULL;
+ blob_info=CloneImageInfo(image_info);
+ blob_info->adjoin=MagickFalse;
+ (void) SetImageInfo(blob_info,MagickTrue,exception);
+ if (*blob_info->magick != '\0')
+ (void) CopyMagickString(image->magick,blob_info->magick,MaxTextExtent);
+ magick_info=GetMagickInfo(image->magick,exception);
+ if (magick_info == (const MagickInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
+ image->filename);
+ return(blob);
+ }
+ (void) CopyMagickString(blob_info->magick,image->magick,MaxTextExtent);
+ if (GetMagickBlobSupport(magick_info) != MagickFalse)
+ {
+ /*
+ Native blob support for this image format.
+ */
+ blob_info->length=0;
+ blob_info->blob=(void *) AcquireQuantumMemory(MagickMaxBlobExtent,
+ sizeof(unsigned char));
+ if (blob_info->blob == (void *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ else
+ {
+ (void) CloseBlob(image);
+ image->blob->exempt=MagickTrue;
+ *image->filename='\0';
+ status=WriteImage(blob_info,image);
+ if ((status == MagickFalse) || (image->blob->length == 0))
+ InheritException(exception,&image->exception);
+ else
+ {
+ *length=image->blob->length;
+ blob=DetachBlob(image->blob);
+ blob=(unsigned char *) ResizeQuantumMemory(blob,*length,
+ sizeof(*blob));
+ }
+ }
+ }
+ else
+ {
+ char
+ unique[MaxTextExtent];
+
+ int
+ file;
+
+ /*
+ Write file to disk in blob image format.
+ */
+ file=AcquireUniqueFileResource(unique);
+ if (file == -1)
+ {
+ ThrowFileException(exception,BlobError,"UnableToWriteBlob",
+ image_info->filename);
+ }
+ else
+ {
+ blob_info->file=fdopen(file,"wb");
+ if (blob_info->file != (FILE *) NULL)
+ {
+ (void) FormatMagickString(image->filename,MaxTextExtent,"%s:%s",
+ image->magick,unique);
+ status=WriteImage(blob_info,image);
+ (void) fclose(blob_info->file);
+ if (status == MagickFalse)
+ InheritException(exception,&image->exception);
+ else
+ blob=FileToBlob(image->filename,~0UL,length,exception);
+ }
+ (void) RelinquishUniqueFileResource(unique);
+ }
+ }
+ blob_info=DestroyImageInfo(blob_info);
+ return(blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m a g e T o F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImageToFile() writes an image to a file. It returns MagickFalse if an error
+% occurs otherwise MagickTrue.
+%
+% The format of the ImageToFile method is:
+%
+% MagickBooleanType ImageToFile(Image *image,char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o filename: Write the image to this file.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline const unsigned char *ReadBlobStream(Image *image,
+ const size_t length,unsigned char *data,ssize_t *count)
+{
+ assert(count != (ssize_t *) NULL);
+ assert(image->blob != (BlobInfo *) NULL);
+ if (image->blob->type != BlobStream)
+ {
+ *count=ReadBlob(image,length,data);
+ return(data);
+ }
+ if (image->blob->offset >= (MagickOffsetType) image->blob->length)
+ {
+ *count=0;
+ image->blob->eof=MagickTrue;
+ return(data);
+ }
+ data=image->blob->data+image->blob->offset;
+ *count=(ssize_t) MagickMin(length,(size_t) (image->blob->length-
+ image->blob->offset));
+ image->blob->offset+=(*count);
+ if (*count != (ssize_t) length)
+ image->blob->eof=MagickTrue;
+ return(data);
+}
+
+MagickExport MagickBooleanType ImageToFile(Image *image,char *filename,
+ ExceptionInfo *exception)
+{
+ int
+ file;
+
+ register const unsigned char
+ *p;
+
+ register size_t
+ i;
+
+ size_t
+ length,
+ quantum;
+
+ ssize_t
+ count;
+
+ struct stat
+ file_info;
+
+ unsigned char
+ *buffer;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(filename != (const char *) NULL);
+ if (*filename == '\0')
+ file=AcquireUniqueFileResource(filename);
+ else
+ if (LocaleCompare(filename,"-") == 0)
+ file=fileno(stdout);
+ else
+ file=open(filename,O_RDWR | O_CREAT | O_EXCL | O_BINARY,S_MODE);
+ if (file == -1)
+ {
+ ThrowFileException(exception,BlobError,"UnableToWriteBlob",filename);
+ return(MagickFalse);
+ }
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(file,&file_info) == 0) && (file_info.st_size != 0))
+ quantum=MagickMin((size_t) file_info.st_size,MagickMaxBufferExtent);
+ buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
+ if (buffer == (unsigned char *) NULL)
+ {
+ file=close(file)-1;
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationError","`%s'",filename);
+ return(MagickFalse);
+ }
+ length=0;
+ p=ReadBlobStream(image,quantum,buffer,&count);
+ for (i=0; count > 0; p=ReadBlobStream(image,quantum,buffer,&count))
+ {
+ length=(size_t) count;
+ for (i=0; i < length; i+=count)
+ {
+ count=write(file,p+i,(size_t) (length-i));
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ if (i < length)
+ break;
+ }
+ file=close(file)-1;
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ if (i < length)
+ {
+ ThrowFileException(exception,BlobError,"UnableToWriteBlob",filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m a g e s T o B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImagesToBlob() implements direct to memory image formats. It returns the
+% image sequence as a blob and its length. The magick member of the ImageInfo
+% structure determines the format of the returned blob (GIF, JPEG, PNG, etc.)
+%
+% Note, some image formats do not permit multiple images to the same image
+% stream (e.g. JPEG). in this instance, just the first image of the
+% sequence is returned as a blob.
+%
+% The format of the ImagesToBlob method is:
+%
+% unsigned char *ImagesToBlob(const ImageInfo *image_info,Image *images,
+% size_t *length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o images: the image list.
+%
+% o length: This pointer to a size_t integer sets the initial length of the
+% blob. On return, it reflects the actual length of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned char *ImagesToBlob(const ImageInfo *image_info,
+ Image *images,size_t *length,ExceptionInfo *exception)
+{
+ const MagickInfo
+ *magick_info;
+
+ ImageInfo
+ *blob_info;
+
+ MagickBooleanType
+ status;
+
+ unsigned char
+ *blob;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ *length=0;
+ blob=(unsigned char *) NULL;
+ blob_info=CloneImageInfo(image_info);
+ (void) SetImageInfo(blob_info,MagickTrue,exception);
+ if (*blob_info->magick != '\0')
+ (void) CopyMagickString(images->magick,blob_info->magick,MaxTextExtent);
+ if (blob_info->adjoin == MagickFalse)
+ {
+ blob_info=DestroyImageInfo(blob_info);
+ return(ImageToBlob(image_info,images,length,exception));
+ }
+ magick_info=GetMagickInfo(images->magick,exception);
+ if (magick_info == (const MagickInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
+ images->filename);
+ return(blob);
+ }
+ (void) CopyMagickString(blob_info->magick,images->magick,MaxTextExtent);
+ if (GetMagickBlobSupport(magick_info) != MagickFalse)
+ {
+ /*
+ Native blob support for this images format.
+ */
+ blob_info->length=0;
+ blob_info->blob=(void *) AcquireQuantumMemory(MagickMaxBlobExtent,
+ sizeof(unsigned char));
+ if (blob_info->blob == (void *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ else
+ {
+ images->blob->exempt=MagickTrue;
+ *images->filename='\0';
+ status=WriteImages(blob_info,images,images->filename,exception);
+ if ((status == MagickFalse) || (images->blob->length == 0))
+ InheritException(exception,&images->exception);
+ else
+ {
+ *length=images->blob->length;
+ blob=DetachBlob(images->blob);
+ blob=(unsigned char *) ResizeQuantumMemory(blob,*length,
+ sizeof(*blob));
+ }
+ }
+ }
+ else
+ {
+ char
+ filename[MaxTextExtent],
+ unique[MaxTextExtent];
+
+ int
+ file;
+
+ /*
+ Write file to disk in blob images format.
+ */
+ file=AcquireUniqueFileResource(unique);
+ if (file == -1)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToWriteBlob",
+ image_info->filename);
+ }
+ else
+ {
+ blob_info->file=fdopen(file,"wb");
+ if (blob_info->file != (FILE *) NULL)
+ {
+ (void) FormatMagickString(filename,MaxTextExtent,"%s:%s",
+ images->magick,unique);
+ status=WriteImages(blob_info,images,filename,exception);
+ (void) fclose(blob_info->file);
+ if (status == MagickFalse)
+ InheritException(exception,&images->exception);
+ else
+ blob=FileToBlob(images->filename,~0UL,length,exception);
+ }
+ (void) RelinquishUniqueFileResource(unique);
+ }
+ }
+ blob_info=DestroyImageInfo(blob_info);
+ return(blob);
+}
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n j e c t I m a g e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InjectImageBlob() injects the image with a copy of itself in the specified
+% format (e.g. inject JPEG into a PDF image).
+%
+% The format of the InjectImageBlob method is:
+%
+% MagickBooleanType InjectImageBlob(const ImageInfo *image_info,
+% Image *image,Image *inject_image,const char *format,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info..
+%
+% o image: the image.
+%
+% o inject_image: inject into the image stream.
+%
+% o format: the image format.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType InjectImageBlob(const ImageInfo *image_info,
+ Image *image,Image *inject_image,const char *format,ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent];
+
+ FILE
+ *unique_file;
+
+ Image
+ *byte_image;
+
+ ImageInfo
+ *write_info;
+
+ int
+ file;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ size_t
+ quantum;
+
+ ssize_t
+ count;
+
+ struct stat
+ file_info;
+
+ unsigned char
+ *buffer;
+
+ /*
+ Write inject image to a temporary file.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(inject_image != (Image *) NULL);
+ assert(inject_image->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ unique_file=(FILE *) NULL;
+ file=AcquireUniqueFileResource(filename);
+ if (file != -1)
+ unique_file=fdopen(file,"wb");
+ if ((file == -1) || (unique_file == (FILE *) NULL))
+ {
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ ThrowFileException(exception,FileOpenError,"UnableToCreateTemporaryFile",
+ image->filename);
+ return(MagickFalse);
+ }
+ byte_image=CloneImage(inject_image,0,0,MagickFalse,exception);
+ if (byte_image == (Image *) NULL)
+ {
+ (void) fclose(unique_file);
+ (void) RelinquishUniqueFileResource(filename);
+ return(MagickFalse);
+ }
+ (void) FormatMagickString(byte_image->filename,MaxTextExtent,"%s:%s",format,
+ filename);
+ DestroyBlob(byte_image);
+ byte_image->blob=CloneBlobInfo((BlobInfo *) NULL);
+ write_info=CloneImageInfo(image_info);
+ SetImageInfoFile(write_info,unique_file);
+ status=WriteImage(write_info,byte_image);
+ write_info=DestroyImageInfo(write_info);
+ byte_image=DestroyImage(byte_image);
+ (void) fclose(unique_file);
+ if (status == MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(filename);
+ return(MagickFalse);
+ }
+ /*
+ Inject into image stream.
+ */
+ file=open(filename,O_RDONLY | O_BINARY);
+ if (file == -1)
+ {
+ (void) RelinquishUniqueFileResource(filename);
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ image_info->filename);
+ return(MagickFalse);
+ }
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(file,&file_info) == 0) && (file_info.st_size != 0))
+ quantum=MagickMin((size_t) file_info.st_size,MagickMaxBufferExtent);
+ buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
+ if (buffer == (unsigned char *) NULL)
+ {
+ (void) RelinquishUniqueFileResource(filename);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ for (i=0; ; i+=count)
+ {
+ count=(ssize_t) read(file,buffer,quantum);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ status=WriteBlobStream(image,(size_t) count,buffer) == count ? MagickTrue :
+ MagickFalse;
+ }
+ file=close(file)-1;
+ (void) RelinquishUniqueFileResource(filename);
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s B l o b E x e m p t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsBlobExempt() returns true if the blob is exempt.
+%
+% The format of the IsBlobExempt method is:
+%
+% MagickBooleanType IsBlobExempt(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType IsBlobExempt(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(image->blob->exempt);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s B l o b S e e k a b l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsBlobSeekable() returns true if the blob is seekable.
+%
+% The format of the IsBlobSeekable method is:
+%
+% MagickBooleanType IsBlobSeekable(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType IsBlobSeekable(const Image *image)
+{
+ MagickBooleanType
+ seekable;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ seekable=(image->blob->type == FileStream) ||
+ (image->blob->type == BlobStream) ? MagickTrue : MagickFalse;
+ return(seekable);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s B l o b T e m p o r a r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsBlobTemporary() returns true if the blob is temporary.
+%
+% The format of the IsBlobTemporary method is:
+%
+% MagickBooleanType IsBlobTemporary(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType IsBlobTemporary(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(image->blob->temporary);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M a p B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MapBlob() creates a mapping from a file to a binary large object.
+%
+% The format of the MapBlob method is:
+%
+% unsigned char *MapBlob(int file,const MapMode mode,
+% const MagickOffsetType offset,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o file: map this file descriptor.
+%
+% o mode: ReadMode, WriteMode, or IOMode.
+%
+% o offset: starting at this offset within the file.
+%
+% o length: the length of the mapping is returned in this pointer.
+%
+*/
+MagickExport unsigned char *MapBlob(int file,const MapMode mode,
+ const MagickOffsetType offset,const size_t length)
+{
+#if defined(MAGICKCORE_HAVE_MMAP_FILEIO)
+ int
+ flags,
+ protection;
+
+ unsigned char
+ *map;
+
+ /*
+ Map file.
+ */
+ flags=0;
+ if (file == -1)
+#if defined(MAP_ANONYMOUS)
+ flags|=MAP_ANONYMOUS;
+#else
+ return((unsigned char *) NULL);
+#endif
+ switch (mode)
+ {
+ case ReadMode:
+ default:
+ {
+ protection=PROT_READ;
+ flags|=MAP_PRIVATE;
+ map=(unsigned char *) mmap((char *) NULL,length,protection,flags,file,
+ (off_t) offset);
+ break;
+ }
+ case WriteMode:
+ {
+ protection=PROT_WRITE;
+ flags|=MAP_SHARED;
+ map=(unsigned char *) mmap((char *) NULL,length,protection,flags,file,
+ (off_t) offset);
+ break;
+ }
+ case IOMode:
+ {
+ protection=PROT_READ | PROT_WRITE;
+ flags|=MAP_SHARED;
+ map=(unsigned char *) mmap((char *) NULL,length,protection,flags,file,
+ (off_t) offset);
+ break;
+ }
+ }
+ if (map == (unsigned char *) MAP_FAILED)
+ return((unsigned char *) NULL);
+ return(map);
+#else
+ (void) file;
+ (void) mode;
+ (void) offset;
+ (void) length;
+ return((unsigned char *) NULL);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M S B O r d e r L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MSBOrderLong() converts a least-significant byte first buffer of integers to
+% most-significant byte first.
+%
+% The format of the MSBOrderLong method is:
+%
+% void MSBOrderLong(unsigned char *buffer,const size_t length)
+%
+% A description of each parameter follows.
+%
+% o buffer: Specifies a pointer to a buffer of integers.
+%
+% o length: Specifies the length of the buffer.
+%
+*/
+MagickExport void MSBOrderLong(unsigned char *buffer,const size_t length)
+{
+ int
+ c;
+
+ register unsigned char
+ *p,
+ *q;
+
+ assert(buffer != (unsigned char *) NULL);
+ q=buffer+length;
+ while (buffer < q)
+ {
+ p=buffer+3;
+ c=(int) (*p);
+ *p=(*buffer);
+ *buffer++=(unsigned char) c;
+ p=buffer+1;
+ c=(int) (*p);
+ *p=(*buffer);
+ *buffer++=(unsigned char) c;
+ buffer+=2;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M S B O r d e r S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MSBOrderShort() converts a least-significant byte first buffer of integers
+% to most-significant byte first.
+%
+% The format of the MSBOrderShort method is:
+%
+% void MSBOrderShort(unsigned char *p,const size_t length)
+%
+% A description of each parameter follows.
+%
+% o p: Specifies a pointer to a buffer of integers.
+%
+% o length: Specifies the length of the buffer.
+%
+*/
+MagickExport void MSBOrderShort(unsigned char *p,const size_t length)
+{
+ int
+ c;
+
+ register unsigned char
+ *q;
+
+ assert(p != (unsigned char *) NULL);
+ q=p+length;
+ while (p < q)
+ {
+ c=(int) (*p);
+ *p=(*(p+1));
+ p++;
+ *p++=(unsigned char) c;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p e n B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenBlob() opens a file associated with the image. A file name of '-' sets
+% the file to stdin for type 'r' and stdout for type 'w'. If the filename
+% suffix is '.gz' or '.Z', the image is decompressed for type 'r' and
+% compressed for type 'w'. If the filename prefix is '|', it is piped to or
+% from a system command.
+%
+% The format of the OpenBlob method is:
+%
+% MagickBooleanType OpenBlob(const ImageInfo *image_info,Image *image,
+% const BlobMode mode,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o mode: the mode for opening the file.
+%
+*/
+MagickExport MagickBooleanType OpenBlob(const ImageInfo *image_info,
+ Image *image,const BlobMode mode,ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent];
+
+ const char
+ *type;
+
+ MagickBooleanType
+ status;
+
+ PolicyRights
+ rights;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image_info->blob != (void *) NULL)
+ {
+ if (image_info->stream != (StreamHandler) NULL)
+ image->blob->stream=(StreamHandler) image_info->stream;
+ AttachBlob(image->blob,image_info->blob,image_info->length);
+ return(MagickTrue);
+ }
+ (void) DetachBlob(image->blob);
+ switch (mode)
+ {
+ default: type="r"; break;
+ case ReadBlobMode: type="r"; break;
+ case ReadBinaryBlobMode: type="rb"; break;
+ case WriteBlobMode: type="w"; break;
+ case WriteBinaryBlobMode: type="w+b"; break;
+ case AppendBlobMode: type="a"; break;
+ case AppendBinaryBlobMode: type="a+b"; break;
+ }
+ if (*type != 'r')
+ image->blob->synchronize=image_info->synchronize;
+ if (image_info->stream != (StreamHandler) NULL)
+ {
+ image->blob->stream=(StreamHandler) image_info->stream;
+ if (*type == 'w')
+ {
+ image->blob->type=FifoStream;
+ return(MagickTrue);
+ }
+ }
+ /*
+ Open image file.
+ */
+ *filename='\0';
+ (void) CopyMagickString(filename,image->filename,MaxTextExtent);
+ rights=ReadPolicyRights;
+ if (*type == 'w')
+ rights=WritePolicyRights;
+ if (IsRightsAuthorized(PathPolicyDomain,rights,filename) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",filename);
+ return(MagickFalse);
+ }
+ if ((LocaleCompare(filename,"-") == 0) ||
+ ((*filename == '\0') && (image_info->file == (FILE *) NULL)))
+ {
+ image->blob->file=(*type == 'r') ? stdin : stdout;
+#if defined(__WINDOWS__) || defined(__OS2__)
+ if (strchr(type,'b') != (char *) NULL)
+ setmode(_fileno(image->blob->file),_O_BINARY);
+#endif
+ image->blob->type=StandardStream;
+ image->blob->exempt=MagickTrue;
+ return(MagickTrue);
+ }
+ if (LocaleNCompare(filename,"fd:",3) == 0)
+ {
+ char
+ mode[MaxTextExtent];
+
+ *mode=(*type);
+ mode[1]='\0';
+ image->blob->file=fdopen(atoi(filename+3),mode);
+#if defined(__WINDOWS__) || defined(__OS2__)
+ if (strchr(type,'b') != (char *) NULL)
+ setmode(_fileno(image->blob->file),_O_BINARY);
+#endif
+ image->blob->type=StandardStream;
+ image->blob->exempt=MagickTrue;
+ return(MagickTrue);
+ }
+#if defined(MAGICKCORE_HAVE_POPEN)
+ if (*filename == '|')
+ {
+ char
+ mode[MaxTextExtent];
+
+ /*
+ Pipe image to or from a system command.
+ */
+#if defined(SIGPIPE)
+ if (*type == 'w')
+ (void) signal(SIGPIPE,SIG_IGN);
+#endif
+ *mode=(*type);
+ mode[1]='\0';
+ image->blob->file=(FILE *) popen(filename+1,mode);
+ if (image->blob->file == (FILE *) NULL)
+ {
+ ThrowFileException(exception,BlobError,"UnableToOpenBlob",filename);
+ return(MagickFalse);
+ }
+ image->blob->type=PipeStream;
+ image->blob->exempt=MagickTrue;
+ return(MagickTrue);
+ }
+#endif
+ status=GetPathAttributes(filename,&image->blob->properties);
+#if defined(S_ISFIFO)
+ if ((status == MagickTrue) && S_ISFIFO(image->blob->properties.st_mode))
+ {
+ image->blob->file=(FILE *) OpenMagickStream(filename,type);
+ if (image->blob->file == (FILE *) NULL)
+ {
+ ThrowFileException(exception,BlobError,"UnableToOpenBlob",filename);
+ return(MagickFalse);
+ }
+ image->blob->type=FileStream;
+ image->blob->exempt=MagickTrue;
+ return(MagickTrue);
+ }
+#endif
+ if (*type == 'w')
+ {
+ /*
+ Form filename for multi-part images.
+ */
+ (void) InterpretImageFilename(image_info,image,image->filename,(int)
+ image->scene,filename);
+ if (image_info->adjoin == MagickFalse)
+ if ((image->previous != (Image *) NULL) ||
+ (GetNextImageInList(image) != (Image *) NULL))
+ {
+ if (LocaleCompare(filename,image->filename) == 0)
+ {
+ char
+ extension[MaxTextExtent],
+ path[MaxTextExtent];
+
+ GetPathComponent(image->filename,RootPath,path);
+ GetPathComponent(image->filename,ExtensionPath,extension);
+ if (*extension == '\0')
+ (void) FormatMagickString(filename,MaxTextExtent,"%s-%lu",
+ path,image->scene);
+ else
+ (void) FormatMagickString(filename,MaxTextExtent,"%s-%lu.%s",
+ path,image->scene,extension);
+ }
+ }
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+#if defined(macintosh)
+ SetApplicationType(filename,image_info->magick,'8BIM');
+#endif
+ }
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ if (((strlen(filename) > 2) &&
+ (LocaleCompare(filename+strlen(filename)-2,".Z") == 0)) ||
+ ((strlen(filename) > 3) &&
+ (LocaleCompare(filename+strlen(filename)-3,".gz") == 0)) ||
+ ((strlen(filename) > 4) &&
+ (LocaleCompare(filename+strlen(filename)-4,".wmz") == 0)) ||
+ ((strlen(filename) > 5) &&
+ (LocaleCompare(filename+strlen(filename)-5,".svgz") == 0)))
+ {
+ image->blob->file=(FILE *) gzopen(filename,type);
+ if (image->blob->file != (FILE *) NULL)
+ image->blob->type=ZipStream;
+ }
+ else
+#endif
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ if ((strlen(filename) > 4) &&
+ (LocaleCompare(filename+strlen(filename)-4,".bz2") == 0))
+ {
+ image->blob->file=(FILE *) BZ2_bzopen(filename,type);
+ if (image->blob->file != (FILE *) NULL)
+ image->blob->type=BZipStream;
+ }
+ else
+#endif
+ if (image_info->file != (FILE *) NULL)
+ {
+ image->blob->file=image_info->file;
+ image->blob->type=FileStream;
+ image->blob->exempt=MagickTrue;
+ }
+ else
+ {
+ image->blob->file=(FILE *) OpenMagickStream(filename,type);
+ if (image->blob->file != (FILE *) NULL)
+ {
+ image->blob->type=FileStream;
+#if defined(MAGICKCORE_HAVE_SETVBUF)
+ (void) setvbuf(image->blob->file,(char *) NULL,(int) _IOFBF,
+ 16384);
+#endif
+ if (*type == 'r')
+ {
+ size_t
+ count;
+
+ unsigned char
+ magick[3];
+
+ (void) ResetMagickMemory(magick,0,sizeof(magick));
+ count=fread(magick,1,sizeof(magick),image->blob->file);
+ (void) rewind(image->blob->file);
+ (void) LogMagickEvent(BlobEvent,GetMagickModule(),
+ " read %ld magic header bytes",(long) count);
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ if (((int) magick[0] == 0x1F) && ((int) magick[1] == 0x8B) &&
+ ((int) magick[2] == 0x08))
+ {
+ (void) fclose(image->blob->file);
+ image->blob->file=(FILE *) gzopen(filename,type);
+ if (image->blob->file != (FILE *) NULL)
+ image->blob->type=ZipStream;
+ }
+#endif
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ if (strncmp((char *) magick,"BZh",3) == 0)
+ {
+ (void) fclose(image->blob->file);
+ image->blob->file=(FILE *) BZ2_bzopen(filename,type);
+ if (image->blob->file != (FILE *) NULL)
+ image->blob->type=BZipStream;
+ }
+#endif
+ }
+ }
+ }
+ if ((image->blob->type == FileStream) && (*type == 'r'))
+ {
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *sans_exception;
+
+ struct stat
+ *properties;
+
+ sans_exception=AcquireExceptionInfo();
+ magick_info=GetMagickInfo(image_info->magick,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ properties=(&image->blob->properties);
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (GetMagickBlobSupport(magick_info) != MagickFalse) &&
+ (properties->st_size <= MagickMaxBufferExtent))
+ {
+ size_t
+ length;
+
+ void
+ *blob;
+
+ length=(size_t) properties->st_size;
+ blob=MapBlob(fileno(image->blob->file),ReadMode,0,length);
+ if (blob != (void *) NULL)
+ {
+ /*
+ Format supports blobs-- use memory-mapped I/O.
+ */
+ if (image_info->file != (FILE *) NULL)
+ image->blob->exempt=MagickFalse;
+ else
+ {
+ (void) fclose(image->blob->file);
+ image->blob->file=(FILE *) NULL;
+ }
+ AttachBlob(image->blob,blob,length);
+ image->blob->mapped=MagickTrue;
+ }
+ }
+ }
+ image->blob->status=MagickFalse;
+ if (image->blob->type != UndefinedStream)
+ image->blob->size=GetBlobSize(image);
+ else
+ {
+ ThrowFileException(exception,BlobError,"UnableToOpenBlob",filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P i n g B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PingBlob() returns all the attributes of an image or image sequence except
+% for the pixels. It is much faster and consumes far less memory than
+% BlobToImage(). On failure, a NULL image is returned and exception
+% describes the reason for the failure.
+%
+% The format of the PingBlob method is:
+%
+% Image *PingBlob(const ImageInfo *image_info,const void *blob,
+% const size_t length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o blob: the address of a character stream in one of the image formats
+% understood by ImageMagick.
+%
+% o length: This size_t integer reflects the length in bytes of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static size_t PingStream(const Image *magick_unused(image),
+ const void *magick_unused(pixels),const size_t columns)
+{
+ return(columns);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport Image *PingBlob(const ImageInfo *image_info,const void *blob,
+ const size_t length,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ ImageInfo
+ *ping_info;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((blob == (const void *) NULL) || (length == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),BlobError,
+ "UnrecognizedImageFormat","`%s'",image_info->magick);
+ return((Image *) NULL);
+ }
+ ping_info=CloneImageInfo(image_info);
+ ping_info->blob=(void *) AcquireQuantumMemory(length,sizeof(unsigned char));
+ if (ping_info->blob == (const void *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitFatalError,"MemoryAllocationFailed","`%s'","");
+ return((Image *) NULL);
+ }
+ (void) CopyMagickMemory(ping_info->blob,blob,length);
+ ping_info->length=length;
+ ping_info->ping=MagickTrue;
+ image=ReadStream(ping_info,&PingStream,exception);
+ ping_info->blob=(void *) RelinquishMagickMemory(ping_info->blob);
+ ping_info=DestroyImageInfo(ping_info);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlob() reads data from the blob or image file and returns it. It
+% returns the number of bytes read.
+%
+% The format of the ReadBlob method is:
+%
+% ssize_t ReadBlob(Image *image,const size_t length,unsigned char *data)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o length: Specifies an integer representing the number of bytes to read
+% from the file.
+%
+% o data: Specifies an area to place the information requested from the
+% file.
+%
+*/
+MagickExport ssize_t ReadBlob(Image *image,const size_t length,
+ unsigned char *data)
+{
+ int
+ c;
+
+ register unsigned char
+ *q;
+
+ ssize_t
+ count;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ if (length == 0)
+ return(0);
+ assert(data != (void *) NULL);
+ count=0;
+ q=data;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ case PipeStream:
+ {
+ switch (length)
+ {
+ default:
+ {
+ count=(ssize_t) fread(q,1,length,image->blob->file);
+ break;
+ }
+ case 2:
+ {
+ c=getc(image->blob->file);
+ if (c == EOF)
+ break;
+ *q++=(unsigned char) c;
+ count++;
+ }
+ case 1:
+ {
+ c=getc(image->blob->file);
+ if (c == EOF)
+ break;
+ *q++=(unsigned char) c;
+ count++;
+ }
+ case 0:
+ break;
+ }
+ break;
+ }
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ switch (length)
+ {
+ default:
+ {
+ count=(ssize_t) gzread(image->blob->file,q,(unsigned int) length);
+ break;
+ }
+ case 2:
+ {
+ c=gzgetc(image->blob->file);
+ if (c == EOF)
+ break;
+ *q++=(unsigned char) c;
+ count++;
+ }
+ case 1:
+ {
+ c=gzgetc(image->blob->file);
+ if (c == EOF)
+ break;
+ *q++=(unsigned char) c;
+ count++;
+ }
+ case 0:
+ break;
+ }
+#endif
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ count=(ssize_t) BZ2_bzread((BZFILE *) image->blob->file,q,(int) length);
+#endif
+ break;
+ }
+ case FifoStream:
+ break;
+ case BlobStream:
+ {
+ register const unsigned char
+ *p;
+
+ if (image->blob->offset >= (MagickOffsetType) image->blob->length)
+ {
+ image->blob->eof=MagickTrue;
+ break;
+ }
+ p=image->blob->data+image->blob->offset;
+ count=(ssize_t) MagickMin(length,(size_t) (image->blob->length-
+ image->blob->offset));
+ image->blob->offset+=count;
+ if (count != (ssize_t) length)
+ image->blob->eof=MagickTrue;
+ (void) CopyMagickMemory(q,p,(size_t) count);
+ break;
+ }
+ }
+ return(count);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b B y t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobByte() reads a single byte from the image file and returns it.
+%
+% The format of the ReadBlobByte method is:
+%
+% int ReadBlobByte(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport int ReadBlobByte(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[1];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ p=ReadBlobStream(image,1,buffer,&count);
+ if (count != 1)
+ return(EOF);
+ return((int) (*p));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b D o u b l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobDouble() reads a double value as a 64-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the ReadBlobDouble method is:
+%
+% double ReadBlobDouble(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport double ReadBlobDouble(Image *image)
+{
+ union
+ {
+ MagickSizeType
+ unsigned_value;
+
+ double
+ double_value;
+ } quantum;
+
+ quantum.double_value=0.0;
+ quantum.unsigned_value=ReadBlobLongLong(image);
+ return(quantum.double_value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b F l o a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobFloat() reads a float value as a 32-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the ReadBlobFloat method is:
+%
+% float ReadBlobFloat(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport float ReadBlobFloat(Image *image)
+{
+ union
+ {
+ unsigned int
+ unsigned_value;
+
+ float
+ float_value;
+ } quantum;
+
+ quantum.float_value=0.0;
+ quantum.unsigned_value=ReadBlobLong(image);
+ return(quantum.float_value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobLong() reads a long value as a 32-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the ReadBlobLong method is:
+%
+% unsigned int ReadBlobLong(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned int ReadBlobLong(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[4];
+
+ unsigned int
+ value;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,4,buffer,&count);
+ if (count != 4)
+ return(0UL);
+ if (image->endian == LSBEndian)
+ {
+ value=(unsigned int) (*p++);
+ value|=((unsigned int) (*p++)) << 8;
+ value|=((unsigned int) (*p++)) << 16;
+ value|=((unsigned int) (*p++)) << 24;
+ return(value);
+ }
+ value=((unsigned int) (*p++)) << 24;
+ value|=((unsigned int) (*p++)) << 16;
+ value|=((unsigned int) (*p++)) << 8;
+ value|=((unsigned int) (*p++));
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b L o n g L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobLongLong() reads a long value as a 64-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the ReadBlobLong method is:
+%
+% MagickSizeType ReadBlobLong(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickSizeType ReadBlobLongLong(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[8];
+
+ MagickSizeType
+ value;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,8,buffer,&count);
+ if (count != 8)
+ return(MagickULLConstant(0));
+ if (image->endian == LSBEndian)
+ {
+ value=(MagickSizeType) (*p++);
+ value|=((MagickSizeType) (*p++)) << 8;
+ value|=((MagickSizeType) (*p++)) << 16;
+ value|=((MagickSizeType) (*p++)) << 24;
+ value|=((MagickSizeType) (*p++)) << 32;
+ value|=((MagickSizeType) (*p++)) << 40;
+ value|=((MagickSizeType) (*p++)) << 48;
+ value|=((MagickSizeType) (*p++)) << 56;
+ return(value & MagickULLConstant(0xffffffffffffffff));
+ }
+ value=((MagickSizeType) (*p++)) << 56;
+ value|=((MagickSizeType) (*p++)) << 48;
+ value|=((MagickSizeType) (*p++)) << 40;
+ value|=((MagickSizeType) (*p++)) << 32;
+ value|=((MagickSizeType) (*p++)) << 24;
+ value|=((MagickSizeType) (*p++)) << 16;
+ value|=((MagickSizeType) (*p++)) << 8;
+ value|=((MagickSizeType) (*p++));
+ return(value & MagickULLConstant(0xffffffffffffffff));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobShort() reads a short value as a 16-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the ReadBlobShort method is:
+%
+% unsigned short ReadBlobShort(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned short ReadBlobShort(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ value;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,2,buffer,&count);
+ if (count != 2)
+ return((unsigned short) 0U);
+ if (image->endian == LSBEndian)
+ {
+ value=(unsigned int) (*p++);
+ value|=((unsigned int) (*p++)) << 8;
+ return((unsigned short) (value & 0xffff));
+ }
+ value=(unsigned int) ((*p++) << 8);
+ value|=(unsigned int) (*p++);
+ return((unsigned short) (value & 0xffff));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b L S B L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobLSBLong() reads a long value as a 32-bit quantity in
+% least-significant byte first order.
+%
+% The format of the ReadBlobLSBLong method is:
+%
+% unsigned int ReadBlobLSBLong(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned int ReadBlobLSBLong(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ value;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[4];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,4,buffer,&count);
+ if (count != 4)
+ return(0U);
+ value=(unsigned int) (*p++);
+ value|=((unsigned int) (*p++)) << 8;
+ value|=((unsigned int) (*p++)) << 16;
+ value|=((unsigned int) (*p++)) << 24;
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b L S B S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobLSBShort() reads a short value as a 16-bit quantity in
+% least-significant byte first order.
+%
+% The format of the ReadBlobLSBShort method is:
+%
+% unsigned short ReadBlobLSBShort(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned short ReadBlobLSBShort(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ value;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,2,buffer,&count);
+ if (count != 2)
+ return((unsigned short) 0U);
+ value=(unsigned int) (*p++);
+ value|=((unsigned int) ((*p++)) << 8);
+ return((unsigned short) (value & 0xffff));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b M S B L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobMSBLong() reads a long value as a 32-bit quantity in
+% most-significant byte first order.
+%
+% The format of the ReadBlobMSBLong method is:
+%
+% unsigned int ReadBlobMSBLong(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned int ReadBlobMSBLong(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ value;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[4];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,4,buffer,&count);
+ if (count != 4)
+ return(0UL);
+ value=((unsigned int) (*p++) << 24);
+ value|=((unsigned int) (*p++) << 16);
+ value|=((unsigned int) (*p++) << 8);
+ value|=(unsigned int) (*p++);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b M S B S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobMSBShort() reads a short value as a 16-bit quantity in
+% most-significant byte first order.
+%
+% The format of the ReadBlobMSBShort method is:
+%
+% unsigned short ReadBlobMSBShort(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned short ReadBlobMSBShort(Image *image)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ value;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *buffer='\0';
+ p=ReadBlobStream(image,2,buffer,&count);
+ if (count != 2)
+ return((unsigned short) 0U);
+ value=(unsigned int) ((*p++) << 8);
+ value|=(unsigned int) (*p++);
+ return((unsigned short) (value & 0xffff));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d B l o b S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadBlobString() reads characters from a blob or file until a newline
+% character is read or an end-of-file condition is encountered.
+%
+% The format of the ReadBlobString method is:
+%
+% char *ReadBlobString(Image *image,char *string)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o string: the address of a character buffer.
+%
+*/
+MagickExport char *ReadBlobString(Image *image,char *string)
+{
+ register const unsigned char
+ *p;
+
+ register long
+ i;
+
+ ssize_t
+ count;
+
+ unsigned char
+ buffer[1];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ for (i=0; i < (MaxTextExtent-1L); i++)
+ {
+ p=ReadBlobStream(image,1,buffer,&count);
+ if (count != 1)
+ {
+ if (i == 0)
+ return((char *) NULL);
+ break;
+ }
+ string[i]=(char) (*p);
+ if ((string[i] == '\n') || (string[i] == '\r'))
+ break;
+ }
+ string[i]='\0';
+ return(string);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e f e r e n c e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReferenceBlob() increments the reference count associated with the pixel
+% blob returning a pointer to the blob.
+%
+% The format of the ReferenceBlob method is:
+%
+% BlobInfo ReferenceBlob(BlobInfo *blob_info)
+%
+% A description of each parameter follows:
+%
+% o blob_info: the blob_info.
+%
+*/
+MagickExport BlobInfo *ReferenceBlob(BlobInfo *blob)
+{
+ assert(blob != (BlobInfo *) NULL);
+ assert(blob->signature == MagickSignature);
+ if (blob->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(blob->semaphore);
+ blob->reference_count++;
+ (void) UnlockSemaphoreInfo(blob->semaphore);
+ return(blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e e k B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeekBlob() sets the offset in bytes from the beginning of a blob or file
+% and returns the resulting offset.
+%
+% The format of the SeekBlob method is:
+%
+% MagickOffsetType SeekBlob(Image *image,const MagickOffsetType offset,
+% const int whence)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o offset: Specifies an integer representing the offset in bytes.
+%
+% o whence: Specifies an integer representing how the offset is
+% treated relative to the beginning of the blob as follows:
+%
+% SEEK_SET Set position equal to offset bytes.
+% SEEK_CUR Set position to current location plus offset.
+% SEEK_END Set position to EOF plus offset.
+%
+*/
+MagickExport MagickOffsetType SeekBlob(Image *image,
+ const MagickOffsetType offset,const int whence)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ {
+ if (fseek(image->blob->file,offset,whence) < 0)
+ return(-1);
+ image->blob->offset=TellBlob(image);
+ break;
+ }
+ case StandardStream:
+ case PipeStream:
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ if (gzseek(image->blob->file,(off_t) offset,whence) < 0)
+ return(-1);
+#endif
+ image->blob->offset=TellBlob(image);
+ break;
+ }
+ case BZipStream:
+ return(-1);
+ case FifoStream:
+ return(-1);
+ case BlobStream:
+ {
+ switch (whence)
+ {
+ case SEEK_SET:
+ default:
+ {
+ if (offset < 0)
+ return(-1);
+ image->blob->offset=offset;
+ break;
+ }
+ case SEEK_CUR:
+ {
+ if ((image->blob->offset+offset) < 0)
+ return(-1);
+ image->blob->offset+=offset;
+ break;
+ }
+ case SEEK_END:
+ {
+ if (((MagickOffsetType) image->blob->length+offset) < 0)
+ return(-1);
+ image->blob->offset=image->blob->length+offset;
+ break;
+ }
+ }
+ if (image->blob->offset <= (MagickOffsetType)
+ ((off_t) image->blob->length))
+ image->blob->eof=MagickFalse;
+ else
+ if (image->blob->mapped != MagickFalse)
+ return(-1);
+ else
+ {
+ image->blob->extent=(size_t) (image->blob->offset+
+ image->blob->quantum);
+ image->blob->data=(unsigned char *) ResizeQuantumMemory(
+ image->blob->data,image->blob->extent+1,
+ sizeof(*image->blob->data));
+ (void) SyncBlob(image);
+ if (image->blob->data == (unsigned char *) NULL)
+ {
+ (void) DetachBlob(image->blob);
+ return(-1);
+ }
+ }
+ break;
+ }
+ }
+ return(image->blob->offset);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t B l o b E x e m p t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetBlobExempt() sets the blob exempt status.
+%
+% The format of the SetBlobExempt method is:
+%
+% MagickBooleanType SetBlobExempt(const Image *image,
+% const MagickBooleanType exempt)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exempt: Set to true if this blob is exempt from being closed.
+%
+*/
+MagickExport void SetBlobExempt(Image *image,const MagickBooleanType exempt)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ image->blob->exempt=exempt;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t B l o b E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetBlobExtent() ensures enough space is allocated for the blob. If the
+% method is successful, subsequent writes to bytes in the specified range are
+% guaranteed not to fail.
+%
+% The format of the SetBlobExtent method is:
+%
+% MagickBooleanType SetBlobExtent(Image *image,const MagickSizeType extent)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o extent: the blob maximum extent.
+%
+*/
+MagickExport MagickBooleanType SetBlobExtent(Image *image,
+ const MagickSizeType extent)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ {
+ if (extent != (MagickSizeType) ((off_t) extent))
+ return(MagickFalse);
+#if !defined(MAGICKCORE_POSIX_FALLOCATE)
+ return(MagickFalse);
+#else
+ {
+ int
+ status;
+
+ MagickOffsetType
+ offset;
+
+ offset=TellBlob(image);
+ status=posix_fallocate(fileno(image->blob->file),(off_t) offset,
+ (off_t) (extent-offset));
+ if (status != 0)
+ return(MagickFalse);
+ }
+#endif
+ break;
+ }
+ case StandardStream:
+ case PipeStream:
+ case ZipStream:
+ return(MagickFalse);
+ case BZipStream:
+ return(MagickFalse);
+ case FifoStream:
+ return(MagickFalse);
+ case BlobStream:
+ {
+ if (image->blob->mapped != MagickFalse)
+ {
+ if (image->blob->file == (FILE *) NULL)
+ return(MagickFalse);
+ (void) UnmapBlob(image->blob->data,image->blob->length);
+#if !defined(MAGICKCORE_POSIX_FALLOCATE)
+ return(MagickFalse);
+#else
+ {
+ int
+ status;
+
+ MagickOffsetType
+ offset;
+
+ offset=TellBlob(image);
+ status=posix_fallocate(fileno(image->blob->file),(off_t) offset,
+ (off_t) (extent-offset));
+ if (status != 0)
+ return(MagickFalse);
+ }
+ image->blob->data=(unsigned char*) MapBlob(fileno(image->blob->file),
+ WriteMode,0,(size_t) extent);
+ image->blob->extent=(size_t) extent;
+ image->blob->length=(size_t) extent;
+ (void) SyncBlob(image);
+ break;
+#endif
+ }
+ if (extent != (MagickSizeType) ((size_t) extent))
+ return(MagickFalse);
+ image->blob->extent=(size_t) extent;
+ image->blob->data=(unsigned char *) ResizeQuantumMemory(image->blob->data,
+ image->blob->extent+1,sizeof(*image->blob->data));
+ (void) SyncBlob(image);
+ if (image->blob->data == (unsigned char *) NULL)
+ {
+ (void) DetachBlob(image->blob);
+ return(MagickFalse);
+ }
+ break;
+ }
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncBlob() flushes the datastream if it is a file or synchronizes the data
+% attributes if it is an blob.
+%
+% The format of the SyncBlob method is:
+%
+% int SyncBlob(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static int SyncBlob(Image *image)
+{
+ int
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ status=0;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ case PipeStream:
+ {
+ status=fflush(image->blob->file);
+ break;
+ }
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ status=gzflush(image->blob->file,Z_SYNC_FLUSH);
+#endif
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ status=BZ2_bzflush((BZFILE *) image->blob->file);
+#endif
+ break;
+ }
+ case FifoStream:
+ break;
+ case BlobStream:
+ {
+#if defined(MAGICKCORE_HAVE_MMAP_FILEIO)
+ if (image->blob->mapped != MagickFalse)
+ status=msync(image->blob->data,image->blob->length,MS_SYNC);
+#endif
+ break;
+ }
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ T e l l B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TellBlob() obtains the current value of the blob or file position.
+%
+% The format of the TellBlob method is:
+%
+% MagickOffsetType TellBlob(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickOffsetType TellBlob(const Image *image)
+{
+ MagickOffsetType
+ offset;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ offset=(-1);
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ {
+ offset=ftell(image->blob->file);
+ break;
+ }
+ case StandardStream:
+ case PipeStream:
+ break;
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ offset=(MagickOffsetType) gztell(image->blob->file);
+#endif
+ break;
+ }
+ case BZipStream:
+ break;
+ case FifoStream:
+ break;
+ case BlobStream:
+ {
+ offset=image->blob->offset;
+ break;
+ }
+ }
+ return(offset);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ U n m a p B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnmapBlob() deallocates the binary large object previously allocated with
+% the MapBlob method.
+%
+% The format of the UnmapBlob method is:
+%
+% MagickBooleanType UnmapBlob(void *map,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o map: the address of the binary large object.
+%
+% o length: the length of the binary large object.
+%
+*/
+MagickExport MagickBooleanType UnmapBlob(void *map,const size_t length)
+{
+#if defined(MAGICKCORE_HAVE_MMAP_FILEIO)
+ int
+ status;
+
+ status=munmap(map,length);
+ return(status == -1 ? MagickFalse : MagickTrue);
+#else
+ (void) map;
+ (void) length;
+ return(MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlob() writes data to a blob or image file. It returns the number of
+% bytes written.
+%
+% The format of the WriteBlob method is:
+%
+% ssize_t WriteBlob(Image *image,const size_t length,
+% const unsigned char *data)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o length: Specifies an integer representing the number of bytes to
+% write to the file.
+%
+% o data: The address of the data to write to the blob or file.
+%
+*/
+MagickExport ssize_t WriteBlob(Image *image,const size_t length,
+ const unsigned char *data)
+{
+ int
+ c;
+
+ register const unsigned char
+ *p;
+
+ ssize_t
+ count;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(data != (const unsigned char *) NULL);
+ assert(image->blob != (BlobInfo *) NULL);
+ assert(image->blob->type != UndefinedStream);
+ if (length == 0)
+ return(0);
+ count=0;
+ p=data;
+ switch (image->blob->type)
+ {
+ case UndefinedStream:
+ break;
+ case FileStream:
+ case StandardStream:
+ case PipeStream:
+ {
+ switch (length)
+ {
+ default:
+ {
+ count=(ssize_t) fwrite((const char *) data,1,length,
+ image->blob->file);
+ break;
+ }
+ case 2:
+ {
+ c=putc((int) *p++,image->blob->file);
+ if (c == EOF)
+ break;
+ count++;
+ }
+ case 1:
+ {
+ c=putc((int) *p++,image->blob->file);
+ if (c == EOF)
+ break;
+ count++;
+ }
+ case 0:
+ break;
+ }
+ break;
+ }
+ case ZipStream:
+ {
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ switch (length)
+ {
+ default:
+ {
+ count=(ssize_t) gzwrite(image->blob->file,(void *) data,
+ (unsigned int) length);
+ break;
+ }
+ case 2:
+ {
+ c=gzputc(image->blob->file,(int) *p++);
+ if (c == EOF)
+ break;
+ count++;
+ }
+ case 1:
+ {
+ c=gzputc(image->blob->file,(int) *p++);
+ if (c == EOF)
+ break;
+ count++;
+ }
+ case 0:
+ break;
+ }
+#endif
+ break;
+ }
+ case BZipStream:
+ {
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ count=(ssize_t) BZ2_bzwrite((BZFILE *) image->blob->file,(void *) data,
+ (int) length);
+#endif
+ break;
+ }
+ case FifoStream:
+ {
+ count=(ssize_t) image->blob->stream(image,data,length);
+ break;
+ }
+ case BlobStream:
+ {
+ register unsigned char
+ *q;
+
+ if ((image->blob->offset+(MagickOffsetType) length) >=
+ (MagickOffsetType) image->blob->extent)
+ {
+ if (image->blob->mapped != MagickFalse)
+ return(0);
+ image->blob->quantum<<=1;
+ image->blob->extent+=length+image->blob->quantum;
+ image->blob->data=(unsigned char *) ResizeQuantumMemory(
+ image->blob->data,image->blob->extent+1,sizeof(*image->blob->data));
+ (void) SyncBlob(image);
+ if (image->blob->data == (unsigned char *) NULL)
+ {
+ (void) DetachBlob(image->blob);
+ return(0);
+ }
+ }
+ q=image->blob->data+image->blob->offset;
+ (void) CopyMagickMemory(q,p,length);
+ image->blob->offset+=length;
+ if (image->blob->offset >= (MagickOffsetType) image->blob->length)
+ image->blob->length=(size_t) image->blob->offset;
+ count=(ssize_t) length;
+ }
+ }
+ return(count);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b B y t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobByte() write an integer to a blob. It returns the number of bytes
+% written (either 0 or 1);
+%
+% The format of the WriteBlobByte method is:
+%
+% ssize_t WriteBlobByte(Image *image,const unsigned char value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobByte(Image *image,const unsigned char value)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ return(WriteBlobStream(image,1,&value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b F l o a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobFloat() writes a float value as a 32-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the WriteBlobFloat method is:
+%
+% ssize_t WriteBlobFloat(Image *image,const float value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobFloat(Image *image,const float value)
+{
+ union
+ {
+ unsigned int
+ unsigned_value;
+
+ float
+ float_value;
+ } quantum;
+
+ quantum.unsigned_value=0U;
+ quantum.float_value=value;
+ return(WriteBlobLong(image,quantum.unsigned_value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobLong() writes a long value as a 32-bit quantity in the byte-order
+% specified by the endian member of the image structure.
+%
+% The format of the WriteBlobLong method is:
+%
+% ssize_t WriteBlobLong(Image *image,const unsigned int value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobLong(Image *image,const unsigned int value)
+{
+ unsigned char
+ buffer[4];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->endian == LSBEndian)
+ {
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ buffer[2]=(unsigned char) (value >> 16);
+ buffer[3]=(unsigned char) (value >> 24);
+ return(WriteBlobStream(image,4,buffer));
+ }
+ buffer[0]=(unsigned char) (value >> 24);
+ buffer[1]=(unsigned char) (value >> 16);
+ buffer[2]=(unsigned char) (value >> 8);
+ buffer[3]=(unsigned char) value;
+ return(WriteBlobStream(image,4,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobShort() writes a short value as a 16-bit quantity in the
+% byte-order specified by the endian member of the image structure.
+%
+% The format of the WriteBlobShort method is:
+%
+% ssize_t WriteBlobShort(Image *image,const unsigned short value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobShort(Image *image,const unsigned short value)
+{
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->endian == LSBEndian)
+ {
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ return(WriteBlobStream(image,2,buffer));
+ }
+ buffer[0]=(unsigned char) (value >> 8);
+ buffer[1]=(unsigned char) value;
+ return(WriteBlobStream(image,2,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b L S B L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobLSBLong() writes a long value as a 32-bit quantity in
+% least-significant byte first order.
+%
+% The format of the WriteBlobLSBLong method is:
+%
+% ssize_t WriteBlobLSBLong(Image *image,const unsigned int value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobLSBLong(Image *image,const unsigned int value)
+{
+ unsigned char
+ buffer[4];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ buffer[2]=(unsigned char) (value >> 16);
+ buffer[3]=(unsigned char) (value >> 24);
+ return(WriteBlobStream(image,4,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b L S B S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobLSBShort() writes a long value as a 16-bit quantity in
+% least-significant byte first order.
+%
+% The format of the WriteBlobLSBShort method is:
+%
+% ssize_t WriteBlobLSBShort(Image *image,const unsigned short value)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o value: Specifies the value to write.
+%
+*/
+MagickExport ssize_t WriteBlobLSBShort(Image *image,const unsigned short value)
+{
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ return(WriteBlobStream(image,2,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b M S B L o n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobMSBLong() writes a long value as a 32-bit quantity in
+% most-significant byte first order.
+%
+% The format of the WriteBlobMSBLong method is:
+%
+% ssize_t WriteBlobMSBLong(Image *image,const unsigned int value)
+%
+% A description of each parameter follows.
+%
+% o value: Specifies the value to write.
+%
+% o image: the image.
+%
+*/
+MagickExport ssize_t WriteBlobMSBLong(Image *image,const unsigned int value)
+{
+ unsigned char
+ buffer[4];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ buffer[0]=(unsigned char) (value >> 24);
+ buffer[1]=(unsigned char) (value >> 16);
+ buffer[2]=(unsigned char) (value >> 8);
+ buffer[3]=(unsigned char) value;
+ return(WriteBlobStream(image,4,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b M S B S h o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobMSBShort() writes a long value as a 16-bit quantity in
+% most-significant byte first order.
+%
+% The format of the WriteBlobMSBShort method is:
+%
+% ssize_t WriteBlobMSBShort(Image *image,const unsigned short value)
+%
+% A description of each parameter follows.
+%
+% o value: Specifies the value to write.
+%
+% o file: Specifies the file to write the data to.
+%
+*/
+MagickExport ssize_t WriteBlobMSBShort(Image *image,const unsigned short value)
+{
+ unsigned char
+ buffer[2];
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ buffer[0]=(unsigned char) (value >> 8);
+ buffer[1]=(unsigned char) value;
+ return(WriteBlobStream(image,2,buffer));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e B l o b S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteBlobString() write a string to a blob. It returns the number of
+% characters written.
+%
+% The format of the WriteBlobString method is:
+%
+% ssize_t WriteBlobString(Image *image,const char *string)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o string: Specifies the string to write.
+%
+*/
+MagickExport ssize_t WriteBlobString(Image *image,const char *string)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(string != (const char *) NULL);
+ return(WriteBlobStream(image,strlen(string),(const unsigned char *) string));
+}
diff --git a/magick/blob.h b/magick/blob.h
new file mode 100644
index 0000000..a2a4f57
--- /dev/null
+++ b/magick/blob.h
@@ -0,0 +1,76 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore Binary Large OBjects methods.
+*/
+#ifndef _MAGICKCORE_BLOB_H
+#define _MAGICKCORE_BLOB_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/image.h"
+#include "magick/stream.h"
+
+#define MagickMaxBufferExtent 0x3c005L
+
+typedef enum
+{
+ ReadMode,
+ WriteMode,
+ IOMode
+} MapMode;
+
+extern MagickExport FILE
+ *GetBlobFileHandle(const Image *);
+
+extern MagickExport Image
+ *BlobToImage(const ImageInfo *,const void *,const size_t,ExceptionInfo *),
+ *PingBlob(const ImageInfo *,const void *,const size_t,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ BlobToFile(char *,const void *,const size_t,ExceptionInfo *),
+ FileToImage(Image *,const char *),
+ GetBlobError(const Image *),
+ ImageToFile(Image *,char *,ExceptionInfo *),
+ InjectImageBlob(const ImageInfo *,Image *,Image *,const char *,
+ ExceptionInfo *),
+ IsBlobExempt(const Image *),
+ IsBlobSeekable(const Image *),
+ IsBlobTemporary(const Image *);
+
+extern MagickExport MagickSizeType
+ GetBlobSize(const Image *);
+
+extern MagickExport StreamHandler
+ GetBlobStreamHandler(const Image *);
+
+extern MagickExport unsigned char
+ *FileToBlob(const char *,const size_t,size_t *,ExceptionInfo *),
+ *GetBlobStreamData(const Image *),
+ *ImageToBlob(const ImageInfo *,Image *,size_t *,ExceptionInfo *),
+ *ImagesToBlob(const ImageInfo *,Image *,size_t *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyBlob(Image *),
+ DuplicateBlob(Image *,const Image *),
+ SetBlobExempt(Image *,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/cache-private.h b/magick/cache-private.h
new file mode 100644
index 0000000..3ce2934
--- /dev/null
+++ b/magick/cache-private.h
@@ -0,0 +1,239 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore cache private methods.
+*/
+#ifndef _MAGICKCORE_CACHE_PRIVATE_H
+#define _MAGICKCORE_CACHE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <time.h>
+#include "magick/random_.h"
+#include "magick/thread-private.h"
+#include "magick/semaphore.h"
+
+typedef enum
+{
+ UndefinedCache,
+ MemoryCache,
+ MapCache,
+ DiskCache
+} CacheType;
+
+typedef void
+ *Cache;
+
+typedef const IndexPacket
+ *(*GetVirtualIndexesFromHandler)(const Image *);
+
+typedef IndexPacket
+ *(*GetAuthenticIndexesFromHandler)(const Image *);
+
+typedef MagickBooleanType
+ (*GetOneAuthenticPixelFromHandler)(Image *,const long,const long,
+ PixelPacket *,ExceptionInfo *),
+ (*GetOneVirtualPixelFromHandler)(const Image *,const VirtualPixelMethod,
+ const long,const long,PixelPacket *,ExceptionInfo *),
+ (*SyncAuthenticPixelsHandler)(Image *,ExceptionInfo *);
+
+typedef const PixelPacket
+ *(*GetVirtualPixelHandler)(const Image *,const VirtualPixelMethod,const long,
+ const long,const unsigned long,const unsigned long,ExceptionInfo *),
+ *(*GetVirtualPixelsHandler)(const Image *);
+
+typedef PixelPacket
+ *(*GetAuthenticPixelsHandler)(Image *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *);
+
+typedef PixelPacket
+ *(*GetAuthenticPixelsFromHandler)(const Image *);
+
+typedef PixelPacket
+ *(*QueueAuthenticPixelsHandler)(Image *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *);
+
+typedef void
+ (*DestroyPixelHandler)(Image *);
+
+typedef struct _CacheMethods
+{
+ GetVirtualPixelHandler
+ get_virtual_pixel_handler;
+
+ GetVirtualPixelsHandler
+ get_virtual_pixels_handler;
+
+ GetVirtualIndexesFromHandler
+ get_virtual_indexes_from_handler;
+
+ GetOneVirtualPixelFromHandler
+ get_one_virtual_pixel_from_handler;
+
+ GetAuthenticPixelsHandler
+ get_authentic_pixels_handler;
+
+ GetAuthenticIndexesFromHandler
+ get_authentic_indexes_from_handler;
+
+ GetOneAuthenticPixelFromHandler
+ get_one_authentic_pixel_from_handler;
+
+ GetAuthenticPixelsFromHandler
+ get_authentic_pixels_from_handler;
+
+ QueueAuthenticPixelsHandler
+ queue_authentic_pixels_handler;
+
+ SyncAuthenticPixelsHandler
+ sync_authentic_pixels_handler;
+
+ DestroyPixelHandler
+ destroy_pixel_handler;
+
+} CacheMethods;
+
+typedef struct _NexusInfo
+ NexusInfo;
+
+typedef struct _CacheInfo
+{
+ ClassType
+ storage_class;
+
+ ColorspaceType
+ colorspace;
+
+ CacheType
+ type;
+
+ MagickBooleanType
+ mapped;
+
+ unsigned long
+ columns,
+ rows;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ length;
+
+ VirtualPixelMethod
+ virtual_pixel_method;
+
+ unsigned long
+ number_threads;
+
+ NexusInfo
+ **nexus_info;
+
+ PixelPacket
+ *pixels;
+
+ IndexPacket
+ *indexes;
+
+ MagickBooleanType
+ active_index_channel;
+
+ int
+ file;
+
+ char
+ filename[MaxTextExtent],
+ cache_filename[MaxTextExtent];
+
+ CacheMethods
+ methods;
+
+ RandomInfo
+ *random_info;
+
+ MagickBooleanType
+ debug;
+
+ MagickThreadType
+ id;
+
+ long
+ reference_count;
+
+ SemaphoreInfo
+ *semaphore,
+ *disk_semaphore;
+
+ time_t
+ timestamp;
+
+ unsigned long
+ signature;
+} CacheInfo;
+
+extern MagickExport Cache
+ AcquirePixelCache(const unsigned long),
+ ClonePixelCache(const Cache),
+ DestroyPixelCache(Cache),
+ GetImagePixelCache(Image *,const MagickBooleanType,ExceptionInfo *),
+ ReferencePixelCache(Cache);
+
+extern MagickExport ClassType
+ GetPixelCacheStorageClass(const Cache);
+
+extern MagickExport ColorspaceType
+ GetPixelCacheColorspace(const Cache);
+
+extern MagickExport const IndexPacket
+ *GetVirtualIndexesFromNexus(const Cache,NexusInfo *);
+
+extern MagickExport const PixelPacket
+ *GetVirtualPixelsFromNexus(const Image *,const VirtualPixelMethod,const long,
+ const long,const unsigned long,const unsigned long,NexusInfo *,
+ ExceptionInfo *),
+ *GetVirtualPixelsNexus(const Cache,NexusInfo *);
+
+extern MagickExport IndexPacket
+ *GetPixelCacheNexusIndexes(const Cache,NexusInfo *);
+
+extern MagickExport MagickBooleanType
+ SyncAuthenticPixelCacheNexus(Image *,NexusInfo *,ExceptionInfo *);
+
+extern MagickExport MagickSizeType
+ GetPixelCacheNexusExtent(const Cache,NexusInfo *);
+
+extern MagickExport NexusInfo
+ **AcquirePixelCacheNexus(const unsigned long),
+ **DestroyPixelCacheNexus(NexusInfo **,const unsigned long);
+
+extern MagickExport PixelPacket
+ *GetAuthenticPixelCacheNexus(Image *,const long,const long,
+ const unsigned long,const unsigned long,NexusInfo *,ExceptionInfo *),
+ *GetPixelCacheNexusPixels(const Cache,NexusInfo *),
+ *QueueAuthenticNexus(Image *,const long,const long,const unsigned long,
+ const unsigned long,NexusInfo *,ExceptionInfo *);
+
+extern MagickExport void
+ ClonePixelCacheMethods(Cache,const Cache),
+ GetPixelCacheMethods(CacheMethods *),
+ SetPixelCacheMethods(Cache,CacheMethods *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/cache-view.c b/magick/cache-view.c
new file mode 100644
index 0000000..20adb04
--- /dev/null
+++ b/magick/cache-view.c
@@ -0,0 +1,1021 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC AAA CCCC H H EEEEE %
+% C A A C H H E %
+% C AAAAA C HHHHH EEE %
+% C A A C H H E %
+% CCCC A A CCCC H H EEEEE %
+% %
+% V V IIIII EEEEE W W %
+% V V I E W W %
+% V V I EEE W W W %
+% V V I E WW WW %
+% V IIIII EEEEE W W %
+% %
+% %
+% MagickCore Cache View Methods %
+% %
+% Software Design %
+% John Cristy %
+% February 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/cache-view.h"
+#include "magick/memory_.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+
+/*
+ Typedef declarations.
+*/
+struct _CacheView
+{
+ Image
+ *image;
+
+ VirtualPixelMethod
+ virtual_pixel_method;
+
+ unsigned long
+ number_threads;
+
+ NexusInfo
+ **nexus_info;
+
+ MagickBooleanType
+ debug;
+
+ unsigned long
+ signature;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireCacheView() acquires a view into the pixel cache, using the
+% VirtualPixelMethod that is defined within the given image itself.
+%
+% The format of the AcquireCacheView method is:
+%
+% CacheView *AcquireCacheView(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport CacheView *AcquireCacheView(const Image *image)
+{
+ CacheView
+ *cache_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_view=(CacheView *) AcquireAlignedMemory(1,sizeof(*cache_view));
+ if (cache_view == (CacheView *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(cache_view,0,sizeof(*cache_view));
+ cache_view->image=ReferenceImage((Image *) image);
+ cache_view->number_threads=GetOpenMPMaximumThreads();
+ cache_view->nexus_info=AcquirePixelCacheNexus(cache_view->number_threads);
+ cache_view->virtual_pixel_method=GetImageVirtualPixelMethod(image);
+ cache_view->debug=IsEventLogging();
+ cache_view->signature=MagickSignature;
+ if (cache_view->nexus_info == (NexusInfo **) NULL)
+ ThrowFatalException(CacheFatalError,"UnableToAcquireCacheView");
+ return(cache_view);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneCacheView() makes an exact copy of the specified cache view.
+%
+% The format of the CloneCacheView method is:
+%
+% CacheView *CloneCacheView(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport CacheView *CloneCacheView(const CacheView *cache_view)
+{
+ CacheView
+ *clone_view;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ clone_view=(CacheView *) AcquireAlignedMemory(1,sizeof(*clone_view));
+ if (clone_view == (CacheView *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(clone_view,0,sizeof(*clone_view));
+ clone_view->image=ReferenceImage(cache_view->image);
+ clone_view->number_threads=cache_view->number_threads;
+ clone_view->nexus_info=AcquirePixelCacheNexus(cache_view->number_threads);
+ clone_view->virtual_pixel_method=cache_view->virtual_pixel_method;
+ clone_view->debug=cache_view->debug;
+ clone_view->signature=MagickSignature;
+ return(clone_view);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyCacheView() destroys the specified view returned by a previous call
+% to AcquireCacheView().
+%
+% The format of the DestroyCacheView method is:
+%
+% CacheView *DestroyCacheView(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport CacheView *DestroyCacheView(CacheView *cache_view)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ if (cache_view->nexus_info != (NexusInfo **) NULL)
+ cache_view->nexus_info=DestroyPixelCacheNexus(cache_view->nexus_info,
+ cache_view->number_threads);
+ cache_view->image=DestroyImage(cache_view->image);
+ cache_view->signature=(~MagickSignature);
+ cache_view=(CacheView *) RelinquishAlignedMemory(cache_view);
+ return(cache_view);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w C o l o r s p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewColorspace() returns the image colorspace associated with the
+% specified view.
+%
+% The format of the GetCacheViewColorspace method is:
+%
+% ColorspaceType GetCacheViewColorspace(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport ColorspaceType GetCacheViewColorspace(const CacheView *cache_view)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ return(cache_view->image->colorspace);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewException() returns the image exception associated with the
+% specified view.
+%
+% The format of the GetCacheViewException method is:
+%
+% ExceptionInfo GetCacheViewException(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport ExceptionInfo *GetCacheViewException(const CacheView *cache_view)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ return(&cache_view->image->exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C a c h e V i e w E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewExtent() returns the extent of the pixels associated with the
+% last call to QueueCacheViewAuthenticPixels() or
+% GetCacheViewAuthenticPixels().
+%
+% The format of the GetCacheViewExtent() method is:
+%
+% MagickSizeType GetCacheViewExtent(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport MagickSizeType GetCacheViewExtent(const CacheView *cache_view)
+{
+ long
+ id;
+
+ MagickSizeType
+ extent;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ assert(cache_view->image->cache != (Cache) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ extent=GetPixelCacheNexusExtent(cache_view->image->cache,
+ cache_view->nexus_info[id]);
+ return(extent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w S t o r a g e C l a s s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewStorageClass() returns the image storage class associated with
+% the specified view.
+%
+% The format of the GetCacheViewStorageClass method is:
+%
+% ClassType GetCacheViewStorageClass(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport ClassType GetCacheViewStorageClass(const CacheView *cache_view)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ return(cache_view->image->storage_class);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewAuthenticPixels() gets pixels from the in-memory or disk pixel
+% cache as defined by the geometry parameters. A pointer to the pixels is
+% returned if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetCacheViewAuthenticPixels method is:
+%
+% PixelPacket *GetCacheViewAuthenticPixels(CacheView *cache_view,
+% const long x,const long y,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *GetCacheViewAuthenticPixels(CacheView *cache_view,
+ const long x,const long y,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+ Cache
+ cache;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ cache=GetImagePixelCache(cache_view->image,MagickTrue,exception);
+ if (cache == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetAuthenticPixelCacheNexus(cache_view->image,x,y,columns,rows,
+ cache_view->nexus_info[id],exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e C a c h e V i e w A u t h e n t i c P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneCacheViewAuthenticPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs.
+%
+% The format of the GetOneCacheViewAuthenticPixel method is:
+%
+% MagickBooleaNType GetOneCacheViewAuthenticPixel(
+% const CacheView *cache_view,const long x,const long y,
+% Pixelpacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y: These values define the offset of the pixel.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneCacheViewAuthenticPixel(
+ const CacheView *cache_view,const long x,const long y,PixelPacket *pixel,
+ ExceptionInfo *exception)
+{
+ Cache
+ cache;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ cache=GetImagePixelCache(cache_view->image,MagickTrue,exception);
+ if (cache == (Cache) NULL)
+ return(MagickFalse);
+ *pixel=cache_view->image->background_color;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetAuthenticPixelCacheNexus(cache_view->image,x,y,1,1,
+ cache_view->nexus_info[id],exception);
+ if (pixels == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w A u t h e n t i c I n d e x Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewAuthenticIndexQueue() returns the indexes associated with the
+% last call to SetCacheViewIndexes() or GetCacheViewAuthenticIndexQueue(). The
+% indexes are authentic and can be updated.
+%
+% The format of the GetCacheViewAuthenticIndexQueue() method is:
+%
+% IndexPacket *GetCacheViewAuthenticIndexQueue(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport IndexPacket *GetCacheViewAuthenticIndexQueue(CacheView *cache_view)
+{
+ IndexPacket
+ *indexes;
+
+ long
+ id;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ assert(cache_view->image->cache != (Cache) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ indexes=GetPixelCacheNexusIndexes(cache_view->image->cache,
+ cache_view->nexus_info[id]);
+ return(indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w A u t h e n t i c P i x e l Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewAuthenticPixelQueue() returns the pixels associated with the
+% last call to QueueCacheViewAuthenticPixels() or
+% GetCacheViewAuthenticPixels(). The pixels are authentic and therefore can be
+% updated.
+%
+% The format of the GetCacheViewAuthenticPixelQueue() method is:
+%
+% PixelPacket *GetCacheViewAuthenticPixelQueue(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport PixelPacket *GetCacheViewAuthenticPixelQueue(CacheView *cache_view)
+{
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ assert(cache_view->image->cache != (Cache) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetPixelCacheNexusPixels(cache_view->image->cache,
+ cache_view->nexus_info[id]);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w V i r t u a l I n d e x Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewVirtualIndexQueue() returns the indexes associated with the
+% last call to GetCacheViewVirtualIndexQueue(). The indexes are virtual and
+% therefore cannot be updated.
+%
+% The format of the GetCacheViewVirtualIndexQueue() method is:
+%
+% const IndexPacket *GetCacheViewVirtualIndexQueue(
+% const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport const IndexPacket *GetCacheViewVirtualIndexQueue(
+ const CacheView *cache_view)
+{
+ const IndexPacket
+ *indexes;
+
+ long
+ id;
+
+ assert(cache_view != (const CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ assert(cache_view->image->cache != (Cache) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ indexes=GetVirtualIndexesFromNexus(cache_view->image->cache,
+ cache_view->nexus_info[id]);
+ return(indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w V i r t u a l P i x e l Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewVirtualPixelQueue() returns the the pixels associated with
+% the last call to GetCacheViewVirtualPixels(). The pixels are virtual
+% and therefore cannot be updated.
+%
+% The format of the GetCacheViewVirtualPixelQueue() method is:
+%
+% const PixelPacket *GetCacheViewVirtualPixelQueue(
+% const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport const PixelPacket *GetCacheViewVirtualPixelQueue(
+ const CacheView *cache_view)
+{
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ assert(cache_view != (const CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ assert(cache_view->image->cache != (Cache) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetVirtualPixelsNexus(cache_view->image->cache,
+ cache_view->nexus_info[id]);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w V i r t u a l P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewVirtualPixels() gets virtual pixels from the in-memory or
+% disk pixel cache as defined by the geometry parameters. A pointer to the
+% pixels is returned if the pixels are transferred, otherwise a NULL is
+% returned.
+%
+% The format of the GetCacheViewVirtualPixels method is:
+%
+% const PixelPacket *GetCacheViewVirtualPixels(
+% const CacheView *cache_view,const long x,const long y,
+% const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const PixelPacket *GetCacheViewVirtualPixels(
+ const CacheView *cache_view,const long x,const long y,
+ const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
+{
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetVirtualPixelsFromNexus(cache_view->image,
+ cache_view->virtual_pixel_method,x,y,columns,rows,
+ cache_view->nexus_info[id],exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e C a c h e V i e w V i r t u a l P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneCacheViewVirtualPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs. If
+% you plan to modify the pixel, use GetOneCacheViewAuthenticPixel() instead.
+%
+% The format of the GetOneCacheViewVirtualPixel method is:
+%
+% MagickBooleanType GetOneCacheViewVirtualPixel(
+% const CacheView *cache_view,const long x,const long y,
+% PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y: These values define the offset of the pixel.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneCacheViewVirtualPixel(
+ const CacheView *cache_view,const long x,const long y,PixelPacket *pixel,
+ ExceptionInfo *exception)
+{
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ *pixel=cache_view->image->background_color;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetVirtualPixelsFromNexus(cache_view->image,
+ cache_view->virtual_pixel_method,x,y,1,1,cache_view->nexus_info[id],
+ exception);
+ if (pixels == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e C a c h e V i e w V i r t u a l P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneCacheViewVirtualMethodPixel() returns a single virtual pixel at
+% the specified (x,y) location. The image background color is returned if an
+% error occurs. If you plan to modify the pixel, use
+% GetOneCacheViewAuthenticPixel() instead.
+%
+% The format of the GetOneCacheViewVirtualPixel method is:
+%
+% MagickBooleanType GetOneCacheViewVirtualMethodPixel(
+% const CacheView *cache_view,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y: These values define the offset of the pixel.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneCacheViewVirtualMethodPixel(
+ const CacheView *cache_view,const VirtualPixelMethod virtual_pixel_method,
+ const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ *pixel=cache_view->image->background_color;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=GetVirtualPixelsFromNexus(cache_view->image,virtual_pixel_method,x,y,1,
+ 1,cache_view->nexus_info[id],exception);
+ if (pixels == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e u e C a c h e V i e w A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueueCacheViewAuthenticPixels() queues authentic pixels from the in-memory or
+% disk pixel cache as defined by the geometry parameters. A pointer to the
+% pixels is returned if the pixels are transferred, otherwise a NULL is
+% returned.
+%
+% The format of the QueueCacheViewAuthenticPixels method is:
+%
+% PixelPacket *QueueCacheViewAuthenticPixels(CacheView *cache_view,
+% const long x,const long y,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket *QueueCacheViewAuthenticPixels(CacheView *cache_view,
+ const long x,const long y,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+ Cache
+ cache;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ cache=GetImagePixelCache(cache_view->image,MagickFalse,exception);
+ if (cache == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ pixels=QueueAuthenticNexus(cache_view->image,x,y,columns,rows,
+ cache_view->nexus_info[id],exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t C a c h e V i e w S t o r a g e C l a s s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetCacheViewStorageClass() sets the image storage class associated with
+% the specified view.
+%
+% The format of the SetCacheViewStorageClass method is:
+%
+% MagickBooleanType SetCacheViewStorageClass(CacheView *cache_view,
+% const ClassType storage_class)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o storage_class: the image storage class: PseudoClass or DirectClass.
+%
+*/
+MagickExport MagickBooleanType SetCacheViewStorageClass(CacheView *cache_view,
+ const ClassType storage_class)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ return(SetImageStorageClass(cache_view->image,storage_class));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t C a c h e V i e w V i r t u a l P i x e l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetCacheViewVirtualPixelMethod() sets the virtual pixel method associated
+% with the specified cache view.
+%
+% The format of the SetCacheViewVirtualPixelMethod method is:
+%
+% MagickBooleanType SetCacheViewVirtualPixelMethod(CacheView *cache_view,
+% const VirtualPixelMethod virtual_pixel_method)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+*/
+MagickExport MagickBooleanType SetCacheViewVirtualPixelMethod(
+ CacheView *cache_view,const VirtualPixelMethod virtual_pixel_method)
+{
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ cache_view->virtual_pixel_method=virtual_pixel_method;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c C a c h e V i e w A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncCacheViewAuthenticPixels() saves the cache view pixels to the in-memory
+% or disk cache. It returns MagickTrue if the pixel region is flushed,
+% otherwise MagickFalse.
+%
+% The format of the SyncCacheViewAuthenticPixels method is:
+%
+% MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *cache_view,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(
+ CacheView *cache_view,ExceptionInfo *exception)
+{
+ long
+ id;
+
+ MagickBooleanType
+ status;
+
+ assert(cache_view != (CacheView *) NULL);
+ assert(cache_view->signature == MagickSignature);
+ if (cache_view->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_view->image->filename);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_view->number_threads);
+ status=SyncAuthenticPixelCacheNexus(cache_view->image,
+ cache_view->nexus_info[id],exception);
+ return(status);
+}
diff --git a/magick/cache-view.h b/magick/cache-view.h
new file mode 100644
index 0000000..850d95a
--- /dev/null
+++ b/magick/cache-view.h
@@ -0,0 +1,103 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore cache view methods.
+*/
+#ifndef _MAGICKCORE_CACHE_VIEW_H
+#define _MAGICKCORE_CACHE_VIEW_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/pixel.h"
+
+typedef enum
+{
+ UndefinedVirtualPixelMethod,
+ BackgroundVirtualPixelMethod,
+ ConstantVirtualPixelMethod, /* deprecated */
+ DitherVirtualPixelMethod,
+ EdgeVirtualPixelMethod,
+ MirrorVirtualPixelMethod,
+ RandomVirtualPixelMethod,
+ TileVirtualPixelMethod,
+ TransparentVirtualPixelMethod,
+ MaskVirtualPixelMethod,
+ BlackVirtualPixelMethod,
+ GrayVirtualPixelMethod,
+ WhiteVirtualPixelMethod,
+ HorizontalTileVirtualPixelMethod,
+ VerticalTileVirtualPixelMethod,
+ HorizontalTileEdgeVirtualPixelMethod,
+ VerticalTileEdgeVirtualPixelMethod,
+ CheckerTileVirtualPixelMethod
+} VirtualPixelMethod;
+
+typedef struct _CacheView
+ CacheView;
+
+extern MagickExport ClassType
+ GetCacheViewStorageClass(const CacheView *);
+
+extern MagickExport ColorspaceType
+ GetCacheViewColorspace(const CacheView *);
+
+extern MagickExport const IndexPacket
+ *GetCacheViewVirtualIndexQueue(const CacheView *);
+
+extern MagickExport const PixelPacket
+ *GetCacheViewVirtualPixels(const CacheView *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *),
+ *GetCacheViewVirtualPixelQueue(const CacheView *);
+
+extern MagickExport ExceptionInfo
+ *GetCacheViewException(const CacheView *);
+
+extern MagickExport IndexPacket
+ *GetCacheViewAuthenticIndexQueue(CacheView *);
+
+extern MagickExport MagickBooleanType
+ GetOneCacheViewVirtualPixel(const CacheView *,const long,const long,
+ PixelPacket *,ExceptionInfo *),
+ GetOneCacheViewVirtualMethodPixel(const CacheView *,
+ const VirtualPixelMethod,const long,const long,PixelPacket *,
+ ExceptionInfo *),
+ GetOneCacheViewAuthenticPixel(const CacheView *,const long,const long,
+ PixelPacket *,ExceptionInfo *),
+ SetCacheViewStorageClass(CacheView *,const ClassType),
+ SetCacheViewVirtualPixelMethod(CacheView *,const VirtualPixelMethod),
+ SyncCacheViewAuthenticPixels(CacheView *,ExceptionInfo *);
+
+extern MagickExport MagickSizeType
+ GetCacheViewExtent(const CacheView *);
+
+extern MagickExport PixelPacket
+ *GetCacheViewAuthenticPixelQueue(CacheView *),
+ *GetCacheViewAuthenticPixels(CacheView *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *),
+ *QueueCacheViewAuthenticPixels(CacheView *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *);
+
+extern MagickExport CacheView
+ *AcquireCacheView(const Image *),
+ *CloneCacheView(const CacheView *),
+ *DestroyCacheView(CacheView *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/cache.c b/magick/cache.c
new file mode 100644
index 0000000..f187793
--- /dev/null
+++ b/magick/cache.c
@@ -0,0 +1,5306 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC AAA CCCC H H EEEEE %
+% C A A C H H E %
+% C AAAAA C HHHHH EEE %
+% C A A C H H E %
+% CCCC A A CCCC H H EEEEE %
+% %
+% %
+% MagickCore Pixel Cache Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1999 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/color-private.h"
+#include "magick/composite-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/utility.h"
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+#include "zlib.h"
+#endif
+
+/*
+ Typedef declarations.
+*/
+typedef struct _MagickModulo
+{
+ long
+ quotient,
+ remainder;
+} MagickModulo;
+
+struct _NexusInfo
+{
+ MagickBooleanType
+ mapped;
+
+ RectangleInfo
+ region;
+
+ MagickSizeType
+ length;
+
+ PixelPacket
+ *cache,
+ *pixels;
+
+ IndexPacket
+ *indexes;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Forward declarations.
+*/
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static const IndexPacket
+ *GetVirtualIndexesFromCache(const Image *);
+
+static const PixelPacket
+ *GetVirtualPixelCache(const Image *,const VirtualPixelMethod,const long,
+ const long,const unsigned long,const unsigned long,ExceptionInfo *),
+ *GetVirtualPixelsCache(const Image *);
+
+static MagickBooleanType
+ GetOneAuthenticPixelFromCache(Image *,const long,const long,PixelPacket *,
+ ExceptionInfo *),
+ GetOneVirtualPixelFromCache(const Image *,const VirtualPixelMethod,
+ const long,const long,PixelPacket *,ExceptionInfo *),
+ OpenPixelCache(Image *,const MapMode,ExceptionInfo *),
+ ReadPixelCacheIndexes(CacheInfo *,NexusInfo *,ExceptionInfo *),
+ ReadPixelCachePixels(CacheInfo *,NexusInfo *,ExceptionInfo *),
+ SyncAuthenticPixelsCache(Image *,ExceptionInfo *),
+ WritePixelCacheIndexes(CacheInfo *,NexusInfo *,ExceptionInfo *),
+ WritePixelCachePixels(CacheInfo *,NexusInfo *,ExceptionInfo *);
+
+static PixelPacket
+ *GetAuthenticPixelsCache(Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *),
+ *QueueAuthenticPixelsCache(Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *),
+ *SetPixelCacheNexusPixels(const Image *,const RectangleInfo *,NexusInfo *,
+ ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+/*
+ Global declarations.
+*/
+static volatile MagickBooleanType
+ instantiate_cache = MagickFalse;
+
+static SemaphoreInfo
+ *cache_semaphore = (SemaphoreInfo *) NULL;
+
+static SplayTreeInfo
+ *cache_resources = (SplayTreeInfo *) NULL;
+
+static time_t
+ cache_timer = 0;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquirePixelCache() acquires a pixel cache.
+%
+% The format of the AcquirePixelCache() method is:
+%
+% Cache AcquirePixelCache(const unsigned long number_threads)
+%
+% A description of each parameter follows:
+%
+% o number_threads: the number of nexus threads.
+%
+*/
+MagickExport Cache AcquirePixelCache(const unsigned long number_threads)
+{
+ CacheInfo
+ *cache_info;
+
+ cache_info=(CacheInfo *) AcquireAlignedMemory(1,sizeof(*cache_info));
+ if (cache_info == (CacheInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(cache_info,0,sizeof(*cache_info));
+ cache_info->type=UndefinedCache;
+ cache_info->colorspace=RGBColorspace;
+ cache_info->file=(-1);
+ cache_info->id=GetMagickThreadId();
+ cache_info->number_threads=number_threads;
+ if (number_threads == 0)
+ cache_info->number_threads=GetOpenMPMaximumThreads();
+ cache_info->nexus_info=AcquirePixelCacheNexus(cache_info->number_threads);
+ if (cache_info->nexus_info == (NexusInfo **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetPixelCacheMethods(&cache_info->methods);
+ cache_info->reference_count=1;
+ cache_info->semaphore=AllocateSemaphoreInfo();
+ cache_info->disk_semaphore=AllocateSemaphoreInfo();
+ cache_info->debug=IsEventLogging();
+ cache_info->signature=MagickSignature;
+ if ((cache_resources == (SplayTreeInfo *) NULL) &&
+ (instantiate_cache == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&cache_semaphore);
+ if ((cache_resources == (SplayTreeInfo *) NULL) &&
+ (instantiate_cache == MagickFalse))
+ {
+ cache_resources=NewSplayTree((int (*)(const void *,const void *))
+ NULL,(void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
+ instantiate_cache=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(cache_semaphore);
+ }
+ (void) AddValueToSplayTree(cache_resources,cache_info,cache_info);
+ return((Cache ) cache_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquirePixelCacheNexus() allocates the NexusInfo structure.
+%
+% The format of the AcquirePixelCacheNexus method is:
+%
+% NexusInfo **AcquirePixelCacheNexus(const unsigned long number_threads)
+%
+% A description of each parameter follows:
+%
+% o number_threads: the number of nexus threads.
+%
+*/
+MagickExport NexusInfo **AcquirePixelCacheNexus(
+ const unsigned long number_threads)
+{
+ register long
+ i;
+
+ NexusInfo
+ **nexus_info;
+
+ nexus_info=(NexusInfo **) AcquireAlignedMemory(number_threads,
+ sizeof(*nexus_info));
+ if (nexus_info == (NexusInfo **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ for (i=0; i < (long) number_threads; i++)
+ {
+ nexus_info[i]=(NexusInfo *) AcquireAlignedMemory(1,sizeof(**nexus_info));
+ if (nexus_info[i] == (NexusInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(nexus_info[i],0,sizeof(*nexus_info[i]));
+ nexus_info[i]->signature=MagickSignature;
+ }
+ return(nexus_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l i p P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClipPixelCacheNexus() clips the cache nexus as defined by the image clip
+% mask. The method returns MagickTrue if the pixel region is clipped,
+% otherwise MagickFalse.
+%
+% The format of the ClipPixelCacheNexus() method is:
+%
+% MagickBooleanType ClipPixelCacheNexus(Image *image,NexusInfo *nexus_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o nexus_info: the cache nexus to clip.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType ClipPixelCacheNexus(Image *image,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickSizeType
+ number_pixels;
+
+ NexusInfo
+ **clip_nexus,
+ **image_nexus;
+
+ register const PixelPacket
+ *__restrict r;
+
+ register IndexPacket
+ *__restrict nexus_indexes,
+ *__restrict indexes;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ /*
+ Apply clip mask.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->clip_mask == (Image *) NULL)
+ return(MagickFalse);
+ cache_info=(CacheInfo *) GetImagePixelCache(image,MagickTrue,exception);
+ if (cache_info == (Cache) NULL)
+ return(MagickFalse);
+ image_nexus=AcquirePixelCacheNexus(1);
+ clip_nexus=AcquirePixelCacheNexus(1);
+ if ((image_nexus == (NexusInfo **) NULL) ||
+ (clip_nexus == (NexusInfo **) NULL))
+ ThrowBinaryException(CacheError,"UnableToGetCacheNexus",image->filename);
+ p=GetAuthenticPixelCacheNexus(image,nexus_info->region.x,nexus_info->region.y,
+ nexus_info->region.width,nexus_info->region.height,image_nexus[0],
+ exception);
+ indexes=GetPixelCacheNexusIndexes(image->cache,image_nexus[0]);
+ q=nexus_info->pixels;
+ nexus_indexes=nexus_info->indexes;
+ r=GetVirtualPixelsFromNexus(image->clip_mask,MaskVirtualPixelMethod,
+ nexus_info->region.x,nexus_info->region.y,nexus_info->region.width,
+ nexus_info->region.height,clip_nexus[0],exception);
+ number_pixels=(MagickSizeType) nexus_info->region.width*
+ nexus_info->region.height;
+ for (i=0; i < (long) number_pixels; i++)
+ {
+ if ((p == (PixelPacket *) NULL) || (r == (const PixelPacket *) NULL))
+ break;
+ if (PixelIntensityToQuantum(r) > ((Quantum) QuantumRange/2))
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=p->opacity;
+ if (cache_info->active_index_channel != MagickFalse)
+ nexus_indexes[i]=indexes[i];
+ }
+ p++;
+ q++;
+ r++;
+ }
+ clip_nexus=DestroyPixelCacheNexus(clip_nexus,1);
+ image_nexus=DestroyPixelCacheNexus(image_nexus,1);
+ if (i < (long) number_pixels)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o n e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClonePixelCache() clones a pixel cache.
+%
+% The format of the ClonePixelCache() method is:
+%
+% Cache ClonePixelCache(const Cache cache)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+*/
+MagickExport Cache ClonePixelCache(const Cache cache)
+{
+ CacheInfo
+ *clone_info;
+
+ const CacheInfo
+ *cache_info;
+
+ assert(cache != (const Cache) NULL);
+ cache_info=(const CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ clone_info=(CacheInfo *) AcquirePixelCache(cache_info->number_threads);
+ if (clone_info == (Cache) NULL)
+ return((Cache) NULL);
+ clone_info->virtual_pixel_method=cache_info->virtual_pixel_method;
+ return((Cache ) clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o n e P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClonePixelCacheNexus() clones the source cache nexus to the destination
+% nexus.
+%
+% The format of the ClonePixelCacheNexus() method is:
+%
+% MagickBooleanType ClonePixelCacheNexus(CacheInfo *destination,
+% CacheInfo *source,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o destination: the destination cache nexus.
+%
+% o source: the source cache nexus.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType AcquireCacheNexusPixels(CacheInfo *cache_info,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ if (nexus_info->length != (MagickSizeType) ((size_t) nexus_info->length))
+ return(MagickFalse);
+ nexus_info->mapped=MagickFalse;
+ nexus_info->cache=(PixelPacket *) AcquireMagickMemory((size_t)
+ nexus_info->length);
+ if (nexus_info->cache == (PixelPacket *) NULL)
+ {
+ nexus_info->mapped=MagickTrue;
+ nexus_info->cache=(PixelPacket *) MapBlob(-1,IOMode,0,(size_t)
+ nexus_info->length);
+ }
+ if (nexus_info->cache == (PixelPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ cache_info->filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+static MagickBooleanType ClonePixelCacheNexus(CacheInfo *destination,
+ CacheInfo *source,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ MagickSizeType
+ number_pixels;
+
+ register long
+ i;
+
+ register const NexusInfo
+ *p;
+
+ register NexusInfo
+ *q;
+
+ status=MagickTrue;
+ for (i=0; i < (long) source->number_threads; i++)
+ {
+ p=source->nexus_info[i];
+ q=destination->nexus_info[i];
+ q->mapped=p->mapped;
+ q->region=p->region;
+ q->length=p->length;
+ q->cache=p->cache;
+ q->pixels=p->pixels;
+ q->indexes=p->indexes;
+ if (p->cache != (PixelPacket *) NULL)
+ {
+ status=AcquireCacheNexusPixels(source,q,exception);
+ if (status != MagickFalse)
+ {
+ (void) CopyMagickMemory(q->cache,p->cache,(size_t) p->length);
+ q->pixels=q->cache;
+ q->indexes=(IndexPacket *) NULL;
+ number_pixels=(MagickSizeType) q->region.width*q->region.height;
+ if (p->indexes != (IndexPacket *) NULL)
+ q->indexes=(IndexPacket *) (q->pixels+number_pixels);
+ }
+ }
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o n e P i x e l C a c h e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %
+% ClonePixelCachePixels() clones the source pixel cache to the destination
+% cache.
+%
+% The format of the ClonePixelCachePixels() method is:
+%
+% MagickBooleanType ClonePixelCachePixels(CacheInfo *cache_info,
+% CacheInfo *source_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+% o source_info: the source pixel cache.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static MagickBooleanType ClosePixelCacheOnDisk(CacheInfo *cache_info)
+{
+ int
+ status;
+
+ AcquireSemaphoreInfo(&cache_info->disk_semaphore);
+ status=close(cache_info->file);
+ cache_info->file=(-1);
+ RelinquishMagickResource(FileResource,1);
+ RelinquishSemaphoreInfo(cache_info->disk_semaphore);
+ return(status == -1 ? MagickFalse : MagickTrue);
+}
+
+static void LimitPixelCacheDescriptors(void)
+{
+ register CacheInfo
+ *p,
+ *q;
+
+ /*
+ Limit # of open file descriptors.
+ */
+ if (GetMagickResource(FileResource) < GetMagickResourceLimit(FileResource))
+ return;
+ AcquireSemaphoreInfo(&cache_semaphore);
+ if (cache_resources == (SplayTreeInfo *) NULL)
+ {
+ RelinquishSemaphoreInfo(cache_semaphore);
+ return;
+ }
+ ResetSplayTreeIterator(cache_resources);
+ p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
+ while (p != (CacheInfo *) NULL)
+ {
+ if ((p->type == DiskCache) && (p->file != -1))
+ {
+ if (IsMagickThreadEqual(p->id) != MagickFalse)
+ break;
+ }
+ p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
+ }
+ for (q=p; p != (CacheInfo *) NULL; )
+ {
+ if ((p->type == DiskCache) && (p->file != -1) &&
+ (p->timestamp < q->timestamp))
+ {
+ if (IsMagickThreadEqual(p->id) != MagickFalse)
+ q=p;
+ }
+ p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
+ }
+ if (q != (CacheInfo *) NULL)
+ (void) ClosePixelCacheOnDisk(q); /* relinquish least recently used cache */
+ RelinquishSemaphoreInfo(cache_semaphore);
+}
+
+static inline MagickSizeType MagickMax(const MagickSizeType x,
+ const MagickSizeType y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline MagickSizeType MagickMin(const MagickSizeType x,
+ const MagickSizeType y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static MagickBooleanType OpenPixelCacheOnDisk(CacheInfo *cache_info,
+ const MapMode mode)
+{
+ int
+ file;
+
+ /*
+ Open pixel cache on disk.
+ */
+ AcquireSemaphoreInfo(&cache_info->disk_semaphore);
+ if (cache_info->file != -1)
+ {
+ RelinquishSemaphoreInfo(cache_info->disk_semaphore);
+ return(MagickTrue); /* cache already open */
+ }
+ LimitPixelCacheDescriptors();
+ if (*cache_info->cache_filename == '\0')
+ file=AcquireUniqueFileResource(cache_info->cache_filename);
+ else
+ switch (mode)
+ {
+ case ReadMode:
+ {
+ file=open(cache_info->cache_filename,O_RDONLY | O_BINARY);
+ break;
+ }
+ case WriteMode:
+ {
+ file=open(cache_info->cache_filename,O_WRONLY | O_CREAT | O_BINARY |
+ O_EXCL,S_MODE);
+ if (file == -1)
+ file=open(cache_info->cache_filename,O_WRONLY | O_BINARY,S_MODE);
+ break;
+ }
+ case IOMode:
+ default:
+ {
+ file=open(cache_info->cache_filename,O_RDWR | O_CREAT | O_BINARY |
+ O_EXCL,S_MODE);
+ if (file == -1)
+ file=open(cache_info->cache_filename,O_RDWR | O_BINARY,S_MODE);
+ break;
+ }
+ }
+ if (file == -1)
+ {
+ RelinquishSemaphoreInfo(cache_info->disk_semaphore);
+ return(MagickFalse);
+ }
+ (void) AcquireMagickResource(FileResource,1);
+ cache_info->file=file;
+ cache_info->timestamp=time(0);
+ RelinquishSemaphoreInfo(cache_info->disk_semaphore);
+ return(MagickTrue);
+}
+
+static inline MagickOffsetType ReadPixelCacheRegion(CacheInfo *cache_info,
+ const MagickOffsetType offset,const MagickSizeType length,
+ unsigned char *__restrict buffer)
+{
+ register MagickOffsetType
+ i;
+
+ ssize_t
+ count;
+
+#if !defined(MAGICKCORE_HAVE_PREAD)
+ (void) LockSemaphoreInfo(cache_info->disk_semaphore);
+ cache_info->timestamp=time(0);
+ if (MagickSeek(cache_info->file,offset,SEEK_SET) < 0)
+ {
+ (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
+ return((MagickOffsetType) -1);
+ }
+#endif
+ count=0;
+ for (i=0; i < (MagickOffsetType) length; i+=count)
+ {
+#if !defined(MAGICKCORE_HAVE_PREAD)
+ count=read(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
+ (MagickSizeType) SSIZE_MAX));
+#else
+ count=pread(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
+ (MagickSizeType) SSIZE_MAX),(off_t) (offset+i));
+#endif
+ if (count > 0)
+ continue;
+ count=0;
+ if (errno != EINTR)
+ {
+ i=(-1);
+ break;
+ }
+ }
+#if !defined(MAGICKCORE_HAVE_PREAD)
+ (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
+#endif
+ return(i);
+}
+
+static inline MagickOffsetType WritePixelCacheRegion(CacheInfo *cache_info,
+ const MagickOffsetType offset,const MagickSizeType length,
+ const unsigned char *__restrict buffer)
+{
+ register MagickOffsetType
+ i;
+
+ ssize_t
+ count;
+
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+ (void) LockSemaphoreInfo(cache_info->disk_semaphore);
+ cache_info->timestamp=time(0);
+ if (MagickSeek(cache_info->file,offset,SEEK_SET) < 0)
+ {
+ (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
+ return((MagickOffsetType) -1);
+ }
+#endif
+ count=0;
+ for (i=0; i < (MagickOffsetType) length; i+=count)
+ {
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+ count=write(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
+ (MagickSizeType) SSIZE_MAX));
+#else
+ count=pwrite(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
+ (MagickSizeType) SSIZE_MAX),(off_t) (offset+i));
+#endif
+ if (count > 0)
+ continue;
+ count=0;
+ if (errno != EINTR)
+ {
+ i=(-1);
+ break;
+ }
+ }
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+ (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
+#endif
+ return(i);
+}
+
+static MagickBooleanType CloneDiskToDiskPixelCache(CacheInfo *clone_info,
+ CacheInfo *cache_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset,
+ source_offset;
+
+ MagickSizeType
+ length;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict pixels;
+
+ unsigned long
+ columns,
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"disk => disk");
+ if (OpenPixelCacheOnDisk(clone_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ clone_info->cache_filename);
+ return(MagickFalse);
+ }
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ columns=(unsigned long) MagickMin(clone_info->columns,cache_info->columns);
+ rows=(unsigned long) MagickMin(clone_info->rows,cache_info->rows);
+ if ((clone_info->active_index_channel != MagickFalse) &&
+ (cache_info->active_index_channel != MagickFalse))
+ {
+ register IndexPacket
+ *indexes;
+
+ /*
+ Clone cache indexes.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*
+ sizeof(*indexes);
+ indexes=(IndexPacket *) AcquireMagickMemory((size_t) length);
+ if (indexes == (IndexPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(indexes,0,(size_t) length);
+ length=columns*sizeof(*indexes);
+ source_offset=(MagickOffsetType) cache_info->columns*cache_info->rows*
+ sizeof(*pixels)+cache_info->columns*rows*sizeof(*indexes);
+ offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
+ sizeof(*pixels)+clone_info->columns*rows*sizeof(*indexes);
+ for (y=0; y < (long) rows; y++)
+ {
+ source_offset-=cache_info->columns*sizeof(*indexes);
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+source_offset,
+ length,(unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ offset-=clone_info->columns*sizeof(*indexes);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ length=(clone_info->columns-cache_info->columns)*sizeof(*indexes);
+ (void) ResetMagickMemory(indexes,0,(size_t) length);
+ offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
+ sizeof(*pixels)+(clone_info->columns*rows+columns)*sizeof(*indexes);
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=clone_info->columns*sizeof(*indexes);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,
+ length,(unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ }
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ }
+ /*
+ Clone cache pixels.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*sizeof(*pixels);
+ pixels=(PixelPacket *) AcquireMagickMemory((size_t) length);
+ if (pixels == (PixelPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(pixels,0,(size_t) length);
+ length=columns*sizeof(*pixels);
+ source_offset=(MagickOffsetType) cache_info->columns*rows*sizeof(*pixels);
+ offset=(MagickOffsetType) clone_info->columns*rows*sizeof(*pixels);
+ for (y=0; y < (long) rows; y++)
+ {
+ source_offset-=cache_info->columns*sizeof(*pixels);
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+source_offset,
+ length,(unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ offset-=clone_info->columns*sizeof(*pixels);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ offset=(MagickOffsetType) (clone_info->columns*rows+columns)*
+ sizeof(*pixels);
+ length=(clone_info->columns-cache_info->columns)*sizeof(*pixels);
+ (void) ResetMagickMemory(pixels,0,(size_t) length);
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=clone_info->columns*sizeof(*pixels);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ }
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ return(MagickTrue);
+}
+
+static MagickBooleanType CloneDiskToMemoryPixelCache(CacheInfo *clone_info,
+ CacheInfo *cache_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict pixels,
+ *__restrict q;
+
+ unsigned long
+ columns,
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"disk => memory");
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ columns=(unsigned long) MagickMin(clone_info->columns,cache_info->columns);
+ rows=(unsigned long) MagickMin(clone_info->rows,cache_info->rows);
+ if ((clone_info->active_index_channel != MagickFalse) &&
+ (cache_info->active_index_channel != MagickFalse))
+ {
+ register IndexPacket
+ *indexes,
+ *q;
+
+ /*
+ Clone cache indexes.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*
+ sizeof(*indexes);
+ indexes=(IndexPacket *) AcquireMagickMemory((size_t) length);
+ if (indexes == (IndexPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(indexes,0,(size_t) length);
+ length=columns*sizeof(IndexPacket);
+ offset=(MagickOffsetType) cache_info->columns*cache_info->rows*
+ sizeof(*pixels)+cache_info->columns*rows*sizeof(*indexes);
+ q=clone_info->indexes+clone_info->columns*rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=cache_info->columns*sizeof(IndexPacket);
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+offset,
+ length,(unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ q-=clone_info->columns;
+ (void) CopyMagickMemory(q,indexes,(size_t) length);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ }
+ /*
+ Clone cache pixels.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*sizeof(*pixels);
+ pixels=(PixelPacket *) AcquireMagickMemory((size_t) length);
+ if (pixels == (PixelPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(pixels,0,(size_t) length);
+ length=columns*sizeof(*pixels);
+ offset=(MagickOffsetType) cache_info->columns*rows*sizeof(*pixels);
+ q=clone_info->pixels+clone_info->columns*rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=cache_info->columns*sizeof(*pixels);
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+offset,length,
+ (unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ q-=clone_info->columns;
+ (void) CopyMagickMemory(q,pixels,(size_t) length);
+ }
+ if (y < (long) rows)
+ {
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ return(MagickTrue);
+}
+
+static MagickBooleanType CloneMemoryToDiskPixelCache(CacheInfo *clone_info,
+ CacheInfo *cache_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict p,
+ *__restrict pixels;
+
+ unsigned long
+ columns,
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"memory => disk");
+ if (OpenPixelCacheOnDisk(clone_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ clone_info->cache_filename);
+ return(MagickFalse);
+ }
+ columns=(unsigned long) MagickMin(clone_info->columns,cache_info->columns);
+ rows=(unsigned long) MagickMin(clone_info->rows,cache_info->rows);
+ if ((clone_info->active_index_channel != MagickFalse) &&
+ (cache_info->active_index_channel != MagickFalse))
+ {
+ register IndexPacket
+ *p,
+ *indexes;
+
+ /*
+ Clone cache indexes.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*
+ sizeof(*indexes);
+ indexes=(IndexPacket *) AcquireMagickMemory((size_t) length);
+ if (indexes == (IndexPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(indexes,0,(size_t) length);
+ length=columns*sizeof(*indexes);
+ p=cache_info->indexes+cache_info->columns*rows;
+ offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
+ sizeof(*pixels)+clone_info->columns*rows*sizeof(*indexes);
+ for (y=0; y < (long) rows; y++)
+ {
+ p-=cache_info->columns;
+ (void) CopyMagickMemory(indexes,p,(size_t) length);
+ offset-=clone_info->columns*sizeof(*indexes);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ length=(clone_info->columns-cache_info->columns)*sizeof(*indexes);
+ (void) ResetMagickMemory(indexes,0,(size_t) length);
+ offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
+ sizeof(*pixels)+(clone_info->columns*rows+columns)*sizeof(*indexes);
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=clone_info->columns*sizeof(*indexes);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,
+ length,(unsigned char *) indexes);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ }
+ indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
+ }
+ /*
+ Clone cache pixels.
+ */
+ length=MagickMax(clone_info->columns,cache_info->columns)*sizeof(*pixels);
+ pixels=(PixelPacket *) AcquireMagickMemory((size_t) length);
+ if (pixels == (PixelPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(pixels,0,(size_t) length);
+ length=columns*sizeof(*pixels);
+ p=cache_info->pixels+cache_info->columns*rows;
+ offset=(MagickOffsetType) clone_info->columns*rows*sizeof(*pixels);
+ for (y=0; y < (long) rows; y++)
+ {
+ p-=cache_info->columns;
+ (void) CopyMagickMemory(pixels,p,(size_t) length);
+ offset-=clone_info->columns*sizeof(*pixels);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ offset=(MagickOffsetType) (clone_info->columns*rows+columns)*
+ sizeof(*pixels);
+ length=(clone_info->columns-cache_info->columns)*sizeof(*pixels);
+ (void) ResetMagickMemory(pixels,0,(size_t) length);
+ for (y=0; y < (long) rows; y++)
+ {
+ offset-=clone_info->columns*sizeof(*pixels);
+ count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
+ (unsigned char *) pixels);
+ if ((MagickSizeType) count != length)
+ break;
+ }
+ if (y < (long) rows)
+ {
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ ThrowFileException(exception,CacheError,"UnableToCloneCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ }
+ pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
+ return(MagickTrue);
+}
+
+static MagickBooleanType CloneMemoryToMemoryPixelCache(CacheInfo *clone_info,
+ CacheInfo *cache_info,ExceptionInfo *magick_unused(exception))
+{
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict pixels,
+ *__restrict source_pixels;
+
+ size_t
+ length;
+
+ unsigned long
+ columns,
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"memory => memory");
+ columns=(unsigned long) MagickMin(clone_info->columns,cache_info->columns);
+ rows=(unsigned long) MagickMin(clone_info->rows,cache_info->rows);
+ if ((clone_info->active_index_channel != MagickFalse) &&
+ (cache_info->active_index_channel != MagickFalse))
+ {
+ register IndexPacket
+ *indexes,
+ *source_indexes;
+
+ /*
+ Clone cache indexes.
+ */
+ length=columns*sizeof(*indexes);
+ if (clone_info->columns == cache_info->columns)
+ (void) CopyMagickMemory(clone_info->indexes,cache_info->indexes,
+ length*rows);
+ else
+ {
+ source_indexes=cache_info->indexes+cache_info->columns*rows;
+ indexes=clone_info->indexes+clone_info->columns*rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ source_indexes-=cache_info->columns;
+ indexes-=clone_info->columns;
+ (void) CopyMagickMemory(indexes,source_indexes,length);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ length=(clone_info->columns-cache_info->columns)*
+ sizeof(*indexes);
+ indexes=clone_info->indexes+clone_info->columns*rows+
+ cache_info->columns;
+ for (y=0; y < (long) rows; y++)
+ {
+ indexes-=clone_info->columns;
+ (void) ResetMagickMemory(indexes,0,length);
+ }
+ }
+ }
+ }
+ /*
+ Clone cache pixels.
+ */
+ length=columns*sizeof(*pixels);
+ if (clone_info->columns == cache_info->columns)
+ (void) CopyMagickMemory(clone_info->pixels,cache_info->pixels,length*rows);
+ else
+ {
+ source_pixels=cache_info->pixels+cache_info->columns*rows;
+ pixels=clone_info->pixels+clone_info->columns*rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ source_pixels-=cache_info->columns;
+ pixels-=clone_info->columns;
+ (void) CopyMagickMemory(pixels,source_pixels,length);
+ }
+ if (clone_info->columns > cache_info->columns)
+ {
+ length=(clone_info->columns-cache_info->columns)*sizeof(*pixels);
+ pixels=clone_info->pixels+clone_info->columns*rows+
+ cache_info->columns;
+ for (y=0; y < (long) rows; y++)
+ {
+ pixels-=clone_info->columns;
+ (void) ResetMagickMemory(pixels,0,length);
+ }
+ }
+ }
+ return(MagickTrue);
+}
+
+static MagickBooleanType ClonePixelCachePixels(CacheInfo *clone_info,
+ CacheInfo *cache_info,ExceptionInfo *exception)
+{
+ if ((clone_info->type != DiskCache) && (cache_info->type != DiskCache))
+ return(CloneMemoryToMemoryPixelCache(clone_info,cache_info,exception));
+ if ((clone_info->type == DiskCache) && (cache_info->type == DiskCache))
+ return(CloneDiskToDiskPixelCache(clone_info,cache_info,exception));
+ if (cache_info->type == DiskCache)
+ return(CloneDiskToMemoryPixelCache(clone_info,cache_info,exception));
+ return(CloneMemoryToDiskPixelCache(clone_info,cache_info,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o n e P i x e l C a c h e M e t h o d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClonePixelCacheMethods() clones the pixel cache methods from one cache to
+% another.
+%
+% The format of the ClonePixelCacheMethods() method is:
+%
+% void ClonePixelCacheMethods(Cache clone,const Cache cache)
+%
+% A description of each parameter follows:
+%
+% o clone: Specifies a pointer to a Cache structure.
+%
+% o cache: the pixel cache.
+%
+*/
+MagickExport void ClonePixelCacheMethods(Cache clone,const Cache cache)
+{
+ CacheInfo
+ *cache_info,
+ *source_info;
+
+ assert(clone != (Cache) NULL);
+ source_info=(CacheInfo *) clone;
+ assert(source_info->signature == MagickSignature);
+ if (source_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ source_info->filename);
+ assert(cache != (Cache) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ source_info->methods=cache_info->methods;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y I m a g e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImagePixelCache() deallocates memory associated with the pixel cache.
+%
+% The format of the DestroyImagePixelCache() method is:
+%
+% void DestroyImagePixelCache(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static void DestroyImagePixelCache(Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->cache == (void *) NULL)
+ return;
+ image->cache=DestroyPixelCache(image->cache);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImagePixels() deallocates memory associated with the pixel cache.
+%
+% The format of the DestroyImagePixels() method is:
+%
+% void DestroyImagePixels(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyImagePixels(Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.destroy_pixel_handler == (DestroyPixelHandler) NULL)
+ return;
+ cache_info->methods.destroy_pixel_handler(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPixelCache() deallocates memory associated with the pixel cache.
+%
+% The format of the DestroyPixelCache() method is:
+%
+% Cache DestroyPixelCache(Cache cache)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+*/
+
+static inline void RelinquishPixelCachePixels(CacheInfo *cache_info)
+{
+ switch (cache_info->type)
+ {
+ case MemoryCache:
+ {
+ if (cache_info->mapped == MagickFalse)
+ cache_info->pixels=(PixelPacket *) RelinquishMagickMemory(
+ cache_info->pixels);
+ else
+ cache_info->pixels=(PixelPacket *) UnmapBlob(cache_info->pixels,
+ (size_t) cache_info->length);
+ RelinquishMagickResource(MemoryResource,cache_info->length);
+ break;
+ }
+ case MapCache:
+ {
+ cache_info->pixels=(PixelPacket *) UnmapBlob(cache_info->pixels,(size_t)
+ cache_info->length);
+ RelinquishMagickResource(MapResource,cache_info->length);
+ }
+ case DiskCache:
+ {
+ if (cache_info->file != -1)
+ (void) ClosePixelCacheOnDisk(cache_info);
+ RelinquishMagickResource(DiskResource,cache_info->length);
+ break;
+ }
+ default:
+ break;
+ }
+ cache_info->type=UndefinedCache;
+ cache_info->mapped=MagickFalse;
+ cache_info->indexes=(IndexPacket *) NULL;
+}
+
+MagickExport Cache DestroyPixelCache(Cache cache)
+{
+ CacheInfo
+ *cache_info;
+
+ CacheType
+ type;
+
+ assert(cache != (Cache) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ (void) LockSemaphoreInfo(cache_info->semaphore);
+ cache_info->reference_count--;
+ if (cache_info->reference_count != 0)
+ {
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ return((Cache) NULL);
+ }
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ if (cache_resources != (SplayTreeInfo *) NULL)
+ (void) DeleteNodeByValueFromSplayTree(cache_resources,cache_info);
+ type=cache_info->type;
+ RelinquishPixelCachePixels(cache_info);
+ if ((type == MapCache) || (type == DiskCache))
+ (void) RelinquishUniqueFileResource(cache_info->cache_filename);
+ *cache_info->cache_filename='\0';
+ if (cache_info->nexus_info != (NexusInfo **) NULL)
+ cache_info->nexus_info=DestroyPixelCacheNexus(cache_info->nexus_info,
+ cache_info->number_threads);
+ if (cache_info->debug != MagickFalse)
+ {
+ char
+ message[MaxTextExtent];
+
+ (void) FormatMagickString(message,MaxTextExtent,"destroy %s",
+ cache_info->filename);
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s",message);
+ }
+ if (cache_info->random_info != (RandomInfo *) NULL)
+ cache_info->random_info=DestroyRandomInfo(cache_info->random_info);
+ cache_info->signature=(~MagickSignature);
+ if (cache_info->disk_semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&cache_info->disk_semaphore);
+ if (cache_info->semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&cache_info->semaphore);
+ cache_info=(CacheInfo *) RelinquishAlignedMemory(cache_info);
+ cache=(Cache) NULL;
+ return(cache);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPixelCacheNexus() destroys a pixel cache nexus.
+%
+% The format of the DestroyPixelCacheNexus() method is:
+%
+% NexusInfo **DestroyPixelCacheNexus(NexusInfo *nexus_info,
+% const unsigned long number_threads)
+%
+% A description of each parameter follows:
+%
+% o nexus_info: the nexus to destroy.
+%
+% o number_threads: the number of nexus threads.
+%
+*/
+
+static inline void RelinquishCacheNexusPixels(NexusInfo *nexus_info)
+{
+ if (nexus_info->mapped == MagickFalse)
+ (void) RelinquishMagickMemory(nexus_info->cache);
+ else
+ (void) UnmapBlob(nexus_info->cache,(size_t) nexus_info->length);
+ nexus_info->cache=(PixelPacket *) NULL;
+ nexus_info->pixels=(PixelPacket *) NULL;
+ nexus_info->indexes=(IndexPacket *) NULL;
+ nexus_info->length=0;
+ nexus_info->mapped=MagickFalse;
+}
+
+MagickExport NexusInfo **DestroyPixelCacheNexus(NexusInfo **nexus_info,
+ const unsigned long number_threads)
+{
+ register long
+ i;
+
+ assert(nexus_info != (NexusInfo **) NULL);
+ for (i=0; i < (long) number_threads; i++)
+ {
+ if (nexus_info[i]->cache != (PixelPacket *) NULL)
+ RelinquishCacheNexusPixels(nexus_info[i]);
+ nexus_info[i]->signature=(~MagickSignature);
+ nexus_info[i]=(NexusInfo *) RelinquishAlignedMemory(nexus_info[i]);
+ }
+ nexus_info=(NexusInfo **) RelinquishAlignedMemory(nexus_info);
+ return(nexus_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P i x e l C a c h e R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPixelCacheResources() destroys the cache resources.
+%
+% The format of the DestroyPixelCacheResources() method is:
+%
+% DestroyPixelCacheResources(void)
+%
+*/
+MagickExport void DestroyPixelCacheResources(void)
+{
+ AcquireSemaphoreInfo(&cache_semaphore);
+ if (cache_resources != (SplayTreeInfo *) NULL)
+ cache_resources=DestroySplayTree(cache_resources);
+ instantiate_cache=MagickFalse;
+ RelinquishSemaphoreInfo(cache_semaphore);
+ DestroySemaphoreInfo(&cache_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c I n d e x e s F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticIndexesFromCache() returns the indexes associated with the last
+% call to QueueAuthenticPixelsCache() or GetAuthenticPixelsCache().
+%
+% The format of the GetAuthenticIndexesFromCache() method is:
+%
+% IndexPacket *GetAuthenticIndexesFromCache(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static IndexPacket *GetAuthenticIndexesFromCache(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ IndexPacket
+ *indexes;
+
+ long
+ id;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ indexes=GetPixelCacheNexusIndexes(image->cache,cache_info->nexus_info[id]);
+ return(indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t A u t h e n t i c I n d e x Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticIndexQueue() returns the authentic black channel or the colormap
+% indexes associated with the last call to QueueAuthenticPixels() or
+% GetVirtualPixels(). NULL is returned if the black channel or colormap
+% indexes are not available.
+%
+% The format of the GetAuthenticIndexQueue() method is:
+%
+% IndexPacket *GetAuthenticIndexQueue(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport IndexPacket *GetAuthenticIndexQueue(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_authentic_indexes_from_handler ==
+ (GetAuthenticIndexesFromHandler) NULL)
+ return((IndexPacket *) NULL);
+ return(cache_info->methods.get_authentic_indexes_from_handler(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelCacheNexus() gets authentic pixels from the in-memory or
+% disk pixel cache as defined by the geometry parameters. A pointer to the
+% pixels is returned if the pixels are transferred, otherwise a NULL is
+% returned.
+%
+% The format of the GetAuthenticPixelCacheNexus() method is:
+%
+% PixelPacket *GetAuthenticPixelCacheNexus(Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o nexus_info: the cache nexus to return.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType IsNexusInCore(const CacheInfo *cache_info,
+ NexusInfo *nexus_info)
+{
+ MagickOffsetType
+ offset;
+
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ if (nexus_info->pixels != (cache_info->pixels+offset))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+MagickExport PixelPacket *GetAuthenticPixelCacheNexus(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ PixelPacket
+ *pixels;
+
+ /*
+ Transfer pixels from the cache.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ pixels=QueueAuthenticNexus(image,x,y,columns,rows,nexus_info,exception);
+ if (pixels == (PixelPacket *) NULL)
+ return((PixelPacket *) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(pixels);
+ if (ReadPixelCachePixels(cache_info,nexus_info,exception) == MagickFalse)
+ return((PixelPacket *) NULL);
+ if (cache_info->active_index_channel != MagickFalse)
+ if (ReadPixelCacheIndexes(cache_info,nexus_info,exception) == MagickFalse)
+ return((PixelPacket *) NULL);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c P i x e l s F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelsFromCache() returns the pixels associated with the last
+% call to the QueueAuthenticPixelsCache() or GetAuthenticPixelsCache() methods.
+%
+% The format of the GetAuthenticPixelsFromCache() method is:
+%
+% PixelPacket *GetAuthenticPixelsFromCache(const Image image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static PixelPacket *GetAuthenticPixelsFromCache(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ pixels=GetPixelCacheNexusPixels(image->cache,cache_info->nexus_info[id]);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t A u t h e n t i c P i x e l Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelQueue() returns the authentic pixels associated with the
+% last call to QueueAuthenticPixels() or GetAuthenticPixels().
+%
+% The format of the GetAuthenticPixelQueue() method is:
+%
+% PixelPacket *GetAuthenticPixelQueue(const Image image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport PixelPacket *GetAuthenticPixelQueue(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_authentic_pixels_from_handler ==
+ (GetAuthenticPixelsFromHandler) NULL)
+ return((PixelPacket *) NULL);
+ return(cache_info->methods.get_authentic_pixels_from_handler(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixels() obtains a pixel region for read/write access. If the
+% region is successfully accessed, a pointer to a PixelPacket array
+% representing the region is returned, otherwise NULL is returned.
+%
+% The returned pointer may point to a temporary working copy of the pixels
+% or it may point to the original pixels in memory. Performance is maximized
+% if the selected region is part of one row, or one or more full rows, since
+% then there is opportunity to access the pixels in-place (without a copy)
+% if the image is in RAM, or in a memory-mapped file. The returned pointer
+% should *never* be deallocated by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or if the storage class is
+% PseduoClass, call GetAuthenticIndexQueue() after invoking
+% GetAuthenticPixels() to obtain the black color component or colormap indexes
+% (of type IndexPacket) corresponding to the region. Once the PixelPacket
+% (and/or IndexPacket) array has been updated, the changes must be saved back
+% to the underlying image using SyncAuthenticPixels() or they may be lost.
+%
+% The format of the GetAuthenticPixels() method is:
+%
+% PixelPacket *GetAuthenticPixels(Image *image,const long x,const long y,
+% const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket *GetAuthenticPixels(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ PixelPacket
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_authentic_pixels_handler ==
+ (GetAuthenticPixelsHandler) NULL)
+ return((PixelPacket *) NULL);
+ pixels=cache_info->methods.get_authentic_pixels_handler(image,x,y,columns,
+ rows,exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c P i x e l s C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelsCache() gets pixels from the in-memory or disk pixel cache
+% as defined by the geometry parameters. A pointer to the pixels is returned
+% if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetAuthenticPixelsCache() method is:
+%
+% PixelPacket *GetAuthenticPixelsCache(Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static PixelPacket *GetAuthenticPixelsCache(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ cache_info=(CacheInfo *) GetImagePixelCache(image,MagickTrue,exception);
+ if (cache_info == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ pixels=GetAuthenticPixelCacheNexus(image,x,y,columns,rows,
+ cache_info->nexus_info[id],exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageExtent() returns the extent of the pixels associated with the
+% last call to QueueAuthenticPixels() or GetAuthenticPixels().
+%
+% The format of the GetImageExtent() method is:
+%
+% MagickSizeType GetImageExtent(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickSizeType GetImageExtent(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ long
+ id;
+
+ MagickSizeType
+ extent;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ extent=GetPixelCacheNexusExtent(image->cache,cache_info->nexus_info[id]);
+ return(extent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImagePixelCache() ensures that there is only a single reference to the
+% pixel cache to be modified, updating the provided cache pointer to point to
+% a clone of the original pixel cache if necessary.
+%
+% The format of the GetImagePixelCache method is:
+%
+% Cache GetImagePixelCache(Image *image,const MagickBooleanType clone,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clone: any value other than MagickFalse clones the cache pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType ValidatePixelCacheMorphology(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ /*
+ Does the image match the pixel cache morphology?
+ */
+ cache_info=(CacheInfo *) image->cache;
+ if ((image->storage_class != cache_info->storage_class) ||
+ (image->colorspace != cache_info->colorspace) ||
+ (image->columns != cache_info->columns) ||
+ (image->rows != cache_info->rows) ||
+ (cache_info->nexus_info == (NexusInfo **) NULL) ||
+ (cache_info->number_threads < GetOpenMPMaximumThreads()))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+MagickExport Cache GetImagePixelCache(Image *image,
+ const MagickBooleanType clone,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickSizeType
+ time_limit;
+
+ MagickBooleanType
+ status;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=MagickTrue;
+ (void) LockSemaphoreInfo(image->semaphore);
+ time_limit=GetMagickResourceLimit(TimeResource);
+ if (cache_timer == 0)
+ cache_timer=time((time_t *) NULL);
+ if ((time_limit != MagickResourceInfinity) &&
+ ((MagickSizeType) (time((time_t *) NULL)-cache_timer) >= time_limit))
+ ThrowFatalException(ResourceLimitFatalError,"TimeLimitExceeded");
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ (void) LockSemaphoreInfo(cache_info->semaphore);
+ if (cache_info->reference_count > 1)
+ {
+ Image
+ clone_image;
+
+ CacheInfo
+ *clone_info;
+
+ /*
+ Clone pixel cache.
+ */
+ clone_image=(*image);
+ clone_image.cache=ClonePixelCache(cache_info);
+ clone_info=(CacheInfo *) clone_image.cache;
+ status=ClonePixelCacheNexus(cache_info,clone_info,exception);
+ if (status != MagickFalse)
+ {
+ status=OpenPixelCache(&clone_image,IOMode,exception);
+ if (status != MagickFalse)
+ {
+ if (clone != MagickFalse)
+ status=ClonePixelCachePixels(clone_info,cache_info,exception);
+ if (status != MagickFalse)
+ {
+ cache_info->reference_count--;
+ image->cache=clone_image.cache;
+ }
+ }
+ }
+ }
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ if (status != MagickFalse)
+ {
+ /*
+ Ensure the image matches the pixel cache morphology.
+ */
+ image->taint=MagickTrue;
+ image->type=UndefinedType;
+ if (image->colorspace == GRAYColorspace)
+ image->colorspace=RGBColorspace;
+ if (ValidatePixelCacheMorphology(image) == MagickFalse)
+ status=OpenPixelCache(image,IOMode,exception);
+ }
+ (void) UnlockSemaphoreInfo(image->semaphore);
+ if (status == MagickFalse)
+ return((Cache) NULL);
+ return(image->cache);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e A u t h e n t i c P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneAuthenticPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs.
+%
+% The format of the GetOneAuthenticPixel() method is:
+%
+% MagickBooleanType GetOneAuthenticPixel(const Image image,const long x,
+% const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneAuthenticPixel(Image *image,const long x,
+ const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ GetOneAuthenticPixelFromHandler
+ get_one_authentic_pixel_from_handler;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ *pixel=image->background_color;
+ get_one_authentic_pixel_from_handler=
+ cache_info->methods.get_one_authentic_pixel_from_handler;
+ if (get_one_authentic_pixel_from_handler ==
+ (GetOneAuthenticPixelFromHandler) NULL)
+ return(MagickFalse);
+ status=cache_info->methods.get_one_authentic_pixel_from_handler(image,x,y,
+ pixel,exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t O n e A u t h e n t i c P i x e l F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneAuthenticPixelFromCache() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs.
+%
+% The format of the GetOneAuthenticPixelFromCache() method is:
+%
+% MagickBooleanType GetOneAuthenticPixelFromCache(const Image image,
+% const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType GetOneAuthenticPixelFromCache(Image *image,
+ const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ PixelPacket
+ *pixels;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ *pixel=image->background_color;
+ pixels=GetAuthenticPixelsCache(image,x,y,1UL,1UL,exception);
+ if (pixels == (PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e V i r t u a l M a g i c k P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneVirtualMagickPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs. If
+% you plan to modify the pixel, use GetOneAuthenticPixel() instead.
+%
+% The format of the GetOneVirtualMagickPixel() method is:
+%
+% MagickBooleanType GetOneVirtualMagickPixel(const Image image,
+% const long x,const long y,MagickPixelPacket *pixel,
+% ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: these values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneVirtualMagickPixel(const Image *image,
+ const long x,const long y,MagickPixelPacket *pixel,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ GetMagickPixelPacket(image,pixel);
+ p=GetVirtualPixelCache(image,GetPixelCacheVirtualMethod(image),x,y,1,1,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ indexes=GetVirtualIndexQueue(image);
+ SetMagickPixelPacket(image,p,indexes,pixel);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e V i r t u a l M e t h o d P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneVirtualMethodPixel() returns a single pixel at the specified (x,y)
+% location as defined by specified pixel method. The image background color
+% is returned if an error occurs. If you plan to modify the pixel, use
+% GetOneAuthenticPixel() instead.
+%
+% The format of the GetOneVirtualMethodPixel() method is:
+%
+% MagickBooleanType GetOneVirtualMethodPixel(const Image image,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,Pixelpacket *pixel,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneVirtualMethodPixel(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ PixelPacket *pixel,ExceptionInfo *exception)
+{
+ GetOneVirtualPixelFromHandler
+ get_one_virtual_pixel_from_handler;
+
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ *pixel=image->background_color;
+ get_one_virtual_pixel_from_handler=
+ cache_info->methods.get_one_virtual_pixel_from_handler;
+ if (get_one_virtual_pixel_from_handler ==
+ (GetOneVirtualPixelFromHandler) NULL)
+ return(MagickFalse);
+ status=get_one_virtual_pixel_from_handler(image,virtual_pixel_method,x,y,
+ pixel,exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e V i r t u a l P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneVirtualPixel() returns a single virtual pixel at the specified
+% (x,y) location. The image background color is returned if an error occurs.
+% If you plan to modify the pixel, use GetOneAuthenticPixel() instead.
+%
+% The format of the GetOneVirtualPixel() method is:
+%
+% MagickBooleanType GetOneVirtualPixel(const Image image,const long x,
+% const long y,PixelPacket *pixel,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetOneVirtualPixel(const Image *image,
+ const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ GetOneVirtualPixelFromHandler
+ get_one_virtual_pixel_from_handler;
+
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ *pixel=image->background_color;
+ get_one_virtual_pixel_from_handler=
+ cache_info->methods.get_one_virtual_pixel_from_handler;
+ if (get_one_virtual_pixel_from_handler ==
+ (GetOneVirtualPixelFromHandler) NULL)
+ return(MagickFalse);
+ status=get_one_virtual_pixel_from_handler(image,GetPixelCacheVirtualMethod(
+ image),x,y,pixel,exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t O n e V i r t u a l P i x e l F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneVirtualPixelFromCache() returns a single virtual pixel at the
+% specified (x,y) location. The image background color is returned if an
+% error occurs.
+%
+% The format of the GetOneVirtualPixelFromCache() method is:
+%
+% MagickBooleanType GetOneVirtualPixelFromCache(const Image image,
+% const VirtualPixelPacket method,const long x,const long y,
+% PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType GetOneVirtualPixelFromCache(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ PixelPacket *pixel,ExceptionInfo *exception)
+{
+ const PixelPacket
+ *pixels;
+
+ *pixel=image->background_color;
+ pixels=GetVirtualPixelCache(image,virtual_pixel_method,x,y,1UL,1UL,exception);
+ if (pixels == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e C o l o r s p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheColorspace() returns the class type of the pixel cache.
+%
+% The format of the GetPixelCacheColorspace() method is:
+%
+% Colorspace GetPixelCacheColorspace(Cache cache)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+*/
+MagickExport ColorspaceType GetPixelCacheColorspace(const Cache cache)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(cache != (Cache) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ return(cache_info->colorspace);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e M e t h o d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheMethods() initializes the CacheMethods structure.
+%
+% The format of the GetPixelCacheMethods() method is:
+%
+% void GetPixelCacheMethods(CacheMethods *cache_methods)
+%
+% A description of each parameter follows:
+%
+% o cache_methods: Specifies a pointer to a CacheMethods structure.
+%
+*/
+MagickExport void GetPixelCacheMethods(CacheMethods *cache_methods)
+{
+ assert(cache_methods != (CacheMethods *) NULL);
+ (void) ResetMagickMemory(cache_methods,0,sizeof(*cache_methods));
+ cache_methods->get_virtual_pixel_handler=GetVirtualPixelCache;
+ cache_methods->get_virtual_pixels_handler=GetVirtualPixelsCache;
+ cache_methods->get_virtual_indexes_from_handler=GetVirtualIndexesFromCache;
+ cache_methods->get_one_virtual_pixel_from_handler=GetOneVirtualPixelFromCache;
+ cache_methods->get_authentic_pixels_handler=GetAuthenticPixelsCache;
+ cache_methods->get_authentic_indexes_from_handler=
+ GetAuthenticIndexesFromCache;
+ cache_methods->get_authentic_pixels_from_handler=GetAuthenticPixelsFromCache;
+ cache_methods->get_one_authentic_pixel_from_handler=
+ GetOneAuthenticPixelFromCache;
+ cache_methods->queue_authentic_pixels_handler=QueueAuthenticPixelsCache;
+ cache_methods->sync_authentic_pixels_handler=SyncAuthenticPixelsCache;
+ cache_methods->destroy_pixel_handler=DestroyImagePixelCache;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e N e x u s E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheNexusExtent() returns the extent of the pixels associated with
+% the last call to SetPixelCacheNexusPixels() or GetPixelCacheNexusPixels().
+%
+% The format of the GetPixelCacheNexusExtent() method is:
+%
+% MagickSizeType GetPixelCacheNexusExtent(const Cache cache,
+% NexusInfo *nexus_info)
+%
+% A description of each parameter follows:
+%
+% o nexus_info: the nexus info.
+%
+*/
+MagickExport MagickSizeType GetPixelCacheNexusExtent(const Cache cache,
+ NexusInfo *nexus_info)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickSizeType
+ extent;
+
+ if (cache == (Cache) NULL)
+ return(0);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ extent=(MagickSizeType) nexus_info->region.width*nexus_info->region.height;
+ if (extent == 0)
+ return((MagickSizeType) cache_info->columns*cache_info->rows);
+ return(extent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e N e x u s I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheNexusIndexes() returns the indexes associated with the
+% specified cache nexus.
+%
+% The format of the GetPixelCacheNexusIndexes() method is:
+%
+% IndexPacket *GetPixelCacheNexusIndexes(const Cache cache,
+% NexusInfo *nexus_info)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+% o nexus_info: the cache nexus to return the colormap indexes.
+%
+*/
+MagickExport IndexPacket *GetPixelCacheNexusIndexes(const Cache cache,
+ NexusInfo *nexus_info)
+{
+ CacheInfo
+ *cache_info;
+
+ if (cache == (Cache) NULL)
+ return((IndexPacket *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->storage_class == UndefinedClass)
+ return((IndexPacket *) NULL);
+ return(nexus_info->indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e N e x u s P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheNexusPixels() returns the pixels associated with the specified
+% cache nexus.
+%
+% The format of the GetPixelCacheNexusPixels() method is:
+%
+% PixelPacket *GetPixelCacheNexusPixels(const Cache cache,
+% NexusInfo *nexus_info)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+% o nexus_info: the cache nexus to return the pixels.
+%
+*/
+MagickExport PixelPacket *GetPixelCacheNexusPixels(const Cache cache,
+ NexusInfo *nexus_info)
+{
+ CacheInfo
+ *cache_info;
+
+ if (cache == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (cache_info->storage_class == UndefinedClass)
+ return((PixelPacket *) NULL);
+ return(nexus_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e S t o r a e C l a s s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheStorageClass() returns the class type of the pixel cache.
+%
+% The format of the GetPixelCacheStorageClass() method is:
+%
+% ClassType GetPixelCacheStorageClass(Cache cache)
+%
+% A description of each parameter follows:
+%
+% o type: GetPixelCacheStorageClass returns DirectClass or PseudoClass.
+%
+% o cache: the pixel cache.
+%
+*/
+MagickExport ClassType GetPixelCacheStorageClass(const Cache cache)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(cache != (Cache) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ return(cache_info->storage_class);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P i x e l C a c h e V i r t u a l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixelCacheVirtualMethod() gets the "virtual pixels" method for the
+% pixel cache. A virtual pixel is any pixel access that is outside the
+% boundaries of the image cache.
+%
+% The format of the GetPixelCacheVirtualMethod() method is:
+%
+% VirtualPixelMethod GetPixelCacheVirtualMethod(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport VirtualPixelMethod GetPixelCacheVirtualMethod(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ return(cache_info->virtual_pixel_method);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l I n d e x e s F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualIndexesFromCache() returns the indexes associated with the last
+% call to QueueAuthenticPixelsCache() or GetVirtualPixelCache().
+%
+% The format of the GetVirtualIndexesFromCache() method is:
+%
+% IndexPacket *GetVirtualIndexesFromCache(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static const IndexPacket *GetVirtualIndexesFromCache(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ const IndexPacket
+ *indexes;
+
+ long
+ id;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ indexes=GetVirtualIndexesFromNexus(image->cache,cache_info->nexus_info[id]);
+ return(indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l I n d e x e s F r o m N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualIndexesFromNexus() returns the indexes associated with the
+% specified cache nexus.
+%
+% The format of the GetVirtualIndexesFromNexus() method is:
+%
+% const IndexPacket *GetVirtualIndexesFromNexus(const Cache cache,
+% NexusInfo *nexus_info)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+% o nexus_info: the cache nexus to return the colormap indexes.
+%
+*/
+MagickExport const IndexPacket *GetVirtualIndexesFromNexus(const Cache cache,
+ NexusInfo *nexus_info)
+{
+ CacheInfo
+ *cache_info;
+
+ if (cache == (Cache) NULL)
+ return((IndexPacket *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->storage_class == UndefinedClass)
+ return((IndexPacket *) NULL);
+ return(nexus_info->indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V i r t u a l I n d e x Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualIndexQueue() returns the virtual black channel or the
+% colormap indexes associated with the last call to QueueAuthenticPixels() or
+% GetVirtualPixels(). NULL is returned if the black channel or colormap
+% indexes are not available.
+%
+% The format of the GetVirtualIndexQueue() method is:
+%
+% const IndexPacket *GetVirtualIndexQueue(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport const IndexPacket *GetVirtualIndexQueue(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_virtual_indexes_from_handler ==
+ (GetVirtualIndexesFromHandler) NULL)
+ return((IndexPacket *) NULL);
+ return(cache_info->methods.get_virtual_indexes_from_handler(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l s F r o m N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelsFromNexus() gets virtual pixels from the in-memory or disk
+% pixel cache as defined by the geometry parameters. A pointer to the pixels
+% is returned if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetVirtualPixelsFromNexus() method is:
+%
+% PixelPacket *GetVirtualPixelsFromNexus(const Image *image,
+% const VirtualPixelMethod method,const long x,const long y,
+% const unsigned long columns,const unsigned long rows,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o nexus_info: the cache nexus to acquire.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static long
+ DitherMatrix[64] =
+ {
+ 0, 48, 12, 60, 3, 51, 15, 63,
+ 32, 16, 44, 28, 35, 19, 47, 31,
+ 8, 56, 4, 52, 11, 59, 7, 55,
+ 40, 24, 36, 20, 43, 27, 39, 23,
+ 2, 50, 14, 62, 1, 49, 13, 61,
+ 34, 18, 46, 30, 33, 17, 45, 29,
+ 10, 58, 6, 54, 9, 57, 5, 53,
+ 42, 26, 38, 22, 41, 25, 37, 21
+ };
+
+static inline long DitherX(const unsigned long columns,const long x)
+{
+ long
+ index;
+
+ index=x+DitherMatrix[x & 0x07]-32L;
+ if (index < 0L)
+ return(0L);
+ if (index >= (long) columns)
+ return((long) columns-1L);
+ return(index);
+}
+
+static inline long DitherY(const unsigned long rows,const long y)
+{
+ long
+ index;
+
+ index=y+DitherMatrix[y & 0x07]-32L;
+ if (index < 0L)
+ return(0L);
+ if (index >= (long) rows)
+ return((long) rows-1L);
+ return(index);
+}
+
+static inline long EdgeX(const unsigned long columns,const long x)
+{
+ if (x < 0L)
+ return(0L);
+ if (x >= (long) columns)
+ return((long) columns-1L);
+ return(x);
+}
+
+static inline long EdgeY(const unsigned long rows,const long y)
+{
+ if (y < 0L)
+ return(0L);
+ if (y >= (long) rows)
+ return((long) rows-1L);
+ return(y);
+}
+
+static inline long RandomX(const unsigned long columns,RandomInfo *random_info)
+{
+ return((long) (columns*GetPseudoRandomValue(random_info)));
+}
+
+static inline long RandomY(const unsigned long rows,RandomInfo *random_info)
+{
+ return((long) (rows*GetPseudoRandomValue(random_info)));
+}
+
+/*
+ VirtualPixelModulo() computes the remainder of dividing offset by extent. It
+ returns not only the quotient (tile the offset falls in) but also the positive
+ remainer within that tile such that 0 <= remainder < extent. This method is
+ essentially a ldiv() using a floored modulo division rather than the normal
+ default truncated modulo division.
+*/
+static inline MagickModulo VirtualPixelModulo(const long offset,
+ const unsigned long extent)
+{
+ MagickModulo
+ modulo;
+
+ modulo.quotient=offset/(long) extent;
+ if (offset < 0L)
+ modulo.quotient--;
+ modulo.remainder=offset-modulo.quotient*(long) extent;
+ return(modulo);
+}
+
+MagickExport const PixelPacket *GetVirtualPixelsFromNexus(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ const unsigned long columns,const unsigned long rows,NexusInfo *nexus_info,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ NexusInfo
+ **virtual_nexus;
+
+ PixelPacket
+ *pixels,
+ virtual_pixel;
+
+ RectangleInfo
+ region;
+
+ register const IndexPacket
+ *__restrict nexus_indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ u,
+ v;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Acquire pixels.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ if (cache_info->type == UndefinedCache)
+ return((const PixelPacket *) NULL);
+ region.x=x;
+ region.y=y;
+ region.width=columns;
+ region.height=rows;
+ pixels=SetPixelCacheNexusPixels(image,®ion,nexus_info,exception);
+ if (pixels == (PixelPacket *) NULL)
+ return((const PixelPacket *) NULL);
+ offset=(MagickOffsetType) region.y*cache_info->columns+region.x;
+ length=(MagickSizeType) (region.height-1)*cache_info->columns+region.width-1;
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ if ((offset >= 0) && (((MagickSizeType) offset+length) < number_pixels))
+ if ((x >= 0) && ((long) (x+columns) <= (long) cache_info->columns) &&
+ (y >= 0) && ((long) (y+rows) <= (long) cache_info->rows))
+ {
+ MagickBooleanType
+ status;
+
+ /*
+ Pixel request is inside cache extents.
+ */
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(pixels);
+ status=ReadPixelCachePixels(cache_info,nexus_info,exception);
+ if (status == MagickFalse)
+ return((const PixelPacket *) NULL);
+ if ((cache_info->storage_class == PseudoClass) ||
+ (cache_info->colorspace == CMYKColorspace))
+ {
+ status=ReadPixelCacheIndexes(cache_info,nexus_info,exception);
+ if (status == MagickFalse)
+ return((const PixelPacket *) NULL);
+ }
+ return(pixels);
+ }
+ /*
+ Pixel request is outside cache extents.
+ */
+ q=pixels;
+ indexes=GetPixelCacheNexusIndexes(cache_info,nexus_info);
+ virtual_nexus=AcquirePixelCacheNexus(1);
+ if (virtual_nexus == (NexusInfo **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "UnableToGetCacheNexus","`%s'",image->filename);
+ return((const PixelPacket *) NULL);
+ }
+ switch (virtual_pixel_method)
+ {
+ case BlackVirtualPixelMethod:
+ {
+ virtual_pixel.red=0;
+ virtual_pixel.green=0;
+ virtual_pixel.blue=0;
+ virtual_pixel.opacity=OpaqueOpacity;
+ break;
+ }
+ case GrayVirtualPixelMethod:
+ {
+ virtual_pixel.red=(Quantum) QuantumRange/2;
+ virtual_pixel.green=(Quantum) QuantumRange/2;
+ virtual_pixel.blue=(Quantum) QuantumRange/2;
+ virtual_pixel.opacity=(Quantum) OpaqueOpacity;
+ break;
+ }
+ case TransparentVirtualPixelMethod:
+ {
+ virtual_pixel.red=(Quantum) 0;
+ virtual_pixel.green=(Quantum) 0;
+ virtual_pixel.blue=(Quantum) 0;
+ virtual_pixel.opacity=(Quantum) TransparentOpacity;
+ break;
+ }
+ case MaskVirtualPixelMethod:
+ case WhiteVirtualPixelMethod:
+ {
+ virtual_pixel.red=(Quantum) QuantumRange;
+ virtual_pixel.green=(Quantum) QuantumRange;
+ virtual_pixel.blue=(Quantum) QuantumRange;
+ virtual_pixel.opacity=OpaqueOpacity;
+ break;
+ }
+ default:
+ {
+ virtual_pixel=image->background_color;
+ break;
+ }
+ }
+ for (v=0; v < (long) rows; v++)
+ {
+ for (u=0; u < (long) columns; u+=length)
+ {
+ length=(MagickSizeType) MagickMin(cache_info->columns-(x+u),columns-u);
+ if ((((x+u) < 0) || ((x+u) >= (long) cache_info->columns)) ||
+ (((y+v) < 0) || ((y+v) >= (long) cache_info->rows)) || (length == 0))
+ {
+ MagickModulo
+ x_modulo,
+ y_modulo;
+
+ /*
+ Transfer a single pixel.
+ */
+ length=(MagickSizeType) 1;
+ switch (virtual_pixel_method)
+ {
+ case BackgroundVirtualPixelMethod:
+ case ConstantVirtualPixelMethod:
+ case BlackVirtualPixelMethod:
+ case GrayVirtualPixelMethod:
+ case TransparentVirtualPixelMethod:
+ case MaskVirtualPixelMethod:
+ case WhiteVirtualPixelMethod:
+ {
+ p=(&virtual_pixel);
+ break;
+ }
+ case EdgeVirtualPixelMethod:
+ default:
+ {
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ EdgeX(cache_info->columns,x+u),EdgeY(cache_info->rows,y+v),
+ 1UL,1UL,virtual_nexus[0],exception);
+ break;
+ }
+ case RandomVirtualPixelMethod:
+ {
+ if (cache_info->random_info == (RandomInfo *) NULL)
+ cache_info->random_info=AcquireRandomInfo();
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ RandomX(cache_info->columns,cache_info->random_info),
+ RandomY(cache_info->rows,cache_info->random_info),1UL,1UL,
+ virtual_nexus[0],exception);
+ break;
+ }
+ case DitherVirtualPixelMethod:
+ {
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ DitherX(cache_info->columns,x+u),DitherY(cache_info->rows,y+v),
+ 1UL,1UL,virtual_nexus[0],exception);
+ break;
+ }
+ case TileVirtualPixelMethod:
+ {
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,y_modulo.remainder,1UL,1UL,virtual_nexus[0],
+ exception);
+ break;
+ }
+ case MirrorVirtualPixelMethod:
+ {
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ if ((x_modulo.quotient & 0x01) == 1L)
+ x_modulo.remainder=(long) cache_info->columns-
+ x_modulo.remainder-1L;
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ if ((y_modulo.quotient & 0x01) == 1L)
+ y_modulo.remainder=(long) cache_info->rows-
+ y_modulo.remainder-1L;
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,y_modulo.remainder,1UL,1UL,virtual_nexus[0],
+ exception);
+ break;
+ }
+ case CheckerTileVirtualPixelMethod:
+ {
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ if (((x_modulo.quotient ^ y_modulo.quotient) & 0x01) != 0L)
+ {
+ p=(&virtual_pixel);
+ break;
+ }
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,y_modulo.remainder,1UL,1UL,virtual_nexus[0],
+ exception);
+ break;
+ }
+ case HorizontalTileVirtualPixelMethod:
+ {
+ if (((y+v) < 0) || ((y+v) >= (long) cache_info->rows))
+ {
+ p=(&virtual_pixel);
+ break;
+ }
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,y_modulo.remainder,1UL,1UL,virtual_nexus[0],
+ exception);
+ break;
+ }
+ case VerticalTileVirtualPixelMethod:
+ {
+ if (((x+u) < 0) || ((x+u) >= (long) cache_info->columns))
+ {
+ p=(&virtual_pixel);
+ break;
+ }
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,y_modulo.remainder,1UL,1UL,virtual_nexus[0],
+ exception);
+ break;
+ }
+ case HorizontalTileEdgeVirtualPixelMethod:
+ {
+ x_modulo=VirtualPixelModulo(x+u,cache_info->columns);
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ x_modulo.remainder,EdgeY(cache_info->rows,y+v),1UL,1UL,
+ virtual_nexus[0],exception);
+ break;
+ }
+ case VerticalTileEdgeVirtualPixelMethod:
+ {
+ y_modulo=VirtualPixelModulo(y+v,cache_info->rows);
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,
+ EdgeX(cache_info->columns,x+u),y_modulo.remainder,1UL,1UL,
+ virtual_nexus[0],exception);
+ break;
+ }
+ }
+ if (p == (const PixelPacket *) NULL)
+ break;
+ *q++=(*p);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ nexus_indexes=GetVirtualIndexesFromNexus(cache_info,
+ virtual_nexus[0]);
+ if (nexus_indexes != (const IndexPacket *) NULL)
+ *indexes++=(*nexus_indexes);
+ }
+ continue;
+ }
+ /*
+ Transfer a run of pixels.
+ */
+ p=GetVirtualPixelsFromNexus(image,virtual_pixel_method,x+u,y+v,
+ (unsigned long) length,1UL,virtual_nexus[0],exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ (void) CopyMagickMemory(q,p,(size_t) length*sizeof(*p));
+ q+=length;
+ if (indexes != (IndexPacket *) NULL)
+ {
+ nexus_indexes=GetVirtualIndexesFromNexus(cache_info,virtual_nexus[0]);
+ if (nexus_indexes != (const IndexPacket *) NULL)
+ {
+ (void) CopyMagickMemory(indexes,nexus_indexes,(size_t) length*
+ sizeof(*nexus_indexes));
+ indexes+=length;
+ }
+ }
+ }
+ }
+ virtual_nexus=DestroyPixelCacheNexus(virtual_nexus,1);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelCache() get virtual pixels from the in-memory or disk pixel
+% cache as defined by the geometry parameters. A pointer to the pixels
+% is returned if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetVirtualPixelCache() method is:
+%
+% const PixelPacket *GetVirtualPixelCache(const Image *image,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static const PixelPacket *GetVirtualPixelCache(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ pixels=GetVirtualPixelsFromNexus(image,virtual_pixel_method,x,y,columns,rows,
+ cache_info->nexus_info[id],exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V i r t u a l P i x e l Q u e u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelQueue() returns the virtual pixels associated with the
+% last call to QueueAuthenticPixels() or GetVirtualPixels().
+%
+% The format of the GetVirtualPixelQueue() method is:
+%
+% const PixelPacket *GetVirtualPixelQueue(const Image image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport const PixelPacket *GetVirtualPixelQueue(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_virtual_pixels_handler ==
+ (GetVirtualPixelsHandler) NULL)
+ return((PixelPacket *) NULL);
+ return(cache_info->methods.get_virtual_pixels_handler(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V i r t u a l P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixels() returns an immutable pixel region. If the
+% region is successfully accessed, a pointer to it is returned, otherwise
+% NULL is returned. The returned pointer may point to a temporary working
+% copy of the pixels or it may point to the original pixels in memory.
+% Performance is maximized if the selected region is part of one row, or one
+% or more full rows, since there is opportunity to access the pixels in-place
+% (without a copy) if the image is in RAM, or in a memory-mapped file. The
+% returned pointer should *never* be deallocated by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or the storage class is PseudoClass,
+% call GetAuthenticIndexQueue() after invoking GetAuthenticPixels() to access
+% the black color component or to obtain the colormap indexes (of type
+% IndexPacket) corresponding to the region.
+%
+% If you plan to modify the pixels, use GetAuthenticPixels() instead.
+%
+% Note, the GetVirtualPixels() and GetAuthenticPixels() methods are not thread-
+% safe. In a threaded environment, use GetCacheViewVirtualPixels() or
+% GetCacheViewAuthenticPixels() instead.
+%
+% The format of the GetVirtualPixels() method is:
+%
+% const PixelPacket *GetVirtualPixels(const Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const PixelPacket *GetVirtualPixels(const Image *image,
+ const long x,const long y,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ const PixelPacket
+ *pixels;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.get_virtual_pixel_handler ==
+ (GetVirtualPixelHandler) NULL)
+ return((const PixelPacket *) NULL);
+ pixels=cache_info->methods.get_virtual_pixel_handler(image,
+ GetPixelCacheVirtualMethod(image),x,y,columns,rows,exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l s F r o m C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelsCache() returns the pixels associated with the last call
+% to QueueAuthenticPixelsCache() or GetVirtualPixelCache().
+%
+% The format of the GetVirtualPixelsCache() method is:
+%
+% PixelPacket *GetVirtualPixelsCache(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static const PixelPacket *GetVirtualPixelsCache(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ const PixelPacket
+ *pixels;
+
+ long
+ id;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ pixels=GetVirtualPixelsNexus(image->cache,cache_info->nexus_info[id]);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l s N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelsNexus() returns the pixels associated with the specified
+% cache nexus.
+%
+% The format of the GetVirtualPixelsNexus() method is:
+%
+% const IndexPacket *GetVirtualPixelsNexus(const Cache cache,
+% NexusInfo *nexus_info)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+% o nexus_info: the cache nexus to return the colormap pixels.
+%
+*/
+MagickExport const PixelPacket *GetVirtualPixelsNexus(const Cache cache,
+ NexusInfo *nexus_info)
+{
+ CacheInfo
+ *cache_info;
+
+ if (cache == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->storage_class == UndefinedClass)
+ return((PixelPacket *) NULL);
+ return((const PixelPacket *) nexus_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M a s k P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MaskPixelCacheNexus() masks the cache nexus as defined by the image mask.
+% The method returns MagickTrue if the pixel region is masked, otherwise
+% MagickFalse.
+%
+% The format of the MaskPixelCacheNexus() method is:
+%
+% MagickBooleanType MaskPixelCacheNexus(Image *image,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o nexus_info: the cache nexus to clip.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline void MagickPixelCompositeMask(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ if (alpha == TransparentOpacity)
+ {
+ *composite=(*q);
+ return;
+ }
+ gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
+ gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*MagickOver_(p->red,alpha,q->red,beta);
+ composite->green=gamma*MagickOver_(p->green,alpha,q->green,beta);
+ composite->blue=gamma*MagickOver_(p->blue,alpha,q->blue,beta);
+ if ((p->colorspace == CMYKColorspace) && (q->colorspace == CMYKColorspace))
+ composite->index=gamma*MagickOver_(p->index,alpha,q->index,beta);
+}
+
+static MagickBooleanType MaskPixelCacheNexus(Image *image,NexusInfo *nexus_info,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickPixelPacket
+ alpha,
+ beta;
+
+ MagickSizeType
+ number_pixels;
+
+ NexusInfo
+ **clip_nexus,
+ **image_nexus;
+
+ register const PixelPacket
+ *__restrict r;
+
+ register IndexPacket
+ *__restrict nexus_indexes,
+ *__restrict indexes;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ /*
+ Apply clip mask.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->mask == (Image *) NULL)
+ return(MagickFalse);
+ cache_info=(CacheInfo *) GetImagePixelCache(image,MagickTrue,exception);
+ if (cache_info == (Cache) NULL)
+ return(MagickFalse);
+ image_nexus=AcquirePixelCacheNexus(1);
+ clip_nexus=AcquirePixelCacheNexus(1);
+ if ((image_nexus == (NexusInfo **) NULL) ||
+ (clip_nexus == (NexusInfo **) NULL))
+ ThrowBinaryException(CacheError,"UnableToGetCacheNexus",image->filename);
+ p=GetAuthenticPixelCacheNexus(image,nexus_info->region.x,nexus_info->region.y,
+ nexus_info->region.width,nexus_info->region.height,image_nexus[0],
+ exception);
+ indexes=GetPixelCacheNexusIndexes(image->cache,image_nexus[0]);
+ q=nexus_info->pixels;
+ nexus_indexes=nexus_info->indexes;
+ r=GetVirtualPixelsFromNexus(image->mask,MaskVirtualPixelMethod,
+ nexus_info->region.x,nexus_info->region.y,nexus_info->region.width,
+ nexus_info->region.height,clip_nexus[0],&image->exception);
+ GetMagickPixelPacket(image,&alpha);
+ GetMagickPixelPacket(image,&beta);
+ number_pixels=(MagickSizeType) nexus_info->region.width*
+ nexus_info->region.height;
+ for (i=0; i < (long) number_pixels; i++)
+ {
+ if ((p == (PixelPacket *) NULL) || (r == (const PixelPacket *) NULL))
+ break;
+ SetMagickPixelPacket(image,p,indexes+i,&alpha);
+ SetMagickPixelPacket(image,q,nexus_indexes+i,&beta);
+ MagickPixelCompositeMask(&beta,(MagickRealType) PixelIntensityToQuantum(r),
+ &alpha,alpha.opacity,&beta);
+ q->red=RoundToQuantum(beta.red);
+ q->green=RoundToQuantum(beta.green);
+ q->blue=RoundToQuantum(beta.blue);
+ q->opacity=RoundToQuantum(beta.opacity);
+ if (cache_info->active_index_channel != MagickFalse)
+ nexus_indexes[i]=indexes[i];
+ p++;
+ q++;
+ r++;
+ }
+ clip_nexus=DestroyPixelCacheNexus(clip_nexus,1);
+ image_nexus=DestroyPixelCacheNexus(image_nexus,1);
+ if (i < (long) number_pixels)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p e n P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenPixelCache() allocates the pixel cache. This includes defining the cache
+% dimensions, allocating space for the image pixels and optionally the
+% colormap indexes, and memory mapping the cache if it is disk based. The
+% cache nexus array is initialized as well.
+%
+% The format of the OpenPixelCache() method is:
+%
+% MagickBooleanType OpenPixelCache(Image *image,const MapMode mode,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o mode: ReadMode, WriteMode, or IOMode.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline void AcquirePixelCachePixels(CacheInfo *cache_info)
+{
+ cache_info->mapped=MagickFalse;
+ cache_info->pixels=(PixelPacket *) AcquireMagickMemory((size_t)
+ cache_info->length);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ cache_info->mapped=MagickTrue;
+ cache_info->pixels=(PixelPacket *) MapBlob(-1,IOMode,0,(size_t)
+ cache_info->length);
+ }
+}
+
+static MagickBooleanType ExtendCache(Image *image,MagickSizeType length)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickOffsetType
+ count,
+ extent,
+ offset;
+
+ cache_info=(CacheInfo *) image->cache;
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ message[MaxTextExtent];
+
+ (void) FormatMagickSize(length,format);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "extend %s (%s[%d], disk, %s)",cache_info->filename,
+ cache_info->cache_filename,cache_info->file,format);
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s",message);
+ }
+ if (length != (MagickSizeType) ((MagickOffsetType) length))
+ return(MagickFalse);
+ extent=(MagickOffsetType) MagickSeek(cache_info->file,0,SEEK_END);
+ if (extent < 0)
+ return(MagickFalse);
+ if ((MagickSizeType) extent >= length)
+ return(MagickTrue);
+ offset=(MagickOffsetType) length-1;
+ count=WritePixelCacheRegion(cache_info,offset,1,(const unsigned char *) "");
+ return(count == (MagickOffsetType) 1 ? MagickTrue : MagickFalse);
+}
+
+static MagickBooleanType OpenPixelCache(Image *image,const MapMode mode,
+ ExceptionInfo *exception)
+{
+ char
+ format[MaxTextExtent],
+ message[MaxTextExtent];
+
+ CacheInfo
+ *cache_info,
+ source_info;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ MagickStatusType
+ status;
+
+ size_t
+ packet_size;
+
+ unsigned long
+ columns;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((image->columns == 0) || (image->rows == 0))
+ ThrowBinaryException(CacheError,"NoPixelsDefinedInCache",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ source_info=(*cache_info);
+ source_info.file=(-1);
+ (void) FormatMagickString(cache_info->filename,MaxTextExtent,"%s[%ld]",
+ image->filename,GetImageIndexInList(image));
+ cache_info->rows=image->rows;
+ cache_info->columns=image->columns;
+ cache_info->active_index_channel=((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace)) ? MagickTrue : MagickFalse;
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ packet_size=sizeof(PixelPacket);
+ if (cache_info->active_index_channel != MagickFalse)
+ packet_size+=sizeof(IndexPacket);
+ length=number_pixels*packet_size;
+ columns=(unsigned long) (length/cache_info->rows/packet_size);
+ if (cache_info->columns != columns)
+ ThrowBinaryException(ResourceLimitError,"PixelCacheAllocationFailed",
+ image->filename);
+ cache_info->length=length;
+ status=AcquireMagickResource(AreaResource,cache_info->length);
+ length=number_pixels*(sizeof(PixelPacket)+sizeof(IndexPacket));
+ if ((status != MagickFalse) && (length == (MagickSizeType) ((size_t) length)))
+ {
+ status=AcquireMagickResource(MemoryResource,cache_info->length);
+ if (((cache_info->type == UndefinedCache) && (status != MagickFalse)) ||
+ (cache_info->type == MemoryCache))
+ {
+ AcquirePixelCachePixels(cache_info);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ cache_info->pixels=source_info.pixels;
+ else
+ {
+ /*
+ Create memory pixel cache.
+ */
+ if (image->debug != MagickFalse)
+ {
+ (void) FormatMagickSize(cache_info->length,format);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "open %s (%s memory, %lux%lu %s)",cache_info->filename,
+ cache_info->mapped != MagickFalse ? "anonymous" : "heap",
+ cache_info->columns,cache_info->rows,format);
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s",
+ message);
+ }
+ cache_info->storage_class=image->storage_class;
+ cache_info->colorspace=image->colorspace;
+ cache_info->type=MemoryCache;
+ cache_info->indexes=(IndexPacket *) NULL;
+ if (cache_info->active_index_channel != MagickFalse)
+ cache_info->indexes=(IndexPacket *) (cache_info->pixels+
+ number_pixels);
+ if (source_info.storage_class != UndefinedClass)
+ {
+ status|=ClonePixelCachePixels(cache_info,&source_info,
+ exception);
+ RelinquishPixelCachePixels(&source_info);
+ }
+ return(MagickTrue);
+ }
+ }
+ RelinquishMagickResource(MemoryResource,cache_info->length);
+ }
+ /*
+ Create pixel cache on disk.
+ */
+ status=AcquireMagickResource(DiskResource,cache_info->length);
+ if (status == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "CacheResourcesExhausted","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ if (OpenPixelCacheOnDisk(cache_info,mode) == MagickFalse)
+ {
+ RelinquishMagickResource(DiskResource,cache_info->length);
+ ThrowFileException(exception,CacheError,"UnableToOpenPixelCache",
+ image->filename);
+ return(MagickFalse);
+ }
+ status=ExtendCache(image,(MagickSizeType) cache_info->offset+
+ cache_info->length);
+ if (status == MagickFalse)
+ {
+ ThrowFileException(exception,CacheError,"UnableToExtendCache",
+ image->filename);
+ return(MagickFalse);
+ }
+ cache_info->storage_class=image->storage_class;
+ cache_info->colorspace=image->colorspace;
+ length=number_pixels*(sizeof(PixelPacket)+sizeof(IndexPacket));
+ status=AcquireMagickResource(AreaResource,cache_info->length);
+ if ((status == MagickFalse) || (length != (MagickSizeType) ((size_t) length)))
+ cache_info->type=DiskCache;
+ else
+ {
+ status=AcquireMagickResource(MapResource,cache_info->length);
+ if ((status == MagickFalse) && (cache_info->type != MapCache) &&
+ (cache_info->type != MemoryCache))
+ cache_info->type=DiskCache;
+ else
+ {
+ cache_info->pixels=(PixelPacket *) MapBlob(cache_info->file,mode,
+ cache_info->offset,(size_t) cache_info->length);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ cache_info->pixels=source_info.pixels;
+ cache_info->type=DiskCache;
+ }
+ else
+ {
+ /*
+ Create file-backed memory-mapped pixel cache.
+ */
+ (void) ClosePixelCacheOnDisk(cache_info);
+ cache_info->type=MapCache;
+ cache_info->mapped=MagickTrue;
+ cache_info->indexes=(IndexPacket *) NULL;
+ if (cache_info->active_index_channel != MagickFalse)
+ cache_info->indexes=(IndexPacket *) (cache_info->pixels+
+ number_pixels);
+ if ((source_info.type != UndefinedCache) && (mode != ReadMode))
+ {
+ status=ClonePixelCachePixels(cache_info,&source_info,
+ exception);
+ RelinquishPixelCachePixels(&source_info);
+ }
+ if (image->debug != MagickFalse)
+ {
+ (void) FormatMagickSize(cache_info->length,format);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "open %s (%s[%d], memory-mapped, %lux%lu %s)",
+ cache_info->filename,cache_info->cache_filename,
+ cache_info->file,cache_info->columns,cache_info->rows,
+ format);
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s",
+ message);
+ }
+ return(MagickTrue);
+ }
+ }
+ RelinquishMagickResource(MapResource,cache_info->length);
+ }
+ if ((source_info.type != UndefinedCache) && (mode != ReadMode))
+ {
+ status=ClonePixelCachePixels(cache_info,&source_info,exception);
+ RelinquishPixelCachePixels(&source_info);
+ }
+ if (image->debug != MagickFalse)
+ {
+ (void) FormatMagickSize(cache_info->length,format);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "open %s (%s[%d], disk, %lux%lu %s)",cache_info->filename,
+ cache_info->cache_filename,cache_info->file,cache_info->columns,
+ cache_info->rows,format);
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s",message);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P e r s i s t P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PersistPixelCache() attaches to or initializes a persistent pixel cache. A
+% persistent pixel cache is one that resides on disk and is not destroyed
+% when the program exits.
+%
+% The format of the PersistPixelCache() method is:
+%
+% MagickBooleanType PersistPixelCache(Image *image,const char *filename,
+% const MagickBooleanType attach,MagickOffsetType *offset,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o filename: the persistent pixel cache filename.
+%
+% o initialize: A value other than zero initializes the persistent pixel
+% cache.
+%
+% o offset: the offset in the persistent cache to store pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType PersistPixelCache(Image *image,
+ const char *filename,const MagickBooleanType attach,MagickOffsetType *offset,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info,
+ *clone_info;
+
+ Image
+ clone_image;
+
+ long
+ pagesize;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (void *) NULL);
+ assert(filename != (const char *) NULL);
+ assert(offset != (MagickOffsetType *) NULL);
+ pagesize=(-1);
+#if defined(MAGICKCORE_HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+ pagesize=sysconf(_SC_PAGESIZE);
+#elif defined(MAGICKCORE_HAVE_GETPAGESIZE) && defined(MAGICKCORE_POSIX_SUPPORT)
+ pagesize=getpagesize();
+#endif
+ if (pagesize <= 0)
+ pagesize=4096;
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (attach != MagickFalse)
+ {
+ /*
+ Attach persistent pixel cache.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),
+ "attach persistent cache");
+ (void) CopyMagickString(cache_info->cache_filename,filename,
+ MaxTextExtent);
+ cache_info->type=DiskCache;
+ cache_info->offset=(*offset);
+ if (OpenPixelCache(image,ReadMode,exception) == MagickFalse)
+ return(MagickFalse);
+ cache_info=(CacheInfo *) ReferencePixelCache(cache_info);
+ *offset+=cache_info->length+pagesize-(cache_info->length % pagesize);
+ return(MagickTrue);
+ }
+ if ((cache_info->type != MemoryCache) && (cache_info->reference_count == 1))
+ {
+ (void) LockSemaphoreInfo(cache_info->semaphore);
+ if ((cache_info->type != MemoryCache) &&
+ (cache_info->reference_count == 1))
+ {
+ int
+ status;
+
+ /*
+ Usurp resident persistent pixel cache.
+ */
+ status=rename(cache_info->cache_filename,filename);
+ if (status == 0)
+ {
+ (void) CopyMagickString(cache_info->cache_filename,filename,
+ MaxTextExtent);
+ cache_info=(CacheInfo *) ReferencePixelCache(cache_info);
+ *offset+=cache_info->length+pagesize-(cache_info->length %
+ pagesize);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),
+ "Usurp resident persistent cache");
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ return(MagickTrue);
+ }
+ }
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ }
+ /*
+ Attach persistent pixel cache.
+ */
+ clone_image=(*image);
+ clone_info=(CacheInfo *) clone_image.cache;
+ image->cache=ClonePixelCache(cache_info);
+ cache_info=(CacheInfo *) ReferencePixelCache(image->cache);
+ (void) CopyMagickString(cache_info->cache_filename,filename,MaxTextExtent);
+ cache_info->type=DiskCache;
+ cache_info->offset=(*offset);
+ cache_info=(CacheInfo *) image->cache;
+ status=ClonePixelCacheNexus(cache_info,clone_info,exception);
+ if (status != MagickFalse)
+ {
+ status=OpenPixelCache(image,IOMode,exception);
+ if (status != MagickFalse)
+ status=ClonePixelCachePixels(cache_info,clone_info,&image->exception);
+ }
+ *offset+=cache_info->length+pagesize-(cache_info->length % pagesize);
+ clone_info=(CacheInfo *) DestroyPixelCache(clone_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ Q u e u e A u t h e n t i c N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueueAuthenticNexus() allocates an region to store image pixels as defined
+% by the region rectangle and returns a pointer to the region. This region is
+% subsequently transferred from the pixel cache with
+% SyncAuthenticPixelsCache(). A pointer to the pixels is returned if the
+% pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the QueueAuthenticNexus() method is:
+%
+% PixelPacket *QueueAuthenticNexus(Image *image,const long x,const long y,
+% const unsigned long columns,const unsigned long rows,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o nexus_info: the cache nexus to set.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket *QueueAuthenticNexus(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ number_pixels;
+
+ RectangleInfo
+ region;
+
+ /*
+ Validate pixel cache geometry.
+ */
+ cache_info=(CacheInfo *) image->cache;
+ if ((cache_info->columns == 0) && (cache_info->rows == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "NoPixelsDefinedInCache","`%s'",image->filename);
+ return((PixelPacket *) NULL);
+ }
+ if ((x < 0) || (y < 0) || (x >= (long) cache_info->columns) ||
+ (y >= (long) cache_info->rows))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "PixelsAreNotAuthentic","`%s'",image->filename);
+ return((PixelPacket *) NULL);
+ }
+ offset=(MagickOffsetType) y*cache_info->columns+x;
+ if (offset < 0)
+ return((PixelPacket *) NULL);
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ offset+=(MagickOffsetType) (rows-1)*cache_info->columns+columns-1;
+ if ((MagickSizeType) offset >= number_pixels)
+ return((PixelPacket *) NULL);
+ /*
+ Return pixel cache.
+ */
+ region.x=x;
+ region.y=y;
+ region.width=columns;
+ region.height=rows;
+ return(SetPixelCacheNexusPixels(image,®ion,nexus_info,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ Q u e u e A u t h e n t i c P i x e l s C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueueAuthenticPixelsCache() allocates an region to store image pixels as
+% defined by the region rectangle and returns a pointer to the region. This
+% region is subsequently transferred from the pixel cache with
+% SyncAuthenticPixelsCache(). A pointer to the pixels is returned if the
+% pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the QueueAuthenticPixelsCache() method is:
+%
+% PixelPacket *QueueAuthenticPixelsCache(Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static PixelPacket *QueueAuthenticPixelsCache(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ long
+ id;
+
+ PixelPacket
+ *pixels;
+
+ cache_info=(CacheInfo *) GetImagePixelCache(image,MagickFalse,exception);
+ if (cache_info == (Cache) NULL)
+ return((PixelPacket *) NULL);
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ pixels=QueueAuthenticNexus(image,x,y,columns,rows,cache_info->nexus_info[id],
+ exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e u e A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueueAuthenticPixels() queues a mutable pixel region. If the region is
+% successfully intialized a pointer to a PixelPacket array representing the
+% region is returned, otherwise NULL is returned. The returned pointer may
+% point to a temporary working buffer for the pixels or it may point to the
+% final location of the pixels in memory.
+%
+% Write-only access means that any existing pixel values corresponding to
+% the region are ignored. This is useful if the initial image is being
+% created from scratch, or if the existing pixel values are to be
+% completely replaced without need to refer to their pre-existing values.
+% The application is free to read and write the pixel buffer returned by
+% QueueAuthenticPixels() any way it pleases. QueueAuthenticPixels() does not
+% initialize the pixel array values. Initializing pixel array values is the
+% application's responsibility.
+%
+% Performance is maximized if the selected region is part of one row, or
+% one or more full rows, since then there is opportunity to access the
+% pixels in-place (without a copy) if the image is in RAM, or in a
+% memory-mapped file. The returned pointer should *never* be deallocated
+% by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or the storage class is PseudoClass,
+% call GetAuthenticIndexQueue() after invoking GetAuthenticPixels() to obtain
+% the black color component or the colormap indexes (of type IndexPacket)
+% corresponding to the region. Once the PixelPacket (and/or IndexPacket)
+% array has been updated, the changes must be saved back to the underlying
+% image using SyncAuthenticPixels() or they may be lost.
+%
+% The format of the QueueAuthenticPixels() method is:
+%
+% PixelPacket *QueueAuthenticPixels(Image *image,const long x,const long y,
+% const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket *QueueAuthenticPixels(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ PixelPacket
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.queue_authentic_pixels_handler ==
+ (QueueAuthenticPixelsHandler) NULL)
+ return((PixelPacket *) NULL);
+ pixels=cache_info->methods.queue_authentic_pixels_handler(image,x,y,columns,
+ rows,exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d P i x e l C a c h e I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadPixelCacheIndexes() reads colormap indexes from the specified region of
+% the pixel cache.
+%
+% The format of the ReadPixelCacheIndexes() method is:
+%
+% MagickBooleanType ReadPixelCacheIndexes(CacheInfo *cache_info,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+% o nexus_info: the cache nexus to read the colormap indexes.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType ReadPixelCacheIndexes(CacheInfo *cache_info,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ register IndexPacket
+ *__restrict q;
+
+ register long
+ y;
+
+ unsigned long
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (cache_info->active_index_channel == MagickFalse)
+ return(MagickFalse);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(MagickTrue);
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ length=(MagickSizeType) nexus_info->region.width*sizeof(IndexPacket);
+ rows=nexus_info->region.height;
+ number_pixels=length*rows;
+ if ((cache_info->columns == nexus_info->region.width) &&
+ (number_pixels == (MagickSizeType) ((size_t) number_pixels)))
+ {
+ length=number_pixels;
+ rows=1UL;
+ }
+ q=nexus_info->indexes;
+ switch (cache_info->type)
+ {
+ case MemoryCache:
+ case MapCache:
+ {
+ register IndexPacket
+ *__restrict p;
+
+ /*
+ Read indexes from memory.
+ */
+ p=cache_info->indexes+offset;
+ for (y=0; y < (long) rows; y++)
+ {
+ (void) CopyMagickMemory(q,p,(size_t) length);
+ p+=cache_info->columns;
+ q+=nexus_info->region.width;
+ }
+ break;
+ }
+ case DiskCache:
+ {
+ /*
+ Read indexes from disk.
+ */
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+number_pixels*
+ sizeof(PixelPacket)+offset*sizeof(*q),length,(unsigned char *) q);
+ if ((MagickSizeType) count < length)
+ break;
+ offset+=cache_info->columns;
+ q+=nexus_info->region.width;
+ }
+ if (y < (long) rows)
+ {
+ ThrowFileException(exception,CacheError,"UnableToReadPixelCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((cache_info->debug != MagickFalse) &&
+ (QuantumTick(nexus_info->region.y,cache_info->rows) != MagickFalse))
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s[%lux%lu%+ld%+ld]",
+ cache_info->filename,nexus_info->region.width,nexus_info->region.height,
+ nexus_info->region.x,nexus_info->region.y);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d P i x e l C a c h e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadPixelCachePixels() reads pixels from the specified region of the pixel
+% cache.
+%
+% The format of the ReadPixelCachePixels() method is:
+%
+% MagickBooleanType ReadPixelCachePixels(CacheInfo *cache_info,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+% o nexus_info: the cache nexus to read the pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType ReadPixelCachePixels(CacheInfo *cache_info,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict q;
+
+ unsigned long
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(MagickTrue);
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ length=(MagickSizeType) nexus_info->region.width*sizeof(PixelPacket);
+ rows=nexus_info->region.height;
+ number_pixels=length*rows;
+ if ((cache_info->columns == nexus_info->region.width) &&
+ (number_pixels == (MagickSizeType) ((size_t) number_pixels)))
+ {
+ length=number_pixels;
+ rows=1UL;
+ }
+ q=nexus_info->pixels;
+ switch (cache_info->type)
+ {
+ case MemoryCache:
+ case MapCache:
+ {
+ register PixelPacket
+ *__restrict p;
+
+ /*
+ Read pixels from memory.
+ */
+ p=cache_info->pixels+offset;
+ for (y=0; y < (long) rows; y++)
+ {
+ (void) CopyMagickMemory(q,p,(size_t) length);
+ p+=cache_info->columns;
+ q+=nexus_info->region.width;
+ }
+ break;
+ }
+ case DiskCache:
+ {
+ /*
+ Read pixels from disk.
+ */
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ count=ReadPixelCacheRegion(cache_info,cache_info->offset+offset*
+ sizeof(*q),length,(unsigned char *) q);
+ if ((MagickSizeType) count < length)
+ break;
+ offset+=cache_info->columns;
+ q+=nexus_info->region.width;
+ }
+ if (y < (long) rows)
+ {
+ ThrowFileException(exception,CacheError,"UnableToReadPixelCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((cache_info->debug != MagickFalse) &&
+ (QuantumTick(nexus_info->region.y,cache_info->rows) != MagickFalse))
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s[%lux%lu%+ld%+ld]",
+ cache_info->filename,nexus_info->region.width,nexus_info->region.height,
+ nexus_info->region.x,nexus_info->region.y);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e f e r e n c e P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReferencePixelCache() increments the reference count associated with the
+% pixel cache returning a pointer to the cache.
+%
+% The format of the ReferencePixelCache method is:
+%
+% Cache ReferencePixelCache(Cache cache_info)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+*/
+MagickExport Cache ReferencePixelCache(Cache cache)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(cache != (Cache *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ (void) LockSemaphoreInfo(cache_info->semaphore);
+ cache_info->reference_count++;
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ return(cache_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t P i x e l C a c h e M e t h o d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetPixelCacheMethods() sets the image pixel methods to the specified ones.
+%
+% The format of the SetPixelCacheMethods() method is:
+%
+% SetPixelCacheMethods(Cache *,CacheMethods *cache_methods)
+%
+% A description of each parameter follows:
+%
+% o cache: the pixel cache.
+%
+% o cache_methods: Specifies a pointer to a CacheMethods structure.
+%
+*/
+MagickExport void SetPixelCacheMethods(Cache cache,CacheMethods *cache_methods)
+{
+ CacheInfo
+ *cache_info;
+
+ GetOneAuthenticPixelFromHandler
+ get_one_authentic_pixel_from_handler;
+
+ GetOneVirtualPixelFromHandler
+ get_one_virtual_pixel_from_handler;
+
+ /*
+ Set cache pixel methods.
+ */
+ assert(cache != (Cache) NULL);
+ assert(cache_methods != (CacheMethods *) NULL);
+ cache_info=(CacheInfo *) cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (cache_methods->get_virtual_pixel_handler != (GetVirtualPixelHandler) NULL)
+ cache_info->methods.get_virtual_pixel_handler=
+ cache_methods->get_virtual_pixel_handler;
+ if (cache_methods->destroy_pixel_handler != (DestroyPixelHandler) NULL)
+ cache_info->methods.destroy_pixel_handler=
+ cache_methods->destroy_pixel_handler;
+ if (cache_methods->get_virtual_indexes_from_handler !=
+ (GetVirtualIndexesFromHandler) NULL)
+ cache_info->methods.get_virtual_indexes_from_handler=
+ cache_methods->get_virtual_indexes_from_handler;
+ if (cache_methods->get_authentic_pixels_handler !=
+ (GetAuthenticPixelsHandler) NULL)
+ cache_info->methods.get_authentic_pixels_handler=
+ cache_methods->get_authentic_pixels_handler;
+ if (cache_methods->queue_authentic_pixels_handler !=
+ (QueueAuthenticPixelsHandler) NULL)
+ cache_info->methods.queue_authentic_pixels_handler=
+ cache_methods->queue_authentic_pixels_handler;
+ if (cache_methods->sync_authentic_pixels_handler !=
+ (SyncAuthenticPixelsHandler) NULL)
+ cache_info->methods.sync_authentic_pixels_handler=
+ cache_methods->sync_authentic_pixels_handler;
+ if (cache_methods->get_authentic_pixels_from_handler !=
+ (GetAuthenticPixelsFromHandler) NULL)
+ cache_info->methods.get_authentic_pixels_from_handler=
+ cache_methods->get_authentic_pixels_from_handler;
+ if (cache_methods->get_authentic_indexes_from_handler !=
+ (GetAuthenticIndexesFromHandler) NULL)
+ cache_info->methods.get_authentic_indexes_from_handler=
+ cache_methods->get_authentic_indexes_from_handler;
+ get_one_virtual_pixel_from_handler=
+ cache_info->methods.get_one_virtual_pixel_from_handler;
+ if (get_one_virtual_pixel_from_handler !=
+ (GetOneVirtualPixelFromHandler) NULL)
+ cache_info->methods.get_one_virtual_pixel_from_handler=
+ cache_methods->get_one_virtual_pixel_from_handler;
+ get_one_authentic_pixel_from_handler=
+ cache_methods->get_one_authentic_pixel_from_handler;
+ if (get_one_authentic_pixel_from_handler !=
+ (GetOneAuthenticPixelFromHandler) NULL)
+ cache_info->methods.get_one_authentic_pixel_from_handler=
+ cache_methods->get_one_authentic_pixel_from_handler;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t P i x e l C a c h e N e x u s P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetPixelCacheNexusPixels() defines the region of the cache for the
+% specified cache nexus.
+%
+% The format of the SetPixelCacheNexusPixels() method is:
+%
+% PixelPacket SetPixelCacheNexusPixels(const Image *image,
+% const RectangleInfo *region,NexusInfo *nexus_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o region: A pointer to the RectangleInfo structure that defines the
+% region of this particular cache nexus.
+%
+% o nexus_info: the cache nexus to set.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static PixelPacket *SetPixelCacheNexusPixels(const Image *image,
+ const RectangleInfo *region,NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ status;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->type == UndefinedCache)
+ return((PixelPacket *) NULL);
+ nexus_info->region.width=region->width == 0UL ? 1UL : region->width;
+ nexus_info->region.height=region->height == 0UL ? 1UL : region->height;
+ nexus_info->region.x=region->x;
+ nexus_info->region.y=region->y;
+ if ((cache_info->type != DiskCache) && (image->clip_mask == (Image *) NULL) &&
+ (image->mask == (Image *) NULL))
+ {
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ length=(MagickSizeType) (nexus_info->region.height-1)*cache_info->columns+
+ nexus_info->region.width-1;
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ if ((offset >= 0) && (((MagickSizeType) offset+length) < number_pixels))
+ {
+ long
+ x,
+ y;
+
+ x=nexus_info->region.x+nexus_info->region.width;
+ y=nexus_info->region.y+nexus_info->region.height;
+ if ((nexus_info->region.x >= 0) &&
+ (x <= (long) cache_info->columns) &&
+ (nexus_info->region.y >= 0) && (y <= (long) cache_info->rows))
+ if ((nexus_info->region.height == 1UL) ||
+ ((nexus_info->region.x == 0) &&
+ ((nexus_info->region.width % cache_info->columns) == 0)))
+ {
+ /*
+ Pixels are accessed directly from memory.
+ */
+ nexus_info->pixels=cache_info->pixels+offset;
+ nexus_info->indexes=(IndexPacket *) NULL;
+ if (cache_info->active_index_channel != MagickFalse)
+ nexus_info->indexes=cache_info->indexes+offset;
+ return(nexus_info->pixels);
+ }
+ }
+ }
+ /*
+ Pixels are stored in a cache region until they are synced to the cache.
+ */
+ number_pixels=(MagickSizeType) nexus_info->region.width*
+ nexus_info->region.height;
+ length=number_pixels*sizeof(PixelPacket);
+ if (cache_info->active_index_channel != MagickFalse)
+ length+=number_pixels*sizeof(IndexPacket);
+ if (nexus_info->cache == (PixelPacket *) NULL)
+ {
+ nexus_info->length=length;
+ status=AcquireCacheNexusPixels(cache_info,nexus_info,exception);
+ if (status == MagickFalse)
+ return((PixelPacket *) NULL);
+ }
+ else
+ if (nexus_info->length != length)
+ {
+ RelinquishCacheNexusPixels(nexus_info);
+ nexus_info->length=length;
+ status=AcquireCacheNexusPixels(cache_info,nexus_info,exception);
+ if (status == MagickFalse)
+ return((PixelPacket *) NULL);
+ }
+ nexus_info->pixels=nexus_info->cache;
+ nexus_info->indexes=(IndexPacket *) NULL;
+ if (cache_info->active_index_channel != MagickFalse)
+ nexus_info->indexes=(IndexPacket *) (nexus_info->pixels+number_pixels);
+ return(nexus_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t P i x e l C a c h e V i r t u a l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetPixelCacheVirtualMethod() sets the "virtual pixels" method for the
+% pixel cache and returns the previous setting. A virtual pixel is any pixel
+% access that is outside the boundaries of the image cache.
+%
+% The format of the SetPixelCacheVirtualMethod() method is:
+%
+% VirtualPixelMethod SetPixelCacheVirtualMethod(const Image *image,
+% const VirtualPixelMethod virtual_pixel_method)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: choose the type of virtual pixel.
+%
+*/
+MagickExport VirtualPixelMethod SetPixelCacheVirtualMethod(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method)
+{
+ CacheInfo
+ *cache_info;
+
+ VirtualPixelMethod
+ method;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ method=cache_info->virtual_pixel_method;
+ cache_info->virtual_pixel_method=virtual_pixel_method;
+ return(method);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c A u t h e n t i c P i x e l C a c h e N e x u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncAuthenticPixelCacheNexus() saves the authentic image pixels to the
+% in-memory or disk cache. The method returns MagickTrue if the pixel region
+% is synced, otherwise MagickFalse.
+%
+% The format of the SyncAuthenticPixelCacheNexus() method is:
+%
+% MagickBooleanType SyncAuthenticPixelCacheNexus(Image *image,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o nexus_info: the cache nexus to sync.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType SyncAuthenticPixelCacheNexus(Image *image,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Transfer pixels to the cache.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->cache == (Cache) NULL)
+ ThrowBinaryException(CacheError,"PixelCacheIsNotOpen",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ if (cache_info->type == UndefinedCache)
+ return(MagickFalse);
+ if ((image->clip_mask != (Image *) NULL) &&
+ (ClipPixelCacheNexus(image,nexus_info,exception) == MagickFalse))
+ return(MagickFalse);
+ if ((image->mask != (Image *) NULL) &&
+ (MaskPixelCacheNexus(image,nexus_info,exception) == MagickFalse))
+ return(MagickFalse);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(MagickTrue);
+ assert(cache_info->signature == MagickSignature);
+ status=WritePixelCachePixels(cache_info,nexus_info,exception);
+ if ((cache_info->active_index_channel != MagickFalse) &&
+ (WritePixelCacheIndexes(cache_info,nexus_info,exception) == MagickFalse))
+ return(MagickFalse);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c A u t h e n t i c P i x e l C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncAuthenticPixelsCache() saves the authentic image pixels to the in-memory
+% or disk cache. The method returns MagickTrue if the pixel region is synced,
+% otherwise MagickFalse.
+%
+% The format of the SyncAuthenticPixelsCache() method is:
+%
+% MagickBooleanType SyncAuthenticPixelsCache(Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType SyncAuthenticPixelsCache(Image *image,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ long
+ id;
+
+ MagickBooleanType
+ status;
+
+ cache_info=(CacheInfo *) image->cache;
+ id=GetOpenMPThreadId();
+ assert(id < (long) cache_info->number_threads);
+ status=SyncAuthenticPixelCacheNexus(image,cache_info->nexus_info[id],
+ exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c A u t h e n t i c P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncAuthenticPixels() saves the image pixels to the in-memory or disk cache.
+% The method returns MagickTrue if the pixel region is flushed, otherwise
+% MagickFalse.
+%
+% The format of the SyncAuthenticPixels() method is:
+%
+% MagickBooleanType SyncAuthenticPixels(Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType SyncAuthenticPixels(Image *image,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->cache != (Cache) NULL);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if (cache_info->methods.sync_authentic_pixels_handler ==
+ (SyncAuthenticPixelsHandler) NULL)
+ return(MagickFalse);
+ return(cache_info->methods.sync_authentic_pixels_handler(image,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e P i x e l C a c h e I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WritePixelCacheIndexes() writes the colormap indexes to the specified
+% region of the pixel cache.
+%
+% The format of the WritePixelCacheIndexes() method is:
+%
+% MagickBooleanType WritePixelCacheIndexes(CacheInfo *cache_info,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+% o nexus_info: the cache nexus to write the colormap indexes.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType WritePixelCacheIndexes(CacheInfo *cache_info,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ register const IndexPacket
+ *__restrict p;
+
+ register long
+ y;
+
+ unsigned long
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (cache_info->active_index_channel == MagickFalse)
+ return(MagickFalse);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(MagickTrue);
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ length=(MagickSizeType) nexus_info->region.width*sizeof(IndexPacket);
+ rows=nexus_info->region.height;
+ number_pixels=(MagickSizeType) length*rows;
+ if ((cache_info->columns == nexus_info->region.width) &&
+ (number_pixels == (MagickSizeType) ((size_t) number_pixels)))
+ {
+ length=number_pixels;
+ rows=1UL;
+ }
+ p=nexus_info->indexes;
+ switch (cache_info->type)
+ {
+ case MemoryCache:
+ case MapCache:
+ {
+ register IndexPacket
+ *__restrict q;
+
+ /*
+ Write indexes to memory.
+ */
+ q=cache_info->indexes+offset;
+ for (y=0; y < (long) rows; y++)
+ {
+ (void) CopyMagickMemory(q,p,(size_t) length);
+ p+=nexus_info->region.width;
+ q+=cache_info->columns;
+ }
+ break;
+ }
+ case DiskCache:
+ {
+ /*
+ Write indexes to disk.
+ */
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ number_pixels=(MagickSizeType) cache_info->columns*cache_info->rows;
+ for (y=0; y < (long) rows; y++)
+ {
+ count=WritePixelCacheRegion(cache_info,cache_info->offset+number_pixels*
+ sizeof(PixelPacket)+offset*sizeof(*p),length,
+ (const unsigned char *) p);
+ if ((MagickSizeType) count < length)
+ break;
+ p+=nexus_info->region.width;
+ offset+=cache_info->columns;
+ }
+ if (y < (long) rows)
+ {
+ ThrowFileException(exception,CacheError,"UnableToWritePixelCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((cache_info->debug != MagickFalse) &&
+ (QuantumTick(nexus_info->region.y,cache_info->rows) != MagickFalse))
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s[%lux%lu%+ld%+ld]",
+ cache_info->filename,nexus_info->region.width,nexus_info->region.height,
+ nexus_info->region.x,nexus_info->region.y);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ W r i t e C a c h e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WritePixelCachePixels() writes image pixels to the specified region of the
+% pixel cache.
+%
+% The format of the WritePixelCachePixels() method is:
+%
+% MagickBooleanType WritePixelCachePixels(CacheInfo *cache_info,
+% NexusInfo *nexus_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_info: the pixel cache.
+%
+% o nexus_info: the cache nexus to write the pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType WritePixelCachePixels(CacheInfo *cache_info,
+ NexusInfo *nexus_info,ExceptionInfo *exception)
+{
+ MagickOffsetType
+ count,
+ offset;
+
+ MagickSizeType
+ length,
+ number_pixels;
+
+ register long
+ y;
+
+ register const PixelPacket
+ *__restrict p;
+
+ unsigned long
+ rows;
+
+ if (cache_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ cache_info->filename);
+ if (IsNexusInCore(cache_info,nexus_info) != MagickFalse)
+ return(MagickTrue);
+ offset=(MagickOffsetType) nexus_info->region.y*cache_info->columns+
+ nexus_info->region.x;
+ length=(MagickSizeType) nexus_info->region.width*sizeof(PixelPacket);
+ rows=nexus_info->region.height;
+ number_pixels=length*rows;
+ if ((cache_info->columns == nexus_info->region.width) &&
+ (number_pixels == (MagickSizeType) ((size_t) number_pixels)))
+ {
+ length=number_pixels;
+ rows=1UL;
+ }
+ p=nexus_info->pixels;
+ switch (cache_info->type)
+ {
+ case MemoryCache:
+ case MapCache:
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Write pixels to memory.
+ */
+ q=cache_info->pixels+offset;
+ for (y=0; y < (long) rows; y++)
+ {
+ (void) CopyMagickMemory(q,p,(size_t) length);
+ p+=nexus_info->region.width;
+ q+=cache_info->columns;
+ }
+ break;
+ }
+ case DiskCache:
+ {
+ /*
+ Write pixels to disk.
+ */
+ if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ count=WritePixelCacheRegion(cache_info,cache_info->offset+offset*
+ sizeof(*p),length,(const unsigned char *) p);
+ if ((MagickSizeType) count < length)
+ break;
+ p+=nexus_info->region.width;
+ offset+=cache_info->columns;
+ }
+ if (y < (long) rows)
+ {
+ ThrowFileException(exception,CacheError,"UnableToWritePixelCache",
+ cache_info->cache_filename);
+ return(MagickFalse);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((cache_info->debug != MagickFalse) &&
+ (QuantumTick(nexus_info->region.y,cache_info->rows) != MagickFalse))
+ (void) LogMagickEvent(CacheEvent,GetMagickModule(),"%s[%lux%lu%+ld%+ld]",
+ cache_info->filename,nexus_info->region.width,nexus_info->region.height,
+ nexus_info->region.x,nexus_info->region.y);
+ return(MagickTrue);
+}
diff --git a/magick/cache.h b/magick/cache.h
new file mode 100644
index 0000000..1fa119a
--- /dev/null
+++ b/magick/cache.h
@@ -0,0 +1,72 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore cache methods.
+*/
+#ifndef _MAGICKCORE_CACHE_H
+#define _MAGICKCORE_CACHE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/blob.h"
+
+extern MagickExport const IndexPacket
+ *GetVirtualIndexQueue(const Image *);
+
+extern MagickExport const PixelPacket
+ *GetVirtualPixels(const Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *),
+ *GetVirtualPixelQueue(const Image *);
+
+extern MagickExport IndexPacket
+ *GetAuthenticIndexQueue(const Image *);
+
+extern MagickExport MagickBooleanType
+ GetOneVirtualMagickPixel(const Image *,const long,const long,
+ MagickPixelPacket *,ExceptionInfo *),
+ GetOneVirtualPixel(const Image *,const long,const long,PixelPacket *,
+ ExceptionInfo *),
+ GetOneVirtualMethodPixel(const Image *,const VirtualPixelMethod,const long,
+ const long,PixelPacket *,ExceptionInfo *),
+ GetOneAuthenticPixel(Image *,const long,const long,PixelPacket *,
+ ExceptionInfo *),
+ PersistPixelCache(Image *,const char *,const MagickBooleanType,
+ MagickOffsetType *,ExceptionInfo *),
+ SyncAuthenticPixels(Image *,ExceptionInfo *);
+
+extern MagickExport MagickSizeType
+ GetImageExtent(const Image *);
+
+extern MagickExport PixelPacket
+ *GetAuthenticPixels(Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *),
+ *GetAuthenticPixelQueue(const Image *),
+ *QueueAuthenticPixels(Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *);
+
+extern MagickExport VirtualPixelMethod
+ GetPixelCacheVirtualMethod(const Image *),
+ SetPixelCacheVirtualMethod(const Image *,const VirtualPixelMethod);
+
+extern MagickExport void
+ DestroyPixelCacheResources(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/cipher.c b/magick/cipher.c
new file mode 100644
index 0000000..26d5db1
--- /dev/null
+++ b/magick/cipher.c
@@ -0,0 +1,1157 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% CCCC IIIII PPPP H H EEEEE RRRR %
+% C I P P H H E R R %
+% C I PPPP HHHHH EEE RRRR %
+% C I P H H E R R %
+% CCCC IIIII P H H EEEEE R R %
+% %
+% %
+% MagickCore Cipher Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/cipher.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/property.h"
+#include "magick/quantum-private.h"
+#include "magick/registry.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/splay-tree.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+
+#if defined(MAGICKCORE_CIPHER_SUPPORT)
+/*
+ Define declarations.
+*/
+#define AESBlocksize 16
+
+/*
+ Typedef declarations.
+*/
+typedef struct _AESInfo
+{
+ StringInfo
+ *key;
+
+ unsigned int
+ blocksize,
+ *encipher_key,
+ *decipher_key;
+
+ long
+ rounds,
+ timestamp;
+
+ unsigned long
+ signature;
+} AESInfo;
+
+/*
+ Global declarations.
+*/
+static unsigned char
+ InverseLog[256] =
+ {
+ 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248,
+ 19, 53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10,
+ 30, 34, 102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190,
+ 217, 112, 144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204,
+ 79, 209, 104, 184, 211, 110, 178, 205, 76, 212, 103, 169, 224, 59,
+ 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208,
+ 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196,
+ 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
+ 254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32,
+ 96, 160, 251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86,
+ 250, 21, 63, 65, 195, 94, 226, 61, 71, 201, 64, 192, 91, 237,
+ 44, 116, 156, 191, 218, 117, 159, 186, 213, 100, 172, 239, 42, 126,
+ 130, 157, 188, 223, 122, 142, 137, 128, 155, 182, 193, 88, 232, 35,
+ 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, 252, 31, 33, 99,
+ 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, 69, 207,
+ 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
+ 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242,
+ 13, 23, 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180,
+ 199, 82, 246, 1
+ },
+ Log[256] =
+ {
+ 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238,
+ 223, 3, 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200,
+ 248, 105, 28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228,
+ 166, 114, 154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36,
+ 18, 240, 130, 69, 53, 147, 218, 142, 150, 143, 219, 189, 54, 208,
+ 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, 102, 221, 253, 48,
+ 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, 126, 110,
+ 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186,
+ 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115,
+ 167, 87, 175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213,
+ 231, 230, 173, 232, 44, 215, 117, 122, 235, 22, 11, 245, 89, 203,
+ 95, 176, 156, 169, 81, 160, 127, 12, 246, 111, 23, 196, 73, 236,
+ 216, 67, 31, 45, 164, 118, 123, 183, 204, 187, 62, 90, 251, 96,
+ 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, 151, 178, 135, 144,
+ 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, 83, 57,
+ 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171,
+ 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153,
+ 227, 165, 103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128,
+ 192, 247, 112, 7,
+ },
+ SBox[256] =
+ {
+ 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215,
+ 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175,
+ 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165,
+ 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154,
+ 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110,
+ 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237,
+ 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239,
+ 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168,
+ 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255,
+ 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61,
+ 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238,
+ 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92,
+ 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213,
+ 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46,
+ 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62,
+ 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158,
+ 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85,
+ 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15,
+ 176, 84, 187, 22
+ };
+
+/*
+ Forward declarations.
+*/
+static AESInfo
+ *DestroyAESInfo(AESInfo *);
+
+static void
+ EncipherAESBlock(AESInfo *,const unsigned char *,unsigned char *),
+ SetAESKey(AESInfo *,const StringInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e A E S I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireAESInfo() allocate the AESInfo structure.
+%
+% The format of the AcquireAESInfo method is:
+%
+% AESInfo *AcquireAESInfo(void)
+%
+*/
+static AESInfo *AcquireAESInfo(void)
+{
+ AESInfo
+ *aes_info;
+
+ aes_info=(AESInfo *) AcquireMagickMemory(sizeof(*aes_info));
+ if (aes_info == (AESInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(aes_info,0,sizeof(*aes_info));
+ aes_info->blocksize=AESBlocksize;
+ aes_info->key=AcquireStringInfo(32);
+ aes_info->encipher_key=(unsigned int *) AcquireQuantumMemory(60UL,sizeof(
+ *aes_info->encipher_key));
+ aes_info->decipher_key=(unsigned int *) AcquireQuantumMemory(60UL,sizeof(
+ *aes_info->decipher_key));
+ if ((aes_info->key == (StringInfo *) NULL) ||
+ (aes_info->encipher_key == (unsigned int *) NULL) ||
+ (aes_info->decipher_key == (unsigned int *) NULL))
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ aes_info->timestamp=(long) time(0);
+ aes_info->signature=MagickSignature;
+ return(aes_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y A E S I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyAESInfo() zeros memory associated with the AESInfo structure.
+%
+% The format of the DestroyAESInfo method is:
+%
+% AESInfo *DestroyAESInfo(AESInfo *aes_info)
+%
+% A description of each parameter follows:
+%
+% o aes_info: the cipher context.
+%
+*/
+static AESInfo *DestroyAESInfo(AESInfo *aes_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(aes_info != (AESInfo *) NULL);
+ assert(aes_info->signature == MagickSignature);
+ if (aes_info->decipher_key != (unsigned int *) NULL)
+ aes_info->decipher_key=(unsigned int *) RelinquishMagickMemory(
+ aes_info->decipher_key);
+ if (aes_info->encipher_key != (unsigned int *) NULL)
+ aes_info->encipher_key=(unsigned int *) RelinquishMagickMemory(
+ aes_info->encipher_key);
+ if (aes_info->key != (StringInfo *) NULL)
+ aes_info->key=DestroyStringInfo(aes_info->key);
+ aes_info->signature=(~MagickSignature);
+ aes_info=(AESInfo *) RelinquishMagickMemory(aes_info);
+ return(aes_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E n c i p h e r A E S B l o c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EncipherAESBlock() enciphers a single block of plaintext to produce a block
+% of ciphertext.
+%
+% The format of the EncipherAESBlock method is:
+%
+% void EncipherAES(AESInfo *aes_info,const unsigned char *plaintext,
+% unsigned char *ciphertext)
+%
+% A description of each parameter follows:
+%
+% o aes_info: the cipher context.
+%
+% o plaintext: the plain text.
+%
+% o ciphertext: the cipher text.
+%
+*/
+
+static inline void AddRoundKey(const unsigned int *ciphertext,
+ const unsigned int *key,unsigned int *plaintext)
+{
+ register long
+ i;
+
+ /*
+ Xor corresponding text input and round key input bytes.
+ */
+ for (i=0; i < 4; i++)
+ plaintext[i]=key[i] ^ ciphertext[i];
+}
+
+static inline unsigned char ByteMultiply(const unsigned char alpha,
+ const unsigned char beta)
+{
+ /*
+ Byte multiply two elements of GF(2^m) (mix columns and inverse mix columns).
+ */
+ if ((alpha == 0) || (beta == 0))
+ return(0);
+ return(InverseLog[(Log[alpha]+Log[beta]) % 0xff]);
+}
+
+static inline unsigned int ByteSubTransform(unsigned int x,
+ unsigned char *s_box)
+{
+ unsigned int
+ key;
+
+ /*
+ Non-linear layer resists differential and linear cryptoanalysis attacks.
+ */
+ key=(s_box[x & 0xff]) | (s_box[(x >> 8) & 0xff] << 8) |
+ (s_box[(x >> 16) & 0xff] << 16) | (s_box[(x >> 24) & 0xff] << 24);
+ return(key);
+}
+
+static void FinalizeRoundKey(const unsigned int *ciphertext,
+ const unsigned int *key,unsigned char *plaintext)
+{
+ register unsigned char
+ *p;
+
+ register unsigned int
+ i,
+ j;
+
+ unsigned int
+ value;
+
+ /*
+ The round key is XORed with the result of the mix-column transformation.
+ */
+ p=plaintext;
+ for (i=0; i < 4; i++)
+ {
+ value=ciphertext[i] ^ key[i];
+ for (j=0; j < 4; j++)
+ *p++=(value >> (8*j)) & 0xff;
+ }
+ /*
+ Reset registers.
+ */
+ value=0;
+}
+
+static void InitializeRoundKey(const unsigned char *ciphertext,
+ const unsigned int *key,unsigned int *plaintext)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned int
+ i,
+ j;
+
+ unsigned int
+ value;
+
+ p=ciphertext;
+ for (i=0; i < 4; i++)
+ {
+ value=0;
+ for (j=0; j < 4; j++)
+ value|=(*p++ << (8*j));
+ plaintext[i]=key[i] ^ value;
+ }
+ /*
+ Reset registers.
+ */
+ value=0;
+}
+
+static inline unsigned int RotateLeft(const unsigned int x)
+{
+ return(((x << 8) | ((x >> 24) & 0xff)));
+}
+
+static void EncipherAESBlock(AESInfo *aes_info,const unsigned char *plaintext,
+ unsigned char *ciphertext)
+{
+ register long
+ i,
+ j;
+
+ static int
+ map[4][4] =
+ {
+ { 0, 1, 2, 3 },
+ { 1, 2, 3, 0 },
+ { 2, 3, 0, 1 },
+ { 3, 0, 1, 2 }
+ };
+
+ static unsigned int
+ D[] =
+ {
+ 0xa56363c6U, 0x847c7cf8U, 0x997777eeU, 0x8d7b7bf6U, 0x0df2f2ffU,
+ 0xbd6b6bd6U, 0xb16f6fdeU, 0x54c5c591U, 0x50303060U, 0x03010102U,
+ 0xa96767ceU, 0x7d2b2b56U, 0x19fefee7U, 0x62d7d7b5U, 0xe6abab4dU,
+ 0x9a7676ecU, 0x45caca8fU, 0x9d82821fU, 0x40c9c989U, 0x877d7dfaU,
+ 0x15fafaefU, 0xeb5959b2U, 0xc947478eU, 0x0bf0f0fbU, 0xecadad41U,
+ 0x67d4d4b3U, 0xfda2a25fU, 0xeaafaf45U, 0xbf9c9c23U, 0xf7a4a453U,
+ 0x967272e4U, 0x5bc0c09bU, 0xc2b7b775U, 0x1cfdfde1U, 0xae93933dU,
+ 0x6a26264cU, 0x5a36366cU, 0x413f3f7eU, 0x02f7f7f5U, 0x4fcccc83U,
+ 0x5c343468U, 0xf4a5a551U, 0x34e5e5d1U, 0x08f1f1f9U, 0x937171e2U,
+ 0x73d8d8abU, 0x53313162U, 0x3f15152aU, 0x0c040408U, 0x52c7c795U,
+ 0x65232346U, 0x5ec3c39dU, 0x28181830U, 0xa1969637U, 0x0f05050aU,
+ 0xb59a9a2fU, 0x0907070eU, 0x36121224U, 0x9b80801bU, 0x3de2e2dfU,
+ 0x26ebebcdU, 0x6927274eU, 0xcdb2b27fU, 0x9f7575eaU, 0x1b090912U,
+ 0x9e83831dU, 0x742c2c58U, 0x2e1a1a34U, 0x2d1b1b36U, 0xb26e6edcU,
+ 0xee5a5ab4U, 0xfba0a05bU, 0xf65252a4U, 0x4d3b3b76U, 0x61d6d6b7U,
+ 0xceb3b37dU, 0x7b292952U, 0x3ee3e3ddU, 0x712f2f5eU, 0x97848413U,
+ 0xf55353a6U, 0x68d1d1b9U, 0x00000000U, 0x2cededc1U, 0x60202040U,
+ 0x1ffcfce3U, 0xc8b1b179U, 0xed5b5bb6U, 0xbe6a6ad4U, 0x46cbcb8dU,
+ 0xd9bebe67U, 0x4b393972U, 0xde4a4a94U, 0xd44c4c98U, 0xe85858b0U,
+ 0x4acfcf85U, 0x6bd0d0bbU, 0x2aefefc5U, 0xe5aaaa4fU, 0x16fbfbedU,
+ 0xc5434386U, 0xd74d4d9aU, 0x55333366U, 0x94858511U, 0xcf45458aU,
+ 0x10f9f9e9U, 0x06020204U, 0x817f7ffeU, 0xf05050a0U, 0x443c3c78U,
+ 0xba9f9f25U, 0xe3a8a84bU, 0xf35151a2U, 0xfea3a35dU, 0xc0404080U,
+ 0x8a8f8f05U, 0xad92923fU, 0xbc9d9d21U, 0x48383870U, 0x04f5f5f1U,
+ 0xdfbcbc63U, 0xc1b6b677U, 0x75dadaafU, 0x63212142U, 0x30101020U,
+ 0x1affffe5U, 0x0ef3f3fdU, 0x6dd2d2bfU, 0x4ccdcd81U, 0x140c0c18U,
+ 0x35131326U, 0x2fececc3U, 0xe15f5fbeU, 0xa2979735U, 0xcc444488U,
+ 0x3917172eU, 0x57c4c493U, 0xf2a7a755U, 0x827e7efcU, 0x473d3d7aU,
+ 0xac6464c8U, 0xe75d5dbaU, 0x2b191932U, 0x957373e6U, 0xa06060c0U,
+ 0x98818119U, 0xd14f4f9eU, 0x7fdcdca3U, 0x66222244U, 0x7e2a2a54U,
+ 0xab90903bU, 0x8388880bU, 0xca46468cU, 0x29eeeec7U, 0xd3b8b86bU,
+ 0x3c141428U, 0x79dedea7U, 0xe25e5ebcU, 0x1d0b0b16U, 0x76dbdbadU,
+ 0x3be0e0dbU, 0x56323264U, 0x4e3a3a74U, 0x1e0a0a14U, 0xdb494992U,
+ 0x0a06060cU, 0x6c242448U, 0xe45c5cb8U, 0x5dc2c29fU, 0x6ed3d3bdU,
+ 0xefacac43U, 0xa66262c4U, 0xa8919139U, 0xa4959531U, 0x37e4e4d3U,
+ 0x8b7979f2U, 0x32e7e7d5U, 0x43c8c88bU, 0x5937376eU, 0xb76d6ddaU,
+ 0x8c8d8d01U, 0x64d5d5b1U, 0xd24e4e9cU, 0xe0a9a949U, 0xb46c6cd8U,
+ 0xfa5656acU, 0x07f4f4f3U, 0x25eaeacfU, 0xaf6565caU, 0x8e7a7af4U,
+ 0xe9aeae47U, 0x18080810U, 0xd5baba6fU, 0x887878f0U, 0x6f25254aU,
+ 0x722e2e5cU, 0x241c1c38U, 0xf1a6a657U, 0xc7b4b473U, 0x51c6c697U,
+ 0x23e8e8cbU, 0x7cdddda1U, 0x9c7474e8U, 0x211f1f3eU, 0xdd4b4b96U,
+ 0xdcbdbd61U, 0x868b8b0dU, 0x858a8a0fU, 0x907070e0U, 0x423e3e7cU,
+ 0xc4b5b571U, 0xaa6666ccU, 0xd8484890U, 0x05030306U, 0x01f6f6f7U,
+ 0x120e0e1cU, 0xa36161c2U, 0x5f35356aU, 0xf95757aeU, 0xd0b9b969U,
+ 0x91868617U, 0x58c1c199U, 0x271d1d3aU, 0xb99e9e27U, 0x38e1e1d9U,
+ 0x13f8f8ebU, 0xb398982bU, 0x33111122U, 0xbb6969d2U, 0x70d9d9a9U,
+ 0x898e8e07U, 0xa7949433U, 0xb69b9b2dU, 0x221e1e3cU, 0x92878715U,
+ 0x20e9e9c9U, 0x49cece87U, 0xff5555aaU, 0x78282850U, 0x7adfdfa5U,
+ 0x8f8c8c03U, 0xf8a1a159U, 0x80898909U, 0x170d0d1aU, 0xdabfbf65U,
+ 0x31e6e6d7U, 0xc6424284U, 0xb86868d0U, 0xc3414182U, 0xb0999929U,
+ 0x772d2d5aU, 0x110f0f1eU, 0xcbb0b07bU, 0xfc5454a8U, 0xd6bbbb6dU,
+ 0x3a16162cU
+ };
+
+ unsigned int
+ alpha,
+ key[4],
+ text[4];
+
+ /*
+ Encipher one block.
+ */
+ (void) memset(text,0,sizeof(text));
+ InitializeRoundKey(plaintext,aes_info->encipher_key,text);
+ for (i=1; i < aes_info->rounds; i++)
+ {
+ /*
+ Linear mixing step: cause diffusion of the bits over multiple rounds.
+ */
+ for (j=0; j < 4; j++)
+ key[j]=D[text[j] & 0xff] ^
+ RotateLeft(D[(text[map[1][j]] >> 8) & 0xff] ^
+ RotateLeft(D[(text[map[2][j]] >> 16) & 0xff] ^
+ RotateLeft(D[(text[map[3][j]] >> 24) & 0xff])));
+ AddRoundKey(key,aes_info->encipher_key+4*i,text);
+ }
+ for (i=0; i < 4; i++)
+ {
+ alpha=(text[i] & 0x000000ff) | ((text[map[1][i]]) & 0x0000ff00) |
+ ((text[map[2][i]]) & 0x00ff0000) | ((text[map[3][i]]) & 0xff000000);
+ key[i]=ByteSubTransform(alpha,SBox);
+ }
+ FinalizeRoundKey(key,aes_info->encipher_key+4*aes_info->rounds,ciphertext);
+ /*
+ Reset registers.
+ */
+ alpha=0;
+ (void) ResetMagickMemory(key,0,sizeof(key));
+ (void) ResetMagickMemory(text,0,sizeof(text));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a s s k e y D e c i p h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PasskeyDecipherImage() converts cipher pixels to plain pixels.
+%
+% The format of the PasskeyDecipherImage method is:
+%
+% MagickBooleanType PasskeyDecipherImage(Image *image,
+% const StringInfo *passkey,ExceptionInfo *exception)
+% MagickBooleanType DecipherImage(Image *image,const char *passphrase,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o passphrase: decipher cipher pixels with this passphrase.
+%
+% o passkey: decrypt cipher pixels with this passkey.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType DecipherImage(Image *image,
+ const char *passphrase,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ StringInfo
+ *passkey;
+
+ if (passphrase == (const char *) NULL)
+ return(MagickTrue);
+ passkey=StringToStringInfo(passphrase);
+ if (passkey == (StringInfo *) NULL)
+ return(MagickFalse);
+ status=PasskeyDecipherImage(image,passkey,exception);
+ passkey=DestroyStringInfo(passkey);
+ return(status);
+}
+
+MagickExport MagickBooleanType PasskeyDecipherImage(Image *image,
+ const StringInfo *passkey,ExceptionInfo *exception)
+{
+#define DecipherImageTag "Decipher/Image "
+
+ AESInfo
+ *aes_info;
+
+ const unsigned char
+ *digest;
+
+ IndexPacket
+ *indexes;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ QuantumInfo
+ *quantum_info;
+
+ QuantumType
+ quantum_type;
+
+ SignatureInfo
+ *signature_info;
+
+ register unsigned char
+ *p;
+
+ size_t
+ length;
+
+ StringInfo
+ *key,
+ *nonce;
+
+ unsigned char
+ input_block[AESBlocksize],
+ output_block[AESBlocksize],
+ *pixels;
+
+ CacheView
+ *image_view;
+
+ /*
+ Generate decipher key and nonce.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (passkey == (const StringInfo *) NULL)
+ return(MagickTrue);
+ quantum_info=AcquireQuantumInfo((const ImageInfo *) NULL,image);
+ if (quantum_info == (QuantumInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ aes_info=AcquireAESInfo();
+ key=CloneStringInfo(passkey);
+ if (key == (StringInfo *) NULL)
+ {
+ aes_info=DestroyAESInfo(aes_info);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ nonce=SplitStringInfo(key,GetStringInfoLength(key)/2);
+ if (nonce == (StringInfo *) NULL)
+ {
+ key=DestroyStringInfo(key);
+ aes_info=DestroyAESInfo(aes_info);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ SetAESKey(aes_info,key);
+ key=DestroyStringInfo(key);
+ signature_info=AcquireSignatureInfo();
+ UpdateSignature(signature_info,nonce);
+ SetStringInfoLength(nonce,sizeof(quantum_info->extent));
+ SetStringInfoDatum(nonce,(const unsigned char *) &quantum_info->extent);
+ UpdateSignature(signature_info,nonce);
+ FinalizeSignature(signature_info);
+ (void) ResetMagickMemory(input_block,0,sizeof(input_block));
+ digest=GetStringInfoDatum(GetSignatureDigest(signature_info));
+ (void) CopyMagickMemory(input_block,digest,MagickMin(AESBlocksize,
+ GetSignatureDigestsize(signature_info))*sizeof(*input_block));
+ nonce=DestroyStringInfo(nonce);
+ signature_info=DestroySignatureInfo(signature_info);
+ /*
+ Convert cipher pixels to plain pixels.
+ */
+ quantum_type=GetQuantumType(image,exception);
+ pixels=GetQuantumPixels(quantum_info);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ length=ExportQuantumPixels(image,image_view,quantum_info,quantum_type,
+ pixels,exception);
+ p=pixels;
+ for (x=0; x < (long) length; x++)
+ {
+ (void) CopyMagickMemory(output_block,input_block,AESBlocksize*
+ sizeof(*output_block));
+ EncipherAESBlock(aes_info,output_block,output_block);
+ (void) CopyMagickMemory(input_block,input_block+1,(AESBlocksize-1)*
+ sizeof(*input_block));
+ input_block[AESBlocksize-1]=(*p);
+ *p++^=(*output_block);
+ }
+ (void) ImportQuantumPixels(image,image_view,quantum_info,quantum_type,
+ pixels,exception);
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,DecipherImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ (void) DeleteImageProperty(image,"cipher:type");
+ (void) DeleteImageProperty(image,"cipher:mode");
+ (void) DeleteImageProperty(image,"cipher:nonce");
+ image->taint=MagickFalse;
+ /*
+ Free resources.
+ */
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ aes_info=DestroyAESInfo(aes_info);
+ (void) ResetMagickMemory(input_block,0,sizeof(input_block));
+ (void) ResetMagickMemory(output_block,0,sizeof(output_block));
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a s s k e y E n c i p h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PasskeyEncipherImage() converts pixels to cipher-pixels.
+%
+% The format of the PasskeyEncipherImage method is:
+%
+% MagickBooleanType PasskeyEncipherImage(Image *image,
+% const StringInfo *passkey,ExceptionInfo *exception)
+% MagickBooleanType EncipherImage(Image *image,const char *passphrase,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o passphrase: encipher pixels with this passphrase.
+%
+% o passkey: decrypt cipher pixels with this passkey.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType EncipherImage(Image *image,
+ const char *passphrase,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ StringInfo
+ *passkey;
+
+ if (passphrase == (const char *) NULL)
+ return(MagickTrue);
+ passkey=StringToStringInfo(passphrase);
+ if (passkey == (StringInfo *) NULL)
+ return(MagickFalse);
+ status=PasskeyEncipherImage(image,passkey,exception);
+ passkey=DestroyStringInfo(passkey);
+ return(status);
+}
+
+MagickExport MagickBooleanType PasskeyEncipherImage(Image *image,
+ const StringInfo *passkey,ExceptionInfo *exception)
+{
+#define EncipherImageTag "Encipher/Image "
+
+ AESInfo
+ *aes_info;
+
+ char
+ *signature;
+
+ const unsigned char
+ *digest;
+
+ IndexPacket
+ *indexes;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ QuantumInfo
+ *quantum_info;
+
+ QuantumType
+ quantum_type;
+
+ register unsigned char
+ *p;
+
+ SignatureInfo
+ *signature_info;
+
+ size_t
+ length;
+
+ StringInfo
+ *key,
+ *nonce;
+
+ unsigned char
+ input_block[AESBlocksize],
+ output_block[AESBlocksize],
+ *pixels;
+
+ CacheView
+ *image_view;
+
+ /*
+ Generate encipher key and nonce.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (passkey == (const StringInfo *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ quantum_info=AcquireQuantumInfo((const ImageInfo *) NULL,image);
+ if (quantum_info == (QuantumInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ aes_info=AcquireAESInfo();
+ key=CloneStringInfo(passkey);
+ if (key == (StringInfo *) NULL)
+ {
+ aes_info=DestroyAESInfo(aes_info);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ nonce=SplitStringInfo(key,GetStringInfoLength(key)/2);
+ if (nonce == (StringInfo *) NULL)
+ {
+ key=DestroyStringInfo(key);
+ aes_info=DestroyAESInfo(aes_info);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ SetAESKey(aes_info,key);
+ key=DestroyStringInfo(key);
+ signature_info=AcquireSignatureInfo();
+ UpdateSignature(signature_info,nonce);
+ SetStringInfoLength(nonce,sizeof(quantum_info->extent));
+ SetStringInfoDatum(nonce,(const unsigned char *) &quantum_info->extent);
+ UpdateSignature(signature_info,nonce);
+ nonce=DestroyStringInfo(nonce);
+ FinalizeSignature(signature_info);
+ (void) ResetMagickMemory(input_block,0,sizeof(input_block));
+ digest=GetStringInfoDatum(GetSignatureDigest(signature_info));
+ (void) CopyMagickMemory(input_block,digest,MagickMin(AESBlocksize,
+ GetSignatureDigestsize(signature_info))*sizeof(*input_block));
+ signature=StringInfoToHexString(GetSignatureDigest(signature_info));
+ (void) SetImageProperty(image,"cipher:type","AES");
+ (void) SetImageProperty(image,"cipher:mode","CFB");
+ (void) SetImageProperty(image,"cipher:nonce",signature);
+ signature=DestroyString(signature);
+ signature_info=DestroySignatureInfo(signature_info);
+ /*
+ Convert plain pixels to cipher pixels.
+ */
+ quantum_type=GetQuantumType(image,exception);
+ pixels=GetQuantumPixels(quantum_info);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ length=ExportQuantumPixels(image,image_view,quantum_info,quantum_type,
+ pixels,exception);
+ p=pixels;
+ for (x=0; x < (long) length; x++)
+ {
+ (void) CopyMagickMemory(output_block,input_block,AESBlocksize*
+ sizeof(*output_block));
+ EncipherAESBlock(aes_info,output_block,output_block);
+ *p^=(*output_block);
+ (void) CopyMagickMemory(input_block,input_block+1,(AESBlocksize-1)*
+ sizeof(*input_block));
+ input_block[AESBlocksize-1]=(*p++);
+ }
+ (void) ImportQuantumPixels(image,image_view,quantum_info,quantum_type,
+ pixels,exception);
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,EncipherImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ image->taint=MagickFalse;
+ /*
+ Free resources.
+ */
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ aes_info=DestroyAESInfo(aes_info);
+ (void) ResetMagickMemory(input_block,0,sizeof(input_block));
+ (void) ResetMagickMemory(output_block,0,sizeof(output_block));
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t A E S K e y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetAESKey() sets the key for the AES cipher. The key length is specified
+% in bits. Valid values are 128, 192, or 256 requiring a key buffer length
+% in bytes of 16, 24, and 32 respectively.
+%
+% The format of the SetAESKey method is:
+%
+% SetAESKey(AESInfo *aes_info,const StringInfo *key)
+%
+% A description of each parameter follows:
+%
+% o aes_info: the cipher context.
+%
+% o key: the key.
+%
+*/
+
+static inline void InverseAddRoundKey(const unsigned int *alpha,
+ unsigned int *beta)
+{
+ register unsigned int
+ i,
+ j;
+
+ for (i=0; i < 4; i++)
+ {
+ beta[i]=0;
+ for (j=0; j < 4; j++)
+ beta[i]|=(ByteMultiply(0xe,(alpha[i] >> (8*j)) & 0xff) ^
+ ByteMultiply(0xb,(alpha[i] >> (8*((j+1) % 4))) & 0xff) ^
+ ByteMultiply(0xd,(alpha[i] >> (8*((j+2) % 4))) & 0xff) ^
+ ByteMultiply(0x9,(alpha[i] >> (8*((j+3) % 4))) & 0xff)) << (8*j);
+ }
+}
+
+static inline unsigned int XTime(unsigned char alpha)
+{
+ unsigned char
+ beta;
+
+ beta=(unsigned char) ((alpha & 0x80) != 0 ? 0x1b : 0);
+ alpha<<=1;
+ alpha^=beta;
+ return(alpha);
+}
+
+static inline unsigned int RotateRight(const unsigned int x)
+{
+ return((x >> 8) | ((x & 0xff) << 24));
+}
+
+static void SetAESKey(AESInfo *aes_info,const StringInfo *key)
+{
+ long
+ bytes,
+ n;
+
+ register long
+ i;
+
+ unsigned char
+ *datum;
+
+ unsigned int
+ alpha,
+ beta;
+
+ /*
+ Determine the number of rounds based on the number of bits in key.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(aes_info != (AESInfo *) NULL);
+ assert(aes_info->signature == MagickSignature);
+ assert(key != (StringInfo *) NULL);
+ n=4;
+ aes_info->rounds=10;
+ if ((8*GetStringInfoLength(key)) >= 256)
+ {
+ n=8;
+ aes_info->rounds=14;
+ }
+ else
+ if ((8*GetStringInfoLength(key)) >= 192)
+ {
+ n=6;
+ aes_info->rounds=12;
+ }
+ /*
+ Generate crypt key.
+ */
+ datum=GetStringInfoDatum(aes_info->key);
+ (void) ResetMagickMemory(datum,0,GetStringInfoLength(aes_info->key));
+ (void) CopyMagickMemory(datum,GetStringInfoDatum(key),MagickMin(
+ GetStringInfoLength(key),GetStringInfoLength(aes_info->key)));
+ for (i=0; i < n; i++)
+ aes_info->encipher_key[i]=datum[4*i] | (datum[4*i+1] << 8) |
+ (datum[4*i+2] << 16) | (datum[4*i+3] << 24);
+ beta=1;
+ bytes=(AESBlocksize/4)*(aes_info->rounds+1);
+ for (i=n; i < bytes; i++)
+ {
+ alpha=aes_info->encipher_key[i-1];
+ if ((i % n) == 0)
+ {
+ alpha=ByteSubTransform(RotateRight(alpha),SBox) ^ beta;
+ beta=XTime((unsigned char) (beta & 0xff));
+ }
+ else
+ if ((n > 6) && ((i % n) == 4))
+ alpha=ByteSubTransform(alpha,SBox);
+ aes_info->encipher_key[i]=aes_info->encipher_key[i-n] ^ alpha;
+ }
+ /*
+ Generate deciper key (in reverse order).
+ */
+ for (i=0; i < 4; i++)
+ {
+ aes_info->decipher_key[i]=aes_info->encipher_key[i];
+ aes_info->decipher_key[bytes-4+i]=aes_info->encipher_key[bytes-4+i];
+ }
+ for (i=4; i < (bytes-4); i+=4)
+ InverseAddRoundKey(aes_info->encipher_key+i,aes_info->decipher_key+i);
+ /*
+ Reset registers.
+ */
+ datum=GetStringInfoDatum(aes_info->key);
+ (void) ResetMagickMemory(datum,0,GetStringInfoLength(aes_info->key));
+ alpha=0;
+ beta=0;
+}
+#else
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a s s k e y D e c i p h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PasskeyDecipherImage() converts cipher pixels to plain pixels.
+%
+% The format of the PasskeyDecipherImage method is:
+%
+% MagickBooleanType PasskeyDecipherImage(Image *image,
+% const StringInfo *passkey,ExceptionInfo *exception)
+% MagickBooleanType DecipherImage(Image *image,const char *passphrase,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o passphrase: decipher cipher pixels with this passphrase.
+%
+% o passkey: decrypt cipher pixels with this passkey.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType DecipherImage(Image *image,
+ const char *passphrase,ExceptionInfo *exception)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) passphrase;
+ ThrowBinaryException(ImageError,"CipherSupportNotEnabled",image->filename);
+}
+
+MagickExport MagickBooleanType PasskeyDecipherImage(Image *image,
+ const StringInfo *passkey,ExceptionInfo *exception)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) passkey;
+ ThrowBinaryException(ImageError,"CipherSupportNotEnabled",image->filename);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a s s k e y E n c i p h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PasskeyEncipherImage() converts pixels to cipher-pixels.
+%
+% The format of the PasskeyEncipherImage method is:
+%
+% MagickBooleanType PasskeyEncipherImage(Image *image,
+% const StringInfo *passkey,ExceptionInfo *exception)
+% MagickBooleanType EncipherImage(Image *image,const char *passphrase,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o passphrase: decipher cipher pixels with this passphrase.
+%
+% o passkey: decrypt cipher pixels with this passkey.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType EncipherImage(Image *image,
+ const char *passphrase,ExceptionInfo *exception)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) passphrase;
+ ThrowBinaryException(ImageError,"CipherSupportNotEnabled",image->filename);
+}
+
+MagickExport MagickBooleanType PasskeyEncipherImage(Image *image,
+ const StringInfo *passkey,ExceptionInfo *exception)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) passkey;
+ ThrowBinaryException(ImageError,"CipherSupportNotEnabled",image->filename);
+}
+#endif
diff --git a/magick/cipher.h b/magick/cipher.h
new file mode 100644
index 0000000..1a9152b
--- /dev/null
+++ b/magick/cipher.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore cipher methods.
+*/
+#ifndef _MAGICKCORE_CIPHER_H
+#define _MAGICKCORE_CIPHER_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ DecipherImage(Image *,const char *,ExceptionInfo *),
+ EncipherImage(Image *,const char *,ExceptionInfo *),
+ PasskeyDecipherImage(Image *,const StringInfo *,ExceptionInfo *),
+ PasskeyEncipherImage(Image *,const StringInfo *,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/client.c b/magick/client.c
new file mode 100644
index 0000000..50b47cd
--- /dev/null
+++ b/magick/client.c
@@ -0,0 +1,162 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC L IIIII EEEEE N N TTTTT %
+% C L I E NN N T %
+% C L I EEE N N N T %
+% C L I E N NN T %
+% CCCC LLLLL IIIII EEEEE N N T %
+% %
+% %
+% MagickCore Client Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/client.h"
+#include "magick/string_.h"
+
+/*
+ Static declaractions.
+*/
+static char
+ client_name[MaxTextExtent] = "ImageMagick",
+ client_path[MaxTextExtent] = "";
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C l i e n t N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetClientName returns the current client name.
+%
+% The format of the GetClientName method is:
+%
+% const char *GetClientName(void)
+%
+*/
+MagickExport const char *GetClientName(void)
+{
+ return(client_name);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C l i e n t P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetClientPath returns the current client name.
+%
+% The format of the GetClientPath method is:
+%
+% const char *GetClientPath(void)
+%
+*/
+MagickExport const char *GetClientPath(void)
+{
+ return(client_path);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t C l i e n t N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetClientName sets the client name and returns it.
+%
+% The format of the SetClientName method is:
+%
+% const char *SetClientName(const char *name)
+%
+% A description of each parameter follows:
+%
+% o client_name: SetClientName() returns the current client name.
+%
+% o name: Specifies the new client name.
+%
+*/
+MagickExport const char *SetClientName(const char *name)
+{
+ if ((name != (char *) NULL) && (*name != '\0'))
+ (void) CopyMagickString(client_name,name,MaxTextExtent);
+ return(client_name);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t C l i e n t P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetClientPath() sets the client path if the name is specified. Otherwise
+% the current client path is returned. A zero-length string is returned if
+% the client path has never been set.
+%
+% The format of the SetClientPath method is:
+%
+% const char *SetClientPath(const char *path)
+%
+% A description of each parameter follows:
+%
+% o client_path: Method SetClientPath returns the current client path.
+%
+% o path: Specifies the new client path.
+%
+%
+*/
+MagickExport const char *SetClientPath(const char *path)
+{
+ if ((path != (char *) NULL) && (*path != '\0'))
+ (void) CopyMagickString(client_path,path,MaxTextExtent);
+ return(client_path);
+}
diff --git a/magick/client.h b/magick/client.h
new file mode 100644
index 0000000..330e1f4
--- /dev/null
+++ b/magick/client.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore client methods.
+*/
+#ifndef _MAGICKCORE_CLIENT_H
+#define _MAGICKCORE_CLIENT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport const char
+ *GetClientPath(void),
+ *GetClientName(void),
+ *SetClientName(const char *),
+ *SetClientPath(const char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/coder.c b/magick/coder.c
new file mode 100644
index 0000000..70f77f9
--- /dev/null
+++ b/magick/coder.c
@@ -0,0 +1,852 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO DDDD EEEEE RRRR %
+% C O O D D E R R %
+% C O O D D EEE RRRR %
+% C O O D D E R R %
+% CCCC OOO DDDD EEEEE R R %
+% %
+% %
+% MagickCore Image Coder Methods %
+% %
+% Software Design %
+% John Cristy %
+% May 2001 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/coder.h"
+#include "magick/configure.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/splay-tree.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define MagickCoderFilename "coder.xml"
+
+/*
+ Declare coder map.
+*/
+static const char
+ *CoderMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<codermap>"
+ " <coder magick=\"8BIM\" name=\"META\" />"
+ " <coder magick=\"8BIMTEXT\" name=\"META\" />"
+ " <coder magick=\"8BIMWTEXT\" name=\"META\" />"
+ " <coder magick=\"A\" name=\"RAW\" />"
+ " <coder magick=\"AI\" name=\"PDF\" />"
+ " <coder magick=\"AFM\" name=\"TTF\" />"
+ " <coder magick=\"APP1JPEG\" name=\"META\" />"
+ " <coder magick=\"APP1\" name=\"META\" />"
+ " <coder magick=\"ARW\" name=\"DNG\" />"
+ " <coder magick=\"BIE\" name=\"JBIG\" />"
+ " <coder magick=\"BMP2\" name=\"BMP\" />"
+ " <coder magick=\"BMP3\" name=\"BMP\" />"
+ " <coder magick=\"B\" name=\"GRAY\" />"
+ " <coder magick=\"BRF\" name=\"BRAILLE\" />"
+ " <coder magick=\"CMYKA\" name=\"CMYK\" />"
+ " <coder magick=\"C\" name=\"GRAY\" />"
+ " <coder magick=\"CR2\" name=\"DNG\" />"
+ " <coder magick=\"CRW\" name=\"DNG\" />"
+ " <coder magick=\"CUR\" name=\"ICON\" />"
+ " <coder magick=\"DCR\" name=\"DNG\" />"
+ " <coder magick=\"DCX\" name=\"PCX\" />"
+ " <coder magick=\"DFONT\" name=\"TTF\" />"
+ " <coder magick=\"EMF\" name=\"EMF\" />"
+ " <coder magick=\"EPDF\" name=\"PDF\" />"
+ " <coder magick=\"EPI\" name=\"PS\" />"
+ " <coder magick=\"EPS2\" name=\"PS2\" />"
+ " <coder magick=\"EPS3\" name=\"PS3\" />"
+ " <coder magick=\"EPSF\" name=\"PS\" />"
+ " <coder magick=\"EPSI\" name=\"PS\" />"
+ " <coder magick=\"EPS\" name=\"PS\" />"
+ " <coder magick=\"EPT2\" name=\"EPT\" />"
+ " <coder magick=\"EPT3\" name=\"EPT\" />"
+ " <coder magick=\"EXIF\" name=\"META\" />"
+ " <coder magick=\"FILE\" name=\"URL\" />"
+ " <coder magick=\"FRACTAL\" name=\"PLASMA\" />"
+ " <coder magick=\"FTP\" name=\"URL\" />"
+ " <coder magick=\"FTS\" name=\"FITS\" />"
+ " <coder magick=\"G3\" name=\"FAX\" />"
+ " <coder magick=\"GIF87\" name=\"GIF\" />"
+ " <coder magick=\"G\" name=\"GRAY\" />"
+ " <coder magick=\"GRANITE\" name=\"MAGICK\" />"
+ " <coder magick=\"H\" name=\"MAGICK\" />"
+ " <coder magick=\"HTM\" name=\"HTML\" />"
+ " <coder magick=\"HTTP\" name=\"URL\" />"
+ " <coder magick=\"ICB\" name=\"TGA\" />"
+ " <coder magick=\"ICC\" name=\"META\" />"
+ " <coder magick=\"ICM\" name=\"META\" />"
+ " <coder magick=\"ICO\" name=\"ICON\" />"
+ " <coder magick=\"IMPLICIT\" name=\"***\" />"
+ " <coder magick=\"IPTC\" name=\"META\" />"
+ " <coder magick=\"IPTCTEXT\" name=\"META\" />"
+ " <coder magick=\"IPTCWTEXT\" name=\"META\" />"
+ " <coder magick=\"ISOBRL\" name=\"BRAILLE\" />"
+ " <coder magick=\"JBG\" name=\"JBIG\" />"
+ " <coder magick=\"JNG\" name=\"PNG\" />"
+ " <coder magick=\"JPC\" name=\"JP2\" />"
+ " <coder magick=\"JPG\" name=\"JPEG\" />"
+ " <coder magick=\"JPX\" name=\"JP2\" />"
+ " <coder magick=\"K\" name=\"GRAY\" />"
+ " <coder magick=\"LOGO\" name=\"MAGICK\" />"
+ " <coder magick=\"M2V\" name=\"MPEG\" />"
+ " <coder magick=\"M4V\" name=\"MPEG\" />"
+ " <coder magick=\"M\" name=\"GRAY\" />"
+ " <coder magick=\"MNG\" name=\"PNG\" />"
+ " <coder magick=\"MOV\" name=\"MPEG\" />"
+ " <coder magick=\"MPG\" name=\"MPEG\" />"
+ " <coder magick=\"MP4\" name=\"MPEG\" />"
+ " <coder magick=\"MPRI\" name=\"MPR\" />"
+ " <coder magick=\"MRW\" name=\"DNG\" />"
+ " <coder magick=\"MSVG\" name=\"SVG\" />"
+ " <coder magick=\"NEF\" name=\"DNG\" />"
+ " <coder magick=\"NETSCAPE\" name=\"MAGICK\" />"
+ " <coder magick=\"O\" name=\"GRAY\" />"
+ " <coder magick=\"ORF\" name=\"DNG\" />"
+ " <coder magick=\"OTF\" name=\"TTF\" />"
+ " <coder magick=\"P7\" name=\"PNM\" />"
+ " <coder magick=\"PAL\" name=\"UYVY\" />"
+ " <coder magick=\"PAM\" name=\"PNM\" />"
+ " <coder magick=\"PBM\" name=\"PNM\" />"
+ " <coder magick=\"PCDS\" name=\"PCD\" />"
+ " <coder magick=\"PCT\" name=\"PICT\" />"
+ " <coder magick=\"PDFA\" name=\"PDF\" />"
+ " <coder magick=\"PEF\" name=\"DNG\" />"
+ " <coder magick=\"PFA\" name=\"TTF\" />"
+ " <coder magick=\"PFB\" name=\"TTF\" />"
+ " <coder magick=\"PFM\" name=\"PNM\" />"
+ " <coder magick=\"PGM\" name=\"PNM\" />"
+ " <coder magick=\"PGX\" name=\"JP2\" />"
+ " <coder magick=\"PICON\" name=\"XPM\" />"
+ " <coder magick=\"PJPEG\" name=\"JPEG\" />"
+ " <coder magick=\"PM\" name=\"XPM\" />"
+ " <coder magick=\"PNG24\" name=\"PNG\" />"
+ " <coder magick=\"PNG32\" name=\"PNG\" />"
+ " <coder magick=\"PNG8\" name=\"PNG\" />"
+ " <coder magick=\"PPM\" name=\"PNM\" />"
+ " <coder magick=\"PTIF\" name=\"TIFF\" />"
+ " <coder magick=\"RADIAL-GRADIENT\" name=\"GRADIENT\" />"
+ " <coder magick=\"RAF\" name=\"DNG\" />"
+ " <coder magick=\"RAS\" name=\"SUN\" />"
+ " <coder magick=\"RGBA\" name=\"RGB\" />"
+ " <coder magick=\"RGBO\" name=\"RGB\" />"
+ " <coder magick=\"R\" name=\"GRAY\" />"
+ " <coder magick=\"ROSE\" name=\"MAGICK\" />"
+ " <coder magick=\"SHTML\" name=\"HTML\" />"
+ " <coder magick=\"SVGZ\" name=\"SVG\" />"
+ " <coder magick=\"TEXT\" name=\"TXT\" />"
+ " <coder magick=\"TIFF64\" name=\"TIFF\" />"
+ " <coder magick=\"TIF\" name=\"TIFF\" />"
+ " <coder magick=\"TTC\" name=\"TTF\" />"
+ " <coder magick=\"UBRL\" name=\"BRAILLE\" />"
+ " <coder magick=\"VDA\" name=\"TGA\" />"
+ " <coder magick=\"VST\" name=\"TGA\" />"
+ " <coder magick=\"WMFWIN32\" name=\"EMF\" />"
+ " <coder magick=\"WMV\" name=\"MPEG\" />"
+ " <coder magick=\"X3F\" name=\"DNG\" />"
+ " <coder magick=\"XTRNARRAY\" name=\"XTRN\" />"
+ " <coder magick=\"XTRNBLOB\" name=\"XTRN\" />"
+ " <coder magick=\"XTRNBSTR\" name=\"XTRN\" />"
+ " <coder magick=\"XTRNFILE\" name=\"XTRN\" />"
+ " <coder magick=\"XTRNIMAGE\" name=\"XTRN\" />"
+ " <coder magick=\"XTRNSTREAM\" name=\"XTRN\" />"
+ " <coder magick=\"XV\" name=\"VIFF\" />"
+ " <coder magick=\"Y\" name=\"GRAY\" />"
+ " <coder magick=\"YCbCrA\" name=\"YCbCr\" />"
+ "</codermap>";
+
+/*
+ Static declarations.
+*/
+static SemaphoreInfo
+ *coder_semaphore = (SemaphoreInfo *) NULL;
+
+static SplayTreeInfo
+ *coder_list = (SplayTreeInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_coder = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeCoderList(ExceptionInfo *),
+ LoadCoderLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C o d e r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyCoderList() deallocates memory associated with the font list.
+%
+% The format of the DestroyCoderList method is:
+%
+% DestroyCoderList(void)
+%
+*/
+MagickExport void DestroyCoderList(void)
+{
+ AcquireSemaphoreInfo(&coder_semaphore);
+ if (coder_list != (SplayTreeInfo *) NULL)
+ coder_list=DestroySplayTree(coder_list);
+ instantiate_coder=MagickFalse;
+ RelinquishSemaphoreInfo(coder_semaphore);
+ DestroySemaphoreInfo(&coder_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C o d e r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCoderInfo searches the coder list for the specified name and if found
+% returns attributes for that coder.
+%
+% The format of the GetCoderInfo method is:
+%
+% const CoderInfo *GetCoderInfo(const char *name,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the coder name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const CoderInfo *GetCoderInfo(const char *name,
+ ExceptionInfo *exception)
+{
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((coder_list == (SplayTreeInfo *) NULL) ||
+ (instantiate_coder == MagickFalse))
+ if (InitializeCoderList(exception) == MagickFalse)
+ return((const CoderInfo *) NULL);
+ if ((coder_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(coder_list) == 0))
+ return((const CoderInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ {
+ ResetSplayTreeIterator(coder_list);
+ return((const CoderInfo *) GetNextValueInSplayTree(coder_list));
+ }
+ return((const CoderInfo *) GetValueFromSplayTree(coder_list,name));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o d e r I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCoderInfoList() returns any coder_map that match the specified pattern.
+% The format of the GetCoderInfoList function is:
+%
+% const CoderInfo **GetCoderInfoList(const char *pattern,
+% unsigned long *number_coders,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_coders: This integer returns the number of coders in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static int CoderInfoCompare(const void *x,const void *y)
+{
+ const CoderInfo
+ **p,
+ **q;
+
+ p=(const CoderInfo **) x,
+ q=(const CoderInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+MagickExport const CoderInfo **GetCoderInfoList(const char *pattern,
+ unsigned long *number_coders,ExceptionInfo *exception)
+{
+ const CoderInfo
+ **coder_map;
+
+ register const CoderInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate coder list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_coders != (unsigned long *) NULL);
+ *number_coders=0;
+ p=GetCoderInfo("*",exception);
+ if (p == (const CoderInfo *) NULL)
+ return((const CoderInfo **) NULL);
+ coder_map=(const CoderInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(coder_list)+1UL,sizeof(*coder_map));
+ if (coder_map == (const CoderInfo **) NULL)
+ return((const CoderInfo **) NULL);
+ /*
+ Generate coder list.
+ */
+ AcquireSemaphoreInfo(&coder_semaphore);
+ ResetSplayTreeIterator(coder_list);
+ p=(const CoderInfo *) GetNextValueInSplayTree(coder_list);
+ for (i=0; p != (const CoderInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ coder_map[i++]=p;
+ p=(const CoderInfo *) GetNextValueInSplayTree(coder_list);
+ }
+ RelinquishSemaphoreInfo(coder_semaphore);
+ qsort((void *) coder_map,(size_t) i,sizeof(*coder_map),CoderInfoCompare);
+ coder_map[i]=(CoderInfo *) NULL;
+ *number_coders=(unsigned long) i;
+ return(coder_map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o d e r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCoderList() returns any coder_map that match the specified pattern.
+%
+% The format of the GetCoderList function is:
+%
+% char **GetCoderList(const char *pattern,unsigned long *number_coders,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_coders: This integer returns the number of coders in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static int CoderCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+MagickExport char **GetCoderList(const char *pattern,
+ unsigned long *number_coders,ExceptionInfo *exception)
+{
+ char
+ **coder_map;
+
+ register const CoderInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate coder list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_coders != (unsigned long *) NULL);
+ *number_coders=0;
+ p=GetCoderInfo("*",exception);
+ if (p == (const CoderInfo *) NULL)
+ return((char **) NULL);
+ coder_map=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(coder_list)+1UL,sizeof(*coder_map));
+ if (coder_map == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Generate coder list.
+ */
+ AcquireSemaphoreInfo(&coder_semaphore);
+ ResetSplayTreeIterator(coder_list);
+ p=(const CoderInfo *) GetNextValueInSplayTree(coder_list);
+ for (i=0; p != (const CoderInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ coder_map[i++]=ConstantString(p->name);
+ p=(const CoderInfo *) GetNextValueInSplayTree(coder_list);
+ }
+ RelinquishSemaphoreInfo(coder_semaphore);
+ qsort((void *) coder_map,(size_t) i,sizeof(*coder_map),CoderCompare);
+ coder_map[i]=(char *) NULL;
+ *number_coders=(unsigned long) i;
+ return(coder_map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e C o d e r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeCoderList() initializes the coder list.
+%
+% The format of the InitializeCoderList method is:
+%
+% MagickBooleanType InitializeCoderList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeCoderList(ExceptionInfo *exception)
+{
+ if ((coder_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_coder == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&coder_semaphore);
+ if ((coder_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_coder == MagickFalse))
+ {
+ (void) LoadCoderLists(MagickCoderFilename,exception);
+ instantiate_coder=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(coder_semaphore);
+ }
+ return(coder_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t C o d e r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListCoderInfo() lists the coder info to a file.
+%
+% The format of the ListCoderInfo coder is:
+%
+% MagickBooleanType ListCoderInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListCoderInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const char
+ *path;
+
+ const CoderInfo
+ **coder_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_coders;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ coder_info=GetCoderInfoList("*",&number_coders,exception);
+ if (coder_info == (const CoderInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_coders; i++)
+ {
+ if (coder_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,coder_info[i]->path) != 0))
+ {
+ if (coder_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",coder_info[i]->path);
+ (void) fprintf(file,"Magick Coder\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=coder_info[i]->path;
+ (void) fprintf(file,"%s",coder_info[i]->magick);
+ for (j=(long) strlen(coder_info[i]->magick); j <= 11; j++)
+ (void) fprintf(file," ");
+ if (coder_info[i]->name != (char *) NULL)
+ (void) fprintf(file,"%s",coder_info[i]->name);
+ (void) fprintf(file,"\n");
+ }
+ coder_info=(const CoderInfo **) RelinquishMagickMemory((void *) coder_info);
+ (void) fflush(file);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d C o d e r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadCoderList() loads the coder configuration file which provides a
+% mapping between coder attributes and a coder name.
+%
+% The format of the LoadCoderList coder is:
+%
+% MagickBooleanType LoadCoderList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The coder list in XML format.
+%
+% o filename: The coder list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void *DestroyCoderNode(void *coder_info)
+{
+ register CoderInfo
+ *p;
+
+ p=(CoderInfo *) coder_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->magick != (char *) NULL)
+ p->magick=DestroyString(p->magick);
+ return(RelinquishMagickMemory(p));
+}
+
+static MagickBooleanType LoadCoderList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ CoderInfo
+ *coder_info;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Load the coder map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading coder configuration file \"%s\" ...",filename);
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (coder_list == (SplayTreeInfo *) NULL)
+ {
+ coder_list=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
+ DestroyCoderNode);
+ if (coder_list == (SplayTreeInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ coder_info=(CoderInfo *) NULL;
+ token=AcquireString(xml);
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeNodeNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadCoderList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<coder") == 0)
+ {
+ /*
+ Coder element.
+ */
+ coder_info=(CoderInfo *) AcquireMagickMemory(sizeof(*coder_info));
+ if (coder_info == (CoderInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(coder_info,0,sizeof(*coder_info));
+ coder_info->path=ConstantString(filename);
+ coder_info->signature=MagickSignature;
+ continue;
+ }
+ if (coder_info == (CoderInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AddValueToSplayTree(coder_list,ConstantString(
+ coder_info->magick),coder_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ coder_info->magick);
+ coder_info=(CoderInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'M':
+ case 'm':
+ {
+ if (LocaleCompare((char *) keyword,"magick") == 0)
+ {
+ coder_info->magick=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ coder_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ coder_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d C o d e r L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadCoderLists() loads one or more coder configuration file which
+% provides a mapping between coder attributes and a coder name.
+%
+% The format of the LoadCoderLists coder is:
+%
+% MagickBooleanType LoadCoderLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadCoderLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadCoderList(CoderMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadCoderList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((coder_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(coder_list) == 0))
+ status|=LoadCoderList(CoderMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/coder.h b/magick/coder.h
new file mode 100644
index 0000000..f4b81bd
--- /dev/null
+++ b/magick/coder.h
@@ -0,0 +1,60 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image coder methods.
+*/
+#ifndef _MAGICKCORE_CODER_H
+#define _MAGICKCORE_CODER_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _CoderInfo
+{
+ char
+ *path,
+ *magick,
+ *name;
+
+ MagickBooleanType
+ stealth;
+
+ struct _CoderInfo
+ *previous,
+ *next; /* deprecated, use GetCoderInfoList() */
+
+ unsigned long
+ signature;
+} CoderInfo;
+
+extern MagickExport char
+ **GetCoderList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const CoderInfo
+ *GetCoderInfo(const char *,ExceptionInfo *),
+ **GetCoderInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ ListCoderInfo(FILE *,ExceptionInfo *);
+
+MagickExport void
+ DestroyCoderList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/color-private.h b/magick/color-private.h
new file mode 100644
index 0000000..d7bac5d
--- /dev/null
+++ b/magick/color-private.h
@@ -0,0 +1,155 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image color methods.
+*/
+#ifndef _MAGICKCORE_COLOR_PRIVATE_H
+#define _MAGICKCORE_COLOR_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/image.h>
+#include <magick/color.h>
+#include <magick/exception-private.h>
+
+static inline MagickBooleanType IsColorEqual(const PixelPacket *p,
+ const PixelPacket *q)
+{
+ if ((p->red == q->red) && (p->green == q->green) && (p->blue == q->blue))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+static inline IndexPacket ConstrainColormapIndex(Image *image,
+ const unsigned long index)
+{
+ if (index < image->colors)
+ return((IndexPacket) index);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ CorruptImageError,"InvalidColormapIndex","`%s'",image->filename);
+ return((IndexPacket) 0);
+}
+
+static inline MagickBooleanType IsGray(const PixelPacket *pixel)
+{
+ if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+static inline MagickBooleanType IsMagickColorEqual(const MagickPixelPacket *p,
+ const MagickPixelPacket *q)
+{
+ if ((p->matte != MagickFalse) && (q->matte == MagickFalse) &&
+ (p->opacity != OpaqueOpacity))
+ return(MagickFalse);
+ if ((q->matte != MagickFalse) && (p->matte == MagickFalse) &&
+ (q->opacity != OpaqueOpacity))
+ return(MagickFalse);
+ if ((p->matte != MagickFalse) && (q->matte != MagickFalse))
+ {
+ if (p->opacity != q->opacity)
+ return(MagickFalse);
+ if (p->opacity == TransparentOpacity)
+ return(MagickTrue);
+ }
+ if (p->red != q->red)
+ return(MagickFalse);
+ if (p->green != q->green)
+ return(MagickFalse);
+ if (p->blue != q->blue)
+ return(MagickFalse);
+ if ((p->colorspace == CMYKColorspace) && (p->index != q->index))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static inline MagickBooleanType IsMagickGray(const MagickPixelPacket *pixel)
+{
+ if (pixel->colorspace != RGBColorspace)
+ return(MagickFalse);
+ if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+static inline MagickRealType MagickPixelIntensity(
+ const MagickPixelPacket *pixel)
+{
+ MagickRealType
+ intensity;
+
+ intensity=0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue;
+ return(intensity);
+}
+
+static inline Quantum MagickPixelIntensityToQuantum(
+ const MagickPixelPacket *pixel)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue+0.5));
+#else
+ return((Quantum) (0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue));
+#endif
+}
+
+static inline MagickRealType MagickPixelLuminance(
+ const MagickPixelPacket *pixel)
+{
+ MagickRealType
+ luminance;
+
+ luminance=0.21267*pixel->red+0.71516*pixel->green+0.07217*pixel->blue;
+ return(luminance);
+}
+
+static inline MagickRealType PixelIntensity(const PixelPacket *pixel)
+{
+ MagickRealType
+ intensity;
+
+ if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return((MagickRealType) pixel->red);
+ intensity=(MagickRealType) (0.299*pixel->red+0.587*pixel->green+0.114*
+ pixel->blue);
+ return(intensity);
+}
+
+static inline Quantum PixelIntensityToQuantum(const PixelPacket *pixel)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return(pixel->red);
+#if (MAGICKCORE_QUANTUM_DEPTH <= 16)
+ return((Quantum) ((306U*(unsigned int) pixel->red+
+ 601U*(unsigned int) pixel->green+117U*(unsigned int) pixel->blue) >> 10U));
+#else
+ return((Quantum) (0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue+0.5));
+#endif
+#else
+ if ((fabs(pixel->red-pixel->green) <= MagickEpsilon) &&
+ (fabs(pixel->green-pixel->blue) <= MagickEpsilon))
+ return((Quantum) pixel->red);
+ return((Quantum) (0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue));
+#endif
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/color.c b/magick/color.c
new file mode 100644
index 0000000..e309099
--- /dev/null
+++ b/magick/color.c
@@ -0,0 +1,3528 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% CCCC OOO L OOO RRRR %
+% C O O L O O R R %
+% C O O L O O RRRR %
+% C O O L O O R R %
+% CCCC OOO LLLLL OOO R R %
+% %
+% %
+% MagickCore Color Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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/blob.h"
+#include "magick/cache-view.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/color-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-private.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define ColorFilename "colors.xml"
+#define MaxTreeDepth 8
+#define NodesInAList 1536
+
+/*
+ Declare color map.
+*/
+static const char
+ *ColorMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<colormap>"
+ " <color name=\"none\" color=\"rgba(0,0,0,0)\" compliance=\"SVG\" />"
+ " <color name=\"black\" color=\"rgb(0,0,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"red\" color=\"rgb(255,0,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"magenta\" color=\"rgb(255,0,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"green\" color=\"rgb(0,128,0)\" compliance=\"SVG\" />"
+ " <color name=\"cyan\" color=\"rgb(0,255,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"blue\" color=\"rgb(0,0,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"yellow\" color=\"rgb(255,255,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"white\" color=\"rgb(255,255,255)\" compliance=\"SVG, X11\" />"
+ " <color name=\"AliceBlue\" color=\"rgb(240,248,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"AntiqueWhite\" color=\"rgb(250,235,215)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"aqua\" color=\"rgb(0,255,255)\" compliance=\"SVG\" />"
+ " <color name=\"aquamarine\" color=\"rgb(127,255,212)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"azure\" color=\"rgb(240,255,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"beige\" color=\"rgb(245,245,220)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"bisque\" color=\"rgb(255,228,196)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"BlanchedAlmond\" color=\"rgb(255,235,205)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"BlueViolet\" color=\"rgb(138,43,226)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"brown\" color=\"rgb(165,42,42)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"burlywood\" color=\"rgb(222,184,135)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"CadetBlue\" color=\"rgb(95,158,160)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"chartreuse\" color=\"rgb(127,255,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"chocolate\" color=\"rgb(210,105,30)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"coral\" color=\"rgb(255,127,80)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"CornflowerBlue\" color=\"rgb(100,149,237)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"cornsilk\" color=\"rgb(255,248,220)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"crimson\" color=\"rgb(220,20,60)\" compliance=\"SVG\" />"
+ " <color name=\"DarkBlue\" color=\"rgb(0,0,139)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkCyan\" color=\"rgb(0,139,139)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkGoldenrod\" color=\"rgb(184,134,11)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkGray\" color=\"rgb(169,169,169)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkGreen\" color=\"rgb(0,100,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkGrey\" color=\"rgb(169,169,169)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkKhaki\" color=\"rgb(189,183,107)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkMagenta\" color=\"rgb(139,0,139)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkOliveGreen\" color=\"rgb(85,107,47)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkOrange\" color=\"rgb(255,140,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkOrchid\" color=\"rgb(153,50,204)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkRed\" color=\"rgb(139,0,0)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkSalmon\" color=\"rgb(233,150,122)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkSeaGreen\" color=\"rgb(143,188,143)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkSlateBlue\" color=\"rgb(72,61,139)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkSlateGray\" color=\"rgb(47,79,79)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkSlateGrey\" color=\"rgb(47,79,79)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DarkTurquoise\" color=\"rgb(0,206,209)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DarkViolet\" color=\"rgb(148,0,211)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DeepPink\" color=\"rgb(255,20,147)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DeepSkyBlue\" color=\"rgb(0,191,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DimGray\" color=\"rgb(105,105,105)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"DimGrey\" color=\"rgb(105,105,105)\" compliance=\"SVG, X11\" />"
+ " <color name=\"DodgerBlue\" color=\"rgb(30,144,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"firebrick\" color=\"rgb(178,34,34)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"FloralWhite\" color=\"rgb(255,250,240)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"ForestGreen\" color=\"rgb(34,139,34)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"fractal\" color=\"rgb(128,128,128)\" compliance=\"SVG\" />"
+ " <color name=\"fuchsia\" color=\"rgb(255,0,255)\" compliance=\"SVG\" />"
+ " <color name=\"gainsboro\" color=\"rgb(220,220,220)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"GhostWhite\" color=\"rgb(248,248,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"gold\" color=\"rgb(255,215,0)\" compliance=\"X11, XPM\" />"
+ " <color name=\"goldenrod\" color=\"rgb(218,165,32)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"gray\" color=\"rgb(126,126,126)\" compliance=\"SVG\" />"
+ " <color name=\"gray74\" color=\"rgb(189,189,189)\" compliance=\"SVG, X11\" />"
+ " <color name=\"gray100\" color=\"rgb(255,255,255)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey\" color=\"rgb(190,190,190)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey0\" color=\"rgb(0,0,0)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey1\" color=\"rgb(3,3,3)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey10\" color=\"rgb(26,26,26)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey100\" color=\"rgb(255,255,255)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey11\" color=\"rgb(28,28,28)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey12\" color=\"rgb(31,31,31)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey13\" color=\"rgb(33,33,33)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey14\" color=\"rgb(36,36,36)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey15\" color=\"rgb(38,38,38)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey16\" color=\"rgb(41,41,41)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey17\" color=\"rgb(43,43,43)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey18\" color=\"rgb(45,45,45)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey19\" color=\"rgb(48,48,48)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey2\" color=\"rgb(5,5,5)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey20\" color=\"rgb(51,51,51)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey21\" color=\"rgb(54,54,54)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey22\" color=\"rgb(56,56,56)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey23\" color=\"rgb(59,59,59)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey24\" color=\"rgb(61,61,61)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey25\" color=\"rgb(64,64,64)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey26\" color=\"rgb(66,66,66)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey27\" color=\"rgb(69,69,69)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey28\" color=\"rgb(71,71,71)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey29\" color=\"rgb(74,74,74)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey3\" color=\"rgb(8,8,8)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey30\" color=\"rgb(77,77,77)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey31\" color=\"rgb(79,79,79)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey32\" color=\"rgb(82,82,82)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey33\" color=\"rgb(84,84,84)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey34\" color=\"rgb(87,87,87)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey35\" color=\"rgb(89,89,89)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey36\" color=\"rgb(92,92,92)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey37\" color=\"rgb(94,94,94)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey38\" color=\"rgb(97,97,97)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey39\" color=\"rgb(99,99,99)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey4\" color=\"rgb(10,10,10)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey40\" color=\"rgb(102,102,102)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey41\" color=\"rgb(105,105,105)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey42\" color=\"rgb(107,107,107)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey43\" color=\"rgb(110,110,110)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey44\" color=\"rgb(112,112,112)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey45\" color=\"rgb(115,115,115)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey45\" color=\"rgb(117,117,117)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey47\" color=\"rgb(120,120,120)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey48\" color=\"rgb(122,122,122)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey49\" color=\"rgb(125,125,125)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey5\" color=\"rgb(13,13,13)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey50\" color=\"rgb(50%,50%,50%)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey51\" color=\"rgb(130,130,130)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey52\" color=\"rgb(133,133,133)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey53\" color=\"rgb(135,135,135)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey54\" color=\"rgb(138,138,138)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey55\" color=\"rgb(140,140,140)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey56\" color=\"rgb(143,143,143)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey57\" color=\"rgb(145,145,145)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey58\" color=\"rgb(148,148,148)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey59\" color=\"rgb(150,150,150)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey6\" color=\"rgb(15,15,15)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey60\" color=\"rgb(153,153,153)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey61\" color=\"rgb(156,156,156)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey62\" color=\"rgb(158,158,158)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey63\" color=\"rgb(161,161,161)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey64\" color=\"rgb(163,163,163)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey65\" color=\"rgb(166,166,166)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey66\" color=\"rgb(168,168,168)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey67\" color=\"rgb(171,171,171)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey68\" color=\"rgb(173,173,173)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey69\" color=\"rgb(176,176,176)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey7\" color=\"rgb(18,18,18)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey70\" color=\"rgb(179,179,179)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey71\" color=\"rgb(181,181,181)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey72\" color=\"rgb(184,184,184)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey73\" color=\"rgb(186,186,186)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey74\" color=\"rgb(189,189,189)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey75\" color=\"rgb(191,191,191)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey76\" color=\"rgb(194,194,194)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey77\" color=\"rgb(196,196,196)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey78\" color=\"rgb(199,199,199)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey79\" color=\"rgb(201,201,201)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey8\" color=\"rgb(20,20,20)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey80\" color=\"rgb(204,204,204)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey81\" color=\"rgb(207,207,207)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey82\" color=\"rgb(209,209,209)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey83\" color=\"rgb(212,212,212)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey84\" color=\"rgb(214,214,214)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey85\" color=\"rgb(217,217,217)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey86\" color=\"rgb(219,219,219)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey87\" color=\"rgb(222,222,222)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey88\" color=\"rgb(224,224,224)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey89\" color=\"rgb(227,227,227)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey9\" color=\"rgb(23,23,23)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey90\" color=\"rgb(229,229,229)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey91\" color=\"rgb(232,232,232)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey92\" color=\"rgb(235,235,235)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey93\" color=\"rgb(237,237,237)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey94\" color=\"rgb(240,240,240)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey95\" color=\"rgb(242,242,242)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey96\" color=\"rgb(245,245,245)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey97\" color=\"rgb(247,247,247)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey98\" color=\"rgb(250,250,250)\" compliance=\"SVG, X11\" />"
+ " <color name=\"grey99\" color=\"rgb(252,252,252)\" compliance=\"SVG, X11\" />"
+ " <color name=\"honeydew\" color=\"rgb(240,255,240)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"HotPink\" color=\"rgb(255,105,180)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"IndianRed\" color=\"rgb(205,92,92)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"indigo\" color=\"rgb(75,0,130)\" compliance=\"SVG\" />"
+ " <color name=\"ivory\" color=\"rgb(255,255,240)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"khaki\" color=\"rgb(240,230,140)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"lavender\" color=\"rgb(230,230,250)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LavenderBlush\" color=\"rgb(255,240,245)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LawnGreen\" color=\"rgb(124,252,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LemonChiffon\" color=\"rgb(255,250,205)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightBlue\" color=\"rgb(173,216,230)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightCoral\" color=\"rgb(240,128,128)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightCyan\" color=\"rgb(224,255,255)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightGoldenrodYellow\" color=\"rgb(250,250,210)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightGray\" color=\"rgb(211,211,211)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightGreen\" color=\"rgb(144,238,144)\" compliance=\"SVG, X11\" />"
+ " <color name=\"LightGrey\" color=\"rgb(211,211,211)\" compliance=\"SVG, X11\" />"
+ " <color name=\"LightPink\" color=\"rgb(255,182,193)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightSalmon\" color=\"rgb(255,160,122)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightSeaGreen\" color=\"rgb(32,178,170)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightSkyBlue\" color=\"rgb(135,206,250)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightSlateGray\" color=\"rgb(119,136,153)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightSlateGrey\" color=\"rgb(119,136,153)\" compliance=\"SVG, X11\" />"
+ " <color name=\"LightSteelBlue\" color=\"rgb(176,196,222)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"LightYellow\" color=\"rgb(255,255,224)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"lime\" color=\"rgb(0,255,0)\" compliance=\"SVG\" />"
+ " <color name=\"LimeGreen\" color=\"rgb(50,205,50)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"linen\" color=\"rgb(250,240,230)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"maroon\" color=\"rgb(128,0,0)\" compliance=\"SVG\" />"
+ " <color name=\"MediumAquamarine\" color=\"rgb(102,205,170)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumBlue\" color=\"rgb(0,0,205)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumOrchid\" color=\"rgb(186,85,211)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumPurple\" color=\"rgb(147,112,219)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumSeaGreen\" color=\"rgb(60,179,113)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumSlateBlue\" color=\"rgb(123,104,238)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumSpringGreen\" color=\"rgb(0,250,154)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumTurquoise\" color=\"rgb(72,209,204)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MediumVioletRed\" color=\"rgb(199,21,133)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MidnightBlue\" color=\"rgb(25,25,112)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MintCream\" color=\"rgb(245,255,250)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"MistyRose\" color=\"rgb(255,228,225)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"moccasin\" color=\"rgb(255,228,181)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"NavajoWhite\" color=\"rgb(255,222,173)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"navy\" color=\"rgb(0,0,128)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"matte\" color=\"rgb(0,0,0,0)\" compliance=\"SVG\" />"
+ " <color name=\"OldLace\" color=\"rgb(253,245,230)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"olive\" color=\"rgb(128,128,0)\" compliance=\"SVG\" />"
+ " <color name=\"OliveDrab\" color=\"rgb(107,142,35)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"opaque\" color=\"rgb(0,0,0)\" compliance=\"SVG\" />"
+ " <color name=\"orange\" color=\"rgb(255,165,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"OrangeRed\" color=\"rgb(255,69,0)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"orchid\" color=\"rgb(218,112,214)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PaleGoldenrod\" color=\"rgb(238,232,170)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PaleGreen\" color=\"rgb(152,251,152)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PaleTurquoise\" color=\"rgb(175,238,238)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PaleVioletRed\" color=\"rgb(219,112,147)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PapayaWhip\" color=\"rgb(255,239,213)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PeachPuff\" color=\"rgb(255,218,185)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"peru\" color=\"rgb(205,133,63)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"pink\" color=\"rgb(255,192,203)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"plum\" color=\"rgb(221,160,221)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"PowderBlue\" color=\"rgb(176,224,230)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"purple\" color=\"rgb(128,0,128)\" compliance=\"SVG\" />"
+ " <color name=\"RosyBrown\" color=\"rgb(188,143,143)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"RoyalBlue\" color=\"rgb(65,105,225)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SaddleBrown\" color=\"rgb(139,69,19)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"salmon\" color=\"rgb(250,128,114)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SandyBrown\" color=\"rgb(244,164,96)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SeaGreen\" color=\"rgb(45,139,87)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"seashell\" color=\"rgb(255,245,238)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"sienna\" color=\"rgb(160,82,45)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"silver\" color=\"rgb(192,192,192)\" compliance=\"SVG\" />"
+ " <color name=\"SkyBlue\" color=\"rgb(135,206,235)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SlateBlue\" color=\"rgb(106,90,205)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SlateGray\" color=\"rgb(112,128,144)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SlateGrey\" color=\"rgb(112,128,144)\" compliance=\"SVG, X11\" />"
+ " <color name=\"snow\" color=\"rgb(255,250,250)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SpringGreen\" color=\"rgb(0,255,127)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"SteelBlue\" color=\"rgb(70,130,180)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"tan\" color=\"rgb(210,180,140)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"teal\" color=\"rgb(0,128,128)\" compliance=\"SVG\" />"
+ " <color name=\"thistle\" color=\"rgb(216,191,216)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"tomato\" color=\"rgb(255,99,71)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"transparent\" color=\"rgba(0,0,0,0)\" compliance=\"SVG\" />"
+ " <color name=\"turquoise\" color=\"rgb(64,224,208)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"violet\" color=\"rgb(238,130,238)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"wheat\" color=\"rgb(245,222,179)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"WhiteSmoke\" color=\"rgb(245,245,245)\" compliance=\"SVG, X11, XPM\" />"
+ " <color name=\"YellowGreen\" color=\"rgb(154,205,50)\" compliance=\"SVG, X11, XPM\" />"
+ "</colormap>";
+
+/*
+ Typedef declarations.
+*/
+typedef struct _NodeInfo
+{
+ struct _NodeInfo
+ *child[16];
+
+ ColorPacket
+ *list;
+
+ MagickSizeType
+ number_unique;
+
+ unsigned long
+ level;
+} NodeInfo;
+
+typedef struct _Nodes
+{
+ NodeInfo
+ nodes[NodesInAList];
+
+ struct _Nodes
+ *next;
+} Nodes;
+
+typedef struct _CubeInfo
+{
+ NodeInfo
+ *root;
+
+ long
+ x,
+ progress;
+
+ unsigned long
+ colors,
+ free_nodes;
+
+ NodeInfo
+ *node_info;
+
+ Nodes
+ *node_queue;
+} CubeInfo;
+
+/*
+ Static declarations.
+*/
+static LinkedListInfo
+ *color_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *color_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_color = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static CubeInfo
+ *GetCubeInfo(void);
+
+static NodeInfo
+ *GetNodeInfo(CubeInfo *,const unsigned long);
+
+static MagickBooleanType
+ InitializeColorList(ExceptionInfo *),
+ LoadColorLists(const char *,ExceptionInfo *);
+
+static void
+ DestroyColorCube(const Image *,NodeInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l a s s i f y I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClassifyImageColors() builds a populated CubeInfo tree for the specified
+% image. The returned tree should be deallocated using DestroyCubeInfo()
+% once it is no longer needed.
+%
+% The format of the ClassifyImageColors() method is:
+%
+% CubeInfo *ClassifyImageColors(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline unsigned long ColorToNodeId(const Image *image,
+ const MagickPixelPacket *pixel,unsigned long index)
+{
+ unsigned long
+ id;
+
+ id=(unsigned long) (
+ ((ScaleQuantumToChar(RoundToQuantum(pixel->red)) >> index) & 0x01) |
+ ((ScaleQuantumToChar(RoundToQuantum(pixel->green)) >> index) & 0x01) << 1 |
+ ((ScaleQuantumToChar(RoundToQuantum(pixel->blue)) >> index) & 0x01) << 2);
+ if (image->matte != MagickFalse)
+ id|=((ScaleQuantumToChar(RoundToQuantum(pixel->opacity)) >> index) &
+ 0x01) << 3;
+ return(id);
+}
+
+static CubeInfo *ClassifyImageColors(const Image *image,
+ ExceptionInfo *exception)
+{
+#define EvaluateImageTag " Compute image colors... "
+
+ CubeInfo
+ *cube_info;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ MagickPixelPacket
+ pixel,
+ target;
+
+ NodeInfo
+ *node_info;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ register unsigned long
+ id,
+ index,
+ level;
+
+ CacheView
+ *image_view;
+
+ /*
+ Initialize color description tree.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cube_info=GetCubeInfo();
+ if (cube_info == (CubeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(cube_info);
+ }
+ GetMagickPixelPacket(image,&pixel);
+ GetMagickPixelPacket(image,&target);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Start at the root and proceed level by level.
+ */
+ node_info=cube_info->root;
+ index=MaxTreeDepth-1;
+ for (level=1; level < MaxTreeDepth; level++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ id=ColorToNodeId(image,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ node_info->child[id]=GetNodeInfo(cube_info,level);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ return(0);
+ }
+ }
+ node_info=node_info->child[id];
+ index--;
+ }
+ for (i=0; i < (long) node_info->number_unique; i++)
+ {
+ SetMagickPixelPacket(image,&node_info->list[i].pixel,
+ &node_info->list[i].index,&target);
+ if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
+ break;
+ }
+ if (i < (long) node_info->number_unique)
+ node_info->list[i].count++;
+ else
+ {
+ if (node_info->number_unique == 0)
+ node_info->list=(ColorPacket *) AcquireMagickMemory(
+ sizeof(*node_info->list));
+ else
+ node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
+ (size_t) (i+1),sizeof(*node_info->list));
+ if (node_info->list == (ColorPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ return(0);
+ }
+ node_info->list[i].pixel=(*p);
+ if ((image->colorspace == CMYKColorspace) ||
+ (image->storage_class == PseudoClass))
+ node_info->list[i].index=indexes[x];
+ node_info->list[i].count=1;
+ node_info->number_unique++;
+ cube_info->colors++;
+ }
+ p++;
+ }
+ proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(cube_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o n c a t e n a t e C o l o r C o m p o n e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConcatenateColorComponent() returns the pixel as a canonical string.
+%
+% The format of the ConcatenateColorComponent() method is:
+%
+% void ConcatenateColorComponent(const MagickPixelPacket *pixel,
+% const ChannelType channel,const ComplianceType compliance,char *tuple)
+%
+% A description of each parameter follows.
+%
+% o pixel: The pixel.
+%
+% channel: The channel.
+%
+% o compliance: Adhere to this color standard: SVG, X11, or XPM.
+%
+% tuple: The color tuple.
+%
+*/
+MagickExport void ConcatenateColorComponent(const MagickPixelPacket *pixel,
+ const ChannelType channel,const ComplianceType compliance,char *tuple)
+{
+ char
+ component[MaxTextExtent];
+
+ MagickRealType
+ color;
+
+ color=0.0;
+ switch (channel)
+ {
+ case RedChannel:
+ {
+ color=pixel->red;
+ break;
+ }
+ case GreenChannel:
+ {
+ color=pixel->green;
+ break;
+ }
+ case BlueChannel:
+ {
+ color=pixel->blue;
+ break;
+ }
+ case AlphaChannel:
+ {
+ color=QuantumRange-pixel->opacity;
+ break;
+ }
+ case IndexChannel:
+ {
+ color=pixel->index;
+ break;
+ }
+ default:
+ break;
+ }
+ if (compliance != SVGCompliance)
+ {
+ if (pixel->depth > 16)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%10lu",
+ (unsigned long) ScaleQuantumToLong(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ if (pixel->depth > 8)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%5d",
+ ScaleQuantumToShort(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ (void) FormatMagickString(component,MaxTextExtent,"%3d",
+ ScaleQuantumToChar(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ if (channel == OpacityChannel)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%g",
+ (double) (QuantumScale*color));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ if (pixel->depth > 8)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%g%%",
+ (double) (100.0*QuantumScale*color));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ (void) FormatMagickString(component,MaxTextExtent,"%d",
+ ScaleQuantumToChar(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f i n e I m a g e H i s t o g r a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageHistogram() traverses the color cube tree and notes each colormap
+% entry. A colormap entry is any node in the color cube tree where the
+% of unique colors is not zero.
+%
+% The format of the DefineImageHistogram method is:
+%
+% DefineImageHistogram(const Image *image,NodeInfo *node_info,
+% ColorPacket **unique_colors)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o node_info: the address of a structure of type NodeInfo which points to a
+% node in the color cube tree that is to be pruned.
+%
+% o histogram: the image histogram.
+%
+*/
+static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
+ ColorPacket **histogram)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=image->matte == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ DefineImageHistogram(image,node_info->child[i],histogram);
+ if (node_info->level == (MaxTreeDepth-1))
+ {
+ register ColorPacket
+ *p;
+
+ p=node_info->list;
+ for (i=0; i < (long) node_info->number_unique; i++)
+ {
+ (*histogram)->pixel=p->pixel;
+ (*histogram)->index=p->index;
+ (*histogram)->count=p->count;
+ (*histogram)++;
+ p++;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C o l o r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyColorList() deallocates memory associated with the color list.
+%
+% The format of the DestroyColorList method is:
+%
+% DestroyColorList(void)
+%
+*/
+
+static void *DestroyColorElement(void *color_info)
+{
+ register ColorInfo
+ *p;
+
+ p=(ColorInfo *) color_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ p=(ColorInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyColorList(void)
+{
+ AcquireSemaphoreInfo(&color_semaphore);
+ if (color_list != (LinkedListInfo *) NULL)
+ color_list=DestroyLinkedList(color_list,DestroyColorElement);
+ instantiate_color=MagickFalse;
+ RelinquishSemaphoreInfo(color_semaphore);
+ DestroySemaphoreInfo(&color_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C u b e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
+%
+% The format of the DestroyCubeInfo method is:
+%
+% DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o cube_info: the address of a structure of type CubeInfo.
+%
+*/
+static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
+{
+ register Nodes
+ *nodes;
+
+ /*
+ Release color cube tree storage.
+ */
+ DestroyColorCube(image,cube_info->root);
+ do
+ {
+ nodes=cube_info->node_queue->next;
+ cube_info->node_queue=(Nodes *)
+ RelinquishMagickMemory(cube_info->node_queue);
+ cube_info->node_queue=nodes;
+ } while (cube_info->node_queue != (Nodes *) NULL);
+ return((CubeInfo *) RelinquishMagickMemory(cube_info));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C o l o r C u b e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyColorCube() traverses the color cube tree and frees the list of
+% unique colors.
+%
+% The format of the DestroyColorCube method is:
+%
+% void DestroyColorCube(const Image *image,const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o node_info: the address of a structure of type NodeInfo which points to a
+% node in the color cube tree that is to be pruned.
+%
+*/
+static void DestroyColorCube(const Image *image,NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=image->matte == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ DestroyColorCube(image,node_info->child[i]);
+ if (node_info->list != (ColorPacket *) NULL)
+ node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C o l o r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetColorInfo() searches the color list for the specified name and if found
+% returns attributes for that color.
+%
+% The format of the GetColorInfo method is:
+%
+% const PixelPacket *GetColorInfo(const char *name,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o color_info: search the color list for the specified name and if found
+% return attributes for that color.
+%
+% o name: the color name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const ColorInfo *GetColorInfo(const char *name,
+ ExceptionInfo *exception)
+{
+ char
+ colorname[MaxTextExtent];
+
+ register const ColorInfo
+ *p;
+
+ register char
+ *q;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((color_list == (LinkedListInfo *) NULL) ||
+ (instantiate_color == MagickFalse))
+ if (InitializeColorList(exception) == MagickFalse)
+ return((const ColorInfo *) NULL);
+ if ((color_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(color_list) != MagickFalse))
+ return((const ColorInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ return((const ColorInfo *) GetValueFromLinkedList(color_list,0));
+ /*
+ Strip names of whitespace.
+ */
+ (void) CopyMagickString(colorname,name,MaxTextExtent);
+ for (q=colorname; *q != '\0'; q++)
+ {
+ if (isspace((int) ((unsigned char) *q)) == 0)
+ continue;
+ (void) CopyMagickString(q,q+1,MaxTextExtent);
+ q--;
+ }
+ /*
+ Search for color tag.
+ */
+ AcquireSemaphoreInfo(&color_semaphore);
+ ResetLinkedListIterator(color_list);
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ while (p != (const ColorInfo *) NULL)
+ {
+ if (LocaleCompare(colorname,p->name) == 0)
+ break;
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ }
+ if (p == (ColorInfo *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "UnrecognizedColor","`%s'",name);
+ else
+ (void) InsertValueInLinkedList(color_list,0,
+ RemoveElementByValueFromLinkedList(color_list,p));
+ RelinquishSemaphoreInfo(color_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o l o r I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetColorInfoList() returns any colors that match the specified pattern.
+%
+% The format of the GetColorInfoList function is:
+%
+% const ColorInfo **GetColorInfoList(const char *pattern,
+% unsigned long *number_colors,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_colors: This integer returns the number of colors in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ColorInfoCompare(const void *x,const void *y)
+{
+ const ColorInfo
+ **p,
+ **q;
+
+ p=(const ColorInfo **) x,
+ q=(const ColorInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const ColorInfo **GetColorInfoList(const char *pattern,
+ unsigned long *number_colors,ExceptionInfo *exception)
+{
+ const ColorInfo
+ **colors;
+
+ register const ColorInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate color list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_colors != (unsigned long *) NULL);
+ *number_colors=0;
+ p=GetColorInfo("*",exception);
+ if (p == (const ColorInfo *) NULL)
+ return((const ColorInfo **) NULL);
+ colors=(const ColorInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(color_list)+1UL,sizeof(*colors));
+ if (colors == (const ColorInfo **) NULL)
+ return((const ColorInfo **) NULL);
+ /*
+ Generate color list.
+ */
+ AcquireSemaphoreInfo(&color_semaphore);
+ ResetLinkedListIterator(color_list);
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ for (i=0; p != (const ColorInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ colors[i++]=p;
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ }
+ RelinquishSemaphoreInfo(color_semaphore);
+ qsort((void *) colors,(size_t) i,sizeof(*colors),ColorInfoCompare);
+ colors[i]=(ColorInfo *) NULL;
+ *number_colors=(unsigned long) i;
+ return(colors);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o l o r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetColorList() returns any colors that match the specified pattern.
+%
+% The format of the GetColorList function is:
+%
+% char **GetColorList(const char *pattern,unsigned long *number_colors,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_colors: This integer returns the number of colors in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ColorCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetColorList(const char *pattern,
+ unsigned long *number_colors,ExceptionInfo *exception)
+{
+ char
+ **colors;
+
+ register const ColorInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate color list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_colors != (unsigned long *) NULL);
+ *number_colors=0;
+ p=GetColorInfo("*",exception);
+ if (p == (const ColorInfo *) NULL)
+ return((char **) NULL);
+ colors=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(color_list)+1UL,sizeof(*colors));
+ if (colors == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Generate color list.
+ */
+ AcquireSemaphoreInfo(&color_semaphore);
+ ResetLinkedListIterator(color_list);
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ for (i=0; p != (const ColorInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ colors[i++]=ConstantString(p->name);
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ }
+ RelinquishSemaphoreInfo(color_semaphore);
+ qsort((void *) colors,(size_t) i,sizeof(*colors),ColorCompare);
+ colors[i]=(char *) NULL;
+ *number_colors=(unsigned long) i;
+ return(colors);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C o l o r T u p l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetColorTuple() returns a color as a color tuple string (e.g. rgba(255,0,0))
+% or hex string (e.g. #FF0000).
+%
+% The format of the GetColorTuple method is:
+%
+% GetColorTuple(const MagickPixelPacket *pixel,const MagickBooleanType hex,
+% char *tuple)
+%
+% A description of each parameter follows.
+%
+% o pixel: the pixel.
+%
+% o hex: A value other than zero returns the tuple in a hexidecimal format.
+%
+% o tuple: Return the color tuple as this string.
+%
+*/
+
+static void ConcatentateHexColorComponent(const MagickPixelPacket *pixel,
+ const ChannelType channel,char *tuple)
+{
+ char
+ component[MaxTextExtent];
+
+ MagickRealType
+ color;
+
+ color=0.0;
+ switch (channel)
+ {
+ case RedChannel:
+ {
+ color=pixel->red;
+ break;
+ }
+ case GreenChannel:
+ {
+ color=pixel->green;
+ break;
+ }
+ case BlueChannel:
+ {
+ color=pixel->blue;
+ break;
+ }
+ case OpacityChannel:
+ {
+ color=(MagickRealType) QuantumRange-pixel->opacity;
+ break;
+ }
+ case IndexChannel:
+ {
+ color=pixel->index;
+ break;
+ }
+ default:
+ break;
+ }
+ if (pixel->depth > 32)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%08lX",
+ ScaleQuantumToLong(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ if (pixel->depth > 16)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%08X",
+ (unsigned int) ScaleQuantumToLong(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ if (pixel->depth > 8)
+ {
+ (void) FormatMagickString(component,MaxTextExtent,"%04X",
+ ScaleQuantumToShort(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+ }
+ (void) FormatMagickString(component,MaxTextExtent,"%02X",
+ ScaleQuantumToChar(RoundToQuantum(color)));
+ (void) ConcatenateMagickString(tuple,component,MaxTextExtent);
+ return;
+}
+
+MagickExport void GetColorTuple(const MagickPixelPacket *pixel,
+ const MagickBooleanType hex,char *tuple)
+{
+ MagickPixelPacket
+ color;
+
+ assert(pixel != (const MagickPixelPacket *) NULL);
+ assert(tuple != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",tuple);
+ *tuple='\0';
+ if (hex != MagickFalse)
+ {
+ /*
+ Convert pixel to hex color.
+ */
+ (void) ConcatenateMagickString(tuple,"#",MaxTextExtent);
+ ConcatentateHexColorComponent(pixel,RedChannel,tuple);
+ ConcatentateHexColorComponent(pixel,GreenChannel,tuple);
+ ConcatentateHexColorComponent(pixel,BlueChannel,tuple);
+ if (pixel->colorspace == CMYKColorspace)
+ ConcatentateHexColorComponent(pixel,IndexChannel,tuple);
+ if ((pixel->matte != MagickFalse) && (pixel->opacity != OpaqueOpacity))
+ ConcatentateHexColorComponent(pixel,OpacityChannel,tuple);
+ return;
+ }
+ /*
+ Convert pixel to rgb() or cmyk() color.
+ */
+ color=(*pixel);
+ if (color.depth > 8)
+ {
+#define SVGCompliant(component) ((MagickRealType) \
+ ScaleCharToQuantum(ScaleQuantumToChar(RoundToQuantum(component))));
+
+ MagickStatusType
+ status;
+
+ /*
+ SVG requires color depths > 8 expressed as percentages.
+ */
+ status=color.red == SVGCompliant(color.red);
+ status&=color.green == SVGCompliant(color.green);
+ status&=color.blue == SVGCompliant(color.blue);
+ if (color.colorspace != CMYKColorspace)
+ status&=color.index == SVGCompliant(color.index);
+ if (color.matte != MagickFalse)
+ status&=color.opacity == SVGCompliant(color.opacity);
+ if (status != MagickFalse)
+ color.depth=8;
+ }
+ (void) ConcatenateMagickString(tuple,MagickOptionToMnemonic(
+ MagickColorspaceOptions,(long) color.colorspace),MaxTextExtent);
+ if (color.matte != MagickFalse)
+ (void) ConcatenateMagickString(tuple,"a",MaxTextExtent);
+ (void) ConcatenateMagickString(tuple,"(",MaxTextExtent);
+ ConcatenateColorComponent(&color,RedChannel,SVGCompliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&color,GreenChannel,SVGCompliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&color,BlueChannel,SVGCompliance,tuple);
+ if (color.colorspace == CMYKColorspace)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&color,IndexChannel,SVGCompliance,tuple);
+ }
+ if (color.matte != MagickFalse)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&color,AlphaChannel,SVGCompliance,tuple);
+ }
+ (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
+ LocaleLower(tuple);
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C u b e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCubeInfo() initializes the CubeInfo data structure.
+%
+% The format of the GetCubeInfo method is:
+%
+% cube_info=GetCubeInfo()
+%
+% A description of each parameter follows.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+*/
+static CubeInfo *GetCubeInfo(void)
+{
+ CubeInfo
+ *cube_info;
+
+ /*
+ Initialize tree to describe color cube.
+ */
+ cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
+ if (cube_info == (CubeInfo *) NULL)
+ return((CubeInfo *) NULL);
+ (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
+ /*
+ Initialize root node.
+ */
+ cube_info->root=GetNodeInfo(cube_info,0);
+ if (cube_info->root == (NodeInfo *) NULL)
+ return((CubeInfo *) NULL);
+ return(cube_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e H i s t o g r a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageHistogram() returns the unique colors in an image.
+%
+% The format of the GetImageHistogram method is:
+%
+% unsigned long GetImageHistogram(const Image *image,
+% unsigned long *number_colors,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o file: Write a histogram of the color distribution to this file handle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ColorPacket *GetImageHistogram(const Image *image,
+ unsigned long *number_colors,ExceptionInfo *exception)
+{
+ ColorPacket
+ *histogram;
+
+ CubeInfo
+ *cube_info;
+
+ *number_colors=0;
+ histogram=(ColorPacket *) NULL;
+ cube_info=ClassifyImageColors(image,exception);
+ if (cube_info != (CubeInfo *) NULL)
+ {
+ histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
+ sizeof(*histogram));
+ if (histogram == (ColorPacket *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ else
+ {
+ ColorPacket
+ *root;
+
+ *number_colors=cube_info->colors;
+ root=histogram;
+ DefineImageHistogram(image,cube_info->root,&root);
+ }
+ }
+ cube_info=DestroyCubeInfo(image,cube_info);
+ return(histogram);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t N o d e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNodeInfo() allocates memory for a new node in the color cube tree and
+% presets all fields to zero.
+%
+% The format of the GetNodeInfo method is:
+%
+% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
+%
+% A description of each parameter follows.
+%
+% o cube_info: A pointer to the CubeInfo structure.
+%
+% o level: Specifies the level in the storage_class the node resides.
+%
+*/
+static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
+{
+ NodeInfo
+ *node_info;
+
+ if (cube_info->free_nodes == 0)
+ {
+ Nodes
+ *nodes;
+
+ /*
+ Allocate a new nodes of nodes.
+ */
+ nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
+ if (nodes == (Nodes *) NULL)
+ return((NodeInfo *) NULL);
+ nodes->next=cube_info->node_queue;
+ cube_info->node_queue=nodes;
+ cube_info->node_info=nodes->nodes;
+ cube_info->free_nodes=NodesInAList;
+ }
+ cube_info->free_nodes--;
+ node_info=cube_info->node_info++;
+ (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
+ node_info->level=level;
+ return(node_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N u m b e r C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNumberColors() returns the number of unique colors in an image.
+%
+% The format of the GetNumberColors method is:
+%
+% unsigned long GetNumberColors(const Image *image,FILE *file,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o file: Write a histogram of the color distribution to this file handle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int HistogramCompare(const void *x,const void *y)
+{
+ const ColorPacket
+ *color_1,
+ *color_2;
+
+ color_1=(const ColorPacket *) x;
+ color_2=(const ColorPacket *) y;
+ if (color_2->pixel.red != color_1->pixel.red)
+ return((int) color_1->pixel.red-(int) color_2->pixel.red);
+ if (color_2->pixel.green != color_1->pixel.green)
+ return((int) color_1->pixel.green-(int) color_2->pixel.green);
+ if (color_2->pixel.blue != color_1->pixel.blue)
+ return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
+ return((int) color_2->count-(int) color_1->count);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
+ ExceptionInfo *exception)
+{
+#define HistogramImageTag "Histogram/Image"
+
+ char
+ color[MaxTextExtent],
+ hex[MaxTextExtent],
+ tuple[MaxTextExtent];
+
+ ColorPacket
+ *histogram;
+
+ MagickPixelPacket
+ pixel;
+
+ register ColorPacket
+ *p;
+
+ register long
+ i;
+
+ unsigned long
+ number_colors;
+
+ number_colors=0;
+ if (file == (FILE *) NULL)
+ {
+ CubeInfo
+ *cube_info;
+
+ cube_info=ClassifyImageColors(image,exception);
+ if (cube_info != (CubeInfo *) NULL)
+ number_colors=cube_info->colors;
+ cube_info=DestroyCubeInfo(image,cube_info);
+ return(number_colors);
+ }
+ histogram=GetImageHistogram(image,&number_colors,exception);
+ if (histogram == (ColorPacket *) NULL)
+ return(number_colors);
+ qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
+ HistogramCompare);
+ GetMagickPixelPacket(image,&pixel);
+ p=histogram;
+ for (i=0; i < (long) number_colors; i++)
+ {
+ SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
+ (void) CopyMagickString(tuple,"(",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
+ if (pixel.colorspace == CMYKColorspace)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
+ }
+ if (pixel.matte != MagickFalse)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
+ }
+ (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
+ (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
+ GetColorTuple(&pixel,MagickTrue,hex);
+ (void) fprintf(file,MagickSizeFormat,p->count);
+ (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(i,number_colors) != MagickFalse))
+ (void) image->progress_monitor(HistogramImageTag,i,number_colors,
+ image->client_data);
+ p++;
+ }
+ (void) fflush(file);
+ histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
+ return(number_colors);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e C o l o r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeColorList() initializes the color list.
+%
+% The format of the InitializeColorList method is:
+%
+% MagickBooleanType InitializeColorList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeColorList(ExceptionInfo *exception)
+{
+ if ((color_list == (LinkedListInfo *) NULL) &&
+ (instantiate_color == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&color_semaphore);
+ if ((color_list == (LinkedListInfo *) NULL) &&
+ (instantiate_color == MagickFalse))
+ {
+ (void) LoadColorLists(ColorFilename,exception);
+ instantiate_color=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(color_semaphore);
+ }
+ return(color_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s C o l o r S i m i l a r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsColorSimilar() returns MagickTrue if the distance between two colors is
+% less than the specified distance in a linear three dimensional color space.
+% This method is used by ColorFloodFill() and other algorithms which
+% compare two colors.
+%
+% The format of the IsColorSimilar method is:
+%
+% void IsColorSimilar(const Image *image,const PixelPacket *p,
+% const PixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType IsColorSimilar(const Image *image,
+ const PixelPacket *p,const PixelPacket *q)
+{
+ MagickRealType
+ fuzz,
+ pixel;
+
+ register MagickRealType
+ alpha,
+ beta,
+ distance;
+
+ if ((image->fuzz == 0.0) && (image->matte == MagickFalse))
+ return(IsColorEqual(p,q));
+ fuzz=3.0*MagickMax(image->fuzz,MagickSQ1_2)*MagickMax(image->fuzz,
+ MagickSQ1_2);
+ alpha=1.0;
+ beta=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
+ }
+ pixel=alpha*p->red-beta*q->red;
+ distance=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ pixel=alpha*p->green-beta*q->green;
+ distance+=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ pixel=alpha*p->blue-beta*q->blue;
+ distance+=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s G r a y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsGrayImage() returns MagickTrue if all the pixels in the image have the
+% same red, green, and blue intensities.
+%
+% The format of the IsGrayImage method is:
+%
+% MagickBooleanType IsGrayImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsGrayImage(const Image *image,
+ ExceptionInfo *exception)
+{
+ ImageType
+ type;
+
+ register const PixelPacket
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
+ (image->type == GrayscaleMatteType))
+ return(MagickTrue);
+ if (image->colorspace == CMYKColorspace)
+ return(MagickFalse);
+ type=BilevelType;
+ switch (image->storage_class)
+ {
+ case DirectClass:
+ case UndefinedClass:
+ {
+ long
+ y;
+
+ register long
+ x;
+
+ CacheView
+ *image_view;
+
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsGrayPixel(p) == MagickFalse)
+ {
+ type=UndefinedType;
+ break;
+ }
+ if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
+ type=GrayscaleType;
+ p++;
+ }
+ if (type == UndefinedType)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ break;
+ }
+ case PseudoClass:
+ {
+ register long
+ i;
+
+ p=image->colormap;
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (IsGrayPixel(p) == MagickFalse)
+ {
+ type=UndefinedType;
+ break;
+ }
+ if ((type == BilevelType) && (IsMonochromePixel(p) == MagickFalse))
+ type=GrayscaleType;
+ p++;
+ }
+ break;
+ }
+ }
+ if (type == UndefinedType)
+ return(MagickFalse);
+ ((Image *) image)->type=type;
+ if ((type == GrayscaleType) && (image->matte != MagickFalse))
+ ((Image *) image)->type=GrayscaleMatteType;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s H i s t o g r a m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
+% less.
+%
+% The format of the IsHistogramImage method is:
+%
+% MagickBooleanType IsHistogramImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsHistogramImage(const Image *image,
+ ExceptionInfo *exception)
+{
+#define MaximumUniqueColors 1024
+
+ CubeInfo
+ *cube_info;
+
+ long
+ y;
+
+ MagickPixelPacket
+ pixel,
+ target;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ register NodeInfo
+ *node_info;
+
+ register long
+ i;
+
+ unsigned long
+ id,
+ index,
+ level;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((image->storage_class == PseudoClass) && (image->colors <= 256))
+ return(MagickTrue);
+ if (image->storage_class == PseudoClass)
+ return(MagickFalse);
+ /*
+ Initialize color description tree.
+ */
+ cube_info=GetCubeInfo();
+ if (cube_info == (CubeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ GetMagickPixelPacket(image,&pixel);
+ GetMagickPixelPacket(image,&target);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Start at the root and proceed level by level.
+ */
+ node_info=cube_info->root;
+ index=MaxTreeDepth-1;
+ for (level=1; level < MaxTreeDepth; level++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ id=ColorToNodeId(image,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ node_info->child[id]=GetNodeInfo(cube_info,level);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ break;
+ }
+ }
+ node_info=node_info->child[id];
+ index--;
+ }
+ if (level < MaxTreeDepth)
+ break;
+ for (i=0; i < (long) node_info->number_unique; i++)
+ {
+ SetMagickPixelPacket(image,&node_info->list[i].pixel,
+ &node_info->list[i].index,&target);
+ if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
+ break;
+ }
+ if (i < (long) node_info->number_unique)
+ node_info->list[i].count++;
+ else
+ {
+ /*
+ Add this unique color to the color list.
+ */
+ if (node_info->number_unique == 0)
+ node_info->list=(ColorPacket *) AcquireMagickMemory(
+ sizeof(*node_info->list));
+ else
+ node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
+ (size_t) (i+1),sizeof(*node_info->list));
+ if (node_info->list == (ColorPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ break;
+ }
+ node_info->list[i].pixel=(*p);
+ if ((image->colorspace == CMYKColorspace) ||
+ (image->storage_class == PseudoClass))
+ node_info->list[i].index=indexes[x];
+ node_info->list[i].count=1;
+ node_info->number_unique++;
+ cube_info->colors++;
+ if (cube_info->colors > MaximumUniqueColors)
+ break;
+ }
+ p++;
+ }
+ if (x < (long) image->columns)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ cube_info=DestroyCubeInfo(image,cube_info);
+ return(y < (long) image->rows ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s I m a g e S i m i l a r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsImageSimilar() returns true if the target is similar to a region of the
+% image.
+%
+% The format of the IsImageSimilar method is:
+%
+% MagickBooleanType IsImageSimilar(const Image *image,
+% const Image *target_image,long *x_offset,long *y_offset,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target_image: the target image.
+%
+% o x_offset: On input the starting x position to search for a match;
+% on output the x position of the first match found.
+%
+% o y_offset: On input the starting y position to search for a match;
+% on output the y position of the first match found.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsImageSimilar(const Image *image,
+ const Image *target_image,long *x_offset,long *y_offset,
+ ExceptionInfo *exception)
+{
+#define SearchImageText " Searching image... "
+
+ long
+ j,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ target,
+ pixel;
+
+ register const PixelPacket
+ *p,
+ *q;
+
+ register const IndexPacket
+ *indexes,
+ *target_indexes;
+
+ register long
+ i,
+ x;
+
+ CacheView
+ *image_view,
+ *target_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(target_image != (Image *) NULL);
+ assert(target_image->signature == MagickSignature);
+ assert(x_offset != (long *) NULL);
+ assert(y_offset != (long *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ x=0;
+ GetMagickPixelPacket(image,&pixel);
+ GetMagickPixelPacket(image,&target);
+ image_view=AcquireCacheView(image);
+ target_view=AcquireCacheView(target_image);
+ for (y=(*y_offset); y < (long) image->rows; y++)
+ {
+ for (x=y == 0 ? *x_offset : 0; x < (long) image->columns; x++)
+ {
+ for (j=0; j < (long) target_image->rows; j++)
+ {
+ for (i=0; i < (long) target_image->columns; i++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,x+i,y+j,1,1,exception);
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ q=GetCacheViewVirtualPixels(target_view,i,j,1,1,exception);
+ target_indexes=GetCacheViewVirtualIndexQueue(target_view);
+ SetMagickPixelPacket(image,q,target_indexes,&target);
+ if (IsMagickColorSimilar(&pixel,&target) == MagickFalse)
+ break;
+ }
+ if (i < (long) target_image->columns)
+ break;
+ }
+ if (j == (long) target_image->rows)
+ break;
+ }
+ if (x < (long) image->columns)
+ break;
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(y,image->rows) != MagickFalse))
+ {
+ status=image->progress_monitor(SearchImageText,y,image->rows,
+ image->client_data);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ target_view=DestroyCacheView(target_view);
+ image_view=DestroyCacheView(image_view);
+ *x_offset=x;
+ *y_offset=y;
+ return(y < (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s M a g i c k C o l o r S i m i l a r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickColorSimilar() returns true if the distance between two colors is
+% less than the specified distance in a linear three dimensional color space.
+% This method is used by ColorFloodFill() and other algorithms which
+% compare two colors.
+%
+% The format of the IsMagickColorSimilar method is:
+%
+% MagickBooleanType IsMagickColorSimilar(const MagickPixelPacket *p,
+% const MagickPixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+*/
+MagickExport MagickBooleanType IsMagickColorSimilar(const MagickPixelPacket *p,
+ const MagickPixelPacket *q)
+{
+ MagickRealType
+ fuzz,
+ pixel;
+
+ register MagickRealType
+ alpha,
+ beta,
+ distance;
+
+ if ((p->fuzz == 0.0) && (q->fuzz == 0.0))
+ return(IsMagickColorEqual(p,q));
+ if (p->fuzz == 0.0)
+ fuzz=MagickMax(q->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2);
+ else
+ if (q->fuzz == 0.0)
+ fuzz=3.0*MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(p->fuzz,MagickSQ1_2);
+ else
+ fuzz=3.0*MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2);
+ alpha=1.0;
+ if (p->matte != MagickFalse)
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ beta=1.0;
+ if (q->matte != MagickFalse)
+ beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
+ if (p->colorspace == CMYKColorspace)
+ {
+ alpha*=(MagickRealType) (QuantumScale*(QuantumRange-p->index));
+ beta*=(MagickRealType) (QuantumScale*(QuantumRange-q->index));
+ }
+ pixel=alpha*p->red-beta*q->red;
+ if ((p->colorspace == HSLColorspace) || (p->colorspace == HSBColorspace) ||
+ (p->colorspace == HWBColorspace))
+ {
+ if (fabs(p->red-q->red) > (QuantumRange/2))
+ {
+ if (p->red > (QuantumRange/2))
+ pixel=alpha*(p->red-QuantumRange)-beta*q->red;
+ else
+ pixel=alpha*p->red-beta*(q->red-QuantumRange);
+ }
+ pixel*=2;
+ }
+ distance=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ pixel=alpha*p->green-beta*q->green;
+ distance+=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ pixel=alpha*p->blue-beta*q->blue;
+ distance+=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ pixel=p->opacity-q->opacity;
+ distance+=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M o n o c h r o m e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMonochromeImage() returns MagickTrue if all the pixels in the image have
+% the same red, green, and blue intensities and the intensity is either
+% 0 or QuantumRange.
+%
+% The format of the IsMonochromeImage method is:
+%
+% MagickBooleanType IsMonochromeImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
+ ExceptionInfo *exception)
+{
+ ImageType
+ type;
+
+ register const PixelPacket
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->type == BilevelType)
+ return(MagickTrue);
+ if (image->colorspace == CMYKColorspace)
+ return(MagickFalse);
+ type=BilevelType;
+ switch (image->storage_class)
+ {
+ case DirectClass:
+ case UndefinedClass:
+ {
+ long
+ y;
+
+ register long
+ x;
+
+ CacheView
+ *image_view;
+
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsMonochromePixel(p) == MagickFalse)
+ {
+ type=UndefinedType;
+ break;
+ }
+ p++;
+ }
+ if (type == UndefinedType)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (y == (long) image->rows)
+ ((Image *) image)->type=BilevelType;
+ break;
+ }
+ case PseudoClass:
+ {
+ register long
+ i;
+
+ p=image->colormap;
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (IsMonochromePixel(p) == MagickFalse)
+ {
+ type=UndefinedType;
+ break;
+ }
+ p++;
+ }
+ break;
+ }
+ }
+ if (type == UndefinedType)
+ return(MagickFalse);
+ ((Image *) image)->type=type;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s O p a c i t y S i m i l a r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsOpacitySimilar() returns true if the distance between two opacity
+% values is less than the specified distance in a linear color space. This
+% method is used by MatteFloodFill() and other algorithms which compare
+% two opacity values.
+%
+% The format of the IsOpacitySimilar method is:
+%
+% void IsOpacitySimilar(const Image *image,const PixelPacket *p,
+% const PixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+*/
+MagickExport MagickBooleanType IsOpacitySimilar(const Image *image,
+ const PixelPacket *p,const PixelPacket *q)
+{
+ MagickRealType
+ fuzz,
+ pixel;
+
+ register MagickRealType
+ distance;
+
+ if (image->matte == MagickFalse)
+ return(MagickTrue);
+ if (p->opacity == q->opacity)
+ return(MagickTrue);
+ fuzz=MagickMax(image->fuzz,MagickSQ1_2)*MagickMax(image->fuzz,MagickSQ1_2);
+ pixel=(MagickRealType) p->opacity-(MagickRealType) q->opacity;
+ distance=pixel*pixel;
+ if (distance > fuzz)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s O p a q u e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
+% an opacity value other than opaque (0).
+%
+% The format of the IsOpaqueImage method is:
+%
+% MagickBooleanType IsOpaqueImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ CacheView
+ *image_view;
+
+ /*
+ Determine if image is opaque.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->matte == MagickFalse)
+ return(MagickTrue);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (p->opacity != OpaqueOpacity)
+ break;
+ p++;
+ }
+ if (x < (long) image->columns)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(y < (long) image->rows ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s P a l e t t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
+% unique colors or less.
+%
+% The format of the IsPaletteImage method is:
+%
+% MagickBooleanType IsPaletteImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsPaletteImage(const Image *image,
+ ExceptionInfo *exception)
+{
+ CubeInfo
+ *cube_info;
+
+ long
+ y;
+
+ MagickPixelPacket
+ pixel,
+ target;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ register NodeInfo
+ *node_info;
+
+ register long
+ i;
+
+ unsigned long
+ id,
+ index,
+ level;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((image->storage_class == PseudoClass) && (image->colors <= 256))
+ return(MagickTrue);
+ if (image->storage_class == PseudoClass)
+ return(MagickFalse);
+ /*
+ Initialize color description tree.
+ */
+ cube_info=GetCubeInfo();
+ if (cube_info == (CubeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ GetMagickPixelPacket(image,&pixel);
+ GetMagickPixelPacket(image,&target);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Start at the root and proceed level by level.
+ */
+ node_info=cube_info->root;
+ index=MaxTreeDepth-1;
+ for (level=1; level < MaxTreeDepth; level++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ id=ColorToNodeId(image,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ node_info->child[id]=GetNodeInfo(cube_info,level);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ break;
+ }
+ }
+ node_info=node_info->child[id];
+ index--;
+ }
+ if (level < MaxTreeDepth)
+ break;
+ for (i=0; i < (long) node_info->number_unique; i++)
+ {
+ SetMagickPixelPacket(image,&node_info->list[i].pixel,
+ &node_info->list[i].index,&target);
+ if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
+ break;
+ }
+ if (i < (long) node_info->number_unique)
+ node_info->list[i].count++;
+ else
+ {
+ /*
+ Add this unique color to the color list.
+ */
+ if (node_info->number_unique == 0)
+ node_info->list=(ColorPacket *) AcquireMagickMemory(
+ sizeof(*node_info->list));
+ else
+ node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
+ (size_t) (i+1),sizeof(*node_info->list));
+ if (node_info->list == (ColorPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ break;
+ }
+ node_info->list[i].pixel=(*p);
+ if ((image->colorspace == CMYKColorspace) ||
+ (image->storage_class == PseudoClass))
+ node_info->list[i].index=indexes[x];
+ node_info->list[i].count=1;
+ node_info->number_unique++;
+ cube_info->colors++;
+ if (cube_info->colors > 256)
+ break;
+ }
+ p++;
+ }
+ if (x < (long) image->columns)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ cube_info=DestroyCubeInfo(image,cube_info);
+ return(y < (long) image->rows ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t C o l o r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListColorInfo() lists color names to the specified file. Color names
+% are a convenience. Rather than defining a color by its red, green, and
+% blue intensities just use a color name such as white, blue, or yellow.
+%
+% The format of the ListColorInfo method is:
+%
+% MagickBooleanType ListColorInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: List color names to this file handle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListColorInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ char
+ tuple[MaxTextExtent];
+
+ const char
+ *path;
+
+ const ColorInfo
+ **color_info;
+
+ register long
+ i;
+
+ unsigned long
+ number_colors;
+
+ /*
+ List name and attributes of each color in the list.
+ */
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ color_info=GetColorInfoList("*",&number_colors,exception);
+ if (color_info == (const ColorInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_colors; i++)
+ {
+ if (color_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,color_info[i]->path) != 0))
+ {
+ if (color_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",color_info[i]->path);
+ (void) fprintf(file,"Name Color "
+ " Compliance\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=color_info[i]->path;
+ (void) fprintf(file,"%-21.21s ",color_info[i]->name);
+ GetColorTuple(&color_info[i]->color,MagickFalse,tuple);
+ (void) fprintf(file,"%-45.45s ",tuple);
+ if ((color_info[i]->compliance & SVGCompliance) != 0)
+ (void) fprintf(file,"SVG ");
+ if ((color_info[i]->compliance & X11Compliance) != 0)
+ (void) fprintf(file,"X11 ");
+ if ((color_info[i]->compliance & XPMCompliance) != 0)
+ (void) fprintf(file,"XPM ");
+ (void) fprintf(file,"\n");
+ }
+ color_info=(const ColorInfo **) RelinquishMagickMemory((void *) color_info);
+ (void) fflush(file);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d C o l o r L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadColorList() loads the color configuration file which provides a mapping
+% between color attributes and a color name.
+%
+% The format of the LoadColorList method is:
+%
+% MagickBooleanType LoadColorList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The color list in XML format.
+%
+% o filename: The color list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadColorList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ ColorInfo
+ *color_info;
+
+ const char
+ *q;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Load the color map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading color file \"%s\" ...",filename);
+ if (xml == (char *) NULL)
+ return(MagickFalse);
+ if (color_list == (LinkedListInfo *) NULL)
+ {
+ color_list=NewLinkedList(0);
+ if (color_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ color_info=(ColorInfo *) NULL;
+ token=AcquireString(xml);
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadColorList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<color") == 0)
+ {
+ /*
+ Color element.
+ */
+ color_info=(ColorInfo *) AcquireMagickMemory(sizeof(*color_info));
+ if (color_info == (ColorInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(color_info,0,sizeof(*color_info));
+ color_info->path=ConstantString(filename);
+ color_info->signature=MagickSignature;
+ continue;
+ }
+ if (color_info == (ColorInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AppendValueToLinkedList(color_list,color_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ color_info->name);
+ color_info=(ColorInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'C':
+ case 'c':
+ {
+ if (LocaleCompare((char *) keyword,"color") == 0)
+ {
+ (void) QueryMagickColor(token,&color_info->color,exception);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"compliance") == 0)
+ {
+ long
+ compliance;
+
+ compliance=color_info->compliance;
+ if (GlobExpression(token,"*SVG*",MagickTrue) != MagickFalse)
+ compliance|=SVGCompliance;
+ if (GlobExpression(token,"*X11*",MagickTrue) != MagickFalse)
+ compliance|=X11Compliance;
+ if (GlobExpression(token,"*XPM*",MagickTrue) != MagickFalse)
+ compliance|=XPMCompliance;
+ color_info->compliance=(ComplianceType) compliance;
+ break;
+ }
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ color_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ color_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d C o l o r L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadColorList() loads one or more color configuration file which provides a
+% mapping between color attributes and a color name.
+%
+% The format of the LoadColorLists method is:
+%
+% MagickBooleanType LoadColorLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadColorLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadColorList(ColorMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadColorList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((color_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(color_list) != MagickFalse))
+ status|=LoadColorList(ColorMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e r y C o l o r D a t a b a s e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueryColorDatabase() returns the red, green, blue, and opacity intensities
+% for a given color name.
+%
+% The format of the QueryColorDatabase method is:
+%
+% MagickBooleanType QueryColorDatabase(const char *name,PixelPacket *color,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the color name (e.g. white, blue, yellow).
+%
+% o color: the red, green, blue, and opacity intensities values of the
+% named color in this structure.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType QueryColorDatabase(const char *name,
+ PixelPacket *color,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ pixel;
+
+ status=QueryMagickColor(name,&pixel,exception);
+ color->opacity=RoundToQuantum(pixel.opacity);
+ if (pixel.colorspace == CMYKColorspace)
+ {
+ color->red=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin(
+ QuantumRange,(MagickRealType) (QuantumScale*pixel.red*(QuantumRange-
+ pixel.index)+pixel.index))));
+ color->green=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin(
+ QuantumRange,(MagickRealType) (QuantumScale*pixel.green*(QuantumRange-
+ pixel.index)+pixel.index))));
+ color->blue=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin(
+ QuantumRange,(MagickRealType) (QuantumScale*pixel.blue*(QuantumRange-
+ pixel.index)+pixel.index))));
+ return(status);
+ }
+ color->red=RoundToQuantum(pixel.red);
+ color->green=RoundToQuantum(pixel.green);
+ color->blue=RoundToQuantum(pixel.blue);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e r y C o l o r n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueryColorname() returns a named color for the given color intensity. If
+% an exact match is not found, a rgb() color is returned instead.
+%
+% The format of the QueryColorname method is:
+%
+% MagickBooleanType QueryColorname(const Image *image,
+% const PixelPacket *color,const ComplianceType compliance,char *name,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o color: the color intensities.
+%
+% o compliance: Adhere to this color standard: SVG, X11, or XPM.
+%
+% o name: Return the color name or hex value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType QueryColorname(const Image *image,
+ const PixelPacket *color,const ComplianceType compliance,char *name,
+ ExceptionInfo *exception)
+{
+ MagickPixelPacket
+ pixel;
+
+ GetMagickPixelPacket(image,&pixel);
+ SetMagickPixelPacket(image,color,(IndexPacket *) NULL,&pixel);
+ return(QueryMagickColorname(image,&pixel,compliance,name,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e r y M a g i c k C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueryMagickColor() returns the red, green, blue, and opacity intensities
+% for a given color name.
+%
+% The format of the QueryMagickColor method is:
+%
+% MagickBooleanType QueryMagickColor(const char *name,
+% MagickPixelPacket *color,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the color name (e.g. white, blue, yellow).
+%
+% o color: the red, green, blue, and opacity intensities values of the
+% named color in this structure.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType QueryMagickColor(const char *name,
+ MagickPixelPacket *color,ExceptionInfo *exception)
+{
+ GeometryInfo
+ geometry_info;
+
+ long
+ type;
+
+ MagickRealType
+ scale;
+
+ MagickStatusType
+ flags;
+
+ register const ColorInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Initialize color return value.
+ */
+ assert(name != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
+ assert(color != (MagickPixelPacket *) NULL);
+ GetMagickPixelPacket((Image *) NULL,color);
+ if ((name == (char *) NULL) || (*name == '\0'))
+ name=BackgroundColor;
+ while (isspace((int) ((unsigned char) *name)) != 0)
+ name++;
+ if (*name == '#')
+ {
+ char
+ c;
+
+ LongPixelPacket
+ pixel;
+
+ QuantumAny
+ range;
+
+ unsigned long
+ depth,
+ n;
+
+ /*
+ Parse hex color.
+ */
+ (void) ResetMagickMemory(&pixel,0,sizeof(pixel));
+ name++;
+ for (n=0; isxdigit((int) ((unsigned char) name[n])) != MagickFalse; n++) ;
+ if ((n % 3) == 0)
+ {
+ do
+ {
+ pixel.red=pixel.green;
+ pixel.green=pixel.blue;
+ pixel.blue=0;
+ for (i=(long) (n/3-1); i >= 0; i--)
+ {
+ c=(*name++);
+ pixel.blue<<=4;
+ if ((c >= '0') && (c <= '9'))
+ pixel.blue|=(int) (c-'0');
+ else
+ if ((c >= 'A') && (c <= 'F'))
+ pixel.blue|=(int) c-((int) 'A'-10);
+ else
+ if ((c >= 'a') && (c <= 'f'))
+ pixel.blue|=(int) c-((int) 'a'-10);
+ else
+ return(MagickFalse);
+ }
+ } while (isxdigit((int) ((unsigned char) *name)) != MagickFalse);
+ depth=4*(n/3);
+ }
+ else
+ {
+ if ((n % 4) != 0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"UnrecognizedColor","`%s'",name);
+ return(MagickFalse);
+ }
+ do
+ {
+ pixel.red=pixel.green;
+ pixel.green=pixel.blue;
+ pixel.blue=pixel.opacity;
+ pixel.opacity=0;
+ for (i=(long) (n/4-1); i >= 0; i--)
+ {
+ c=(*name++);
+ pixel.opacity<<=4;
+ if ((c >= '0') && (c <= '9'))
+ pixel.opacity|=(int) (c-'0');
+ else
+ if ((c >= 'A') && (c <= 'F'))
+ pixel.opacity|=(int) c-((int) 'A'-10);
+ else
+ if ((c >= 'a') && (c <= 'f'))
+ pixel.opacity|=(int) c-((int) 'a'-10);
+ else
+ return(MagickFalse);
+ }
+ } while (isxdigit((int) ((unsigned char) *name)) != MagickFalse);
+ depth=4*(n/4);
+ }
+ color->colorspace=RGBColorspace;
+ color->matte=MagickFalse;
+ range=GetQuantumRange(depth);
+ color->red=(MagickRealType) ScaleAnyToQuantum(pixel.red,range);
+ color->green=(MagickRealType) ScaleAnyToQuantum(pixel.green,range);
+ color->blue=(MagickRealType) ScaleAnyToQuantum(pixel.blue,range);
+ color->opacity=(MagickRealType) OpaqueOpacity;
+ if ((n % 3) != 0)
+ {
+ color->matte=MagickTrue;
+ color->opacity=(MagickRealType) (QuantumRange-ScaleAnyToQuantum(
+ pixel.opacity,range));
+ }
+ color->index=0.0;
+ return(MagickTrue);
+ }
+ if (strchr(name,'(') != (char *) NULL)
+ {
+ char
+ colorspace[MaxTextExtent];
+
+ /*
+ Parse color of the form rgb(100,255,0).
+ */
+ (void) CopyMagickString(colorspace,name,MaxTextExtent);
+ for (i=0; colorspace[i] != '\0'; i++)
+ if (colorspace[i] == '(')
+ break;
+ colorspace[i--]='\0';
+ LocaleLower(colorspace);
+ color->matte=MagickFalse;
+ if ((i > 0) && (colorspace[i] == 'a'))
+ {
+ colorspace[i]='\0';
+ color->matte=MagickTrue;
+ }
+ type=ParseMagickOption(MagickColorspaceOptions,MagickFalse,colorspace);
+ if (type < 0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"UnrecognizedColor","`%s'",name);
+ return(MagickFalse);
+ }
+ color->colorspace=(ColorspaceType) type;
+ SetGeometryInfo(&geometry_info);
+ flags=ParseGeometry(name+i+1,&geometry_info);
+ scale=(MagickRealType) ScaleCharToQuantum(1);
+ if ((flags & PercentValue) != 0)
+ scale=(MagickRealType) (QuantumRange/100.0);
+ if ((flags & RhoValue) != 0)
+ color->red=(MagickRealType) RoundToQuantum(scale*geometry_info.rho);
+ if ((flags & SigmaValue) != 0)
+ color->green=(MagickRealType) RoundToQuantum(scale*geometry_info.sigma);
+ if ((flags & XiValue) != 0)
+ color->blue=(MagickRealType) RoundToQuantum(scale*geometry_info.xi);
+ color->opacity=(MagickRealType) OpaqueOpacity;
+ if ((flags & PsiValue) != 0)
+ {
+ if (color->colorspace == CMYKColorspace)
+ color->index=(MagickRealType) RoundToQuantum(scale*
+ geometry_info.psi);
+ else
+ if (color->matte != MagickFalse)
+ color->opacity=(MagickRealType) RoundToQuantum((MagickRealType)
+ (QuantumRange-QuantumRange*geometry_info.psi));
+ }
+ if (((flags & ChiValue) != 0) && (color->matte != MagickFalse))
+ color->opacity=(MagickRealType) RoundToQuantum((MagickRealType)
+ (QuantumRange-QuantumRange*geometry_info.chi));
+ if (LocaleCompare(colorspace,"gray") == 0)
+ {
+ color->green=color->red;
+ color->blue=color->red;
+ if (((flags & SigmaValue) != 0) && (color->matte != MagickFalse))
+ color->opacity=(MagickRealType) RoundToQuantum((MagickRealType)
+ (QuantumRange-QuantumRange*geometry_info.sigma));
+ }
+ if (LocaleCompare(colorspace,"HSL") == 0)
+ {
+ PixelPacket
+ pixel;
+
+ geometry_info.rho=fmod(fmod(geometry_info.rho,360.0)+360.0,360.0)/
+ 360.0;
+ geometry_info.sigma/=100.0;
+ geometry_info.xi/=100.0;
+ ConvertHSLToRGB(geometry_info.rho,geometry_info.sigma,
+ geometry_info.xi,&pixel.red,&pixel.green,&pixel.blue);
+ color->colorspace=RGBColorspace;
+ color->red=(MagickRealType) pixel.red;
+ color->green=(MagickRealType) pixel.green;
+ color->blue=(MagickRealType) pixel.blue;
+ }
+ return(MagickTrue);
+ }
+ /*
+ Parse named color.
+ */
+ p=GetColorInfo(name,exception);
+ if (p == (const ColorInfo *) NULL)
+ return(MagickFalse);
+ color->colorspace=RGBColorspace;
+ color->matte=p->color.opacity != OpaqueOpacity ? MagickTrue : MagickFalse;
+ color->red=(MagickRealType) p->color.red;
+ color->green=(MagickRealType) p->color.green;
+ color->blue=(MagickRealType) p->color.blue;
+ color->opacity=(MagickRealType) p->color.opacity;
+ color->index=0.0;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u e r y M a g i c k C o l o r n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueryMagickColorname() returns a named color for the given color intensity.
+% If an exact match is not found, a hex value is returned instead. For
+% example an intensity of rgb:(0,0,0) returns black whereas rgb:(223,223,223)
+% returns #dfdfdf.
+%
+% The format of the QueryMagickColorname method is:
+%
+% MagickBooleanType QueryMagickColorname(const Image *image,
+% const PixelPacket *color,const ComplianceType compliance,char *name,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o color: the color intensities.
+%
+% o Compliance: Adhere to this color standard: SVG, X11, or XPM.
+%
+% o name: Return the color name or hex value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType QueryMagickColorname(const Image *image,
+ const MagickPixelPacket *color,const ComplianceType compliance,
+ char *name,ExceptionInfo *exception)
+{
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ opacity;
+
+ register const ColorInfo
+ *p;
+
+ *name='\0';
+ pixel=(*color);
+ if (compliance == XPMCompliance)
+ {
+ pixel.matte=MagickFalse;
+ pixel.depth=(unsigned long) MagickMin(1.0*image->depth,16.0);
+ GetColorTuple(&pixel,MagickTrue,name);
+ return(MagickTrue);
+ }
+ GetColorTuple(&pixel,compliance != SVGCompliance ? MagickTrue : MagickFalse,
+ name);
+ (void) GetColorInfo("*",exception);
+ ResetLinkedListIterator(color_list);
+ opacity=image->matte != MagickFalse ? color->opacity : OpaqueOpacity;
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ while (p != (const ColorInfo *) NULL)
+ {
+ if (((p->compliance & compliance) != 0) && ((p->color.red == color->red)) &&
+ (p->color.green == color->green) && (p->color.blue == color->blue) &&
+ (p->color.opacity == opacity))
+ {
+ (void) CopyMagickString(name,p->name,MaxTextExtent);
+ break;
+ }
+ p=(const ColorInfo *) GetNextValueInLinkedList(color_list);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n i q u e I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UniqueImageColors() returns the unique colors of an image.
+%
+% The format of the UniqueImageColors method is:
+%
+% Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info,ExceptionInfo *exception)
+{
+#define UniqueColorsImageTag "UniqueColors/Image"
+
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=image->matte == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
+ if (node_info->level == (MaxTreeDepth-1))
+ {
+ register ColorPacket
+ *p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=node_info->list;
+ for (i=0; i < (long) node_info->number_unique; i++)
+ {
+ q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ continue;
+ indexes=GetAuthenticIndexQueue(image);
+ *q=p->pixel;
+ if (image->colorspace == CMYKColorspace)
+ *indexes=p->index;
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ cube_info->x++;
+ p++;
+ }
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(cube_info->progress,cube_info->colors) != MagickFalse))
+ (void) image->progress_monitor(UniqueColorsImageTag,cube_info->progress,
+ cube_info->colors,image->client_data);
+ cube_info->progress++;
+ }
+}
+
+MagickExport Image *UniqueImageColors(const Image *image,
+ ExceptionInfo *exception)
+{
+ CubeInfo
+ *cube_info;
+
+ Image
+ *unique_image;
+
+ cube_info=ClassifyImageColors(image,exception);
+ if (cube_info == (CubeInfo *) NULL)
+ return((Image *) NULL);
+ unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
+ if (unique_image == (Image *) NULL)
+ return(unique_image);
+ if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&unique_image->exception);
+ unique_image=DestroyImage(unique_image);
+ return((Image *) NULL);
+ }
+ UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
+ if (cube_info->colors < MaxColormapSize)
+ {
+ QuantizeInfo
+ *quantize_info;
+
+ quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
+ quantize_info->number_colors=MaxColormapSize;
+ quantize_info->dither=MagickFalse;
+ quantize_info->tree_depth=8;
+ (void) QuantizeImage(quantize_info,unique_image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ }
+ cube_info=DestroyCubeInfo(image,cube_info);
+ return(unique_image);
+}
diff --git a/magick/color.h b/magick/color.h
new file mode 100644
index 0000000..eaed174
--- /dev/null
+++ b/magick/color.h
@@ -0,0 +1,125 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image color methods.
+*/
+#ifndef _MAGICKCORE_COLOR_H
+#define _MAGICKCORE_COLOR_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/pixel.h>
+#include <magick/exception.h>
+
+typedef enum
+{
+ UndefinedCompliance,
+ NoCompliance = 0x0000,
+ SVGCompliance = 0x0001,
+ X11Compliance = 0x0002,
+ XPMCompliance = 0x0004,
+ AllCompliance = 0x7fffffff
+} ComplianceType;
+
+typedef struct _ColorInfo
+{
+ char
+ *path,
+ *name;
+
+ ComplianceType
+ compliance;
+
+ MagickPixelPacket
+ color;
+
+ MagickBooleanType
+ stealth;
+
+ struct _ColorInfo
+ *previous,
+ *next; /* deprecated, use GetColorInfoList() */
+
+ unsigned long
+ signature;
+} ColorInfo;
+
+typedef struct _ColorPacket
+{
+ PixelPacket
+ pixel;
+
+ IndexPacket
+ index;
+
+ MagickSizeType
+ count;
+} ColorPacket;
+
+typedef struct _ErrorInfo
+{
+ double
+ mean_error_per_pixel,
+ normalized_mean_error,
+ normalized_maximum_error;
+} ErrorInfo;
+
+extern MagickExport char
+ **GetColorList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const ColorInfo
+ *GetColorInfo(const char *,ExceptionInfo *),
+ **GetColorInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport ColorPacket
+ *GetImageHistogram(const Image *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport Image
+ *UniqueImageColors(const Image *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ IsColorSimilar(const Image *,const PixelPacket *,const PixelPacket *),
+ IsGrayImage(const Image *,ExceptionInfo *),
+ IsHistogramImage(const Image *,ExceptionInfo *),
+ IsImageSimilar(const Image *,const Image *,long *x,long *y,ExceptionInfo *),
+ IsMagickColorSimilar(const MagickPixelPacket *,const MagickPixelPacket *),
+ IsMonochromeImage(const Image *,ExceptionInfo *),
+ IsOpacitySimilar(const Image *,const PixelPacket *,const PixelPacket *),
+ IsOpaqueImage(const Image *,ExceptionInfo *),
+ IsPaletteImage(const Image *,ExceptionInfo *),
+ ListColorInfo(FILE *,ExceptionInfo *),
+ QueryColorDatabase(const char *,PixelPacket *,ExceptionInfo *),
+ QueryColorname(const Image *,const PixelPacket *,const ComplianceType,char *,
+ ExceptionInfo *),
+ QueryMagickColor(const char *,MagickPixelPacket *,ExceptionInfo *),
+ QueryMagickColorname(const Image *,const MagickPixelPacket *,
+ const ComplianceType,char *,ExceptionInfo *);
+
+extern MagickExport unsigned long
+ GetNumberColors(const Image *,FILE *,ExceptionInfo *);
+
+extern MagickExport void
+ ConcatenateColorComponent(const MagickPixelPacket *,const ChannelType,
+ const ComplianceType,char *),
+ DestroyColorList(void),
+ GetColorTuple(const MagickPixelPacket *,const MagickBooleanType,char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/colorspace-private.h b/magick/colorspace-private.h
new file mode 100644
index 0000000..a8a15e9
--- /dev/null
+++ b/magick/colorspace-private.h
@@ -0,0 +1,71 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image colorspace private methods.
+*/
+#ifndef _MAGICKCORE_COLORSPACE_PRIVATE_H
+#define _MAGICKCORE_COLORSPACE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/pixel.h>
+
+static inline void ConvertRGBToCMYK(MagickPixelPacket *pixel)
+{
+ MagickRealType
+ black,
+ cyan,
+ magenta,
+ yellow;
+
+ cyan=(MagickRealType) (QuantumRange-pixel->red);
+ magenta=(MagickRealType) (QuantumRange-pixel->green);
+ yellow=(MagickRealType) (QuantumRange-pixel->blue);
+ black=(MagickRealType) QuantumRange;
+ if (cyan < black)
+ black=cyan;
+ if (magenta < black)
+ black=magenta;
+ if (yellow < black)
+ black=yellow;
+ if (black == QuantumRange)
+ {
+ cyan=0.0;
+ magenta=0.0;
+ yellow=0.0;
+ }
+ else
+ {
+ cyan=(MagickRealType) (QuantumRange*(cyan-black)/
+ (QuantumRange-black));
+ magenta=(MagickRealType) (QuantumRange*(magenta-black)/
+ (QuantumRange-black));
+ yellow=(MagickRealType) (QuantumRange*(yellow-black)/
+ (QuantumRange-black));
+ }
+ pixel->colorspace=CMYKColorspace;
+ pixel->red=cyan;
+ pixel->green=magenta;
+ pixel->blue=yellow;
+ pixel->index=black;
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/colorspace.c b/magick/colorspace.c
new file mode 100644
index 0000000..2611ed2
--- /dev/null
+++ b/magick/colorspace.c
@@ -0,0 +1,2366 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO L OOO RRRR SSSSS PPPP AAA CCCC EEEEE %
+% C O O L O O R R SS P P A A C E %
+% C O O L O O RRRR SSS PPPP AAAAA C EEE %
+% C O O L O O R R SS P A A C E %
+% CCCC OOO LLLLL OOO R R SSSSS P A A CCCC EEEEE %
+% %
+% %
+% MagickCore Image Colorspace Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/colorspace-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/gem.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel-private.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+ Typedef declarations.
+*/
+typedef struct _TransformPacket
+{
+ MagickRealType
+ x,
+ y,
+ z;
+} TransformPacket;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R G B T r a n s f o r m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RGBTransformImage() converts the reference image from RGB to an alternate
+% colorspace. The transformation matrices are not the standard ones: the
+% weights are rescaled to normalized the range of the transformed values to
+% be [0..QuantumRange].
+%
+% The format of the RGBTransformImage method is:
+%
+% MagickBooleanType RGBTransformImage(Image *image,
+% const ColorspaceType colorspace)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colorspace: the colorspace to transform the image to.
+%
+*/
+
+static inline void ConvertRGBToXYZ(const Quantum red,const Quantum green,
+ const Quantum blue,double *X,double *Y,double *Z)
+{
+ double
+ b,
+ g,
+ r;
+
+ assert(X != (double *) NULL);
+ assert(Y != (double *) NULL);
+ assert(Z != (double *) NULL);
+ r=QuantumScale*red;
+ g=QuantumScale*green;
+ b=QuantumScale*blue;
+ *X=0.4124240*r+0.3575790*g+0.1804640*b;
+ *Y=0.2126560*r+0.7151580*g+0.0721856*b;
+ *Z=0.0193324*r+0.1191930*g+0.9504440*b;
+}
+
+static inline void ConvertXYZToLab(const double X,const double Y,const double Z,
+ double *L,double *a,double *b)
+{
+ double
+ x,
+ y,
+ z;
+
+ assert(L != (double *) NULL);
+ assert(a != (double *) NULL);
+ assert(b != (double *) NULL);
+ x=X/0.9504559271;
+ if (x > (216/24389.0))
+ x=pow(x,1.0/3.0);
+ else
+ x=(7.787*x)+(16.0/116.0);
+ y=Y/1.00000;
+ if (y > (216/24389.0))
+ y=pow(y,1.0/3.0);
+ else
+ y=(7.787*y)+(16.0/116.0);
+ z=Z/1.0890577508;
+ if (z > (216/24389.0))
+ z=pow(z,1.0/3.0);
+ else
+ z=(7.787*z)+(16.0/116.0);
+ *L=0.5*((1.160*y)-0.160+1.0);
+ *a=0.5*(5.000*(x-y)+1.0);
+ *b=0.5*(2.000*(y-z)+1.0);
+}
+
+MagickExport MagickBooleanType RGBTransformImage(Image *image,
+ const ColorspaceType colorspace)
+{
+#define RGBTransformImageTag "RGBTransform/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status,
+ sync;
+
+ PrimaryInfo
+ primary_info;
+
+ register long
+ i;
+
+ TransformPacket
+ *x_map,
+ *y_map,
+ *z_map;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(colorspace != RGBColorspace);
+ assert(colorspace != TransparentColorspace);
+ assert(colorspace != UndefinedColorspace);
+ switch (image->colorspace)
+ {
+ case GRAYColorspace:
+ case Rec601LumaColorspace:
+ case Rec709LumaColorspace:
+ case RGBColorspace:
+ case TransparentColorspace:
+ break;
+ default:
+ {
+ (void) TransformImageColorspace(image,image->colorspace);
+ break;
+ }
+ }
+ if (SetImageColorspace(image,colorspace) == MagickFalse)
+ return(MagickFalse);
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ switch (colorspace)
+ {
+ case CMYColorspace:
+ {
+ /*
+ Convert RGB to CMY colorspace.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) (QuantumRange-q->red));
+ q->green=RoundToQuantum((MagickRealType) (QuantumRange-q->green));
+ q->blue=RoundToQuantum((MagickRealType) (QuantumRange-q->blue));
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ image->type=image->matte == MagickFalse ? ColorSeparationType :
+ ColorSeparationMatteType;
+ return(status);
+ }
+ case CMYKColorspace:
+ {
+ MagickPixelPacket
+ zero;
+
+ /*
+ Convert RGB to CMYK colorspace.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ ConvertRGBToCMYK(&pixel);
+ SetPixelPacket(image,&pixel,q,indexes+x);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ image->type=image->matte == MagickFalse ? ColorSeparationType :
+ ColorSeparationMatteType;
+ return(status);
+ }
+ case HSBColorspace:
+ {
+ /*
+ Transform image from RGB to HSB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ brightness,
+ hue,
+ saturation;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ hue=0.0;
+ saturation=0.0;
+ brightness=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ ConvertRGBToHSB(q->red,q->green,q->blue,&hue,&saturation,&brightness);
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*saturation);
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case HSLColorspace:
+ {
+ /*
+ Transform image from RGB to HSL.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ hue,
+ lightness,
+ saturation;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ hue=0.0;
+ saturation=0.0;
+ lightness=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ ConvertRGBToHSL(q->red,q->green,q->blue,&hue,&saturation,&lightness);
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*saturation);
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*lightness);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case HWBColorspace:
+ {
+ /*
+ Transform image from RGB to HWB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ blackness,
+ hue,
+ whiteness;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ hue=0.0;
+ whiteness=0.0;
+ blackness=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ ConvertRGBToHWB(q->red,q->green,q->blue,&hue,&whiteness,&blackness);
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*hue);
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*whiteness);
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*blackness);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case LabColorspace:
+ {
+ /*
+ Transform image from RGB to Lab.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ a,
+ b,
+ L,
+ X,
+ Y,
+ Z;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ L=0.0;
+ a=0.0;
+ b=0.0;
+ X=0.0;
+ Y=0.0;
+ Z=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ ConvertRGBToXYZ(q->red,q->green,q->blue,&X,&Y,&Z);
+ ConvertXYZToLab(X,Y,Z,&L,&a,&b);
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*L);
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*a);
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*b);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case LogColorspace:
+ {
+#define ReferenceBlack 95.0
+#define ReferenceWhite 685.0
+#define DisplayGamma (1.0/1.7)
+
+ const char
+ *value;
+
+ double
+ black,
+ density,
+ gamma,
+ reference_black,
+ reference_white;
+
+ Quantum
+ *logmap;
+
+ /*
+ Transform RGB to Log colorspace.
+ */
+ density=2.03728;
+ gamma=DisplayGamma;
+ value=GetImageProperty(image,"gamma");
+ if (value != (const char *) NULL)
+ gamma=1.0/atof(value) != 0.0 ? atof(value) : 1.0;
+ reference_black=ReferenceBlack;
+ value=GetImageProperty(image,"reference-black");
+ if (value != (const char *) NULL)
+ reference_black=atof(value);
+ reference_white=ReferenceWhite;
+ value=GetImageProperty(image,"reference-white");
+ if (value != (const char *) NULL)
+ reference_white=atof(value);
+ logmap=(Quantum *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*logmap));
+ if (logmap == (Quantum *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ black=pow(10.0,(reference_black-reference_white)*(gamma/density)*
+ 0.002/0.6);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ logmap[i]=ScaleMapToQuantum((MagickRealType) (MaxMap*(reference_white+
+ log10(black+((MagickRealType) i/MaxMap)*(1.0-black))/((gamma/density)*
+ 0.002/0.6))/1024.0+0.5));
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=(long) image->columns; x != 0; x--)
+ {
+ q->red=logmap[ScaleQuantumToMap(q->red)];
+ q->green=logmap[ScaleQuantumToMap(q->green)];
+ q->blue=logmap[ScaleQuantumToMap(q->blue)];
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ logmap=(Quantum *) RelinquishMagickMemory(logmap);
+ return(status);
+ }
+ default:
+ break;
+ }
+ /*
+ Allocate the tables.
+ */
+ x_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*x_map));
+ y_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*y_map));
+ z_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*z_map));
+ if ((x_map == (TransformPacket *) NULL) ||
+ (y_map == (TransformPacket *) NULL) ||
+ (z_map == (TransformPacket *) NULL))
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ (void) ResetMagickMemory(&primary_info,0,sizeof(primary_info));
+ switch (colorspace)
+ {
+ case OHTAColorspace:
+ {
+ /*
+ Initialize OHTA tables:
+
+ I1 = 0.33333*R+0.33334*G+0.33333*B
+ I2 = 0.50000*R+0.00000*G-0.50000*B
+ I3 =-0.25000*R+0.50000*G-0.25000*B
+
+ I and Q, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange.
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.33333f*(MagickRealType) i;
+ y_map[i].x=0.33334f*(MagickRealType) i;
+ z_map[i].x=0.33333f*(MagickRealType) i;
+ x_map[i].y=0.50000f*(MagickRealType) i;
+ y_map[i].y=0.00000f*(MagickRealType) i;
+ z_map[i].y=(-0.50000f)*(MagickRealType) i;
+ x_map[i].z=(-0.25000f)*(MagickRealType) i;
+ y_map[i].z=0.50000f*(MagickRealType) i;
+ z_map[i].z=(-0.25000f)*(MagickRealType) i;
+ }
+ break;
+ }
+ case Rec601LumaColorspace:
+ case GRAYColorspace:
+ {
+ /*
+ Initialize Rec601 luma tables:
+
+ G = 0.29900*R+0.58700*G+0.11400*B
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.29900f*(MagickRealType) i;
+ y_map[i].x=0.58700f*(MagickRealType) i;
+ z_map[i].x=0.11400f*(MagickRealType) i;
+ x_map[i].y=0.29900f*(MagickRealType) i;
+ y_map[i].y=0.58700f*(MagickRealType) i;
+ z_map[i].y=0.11400f*(MagickRealType) i;
+ x_map[i].z=0.29900f*(MagickRealType) i;
+ y_map[i].z=0.58700f*(MagickRealType) i;
+ z_map[i].z=0.11400f*(MagickRealType) i;
+ }
+ image->type=GrayscaleType;
+ break;
+ }
+ case Rec601YCbCrColorspace:
+ case YCbCrColorspace:
+ {
+ /*
+ Initialize YCbCr tables (ITU-R BT.601):
+
+ Y = 0.299000*R+0.587000*G+0.114000*B
+ Cb= -0.168736*R-0.331264*G+0.500000*B
+ Cr= 0.500000*R-0.418688*G-0.081312*B
+
+ Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange.
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.299000f*(MagickRealType) i;
+ y_map[i].x=0.587000f*(MagickRealType) i;
+ z_map[i].x=0.114000f*(MagickRealType) i;
+ x_map[i].y=(-0.168730f)*(MagickRealType) i;
+ y_map[i].y=(-0.331264f)*(MagickRealType) i;
+ z_map[i].y=0.500000f*(MagickRealType) i;
+ x_map[i].z=0.500000f*(MagickRealType) i;
+ y_map[i].z=(-0.418688f)*(MagickRealType) i;
+ z_map[i].z=(-0.081312f)*(MagickRealType) i;
+ }
+ break;
+ }
+ case Rec709LumaColorspace:
+ {
+ /*
+ Initialize Rec709 luma tables:
+
+ G = 0.21260*R+0.71520*G+0.07220*B
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.21260f*(MagickRealType) i;
+ y_map[i].x=0.71520f*(MagickRealType) i;
+ z_map[i].x=0.07220f*(MagickRealType) i;
+ x_map[i].y=0.21260f*(MagickRealType) i;
+ y_map[i].y=0.71520f*(MagickRealType) i;
+ z_map[i].y=0.07220f*(MagickRealType) i;
+ x_map[i].z=0.21260f*(MagickRealType) i;
+ y_map[i].z=0.71520f*(MagickRealType) i;
+ z_map[i].z=0.07220f*(MagickRealType) i;
+ }
+ break;
+ }
+ case Rec709YCbCrColorspace:
+ {
+ /*
+ Initialize YCbCr tables (ITU-R BT.709):
+
+ Y = 0.212600*R+0.715200*G+0.072200*B
+ Cb= -0.114572*R-0.385428*G+0.500000*B
+ Cr= 0.500000*R-0.454153*G-0.045847*B
+
+ Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange.
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.212600f*(MagickRealType) i;
+ y_map[i].x=0.715200f*(MagickRealType) i;
+ z_map[i].x=0.072200f*(MagickRealType) i;
+ x_map[i].y=(-0.114572f)*(MagickRealType) i;
+ y_map[i].y=(-0.385428f)*(MagickRealType) i;
+ z_map[i].y=0.500000f*(MagickRealType) i;
+ x_map[i].z=0.500000f*(MagickRealType) i;
+ y_map[i].z=(-0.454153f)*(MagickRealType) i;
+ z_map[i].z=(-0.045847f)*(MagickRealType) i;
+ }
+ break;
+ }
+ case sRGBColorspace:
+ {
+ /*
+ Linear RGB to nonlinear sRGB (http://www.w3.org/Graphics/Color/sRGB):
+
+ R = 1.0*R+0.0*G+0.0*B
+ G = 0.0*R+0.1*G+0.0*B
+ B = 0.0*R+0.0*G+1.0*B
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ MagickRealType
+ v;
+
+ v=(MagickRealType) i/(MagickRealType) MaxMap;
+ if (((MagickRealType) i/(MagickRealType) MaxMap) <= 0.03928f)
+ v/=12.92f;
+ else
+ v=(MagickRealType) MaxMap*pow((((double) i/MaxMap)+0.055)/1.055,2.4);
+ x_map[i].x=1.0f*v;
+ y_map[i].x=0.0f*v;
+ z_map[i].x=0.0f*v;
+ x_map[i].y=0.0f*v;
+ y_map[i].y=1.0f*v;
+ z_map[i].y=0.0f*v;
+ x_map[i].z=0.0f*v;
+ y_map[i].z=0.0f*v;
+ z_map[i].z=1.0f*v;
+ }
+ break;
+ }
+ case XYZColorspace:
+ {
+ /*
+ Initialize CIE XYZ tables (ITU-R 709 RGB):
+
+ X = 0.4124240*R+0.3575790*G+0.1804640*B
+ Y = 0.2126560*R+0.7151580*G+0.0721856*B
+ Z = 0.0193324*R+0.1191930*G+0.9504440*B
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.4124240f*(MagickRealType) i;
+ y_map[i].x=0.3575790f*(MagickRealType) i;
+ z_map[i].x=0.1804640f*(MagickRealType) i;
+ x_map[i].y=0.2126560f*(MagickRealType) i;
+ y_map[i].y=0.7151580f*(MagickRealType) i;
+ z_map[i].y=0.0721856f*(MagickRealType) i;
+ x_map[i].z=0.0193324f*(MagickRealType) i;
+ y_map[i].z=0.1191930f*(MagickRealType) i;
+ z_map[i].z=0.9504440f*(MagickRealType) i;
+ }
+ break;
+ }
+ case YCCColorspace:
+ {
+ /*
+ Initialize YCC tables:
+
+ Y = 0.29900*R+0.58700*G+0.11400*B
+ C1= -0.29900*R-0.58700*G+0.88600*B
+ C2= 0.70100*R-0.58700*G-0.11400*B
+
+ YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137.
+ */
+ primary_info.y=(double) ScaleQuantumToMap(ScaleCharToQuantum(156));
+ primary_info.z=(double) ScaleQuantumToMap(ScaleCharToQuantum(137));
+ for (i=0; i <= (long) (0.018*MaxMap); i++)
+ {
+ x_map[i].x=0.003962014134275617f*(MagickRealType) i;
+ y_map[i].x=0.007778268551236748f*(MagickRealType) i;
+ z_map[i].x=0.001510600706713781f*(MagickRealType) i;
+ x_map[i].y=(-0.002426619775463276f)*(MagickRealType) i;
+ y_map[i].y=(-0.004763965913702149f)*(MagickRealType) i;
+ z_map[i].y=0.007190585689165425f*(MagickRealType) i;
+ x_map[i].z=0.006927257754597858f*(MagickRealType) i;
+ y_map[i].z=(-0.005800713697502058f)*(MagickRealType) i;
+ z_map[i].z=(-0.0011265440570958f)*(MagickRealType) i;
+ }
+ for ( ; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.2201118963486454*(1.099f*(MagickRealType) i-0.099f);
+ y_map[i].x=0.4321260306242638*(1.099f*(MagickRealType) i-0.099f);
+ z_map[i].x=0.08392226148409894*(1.099f*(MagickRealType) i-0.099f);
+ x_map[i].y=(-0.1348122097479598)*(1.099f*(MagickRealType) i-0.099f);
+ y_map[i].y=(-0.2646647729834528)*(1.099f*(MagickRealType) i-0.099f);
+ z_map[i].y=0.3994769827314126*(1.099f*(MagickRealType) i-0.099f);
+ x_map[i].z=0.3848476530332144*(1.099f*(MagickRealType) i-0.099f);
+ y_map[i].z=(-0.3222618720834477)*(1.099f*(MagickRealType) i-0.099f);
+ z_map[i].z=(-0.06258578094976668)*(1.099f*(MagickRealType) i-0.099f);
+ }
+ break;
+ }
+ case YIQColorspace:
+ {
+ /*
+ Initialize YIQ tables:
+
+ Y = 0.29900*R+0.58700*G+0.11400*B
+ I = 0.59600*R-0.27400*G-0.32200*B
+ Q = 0.21100*R-0.52300*G+0.31200*B
+
+ I and Q, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange.
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.29900f*(MagickRealType) i;
+ y_map[i].x=0.58700f*(MagickRealType) i;
+ z_map[i].x=0.11400f*(MagickRealType) i;
+ x_map[i].y=0.59600f*(MagickRealType) i;
+ y_map[i].y=(-0.27400f)*(MagickRealType) i;
+ z_map[i].y=(-0.32200f)*(MagickRealType) i;
+ x_map[i].z=0.21100f*(MagickRealType) i;
+ y_map[i].z=(-0.52300f)*(MagickRealType) i;
+ z_map[i].z=0.31200f*(MagickRealType) i;
+ }
+ break;
+ }
+ case YPbPrColorspace:
+ {
+ /*
+ Initialize YPbPr tables (ITU-R BT.601):
+
+ Y = 0.299000*R+0.587000*G+0.114000*B
+ Pb= -0.168736*R-0.331264*G+0.500000*B
+ Pr= 0.500000*R-0.418688*G-0.081312*B
+
+ Pb and Pr, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange.
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.299000f*(MagickRealType) i;
+ y_map[i].x=0.587000f*(MagickRealType) i;
+ z_map[i].x=0.114000f*(MagickRealType) i;
+ x_map[i].y=(-0.168736f)*(MagickRealType) i;
+ y_map[i].y=(-0.331264f)*(MagickRealType) i;
+ z_map[i].y=0.500000f*(MagickRealType) i;
+ x_map[i].z=0.500000f*(MagickRealType) i;
+ y_map[i].z=(-0.418688f)*(MagickRealType) i;
+ z_map[i].z=(-0.081312f)*(MagickRealType) i;
+ }
+ break;
+ }
+ case YUVColorspace:
+ default:
+ {
+ /*
+ Initialize YUV tables:
+
+ Y = 0.29900*R+0.58700*G+0.11400*B
+ U = -0.14740*R-0.28950*G+0.43690*B
+ V = 0.61500*R-0.51500*G-0.10000*B
+
+ U and V, normally -0.5 through 0.5, are normalized to the range 0
+ through QuantumRange. Note that U = 0.493*(B-Y), V = 0.877*(R-Y).
+ */
+ primary_info.y=(double) (MaxMap+1.0)/2.0;
+ primary_info.z=(double) (MaxMap+1.0)/2.0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=0.29900f*(MagickRealType) i;
+ y_map[i].x=0.58700f*(MagickRealType) i;
+ z_map[i].x=0.11400f*(MagickRealType) i;
+ x_map[i].y=(-0.14740f)*(MagickRealType) i;
+ y_map[i].y=(-0.28950f)*(MagickRealType) i;
+ z_map[i].y=0.43690f*(MagickRealType) i;
+ x_map[i].z=0.61500f*(MagickRealType) i;
+ y_map[i].z=(-0.51500f)*(MagickRealType) i;
+ z_map[i].z=(-0.10000f)*(MagickRealType) i;
+ }
+ break;
+ }
+ }
+ /*
+ Convert from RGB.
+ */
+ switch (image->storage_class)
+ {
+ case DirectClass:
+ default:
+ {
+ /*
+ Convert DirectClass image.
+ */
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ register unsigned long
+ blue,
+ green,
+ red;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ red=ScaleQuantumToMap(q->red);
+ green=ScaleQuantumToMap(q->green);
+ blue=ScaleQuantumToMap(q->blue);
+ pixel.red=(x_map[red].x+y_map[green].x+z_map[blue].x)+
+ (MagickRealType) primary_info.x;
+ pixel.green=(x_map[red].y+y_map[green].y+z_map[blue].y)+
+ (MagickRealType) primary_info.y;
+ pixel.blue=(x_map[red].z+y_map[green].z+z_map[blue].z)+
+ (MagickRealType) primary_info.z;
+ q->red=ScaleMapToQuantum(pixel.red);
+ q->green=ScaleMapToQuantum(pixel.green);
+ q->blue=ScaleMapToQuantum(pixel.blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RGBTransformImage)
+#endif
+ proceed=SetImageProgress(image,RGBTransformImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ break;
+ }
+ case PseudoClass:
+ {
+ register unsigned long
+ blue,
+ green,
+ red;
+
+ /*
+ Convert PseudoClass image.
+ */
+ image_view=AcquireCacheView(image);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ red=ScaleQuantumToMap(image->colormap[i].red);
+ green=ScaleQuantumToMap(image->colormap[i].green);
+ blue=ScaleQuantumToMap(image->colormap[i].blue);
+ pixel.red=x_map[red].x+y_map[green].x+z_map[blue].x+primary_info.x;
+ pixel.green=x_map[red].y+y_map[green].y+z_map[blue].y+primary_info.y;
+ pixel.blue=x_map[red].z+y_map[green].z+z_map[blue].z+primary_info.z;
+ image->colormap[i].red=ScaleMapToQuantum(pixel.red);
+ image->colormap[i].green=ScaleMapToQuantum(pixel.green);
+ image->colormap[i].blue=ScaleMapToQuantum(pixel.blue);
+ }
+ image_view=DestroyCacheView(image_view);
+ (void) SyncImage(image);
+ break;
+ }
+ }
+ /*
+ Relinquish resources.
+ */
+ z_map=(TransformPacket *) RelinquishMagickMemory(z_map);
+ y_map=(TransformPacket *) RelinquishMagickMemory(y_map);
+ x_map=(TransformPacket *) RelinquishMagickMemory(x_map);
+ if (SetImageColorspace(image,colorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e C o l o r s p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageColorspace() sets the colorspace member of the Image structure.
+%
+% The format of the SetImageColorspace method is:
+%
+% MagickBooleanType SetImageColorspace(Image *image,
+% const ColorspaceType colorspace)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colorspace: the colorspace.
+%
+*/
+MagickExport MagickBooleanType SetImageColorspace(Image *image,
+ const ColorspaceType colorspace)
+{
+ Cache
+ cache;
+
+ if (image->colorspace == colorspace)
+ return(MagickTrue);
+ image->colorspace=colorspace;
+ cache=GetImagePixelCache(image,MagickTrue,&image->exception);
+ image->colorspace=colorspace; /* GRAY colorspace might get reset to RGB */
+ return(cache == (Cache) NULL ? MagickFalse: MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s f o r m I m a g e C o l o r s p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformImageColorspace() transforms an image colorspace.
+%
+% The format of the TransformImageColorspace method is:
+%
+% MagickBooleanType TransformImageColorspace(Image *image,
+% const ColorspaceType colorspace)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colorspace: the colorspace.
+%
+*/
+MagickExport MagickBooleanType TransformImageColorspace(Image *image,
+ const ColorspaceType colorspace)
+{
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (colorspace == UndefinedColorspace)
+ {
+ if (SetImageColorspace(image,colorspace) == MagickFalse)
+ return(MagickFalse);
+ return(MagickTrue);
+ }
+ if (image->colorspace == colorspace)
+ return(MagickTrue);
+ if ((colorspace == RGBColorspace) || (colorspace == TransparentColorspace))
+ return(TransformRGBImage(image,image->colorspace));
+ status=MagickTrue;
+ if ((image->colorspace != RGBColorspace) &&
+ (image->colorspace != TransparentColorspace) &&
+ (image->colorspace != GRAYColorspace))
+ status=TransformRGBImage(image,image->colorspace);
+ if (RGBTransformImage(image,colorspace) == MagickFalse)
+ status=MagickFalse;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ T r a n s f o r m R G B I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformRGBImage() converts the reference image from an alternate
+% colorspace to RGB. The transformation matrices are not the standard ones:
+% the weights are rescaled to normalize the range of the transformed values to
+% be [0..QuantumRange].
+%
+% The format of the TransformRGBImage method is:
+%
+% MagickBooleanType TransformRGBImage(Image *image,
+% const ColorspaceType colorspace)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colorspace: the colorspace to transform the image to.
+%
+*/
+
+static inline void ConvertLabToXYZ(const double L,const double a,const double b,
+ double *X,double *Y,double *Z)
+{
+ double
+ cube,
+ x,
+ y,
+ z;
+
+ assert(X != (double *) NULL);
+ assert(Y != (double *) NULL);
+ assert(Z != (double *) NULL);
+ y=(100.0*L+16.0)/116.0;
+ x=255.0*(a > 0.5 ? a-1.0 : a)/500.0+y;
+ z=y-255.0*(b > 0.5 ? b-1.0 : b)/200.0;
+ cube=y*y*y;
+ if (cube > 0.008856)
+ y=cube;
+ else
+ y=(y-16.0/116.0)/7.787;
+ cube=x*x*x;
+ if (cube > 0.008856)
+ x=cube;
+ else
+ x=(x-16.0/116.0)/7.787;
+ cube=z*z*z;
+ if (cube > 0.008856)
+ z=cube;
+ else
+ z=(z-16.0/116.0)/7.787;
+ *X=0.9504559271*x;
+ *Y=1.0000000000*y;
+ *Z=1.0890577508*z;
+}
+
+static inline unsigned short RoundToYCC(const MagickRealType value)
+{
+ if (value <= 0.0)
+ return(0UL);
+ if (value >= 350.0)
+ return(350);
+ return((unsigned short) (value+0.5));
+}
+
+static inline void ConvertXYZToRGB(const double x,const double y,const double z,
+ Quantum *red,Quantum *green,Quantum *blue)
+{
+ double
+ b,
+ g,
+ r;
+
+ /*
+ Convert XYZ to RGB colorspace.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ r=3.2404542*x-1.5371385*y-0.4985314*z;
+ g=(-0.9692660*x+1.8760108*y+0.0415560*z);
+ b=0.0556434*x-0.2040259*y+1.0572252*z;
+ if (r > 0.0031308)
+ r=1.055*pow(r,1.0/2.4)-0.055;
+ else
+ r*=12.92;
+ if (g > 0.0031308)
+ g=1.055*pow(g,1.0/2.4)-0.055;
+ else
+ g*=12.92;
+ if (b > 0.0031308)
+ b=1.055*pow(b,1.0/2.4)-0.055;
+ else
+ b*=12.92;
+ *red=RoundToQuantum((MagickRealType) QuantumRange*r);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*g);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*b);
+}
+
+static inline void ConvertCMYKToRGB(MagickPixelPacket *pixel)
+{
+ pixel->red=(MagickRealType) QuantumRange-(QuantumScale*pixel->red*
+ (QuantumRange-pixel->index)+pixel->index);
+ pixel->green=(MagickRealType) QuantumRange-(QuantumScale*pixel->green*
+ (QuantumRange-pixel->index)+pixel->index);
+ pixel->blue=(MagickRealType) QuantumRange-(QuantumScale*pixel->blue*
+ (QuantumRange-pixel->index)+pixel->index);
+}
+
+MagickExport MagickBooleanType TransformRGBImage(Image *image,
+ const ColorspaceType colorspace)
+{
+#define D50X (0.9642)
+#define D50Y (1.0)
+#define D50Z (0.8249)
+#define TransformRGBImageTag "Transform/Image"
+
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ static const unsigned char
+ YCCMap[351] = /* Photo CD information beyond 100% white, Gamma 2.2 */
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 28, 29, 30, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+ 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 56, 57, 58,
+ 59, 60, 61, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73,
+ 74, 76, 77, 78, 79, 80, 81, 82, 83, 84, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134,
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162,
+ 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
+ 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 193, 194, 195, 196, 197, 198, 199, 200, 201, 201,
+ 202, 203, 204, 205, 206, 207, 207, 208, 209, 210, 211, 211, 212, 213,
+ 214, 215, 215, 216, 217, 218, 218, 219, 220, 221, 221, 222, 223, 224,
+ 224, 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, 233,
+ 234, 234, 235, 236, 236, 237, 237, 238, 238, 239, 240, 240, 241, 241,
+ 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247,
+ 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251,
+ 251, 251, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253,
+ 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254,
+ 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255
+ };
+#endif
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ TransformPacket
+ *y_map,
+ *x_map,
+ *z_map;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ switch (colorspace)
+ {
+ case GRAYColorspace:
+ case Rec601LumaColorspace:
+ case Rec709LumaColorspace:
+ case RGBColorspace:
+ case TransparentColorspace:
+ case UndefinedColorspace:
+ return(MagickTrue);
+ default:
+ break;
+ }
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ switch (colorspace)
+ {
+ case CMYColorspace:
+ {
+ /*
+ Transform image from CMY to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) (QuantumRange-q->red));
+ q->green=RoundToQuantum((MagickRealType) (QuantumRange-q->green));
+ q->blue=RoundToQuantum((MagickRealType) (QuantumRange-q->blue));
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case CMYKColorspace:
+ {
+ MagickPixelPacket
+ zero;
+
+ /*
+ Transform image from CMYK to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ ConvertCMYKToRGB(&pixel);
+ SetPixelPacket(image,&pixel,q,indexes+x);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case HSBColorspace:
+ {
+ /*
+ Transform image from HSB to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ brightness,
+ hue,
+ saturation;
+
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ hue=(double) (QuantumScale*q->red);
+ saturation=(double) (QuantumScale*q->green);
+ brightness=(double) (QuantumScale*q->blue);
+ ConvertHSBToRGB(hue,saturation,brightness,&q->red,&q->green,&q->blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case HSLColorspace:
+ {
+ /*
+ Transform image from HSL to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ hue,
+ lightness,
+ saturation;
+
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ hue=(double) (QuantumScale*q->red);
+ saturation=(double) (QuantumScale*q->green);
+ lightness=(double) (QuantumScale*q->blue);
+ ConvertHSLToRGB(hue,saturation,lightness,&q->red,&q->green,&q->blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case HWBColorspace:
+ {
+ /*
+ Transform image from HWB to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ blackness,
+ hue,
+ whiteness;
+
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ hue=(double) (QuantumScale*q->red);
+ whiteness=(double) (QuantumScale*q->green);
+ blackness=(double) (QuantumScale*q->blue);
+ ConvertHWBToRGB(hue,whiteness,blackness,&q->red,&q->green,&q->blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case LabColorspace:
+ {
+ /*
+ Transform image from Lab to RGB.
+ */
+ if (image->storage_class == PseudoClass)
+ {
+ if (SyncImage(image) == MagickFalse)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ a,
+ b,
+ L,
+ X,
+ Y,
+ Z;
+
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ X=0.0;
+ Y=0.0;
+ Z=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ L=QuantumScale*q->red;
+ a=QuantumScale*q->green;
+ b=QuantumScale*q->blue;
+ ConvertLabToXYZ(L,a,b,&X,&Y,&Z);
+ ConvertXYZToRGB(X,Y,Z,&q->red,&q->green,&q->blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ case LogColorspace:
+ {
+ const char
+ *value;
+
+ double
+ black,
+ density,
+ gamma,
+ reference_black,
+ reference_white;
+
+ Quantum
+ *logmap;
+
+ /*
+ Transform Log to RGB colorspace.
+ */
+ density=2.03728;
+ gamma=DisplayGamma;
+ value=GetImageProperty(image,"gamma");
+ if (value != (const char *) NULL)
+ gamma=1.0/atof(value) != 0.0 ? atof(value) : 1.0;
+ reference_black=ReferenceBlack;
+ value=GetImageProperty(image,"reference-black");
+ if (value != (const char *) NULL)
+ reference_black=atof(value);
+ reference_white=ReferenceWhite;
+ value=GetImageProperty(image,"reference-white");
+ if (value != (const char *) NULL)
+ reference_white=atof(value);
+ logmap=(Quantum *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*logmap));
+ if (logmap == (Quantum *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ black=pow(10.0,(reference_black-reference_white)*(gamma/density)*
+ 0.002/0.6);
+ for (i=0; i <= (long) (reference_black*MaxMap/1024.0); i++)
+ logmap[i]=(Quantum) 0;
+ for ( ; i < (long) (reference_white*MaxMap/1024.0); i++)
+ logmap[i]=RoundToQuantum((MagickRealType) QuantumRange/(1.0-black)*
+ (pow(10.0,(1024.0*i/MaxMap-reference_white)*
+ (gamma/density)*0.002/0.6)-black));
+ for ( ; i <= (long) MaxMap; i++)
+ logmap[i]=(Quantum) QuantumRange;
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=(long) image->columns; x != 0; x--)
+ {
+ q->red=logmap[ScaleQuantumToMap(q->red)];
+ q->green=logmap[ScaleQuantumToMap(q->green)];
+ q->blue=logmap[ScaleQuantumToMap(q->blue)];
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ logmap=(Quantum *) RelinquishMagickMemory(logmap);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(status);
+ }
+ default:
+ break;
+ }
+ /*
+ Allocate the tables.
+ */
+ x_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*x_map));
+ y_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*y_map));
+ z_map=(TransformPacket *) AcquireQuantumMemory((size_t) MaxMap+1UL,
+ sizeof(*z_map));
+ if ((x_map == (TransformPacket *) NULL) ||
+ (y_map == (TransformPacket *) NULL) ||
+ (z_map == (TransformPacket *) NULL))
+ {
+ if (z_map != (TransformPacket *) NULL)
+ z_map=(TransformPacket *) RelinquishMagickMemory(z_map);
+ if (y_map != (TransformPacket *) NULL)
+ y_map=(TransformPacket *) RelinquishMagickMemory(y_map);
+ if (x_map != (TransformPacket *) NULL)
+ x_map=(TransformPacket *) RelinquishMagickMemory(x_map);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ switch (colorspace)
+ {
+ case OHTAColorspace:
+ {
+ /*
+ Initialize OHTA tables:
+
+ R = I1+1.00000*I2-0.66668*I3
+ G = I1+0.00000*I2+1.33333*I3
+ B = I1-1.00000*I2-0.66668*I3
+
+ I and Q, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.500000f*(2.000000*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].x=(-0.333340f)*(2.000000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=0.000000f;
+ z_map[i].y=0.666665f*(2.000000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=(-0.500000f)*(2.000000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].z=(-0.333340f)*(2.000000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ }
+ break;
+ }
+ case Rec601YCbCrColorspace:
+ case YCbCrColorspace:
+ {
+ /*
+ Initialize YCbCr tables:
+
+ R = Y +1.402000*Cr
+ G = Y-0.344136*Cb-0.714136*Cr
+ B = Y+1.772000*Cb
+
+ Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.000000f;
+ z_map[i].x=(1.402000f*0.500000f)*(2.000000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=(-0.344136f*0.500000f)*(2.000000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ z_map[i].y=(-0.714136f*0.500000f)*(2.000000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=(1.772000f*0.500000f)*(2.000000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ z_map[i].z=0.000000f;
+ }
+ break;
+ }
+ case Rec709YCbCrColorspace:
+ {
+ /*
+ Initialize YCbCr tables:
+
+ R = Y +1.574800*Cr
+ G = Y-0.187324*Cb-0.468124*Cr
+ B = Y+1.855600*Cb
+
+ Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.000000f;
+ z_map[i].x=(1.574800f*0.50000f)*(2.00000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=(-0.187324f*0.50000f)*(2.00000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ z_map[i].y=(-0.468124f*0.50000f)*(2.00000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=(1.855600f*0.50000f)*(2.00000f*(MagickRealType) i-
+ (MagickRealType) MaxMap);
+ z_map[i].z=0.00000f;
+ }
+ break;
+ }
+ case sRGBColorspace:
+ {
+ /*
+ Nonlinear sRGB to linear RGB.
+
+ R = 1.0*R+0.0*G+0.0*B
+ G = 0.0*R+1.0*G+0.0*B
+ B = 0.0*R+0.0*G+1.0*B
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=1.0f*(MagickRealType) i;
+ y_map[i].x=0.0f*(MagickRealType) i;
+ z_map[i].x=0.0f*(MagickRealType) i;
+ x_map[i].y=0.0f*(MagickRealType) i;
+ y_map[i].y=1.0f*(MagickRealType) i;
+ z_map[i].y=0.0f*(MagickRealType) i;
+ x_map[i].z=0.0f*(MagickRealType) i;
+ y_map[i].z=0.0f*(MagickRealType) i;
+ z_map[i].z=1.0f*(MagickRealType) i;
+ }
+ break;
+ }
+ case XYZColorspace:
+ {
+ /*
+ Initialize CIE XYZ tables (ITU R-709 RGB):
+
+ R = 3.2407100*X-1.5372600*Y-0.4985710*Z
+ G = -0.9692580*X+1.8759900*Y+0.0415557*Z
+ B = 0.0556352*X-0.2039960*Y+1.0570700*Z
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=3.2407100f*(MagickRealType) i;
+ x_map[i].y=(-0.9692580f)*(MagickRealType) i;
+ x_map[i].z=0.0556352f*(MagickRealType) i;
+ y_map[i].x=(-1.5372600f)*(MagickRealType) i;
+ y_map[i].y=1.8759900f*(MagickRealType) i;
+ y_map[i].z=(-0.2039960f)*(MagickRealType) i;
+ z_map[i].x=(-0.4985710f)*(MagickRealType) i;
+ z_map[i].y=0.0415557f*(MagickRealType) i;
+ z_map[i].z=1.0570700f*(MagickRealType) i;
+ }
+ break;
+ }
+ case YCCColorspace:
+ {
+ /*
+ Initialize YCC tables:
+
+ R = Y +1.340762*C2
+ G = Y-0.317038*C1-0.682243*C2
+ B = Y+1.632639*C1
+
+ YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=1.3584000f*(MagickRealType) i;
+ y_map[i].x=0.0000000f;
+ z_map[i].x=1.8215000f*((MagickRealType) i-(MagickRealType)
+ ScaleQuantumToMap(ScaleCharToQuantum(137)));
+ x_map[i].y=1.3584000f*(MagickRealType) i;
+ y_map[i].y=(-0.4302726f)*((MagickRealType) i-(MagickRealType)
+ ScaleQuantumToMap(ScaleCharToQuantum(156)));
+ z_map[i].y=(-0.9271435f)*((MagickRealType) i-(MagickRealType)
+ ScaleQuantumToMap(ScaleCharToQuantum(137)));
+ x_map[i].z=1.3584000f*(MagickRealType) i;
+ y_map[i].z=2.2179000f*((MagickRealType) i-(MagickRealType)
+ ScaleQuantumToMap(ScaleCharToQuantum(156)));
+ z_map[i].z=0.0000000f;
+ }
+ break;
+ }
+ case YIQColorspace:
+ {
+ /*
+ Initialize YIQ tables:
+
+ R = Y+0.95620*I+0.62140*Q
+ G = Y-0.27270*I-0.64680*Q
+ B = Y-1.10370*I+1.70060*Q
+
+ I and Q, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.47810f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].x=0.31070f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=(-0.13635f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].y=(-0.32340f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=(-0.55185f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].z=0.85030f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ }
+ break;
+ }
+ case YPbPrColorspace:
+ {
+ /*
+ Initialize YPbPr tables:
+
+ R = Y +1.402000*C2
+ G = Y-0.344136*C1+0.714136*C2
+ B = Y+1.772000*C1
+
+ Pb and Pr, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.000000f;
+ z_map[i].x=0.701000f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=(-0.172068f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].y=0.357068f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=0.88600f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].z=0.00000f;
+ }
+ break;
+ }
+ case YUVColorspace:
+ default:
+ {
+ /*
+ Initialize YUV tables:
+
+ R = Y +1.13980*V
+ G = Y-0.39380*U-0.58050*V
+ B = Y+2.02790*U
+
+ U and V, normally -0.5 through 0.5, must be normalized to the range 0
+ through QuantumRange.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ x_map[i].x=(MagickRealType) i;
+ y_map[i].x=0.00000f;
+ z_map[i].x=0.56990f*(2.0000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].y=(MagickRealType) i;
+ y_map[i].y=(-0.19690f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].y=(-0.29025f)*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ x_map[i].z=(MagickRealType) i;
+ y_map[i].z=1.01395f*(2.00000f*(MagickRealType) i-(MagickRealType)
+ MaxMap);
+ z_map[i].z=0.00000f;
+ }
+ break;
+ }
+ }
+ /*
+ Convert to RGB.
+ */
+ switch (image->storage_class)
+ {
+ case DirectClass:
+ default:
+ {
+ /*
+ Convert DirectClass image.
+ */
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ register unsigned long
+ blue,
+ green,
+ red;
+
+ red=ScaleQuantumToMap(q->red);
+ green=ScaleQuantumToMap(q->green);
+ blue=ScaleQuantumToMap(q->blue);
+ pixel.red=x_map[red].x+y_map[green].x+z_map[blue].x;
+ pixel.green=x_map[red].y+y_map[green].y+z_map[blue].y;
+ pixel.blue=x_map[red].z+y_map[green].z+z_map[blue].z;
+ switch (colorspace)
+ {
+ case YCCColorspace:
+ {
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ pixel.red=(MagickRealType) ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.red)]);
+ pixel.green=(MagickRealType) ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.green)]);
+ pixel.blue=(MagickRealType) ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.blue)]);
+#endif
+ break;
+ }
+ case sRGBColorspace:
+ {
+ if ((QuantumScale*pixel.red) <= 0.0031308)
+ pixel.red*=12.92f;
+ else
+ pixel.red=(MagickRealType) QuantumRange*(1.055*
+ pow(QuantumScale*pixel.red,(1.0/2.4))-0.055);
+ if ((QuantumScale*pixel.green) <= 0.0031308)
+ pixel.green*=12.92f;
+ else
+ pixel.green=(MagickRealType) QuantumRange*(1.055*
+ pow(QuantumScale*pixel.green,(1.0/2.4))-0.055);
+ if ((QuantumScale*pixel.blue) <= 0.0031308)
+ pixel.blue*=12.92f;
+ else
+ pixel.blue=(MagickRealType) QuantumRange*(1.055*
+ pow(QuantumScale*pixel.blue,(1.0/2.4))-0.055);
+ break;
+ }
+ default:
+ break;
+ }
+ q->red=ScaleMapToQuantum((MagickRealType) MaxMap*QuantumScale*
+ pixel.red);
+ q->green=ScaleMapToQuantum((MagickRealType) MaxMap*QuantumScale*
+ pixel.green);
+ q->blue=ScaleMapToQuantum((MagickRealType) MaxMap*QuantumScale*
+ pixel.blue);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransformRGBImage)
+#endif
+ proceed=SetImageProgress(image,TransformRGBImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ break;
+ }
+ case PseudoClass:
+ {
+ /*
+ Convert PseudoClass image.
+ */
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register unsigned long
+ blue,
+ green,
+ red;
+
+ red=ScaleQuantumToMap(image->colormap[i].red);
+ green=ScaleQuantumToMap(image->colormap[i].green);
+ blue=ScaleQuantumToMap(image->colormap[i].blue);
+ pixel.red=x_map[red].x+y_map[green].x+z_map[blue].x;
+ pixel.green=x_map[red].y+y_map[green].y+z_map[blue].y;
+ pixel.blue=x_map[red].z+y_map[green].z+z_map[blue].z;
+ switch (colorspace)
+ {
+ case YCCColorspace:
+ {
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ image->colormap[i].red=ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.red)]);
+ image->colormap[i].green=ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.green)]);
+ image->colormap[i].blue=ScaleCharToQuantum(YCCMap[RoundToYCC(
+ 255.0*QuantumScale*pixel.blue)]);
+#endif
+ break;
+ }
+ case sRGBColorspace:
+ {
+ if ((QuantumScale*pixel.red) <= 0.0031308)
+ pixel.red*=12.92f;
+ else
+ pixel.red=(MagickRealType) QuantumRange*(1.055*pow(QuantumScale*
+ pixel.red,(1.0/2.4))-0.055);
+ if ((QuantumScale*pixel.green) <= 0.0031308)
+ pixel.green*=12.92f;
+ else
+ pixel.green=(MagickRealType) QuantumRange*(1.055*pow(QuantumScale*
+ pixel.green,(1.0/2.4))-0.055);
+ if ((QuantumScale*pixel.blue) <= 0.0031308)
+ pixel.blue*=12.92f;
+ else
+ pixel.blue=(MagickRealType) QuantumRange*(1.055*pow(QuantumScale*
+ pixel.blue,(1.0/2.4))-0.055);
+ }
+ default:
+ {
+ image->colormap[i].red=ScaleMapToQuantum((MagickRealType) MaxMap*
+ QuantumScale*pixel.red);
+ image->colormap[i].green=ScaleMapToQuantum((MagickRealType) MaxMap*
+ QuantumScale*pixel.green);
+ image->colormap[i].blue=ScaleMapToQuantum((MagickRealType) MaxMap*
+ QuantumScale*pixel.blue);
+ break;
+ }
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ (void) SyncImage(image);
+ break;
+ }
+ }
+ /*
+ Relinquish resources.
+ */
+ z_map=(TransformPacket *) RelinquishMagickMemory(z_map);
+ y_map=(TransformPacket *) RelinquishMagickMemory(y_map);
+ x_map=(TransformPacket *) RelinquishMagickMemory(x_map);
+ if (SetImageColorspace(image,RGBColorspace) == MagickFalse)
+ return(MagickFalse);
+ return(MagickTrue);
+}
diff --git a/magick/colorspace.h b/magick/colorspace.h
new file mode 100644
index 0000000..56170ce
--- /dev/null
+++ b/magick/colorspace.h
@@ -0,0 +1,62 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image colorspace methods.
+*/
+#ifndef _MAGICKCORE_COLORSPACE_H
+#define _MAGICKCORE_COLORSPACE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedColorspace,
+ RGBColorspace,
+ GRAYColorspace,
+ TransparentColorspace,
+ OHTAColorspace,
+ LabColorspace,
+ XYZColorspace,
+ YCbCrColorspace,
+ YCCColorspace,
+ YIQColorspace,
+ YPbPrColorspace,
+ YUVColorspace,
+ CMYKColorspace,
+ sRGBColorspace,
+ HSBColorspace,
+ HSLColorspace,
+ HWBColorspace,
+ Rec601LumaColorspace,
+ Rec601YCbCrColorspace,
+ Rec709LumaColorspace,
+ Rec709YCbCrColorspace,
+ LogColorspace,
+ CMYColorspace
+} ColorspaceType;
+
+extern MagickExport MagickBooleanType
+ RGBTransformImage(Image *,const ColorspaceType),
+ SetImageColorspace(Image *,const ColorspaceType),
+ TransformImageColorspace(Image *,const ColorspaceType),
+ TransformRGBImage(Image *,const ColorspaceType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/compare.c b/magick/compare.c
new file mode 100644
index 0000000..6e05119
--- /dev/null
+++ b/magick/compare.c
@@ -0,0 +1,1584 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO M M PPPP AAA RRRR EEEEE %
+% C O O MM MM P P A A R R E %
+% C O O M M M PPPP AAAAA RRRR EEE %
+% C O O M M P A A R R E %
+% CCCC OOO M M P A A R R EEEEE %
+% %
+% %
+% MagickCore Image Comparison Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/colorspace-private.h"
+#include "magick/compare.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/pixel-private.h"
+#include "magick/resource_.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e I m a g e C h a n n e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareImageChannels() compares one or more image channels of an image
+% to a reconstructed image and returns the difference image.
+%
+% The format of the CompareImageChannels method is:
+%
+% Image *CompareImageChannels(const Image *image,
+% const Image *reconstruct_image,const ChannelType channel,
+% const MetricType metric,double *distortion,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o reconstruct_image: the reconstruct image.
+%
+% o channel: the channel.
+%
+% o metric: the metric.
+%
+% o distortion: the computed distortion between the images.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *CompareImages(Image *image,const Image *reconstruct_image,
+ const MetricType metric,double *distortion,ExceptionInfo *exception)
+{
+ Image
+ *highlight_image;
+
+ highlight_image=CompareImageChannels(image,reconstruct_image,AllChannels,
+ metric,distortion,exception);
+ return(highlight_image);
+}
+
+MagickExport Image *CompareImageChannels(Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ const MetricType metric,double *distortion,ExceptionInfo *exception)
+{
+ const char
+ *artifact;
+
+ Image
+ *difference_image,
+ *highlight_image;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ highlight,
+ lowlight,
+ zero;
+
+ CacheView
+ *highlight_view,
+ *image_view,
+ *reconstruct_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(reconstruct_image != (const Image *) NULL);
+ assert(reconstruct_image->signature == MagickSignature);
+ assert(distortion != (double *) NULL);
+ *distortion=0.0;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((reconstruct_image->columns != image->columns) ||
+ (reconstruct_image->rows != image->rows))
+ ThrowImageException(ImageError,"ImageSizeDiffers");
+ status=GetImageChannelDistortion(image,reconstruct_image,channel,metric,
+ distortion,exception);
+ if (status == MagickFalse)
+ return((Image *) NULL);
+ difference_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (difference_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) SetImageAlphaChannel(difference_image,OpaqueAlphaChannel);
+ highlight_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (highlight_image == (Image *) NULL)
+ {
+ difference_image=DestroyImage(difference_image);
+ return((Image *) NULL);
+ }
+ if (SetImageStorageClass(highlight_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&highlight_image->exception);
+ difference_image=DestroyImage(difference_image);
+ highlight_image=DestroyImage(highlight_image);
+ return((Image *) NULL);
+ }
+ (void) SetImageAlphaChannel(highlight_image,OpaqueAlphaChannel);
+ (void) QueryMagickColor("#f1001ecc",&highlight,exception);
+ artifact=GetImageArtifact(image,"highlight-color");
+ if (artifact != (const char *) NULL)
+ (void) QueryMagickColor(artifact,&highlight,exception);
+ (void) QueryMagickColor("#ffffffcc",&lowlight,exception);
+ artifact=GetImageArtifact(image,"lowlight-color");
+ if (artifact != (const char *) NULL)
+ (void) QueryMagickColor(artifact,&lowlight,exception);
+ if (highlight_image->colorspace == CMYKColorspace)
+ {
+ ConvertRGBToCMYK(&highlight);
+ ConvertRGBToCMYK(&lowlight);
+ }
+ /*
+ Generate difference image.
+ */
+ status=MagickTrue;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+ highlight_view=AcquireCacheView(highlight_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel,
+ reconstruct_pixel;
+
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register IndexPacket
+ *__restrict highlight_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict r;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,
+ 1,exception);
+ r=QueueCacheViewAuthenticPixels(highlight_view,0,y,highlight_image->columns,
+ 1,exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ highlight_indexes=GetCacheViewAuthenticIndexQueue(highlight_view);
+ pixel=zero;
+ reconstruct_pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickStatusType
+ difference;
+
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
+ &reconstruct_pixel);
+ difference=MagickFalse;
+ if (channel == AllChannels)
+ {
+ if (IsMagickColorSimilar(&pixel,&reconstruct_pixel) == MagickFalse)
+ difference=MagickTrue;
+ }
+ else
+ {
+ if (((channel & RedChannel) != 0) && (p->red != q->red))
+ difference=MagickTrue;
+ if (((channel & GreenChannel) != 0) && (p->green != q->green))
+ difference=MagickTrue;
+ if (((channel & BlueChannel) != 0) && (p->blue != q->blue))
+ difference=MagickTrue;
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse) && (p->opacity != q->opacity))
+ difference=MagickTrue;
+ if ((((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace)) &&
+ (indexes[x] != reconstruct_indexes[x]))
+ difference=MagickTrue;
+ }
+ if (difference != MagickFalse)
+ SetPixelPacket(highlight_image,&highlight,r,highlight_indexes+x);
+ else
+ SetPixelPacket(highlight_image,&lowlight,r,highlight_indexes+x);
+ p++;
+ q++;
+ r++;
+ }
+ sync=SyncCacheViewAuthenticPixels(highlight_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ highlight_view=DestroyCacheView(highlight_view);
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ (void) CompositeImage(difference_image,image->compose,highlight_image,0,0);
+ highlight_image=DestroyImage(highlight_image);
+ if (status == MagickFalse)
+ difference_image=DestroyImage(difference_image);
+ return(difference_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l D i s t o r t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelDistortion() compares one or more image channels of an image
+% to a reconstructed image and returns the specified distortion metric.
+%
+% The format of the CompareImageChannels method is:
+%
+% MagickBooleanType GetImageChannelDistortion(const Image *image,
+% const Image *reconstruct_image,const ChannelType channel,
+% const MetricType metric,double *distortion,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o reconstruct_image: the reconstruct image.
+%
+% o channel: the channel.
+%
+% o metric: the metric.
+%
+% o distortion: the computed distortion between the images.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType GetImageDistortion(Image *image,
+ const Image *reconstruct_image,const MetricType metric,double *distortion,
+ ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetImageChannelDistortion(image,reconstruct_image,AllChannels,
+ metric,distortion,exception);
+ return(status);
+}
+
+static MagickBooleanType GetAbsoluteError(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,double *distortion,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ /*
+ Compute the absolute difference in pixels between two images.
+ */
+ status=MagickTrue;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ channel_distortion[AllChannels+1];
+
+ MagickPixelPacket
+ pixel,
+ reconstruct_pixel;
+
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ i,
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,
+ 1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ pixel=zero;
+ reconstruct_pixel=pixel;
+ (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ SetMagickPixelPacket(reconstruct_image,q,reconstruct_indexes+x,
+ &reconstruct_pixel);
+ if (IsMagickColorSimilar(&pixel,&reconstruct_pixel) == MagickFalse)
+ {
+ if ((channel & RedChannel) != 0)
+ channel_distortion[RedChannel]++;
+ if ((channel & GreenChannel) != 0)
+ channel_distortion[GreenChannel]++;
+ if ((channel & BlueChannel) != 0)
+ channel_distortion[BlueChannel]++;
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ channel_distortion[OpacityChannel]++;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ channel_distortion[BlackChannel]++;
+ channel_distortion[AllChannels]++;
+ }
+ p++;
+ q++;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetAbsoluteError)
+#endif
+ for (i=0; i <= (long) AllChannels; i++)
+ distortion[i]+=channel_distortion[i];
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+static unsigned long GetNumberChannels(const Image *image,
+ const ChannelType channel)
+{
+ unsigned long
+ channels;
+
+ channels=0;
+ if ((channel & RedChannel) != 0)
+ channels++;
+ if ((channel & GreenChannel) != 0)
+ channels++;
+ if ((channel & BlueChannel) != 0)
+ channels++;
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ channels++;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ channels++;
+ return(channels);
+}
+
+static MagickBooleanType GetMeanAbsoluteError(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ double *distortion,ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ status=MagickTrue;
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ channel_distortion[AllChannels+1];
+
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ i,
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,
+ reconstruct_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ distance;
+
+ if ((channel & RedChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->red-(double) q->red);
+ channel_distortion[RedChannel]+=distance;
+ channel_distortion[AllChannels]+=distance;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->green-(double) q->green);
+ channel_distortion[GreenChannel]+=distance;
+ channel_distortion[AllChannels]+=distance;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->blue-(double) q->blue);
+ channel_distortion[BlueChannel]+=distance;
+ channel_distortion[AllChannels]+=distance;
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ {
+ distance=QuantumScale*fabs(p->opacity-(double) q->opacity);
+ channel_distortion[OpacityChannel]+=distance;
+ channel_distortion[AllChannels]+=distance;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ distance=QuantumScale*fabs(indexes[x]-(double)
+ reconstruct_indexes[x]);
+ channel_distortion[BlackChannel]+=distance;
+ channel_distortion[AllChannels]+=distance;
+ }
+ p++;
+ q++;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetMeanAbsoluteError)
+#endif
+ for (i=0; i <= (long) AllChannels; i++)
+ distortion[i]+=channel_distortion[i];
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ for (i=0; i <= (long) AllChannels; i++)
+ distortion[i]/=((double) image->columns*image->rows);
+ distortion[AllChannels]/=(double) GetNumberChannels(image,channel);
+ return(status);
+}
+
+static MagickBooleanType GetMeanErrorPerPixel(Image *image,
+ const Image *reconstruct_image,const ChannelType channel,double *distortion,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ alpha,
+ area,
+ beta,
+ maximum_error,
+ mean_error;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ status=MagickTrue;
+ alpha=1.0;
+ beta=1.0;
+ area=0.0;
+ maximum_error=0.0;
+ mean_error=0.0;
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,
+ 1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ distance;
+
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte != MagickFalse)
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ if (reconstruct_image->matte != MagickFalse)
+ beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
+ }
+ if ((channel & RedChannel) != 0)
+ {
+ distance=fabs(alpha*p->red-beta*q->red);
+ distortion[RedChannel]+=distance;
+ distortion[AllChannels]+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ distance=fabs(alpha*p->green-beta*q->green);
+ distortion[GreenChannel]+=distance;
+ distortion[AllChannels]+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ distance=fabs(alpha*p->blue-beta*q->blue);
+ distortion[BlueChannel]+=distance;
+ distortion[AllChannels]+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ {
+ distance=fabs((double) p->opacity-q->opacity);
+ distortion[OpacityChannel]+=distance;
+ distortion[AllChannels]+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace))
+ {
+ distance=fabs(alpha*indexes[x]-beta*reconstruct_indexes[x]);
+ distortion[BlackChannel]+=distance;
+ distortion[AllChannels]+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ p++;
+ q++;
+ }
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ image->error.mean_error_per_pixel=distortion[AllChannels]/area;
+ image->error.normalized_mean_error=QuantumScale*QuantumScale*mean_error/area;
+ image->error.normalized_maximum_error=QuantumScale*maximum_error;
+ return(status);
+}
+
+static MagickBooleanType GetMeanSquaredError(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ double *distortion,ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ status=MagickTrue;
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ channel_distortion[AllChannels+1];
+
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ i,
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,
+ reconstruct_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ distance;
+
+ if ((channel & RedChannel) != 0)
+ {
+ distance=QuantumScale*(p->red-(MagickRealType) q->red);
+ channel_distortion[RedChannel]+=distance*distance;
+ channel_distortion[AllChannels]+=distance*distance;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ distance=QuantumScale*(p->green-(MagickRealType) q->green);
+ channel_distortion[GreenChannel]+=distance*distance;
+ channel_distortion[AllChannels]+=distance*distance;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ distance=QuantumScale*(p->blue-(MagickRealType) q->blue);
+ channel_distortion[BlueChannel]+=distance*distance;
+ channel_distortion[AllChannels]+=distance*distance;
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ {
+ distance=QuantumScale*(p->opacity-(MagickRealType) q->opacity);
+ channel_distortion[OpacityChannel]+=distance*distance;
+ channel_distortion[AllChannels]+=distance*distance;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace))
+ {
+ distance=QuantumScale*(indexes[x]-(MagickRealType)
+ reconstruct_indexes[x]);
+ channel_distortion[BlackChannel]+=distance*distance;
+ channel_distortion[AllChannels]+=distance*distance;
+ }
+ p++;
+ q++;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetMeanSquaredError)
+#endif
+ for (i=0; i <= (long) AllChannels; i++)
+ distortion[i]+=channel_distortion[i];
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ for (i=0; i <= (long) AllChannels; i++)
+ distortion[i]/=((double) image->columns*image->rows);
+ distortion[AllChannels]/=(double) GetNumberChannels(image,channel);
+ return(status);
+}
+
+static MagickBooleanType GetPeakAbsoluteError(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ double *distortion,ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ status=MagickTrue;
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ channel_distortion[AllChannels+1];
+
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ i,
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,
+ reconstruct_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ (void) ResetMagickMemory(channel_distortion,0,sizeof(channel_distortion));
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ distance;
+
+ if ((channel & RedChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->red-(double) q->red);
+ if (distance > channel_distortion[RedChannel])
+ channel_distortion[RedChannel]=distance;
+ if (distance > channel_distortion[AllChannels])
+ channel_distortion[AllChannels]=distance;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->green-(double) q->green);
+ if (distance > channel_distortion[GreenChannel])
+ channel_distortion[GreenChannel]=distance;
+ if (distance > channel_distortion[AllChannels])
+ channel_distortion[AllChannels]=distance;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ distance=QuantumScale*fabs(p->blue-(double) q->blue);
+ if (distance > channel_distortion[BlueChannel])
+ channel_distortion[BlueChannel]=distance;
+ if (distance > channel_distortion[AllChannels])
+ channel_distortion[AllChannels]=distance;
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ {
+ distance=QuantumScale*fabs(p->opacity-(double) q->opacity);
+ if (distance > channel_distortion[OpacityChannel])
+ channel_distortion[OpacityChannel]=distance;
+ if (distance > channel_distortion[AllChannels])
+ channel_distortion[AllChannels]=distance;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace))
+ {
+ distance=QuantumScale*fabs(indexes[x]-(double)
+ reconstruct_indexes[x]);
+ if (distance > channel_distortion[BlackChannel])
+ channel_distortion[BlackChannel]=distance;
+ if (distance > channel_distortion[AllChannels])
+ channel_distortion[AllChannels]=distance;
+ }
+ p++;
+ q++;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetPeakAbsoluteError)
+#endif
+ for (i=0; i <= (long) AllChannels; i++)
+ if (channel_distortion[i] > distortion[i])
+ distortion[i]=channel_distortion[i];
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+static MagickBooleanType GetPeakSignalToNoiseRatio(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ double *distortion,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
+ exception);
+ if ((channel & RedChannel) != 0)
+ distortion[RedChannel]=20.0*log10((double) 1.0/sqrt(
+ distortion[RedChannel]));
+ if ((channel & GreenChannel) != 0)
+ distortion[GreenChannel]=20.0*log10((double) 1.0/sqrt(
+ distortion[GreenChannel]));
+ if ((channel & BlueChannel) != 0)
+ distortion[BlueChannel]=20.0*log10((double) 1.0/sqrt(
+ distortion[BlueChannel]));
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ distortion[OpacityChannel]=20.0*log10((double) 1.0/sqrt(
+ distortion[OpacityChannel]));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ distortion[BlackChannel]=20.0*log10((double) 1.0/sqrt(
+ distortion[BlackChannel]));
+ distortion[AllChannels]=20.0*log10((double) 1.0/sqrt(
+ distortion[AllChannels]));
+ return(status);
+}
+
+static MagickBooleanType GetRootMeanSquaredError(const Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ double *distortion,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetMeanSquaredError(image,reconstruct_image,channel,distortion,
+ exception);
+ if ((channel & RedChannel) != 0)
+ distortion[RedChannel]=sqrt(distortion[RedChannel]);
+ if ((channel & GreenChannel) != 0)
+ distortion[GreenChannel]=sqrt(distortion[GreenChannel]);
+ if ((channel & BlueChannel) != 0)
+ distortion[BlueChannel]=sqrt(distortion[BlueChannel]);
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ distortion[OpacityChannel]=sqrt(distortion[OpacityChannel]);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ distortion[BlackChannel]=sqrt(distortion[BlackChannel]);
+ distortion[AllChannels]=sqrt(distortion[AllChannels]);
+ return(status);
+}
+
+MagickExport MagickBooleanType GetImageChannelDistortion(Image *image,
+ const Image *reconstruct_image,const ChannelType channel,
+ const MetricType metric,double *distortion,ExceptionInfo *exception)
+{
+ double
+ *channel_distortion;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ length;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(reconstruct_image != (const Image *) NULL);
+ assert(reconstruct_image->signature == MagickSignature);
+ assert(distortion != (double *) NULL);
+ *distortion=0.0;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((reconstruct_image->columns != image->columns) ||
+ (reconstruct_image->rows != image->rows))
+ ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
+ /*
+ Get image distortion.
+ */
+ length=AllChannels+1UL;
+ channel_distortion=(double *) AcquireQuantumMemory(length,
+ sizeof(*channel_distortion));
+ if (channel_distortion == (double *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(channel_distortion,0,length*
+ sizeof(*channel_distortion));
+ switch (metric)
+ {
+ case AbsoluteErrorMetric:
+ {
+ status=GetAbsoluteError(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanAbsoluteErrorMetric:
+ {
+ status=GetMeanAbsoluteError(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanErrorPerPixelMetric:
+ {
+ status=GetMeanErrorPerPixel(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanSquaredErrorMetric:
+ {
+ status=GetMeanSquaredError(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case PeakAbsoluteErrorMetric:
+ default:
+ {
+ status=GetPeakAbsoluteError(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case PeakSignalToNoiseRatioMetric:
+ {
+ status=GetPeakSignalToNoiseRatio(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ case RootMeanSquaredErrorMetric:
+ {
+ status=GetRootMeanSquaredError(image,reconstruct_image,channel,
+ channel_distortion,exception);
+ break;
+ }
+ }
+ *distortion=channel_distortion[AllChannels];
+ channel_distortion=(double *) RelinquishMagickMemory(channel_distortion);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l D i s t o r t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelDistrortion() compares the image channels of an image to a
+% reconstructed image and returns the specified distortion metric for each
+% channel.
+%
+% The format of the CompareImageChannels method is:
+%
+% double *GetImageChannelDistortions(const Image *image,
+% const Image *reconstruct_image,const MetricType metric,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o reconstruct_image: the reconstruct image.
+%
+% o metric: the metric.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport double *GetImageChannelDistortions(Image *image,
+ const Image *reconstruct_image,const MetricType metric,
+ ExceptionInfo *exception)
+{
+ double
+ *channel_distortion;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ length;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(reconstruct_image != (const Image *) NULL);
+ assert(reconstruct_image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((reconstruct_image->columns != image->columns) ||
+ (reconstruct_image->rows != image->rows))
+ {
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ImageError,"ImageSizeDiffers","`%s'",image->filename);
+ return((double *) NULL);
+ }
+ /*
+ Get image distortion.
+ */
+ length=AllChannels+1UL;
+ channel_distortion=(double *) AcquireQuantumMemory(length,
+ sizeof(*channel_distortion));
+ if (channel_distortion == (double *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(channel_distortion,0,length*
+ sizeof(*channel_distortion));
+ switch (metric)
+ {
+ case AbsoluteErrorMetric:
+ {
+ status=GetAbsoluteError(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanAbsoluteErrorMetric:
+ {
+ status=GetMeanAbsoluteError(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanErrorPerPixelMetric:
+ {
+ status=GetMeanErrorPerPixel(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case MeanSquaredErrorMetric:
+ {
+ status=GetMeanSquaredError(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case PeakAbsoluteErrorMetric:
+ default:
+ {
+ status=GetPeakAbsoluteError(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case PeakSignalToNoiseRatioMetric:
+ {
+ status=GetPeakSignalToNoiseRatio(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ case RootMeanSquaredErrorMetric:
+ {
+ status=GetRootMeanSquaredError(image,reconstruct_image,AllChannels,
+ channel_distortion,exception);
+ break;
+ }
+ }
+ return(channel_distortion);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s I m a g e s E q u a l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsImagesEqual() measures the difference between colors at each pixel
+% location of two images. A value other than 0 means the colors match
+% exactly. Otherwise an error measure is computed by summing over all
+% pixels in an image the distance squared in RGB space between each image
+% pixel and its corresponding pixel in the reconstruct image. The error
+% measure is assigned to these image members:
+%
+% o mean_error_per_pixel: The mean error for any single pixel in
+% the image.
+%
+% o normalized_mean_error: The normalized mean quantization error for
+% any single pixel in the image. This distance measure is normalized to
+% a range between 0 and 1. It is independent of the range of red, green,
+% and blue values in the image.
+%
+% o normalized_maximum_error: The normalized maximum quantization
+% error for any single pixel in the image. This distance measure is
+% normalized to a range between 0 and 1. It is independent of the range
+% of red, green, and blue values in your image.
+%
+% A small normalized mean square error, accessed as
+% image->normalized_mean_error, suggests the images are very similar in
+% spatial layout and color.
+%
+% The format of the IsImagesEqual method is:
+%
+% MagickBooleanType IsImagesEqual(Image *image,
+% const Image *reconstruct_image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o reconstruct_image: the reconstruct image.
+%
+*/
+MagickExport MagickBooleanType IsImagesEqual(Image *image,
+ const Image *reconstruct_image)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ area,
+ maximum_error,
+ mean_error,
+ mean_error_per_pixel;
+
+ CacheView
+ *image_view,
+ *reconstruct_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(reconstruct_image != (const Image *) NULL);
+ assert(reconstruct_image->signature == MagickSignature);
+ if ((reconstruct_image->columns != image->columns) ||
+ (reconstruct_image->rows != image->rows))
+ ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
+ area=0.0;
+ maximum_error=0.0;
+ mean_error_per_pixel=0.0;
+ mean_error=0.0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ reconstruct_view=AcquireCacheView(reconstruct_image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reconstruct_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reconstruct_view,0,y,reconstruct_image->columns,
+ 1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reconstruct_indexes=GetCacheViewVirtualIndexQueue(reconstruct_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ distance;
+
+ distance=fabs(p->red-(double) q->red);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ distance=fabs(p->green-(double) q->green);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ distance=fabs(p->blue-(double) q->blue);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ if (image->matte != MagickFalse)
+ {
+ distance=fabs(p->opacity-(double) q->opacity);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ if ((image->colorspace == CMYKColorspace) &&
+ (reconstruct_image->colorspace == CMYKColorspace))
+ {
+ distance=fabs(indexes[x]-(double) reconstruct_indexes[x]);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ area++;
+ }
+ p++;
+ q++;
+ }
+ }
+ reconstruct_view=DestroyCacheView(reconstruct_view);
+ image_view=DestroyCacheView(image_view);
+ image->error.mean_error_per_pixel=(double) (mean_error_per_pixel/area);
+ image->error.normalized_mean_error=(double) (QuantumScale*QuantumScale*
+ mean_error/area);
+ image->error.normalized_maximum_error=(double) (QuantumScale*maximum_error);
+ status=image->error.mean_error_per_pixel == 0.0 ? MagickTrue : MagickFalse;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S i m i l a r i t y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SimilarityImage() compares the reference image of the image and returns the
+% best match offset. In addition, it returns a similarity image such that an
+% exact match location is completely white and if none of the pixels match,
+% black, otherwise some gray level in-between.
+%
+% The format of the SimilarityImageImage method is:
+%
+% Image *SimilarityImage(const Image *image,const Image *reference,
+% RectangleInfo *offset,double *similarity,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o reference: find an area of the image that closely resembles this image.
+%
+% o the best match offset of the reference image within the image.
+%
+% o similarity: the computed similarity between the images.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static double GetSimilarityMetric(const Image *image,const Image *reference,
+ const long x_offset,const long y_offset,ExceptionInfo *exception)
+{
+ double
+ similarity;
+
+ long
+ y;
+
+ CacheView
+ *image_view,
+ *reference_view;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Compute the similarity in pixels between two images.
+ */
+ status=MagickTrue;
+ similarity=0.0;
+ image_view=AcquireCacheView(image);
+ reference_view=AcquireCacheView(reference);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) reference->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes,
+ *__restrict reference_indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset+y,
+ reference->columns,1,exception);
+ q=GetCacheViewVirtualPixels(reference_view,0,y,reference->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ reference_indexes=GetCacheViewVirtualIndexQueue(reference_view);
+ for (x=0; x < (long) reference->columns; x++)
+ {
+ double
+ thread_similarity;
+
+ MagickRealType
+ distance;
+
+ thread_similarity=0.0;
+ distance=QuantumScale*(p->red-(MagickRealType) q->red);
+ thread_similarity+=distance*distance;
+ distance=QuantumScale*(p->green-(MagickRealType) q->green);
+ thread_similarity+=distance*distance;
+ distance=QuantumScale*(p->blue-(MagickRealType) q->blue);
+ thread_similarity+=distance*distance;
+ if ((image->matte != MagickFalse) && (reference->matte != MagickFalse))
+ {
+ distance=QuantumScale*(p->opacity-(MagickRealType) q->opacity);
+ thread_similarity+=distance*distance;
+ }
+ if ((image->colorspace == CMYKColorspace) &&
+ (reference->colorspace == CMYKColorspace))
+ {
+ distance=QuantumScale*(indexes[x]-(MagickRealType)
+ reference_indexes[x]);
+ thread_similarity+=distance*distance;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetSimilarityMetric)
+#endif
+ similarity+=thread_similarity;
+ p++;
+ q++;
+ }
+ }
+ reference_view=DestroyCacheView(reference_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ return(0.0);
+ similarity/=((double) reference->columns*reference->rows);
+ similarity/=(double) GetNumberChannels(reference,AllChannels);
+ return(sqrt(similarity));
+}
+
+MagickExport Image *SimilarityImage(Image *image,const Image *reference,
+ RectangleInfo *offset,double *similarity_metric,ExceptionInfo *exception)
+{
+#define SimilarityImageTag "Similarity/Image"
+
+ long
+ progress,
+ y;
+
+ Image
+ *similarity_image;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *similarity_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert(offset != (RectangleInfo *) NULL);
+ SetGeometry(reference,offset);
+ *similarity_metric=1.0;
+ if ((reference->columns > image->columns) ||
+ (reference->rows > image->rows))
+ ThrowImageException(ImageError,"ImageSizeDiffers");
+ similarity_image=CloneImage(image,image->columns-reference->columns+1,
+ image->rows-reference->rows+1,MagickTrue,exception);
+ if (similarity_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(similarity_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&similarity_image->exception);
+ similarity_image=DestroyImage(similarity_image);
+ return((Image *) NULL);
+ }
+ /*
+ Measure similarity of reference image against image.
+ */
+ status=MagickTrue;
+ progress=0;
+ similarity_view=AcquireCacheView(similarity_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) (image->rows-reference->rows+1); y++)
+ {
+ double
+ similarity;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(similarity_view,0,y,
+ similarity_image->columns,1,exception);
+ if (q == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) (image->columns-reference->columns+1); x++)
+ {
+ similarity=GetSimilarityMetric(image,reference,x,y,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SimilarityImage)
+#endif
+ if (similarity < *similarity_metric)
+ {
+ *similarity_metric=similarity;
+ offset->x=x;
+ offset->y=y;
+ }
+ q->red=RoundToQuantum(QuantumRange-QuantumRange*similarity);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(similarity_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SimilarityImage)
+#endif
+ proceed=SetImageProgress(image,SimilarityImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ similarity_view=DestroyCacheView(similarity_view);
+ return(similarity_image);
+}
diff --git a/magick/compare.h b/magick/compare.h
new file mode 100644
index 0000000..c530018
--- /dev/null
+++ b/magick/compare.h
@@ -0,0 +1,62 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image compare methods.
+*/
+#ifndef _MAGICKCORE_COMPARE_H
+#define _MAGICKCORE_COMPARE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/image.h"
+
+typedef enum
+{
+ UndefinedMetric,
+ AbsoluteErrorMetric,
+ MeanAbsoluteErrorMetric,
+ MeanErrorPerPixelMetric,
+ MeanSquaredErrorMetric,
+ PeakAbsoluteErrorMetric,
+ PeakSignalToNoiseRatioMetric,
+ RootMeanSquaredErrorMetric
+} MetricType;
+
+extern MagickExport double
+ *GetImageChannelDistortions(Image *,const Image *,const MetricType,
+ ExceptionInfo *);
+
+extern MagickExport Image
+ *CompareImageChannels(Image *,const Image *,const ChannelType,
+ const MetricType,double *,ExceptionInfo *),
+ *CompareImages(Image *,const Image *,const MetricType,double *,
+ ExceptionInfo *),
+ *SimilarityImage(Image *,const Image *,RectangleInfo *,double *,
+ ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ GetImageChannelDistortion(Image *,const Image *,const ChannelType,
+ const MetricType,double *,ExceptionInfo *),
+ GetImageDistortion(Image *,const Image *,const MetricType,double *,
+ ExceptionInfo *),
+ IsImagesEqual(Image *,const Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/composite-private.h b/magick/composite-private.h
new file mode 100644
index 0000000..04d957b
--- /dev/null
+++ b/magick/composite-private.h
@@ -0,0 +1,161 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image composite private methods.
+*/
+#ifndef _MAGICKCORE_COMPOSITE_PRIVATE_H
+#define _MAGICKCORE_COMPOSITE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ ImageMagick Alpha Composite Inline Methods (special export)
+*/
+
+#include "magick/color.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+
+static inline MagickRealType RoundToUnity(const MagickRealType value)
+{
+ return(value < 0.0 ? 0.0 : (value > 1.0) ? 1.0 : value);
+}
+
+static inline MagickRealType MagickOver_(const MagickRealType p,
+ const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
+{
+ return((1.0-QuantumScale*alpha)*p+
+ (1.0-QuantumScale*beta)*q*QuantumScale*alpha);
+}
+
+static inline void MagickCompositeOver(const PixelPacket *p,
+ const MagickRealType alpha,const PixelPacket *q,const MagickRealType beta,
+ PixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ /*
+ Compose pixel p over pixel q with the given opacities.
+ */
+ if (alpha == TransparentOpacity)
+ {
+ if (composite != q)
+ *composite=(*q);
+ return;
+ }
+ gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ composite->opacity=(Quantum) (QuantumRange*(1.0-gamma)+0.5);
+ gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=(Quantum) (gamma*MagickOver_((MagickRealType) p->red,alpha,
+ (MagickRealType) q->red,beta)+0.5);
+ composite->green=(Quantum) (gamma*MagickOver_((MagickRealType) p->green,alpha,
+ (MagickRealType) q->green,beta)+0.5);
+ composite->blue=(Quantum) (gamma*MagickOver_((MagickRealType) p->blue,alpha,
+ (MagickRealType) q->blue,beta)+0.5);
+#else
+ composite->opacity=(Quantum) (QuantumRange*(1.0-gamma));
+ gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=(Quantum) (gamma*MagickOver_((MagickRealType) p->red,alpha,
+ (MagickRealType) q->red,beta));
+ composite->green=(Quantum) (gamma*MagickOver_((MagickRealType) p->green,alpha,
+ (MagickRealType) q->green,beta));
+ composite->blue=(Quantum) (gamma*MagickOver_((MagickRealType) p->blue,alpha,
+ (MagickRealType) q->blue,beta));
+#endif
+}
+
+static inline void MagickPixelCompositeOver(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ /*
+ Compose pixel p over pixel q with the given opacities.
+ */
+ if (alpha == OpaqueOpacity)
+ {
+ *composite=(*p);
+ return;
+ }
+ gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*MagickOver_(p->red,alpha,q->red,beta);
+ composite->green=gamma*MagickOver_(p->green,alpha,q->green,beta);
+ composite->blue=gamma*MagickOver_(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*MagickOver_(p->index,alpha,q->index,beta);
+}
+
+static inline void MagickPixelCompositePlus(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ /*
+ Add two pixels with the given opacities.
+ */
+ Sa=1.0-QuantumScale*alpha;
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da); /* 'Plus' blending -- not 'Over' blending */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*(Sa*p->red+Da*q->red);
+ composite->green=gamma*(Sa*p->green+Da*q->green);
+ composite->blue=gamma*(Sa*p->blue+Da*q->blue);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*(Sa*p->index+Da*q->index);
+}
+
+/*
+ Blend pixel colors p and q by the amount given.
+*/
+static inline void MagickPixelCompositeBlend(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickPixelCompositePlus(p,(MagickRealType) (QuantumRange-alpha*
+ (QuantumRange-p->opacity)),q,(MagickRealType) (QuantumRange-beta*
+ (QuantumRange-q->opacity)),composite);
+}
+
+/*
+ Blend pixel colors p and q by the amount given and area.
+*/
+static inline void MagickPixelCompositeAreaBlend(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,const MagickRealType area,
+ MagickPixelPacket *composite)
+{
+ MagickPixelCompositePlus(p,(MagickRealType) QuantumRange-(1.0-area)*
+ (QuantumRange-alpha),q,(MagickRealType) (QuantumRange-area*(QuantumRange-
+ beta)),composite);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/composite.c b/magick/composite.c
new file mode 100644
index 0000000..ff3e831
--- /dev/null
+++ b/magick/composite.c
@@ -0,0 +1,2371 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
+% C O O MM MM P P O O SS I T E %
+% C O O M M M PPPP O O SSS I T EEE %
+% C O O M M P O O SS I T E %
+% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
+% %
+% %
+% MagickCore Image Composite Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/pixel-private.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/resample.h"
+#include "magick/resource_.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p o s i t e I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompositeImageChannel() returns the second image composited onto the first
+% at the specified offset, using the specified composite method.
+%
+% The format of the CompositeImageChannel method is:
+%
+% MagickBooleanType CompositeImage(Image *image,
+% const CompositeOperator compose,Image *composite_image,
+% const long x_offset,const long y_offset)
+% MagickBooleanType CompositeImageChannel(Image *image,
+% const ChannelType channel,const CompositeOperator compose,
+% Image *composite_image,const long x_offset,const long y_offset)
+%
+% A description of each parameter follows:
+%
+% o image: the destination image, modified by he composition
+%
+% o channel: the channel.
+%
+% o compose: This operator affects how the composite is applied to
+% the image. The operators and how they are utilized are listed here
+% http://www.w3.org/TR/SVG12/#compositing.
+%
+% o composite_image: the composite (source) image.
+%
+% o x_offset: the column offset of the composited image.
+%
+% o y_offset: the row offset of the composited image.
+%
+% Extra Controls from Image meta-data in 'composite_image' (artifacts)
+%
+% o "compose:args"
+% A string containing extra numerical arguments for specific compose
+% methods, generally expressed as a 'geometry' or a comma separated list
+% of numbers.
+%
+% Compose methods needing such arguments include "BlendCompositeOp" and
+% "DisplaceCompositeOp".
+%
+% o "compose:outside-overlay"
+% Modify how the composition is to effect areas not directly covered
+% by the 'composite_image' at the offset given. Normally this is
+% dependant on the 'compose' method, especially Duff-Porter methods.
+%
+% If set to "false" then disable all normal handling of pixels not
+% covered by the composite_image. Typically used for repeated tiling
+% of the composite_image by the calling API.
+%
+% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
+%
+*/
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+/*
+** Programmers notes on SVG specification.
+**
+** A Composition is defined by...
+** Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
+** Blending areas : X = 1 for area of overlap ie: f(Sc,Dc)
+** Y = 1 for source preserved
+** Z = 1 for destination preserved
+**
+** Conversion to transparency (then optimized)
+** Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
+** Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
+**
+** Where...
+** Sca = Sc*Sa normalized Source color divided by Source alpha
+** Dca = Dc*Da normalized Dest color divided by Dest alpha
+** Dc' = Dca'/Da' the desired color value for this channel.
+**
+** Da' in in the follow formula as 'gamma' The resulting alpla value.
+**
+**
+** Most functions use a blending mode of over (X=1,Y=1,Z=1)
+** this results in the following optimizations...
+** gamma = Sa+Da-Sa*Da;
+** gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
+** opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
+*/
+
+static inline MagickRealType Add(const MagickRealType p,const MagickRealType q)
+{
+ MagickRealType
+ pixel;
+
+ pixel=p+q;
+ if (pixel > QuantumRange)
+ pixel-=(QuantumRange+1.0);
+ return(pixel);
+}
+
+static inline void CompositeAdd(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ composite->red=Add(p->red,q->red);
+ composite->green=Add(p->green,q->green);
+ composite->blue=Add(p->blue,q->blue);
+ composite->opacity=Add(alpha,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=Add(p->index,q->index);
+}
+
+static inline MagickRealType Atop(const MagickRealType p,
+ const MagickRealType Sa,const MagickRealType q,
+ const MagickRealType magick_unused(Da))
+{
+ return(p*Sa+q*(1.0-Sa)); /* Da optimized out, Da/gamma => 1.0 */
+}
+
+static inline void CompositeAtop(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ composite->opacity=beta; /* optimized 1.0-Gamma */
+ composite->red=Atop(p->red,Sa,q->red,1.0);
+ composite->green=Atop(p->green,Sa,q->green,1.0);
+ composite->blue=Atop(p->blue,Sa,q->blue,1.0);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=Atop(p->index,Sa,q->index,1.0);
+}
+
+/*
+ What is this Composition method for, can't find any specification!
+ WARNING this is not doing correct 'over' blend handling (Anthony Thyssen).
+*/
+static inline void CompositeBumpmap(const MagickPixelPacket *p,
+ const MagickRealType magick_unused(alpha),const MagickPixelPacket *q,
+ const MagickRealType magick_unused(beta),MagickPixelPacket *composite)
+{
+ MagickRealType
+ intensity;
+
+ intensity=MagickPixelIntensity(p);
+ composite->red=QuantumScale*intensity*q->red;
+ composite->green=QuantumScale*intensity*q->green;
+ composite->blue=QuantumScale*intensity*q->blue;
+ composite->opacity=(MagickRealType) QuantumScale*intensity*p->opacity;
+ if (q->colorspace == CMYKColorspace)
+ composite->index=QuantumScale*intensity*q->index;
+}
+
+static inline void CompositeClear(const MagickPixelPacket *q,
+ MagickPixelPacket *composite)
+{
+ composite->opacity=(MagickRealType) TransparentOpacity;
+ composite->red=0.0;
+ composite->green=0.0;
+ composite->blue=0.0;
+ if (q->colorspace == CMYKColorspace)
+ composite->index=0.0;
+}
+
+static MagickRealType ColorBurn(const MagickRealType Sca,
+ const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
+{
+#if 0
+ /*
+ Oct 2004 SVG specification.
+ */
+ if (Sca*Da + Dca*Sa <= Sa*Da)
+ return(Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Sa*(Sca*Da+Dca*Sa-Sa*Da)/Sca + Sca*(1.0-Da) + Dca*(1.0-Sa));
+#else
+ /*
+ March 2009 SVG specification.
+ */
+ if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
+ return(Sa*Da+Dca*(1.0-Sa));
+ if (Sca < MagickEpsilon)
+ return(Dca*(1.0-Sa));
+ return(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
+#endif
+}
+
+static inline void CompositeColorBurn(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*ColorBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*ColorBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*ColorBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*ColorBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+
+static MagickRealType ColorDodge(const MagickRealType Sca,
+ const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
+{
+#if 0
+ /*
+ Oct 2004 SVG specification.
+ */
+ if ((Sca*Da+Dca*Sa) >= Sa*Da)
+ return( Sa*Da + Sca*(1.0-Da) + Dca*(1.0-Sa) );
+ return( Dca*Sa*Sa/(Sa-Sca) + Sca*(1.0-Da) + Dca*(1.0-Sa) );
+#endif
+#if 0
+ /*
+ New specification, March 2009 SVG specification. This specification was
+ also wrong of non-overlap cases.
+ */
+ if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
+ return(Sca*(1.0-Da));
+ if (fabs(Sca-Sa) < MagickEpsilon)
+ return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Sa*MagickMin(Da,Dca*Sa/(Sa-Sca)));
+#endif
+ /*
+ Working from first principles using the original formula:
+
+ f(Sc,Dc) = Dc/(1-Sc)
+
+ This works correctly! Looks like the 2004 model was right but just
+ required a extra condition for correct handling.
+ */
+ if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
+ return(Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if (fabs(Sca-Sa) < MagickEpsilon)
+ return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeColorDodge(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*ColorDodge(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*ColorDodge(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*ColorDodge(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*ColorDodge(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static inline MagickRealType Darken(const MagickRealType p,
+ const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
+{
+ if (p < q)
+ return(MagickOver_(p,alpha,q,beta)); /* src-over */
+ return(MagickOver_(q,beta,p,alpha)); /* dst-over */
+}
+
+static inline void CompositeDarken(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ gamma=1.0-QuantumScale*QuantumScale*alpha*beta;
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Darken(p->red,alpha,q->red,beta);
+ composite->green=gamma*Darken(p->green,alpha,q->green,beta);
+ composite->blue=gamma*Darken(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Darken(p->index,alpha,q->index,beta);
+}
+
+static inline MagickRealType Difference(const MagickRealType p,
+ const MagickRealType Sa,const MagickRealType q,const MagickRealType Da)
+{
+ /*
+ Optimized by Multipling by QuantumRange (taken from gamma).
+ */
+ return(Sa*p+Da*q-Sa*Da*2.0*MagickMin(p,q));
+}
+
+static inline void CompositeDifference(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ /*
+ Values not normalized as an optimization.
+ */
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Difference(p->red,Sa,q->red,Da);
+ composite->green=gamma*Difference(p->green,Sa,q->green,Da);
+ composite->blue=gamma*Difference(p->blue,Sa,q->blue,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Difference(p->index,Sa,q->index,Da);
+}
+
+static MagickRealType Divide(const MagickRealType Sca,const MagickRealType Sa,
+ const MagickRealType Dca,const MagickRealType Da)
+{
+ /*
+ Divide:
+
+ f(Sc,Dc) = Sc/Dc
+
+ But with appropriate handling for special case of Dc == 0 specifically
+ f(Black,Black) = Black and f(non-Black,Black) = White. It is however
+ also important to correctly do 'over' alpha blending which is why it
+ becomes so complex looking.
+ */
+ if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
+ return(Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if (fabs(Dca) < MagickEpsilon)
+ return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeDivide(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Divide(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*Divide(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*Divide(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Divide(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static MagickRealType Exclusion(const MagickRealType Sca,
+ const MagickRealType Sa, const MagickRealType Dca,const MagickRealType Da)
+{
+ return(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeExclusion(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma,
+ Sa,
+ Da;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Exclusion(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*Exclusion(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*Exclusion(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Exclusion(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static MagickRealType HardLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ if ((2.0*Sca) < Sa)
+ return(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeHardLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*HardLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*HardLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*HardLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*HardLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+
+static void CompositeHSB(const MagickRealType red,const MagickRealType green,
+ const MagickRealType blue,double *hue,double *saturation,double *brightness)
+{
+ MagickRealType
+ delta,
+ max,
+ min;
+
+ /*
+ Convert RGB to HSB colorspace.
+ */
+ assert(hue != (double *) NULL);
+ assert(saturation != (double *) NULL);
+ assert(brightness != (double *) NULL);
+ max=(red > green ? red : green);
+ if (blue > max)
+ max=blue;
+ min=(red < green ? red : green);
+ if (blue < min)
+ min=blue;
+ *hue=0.0;
+ *saturation=0.0;
+ *brightness=(double) (QuantumScale*max);
+ if (max == 0.0)
+ return;
+ *saturation=(double) (1.0-min/max);
+ delta=max-min;
+ if (delta == 0.0)
+ return;
+ if (red == max)
+ *hue=(double) ((green-blue)/delta);
+ else
+ if (green == max)
+ *hue=(double) (2.0+(blue-red)/delta);
+ else
+ if (blue == max)
+ *hue=(double) (4.0+(red-green)/delta);
+ *hue/=6.0;
+ if (*hue < 0.0)
+ *hue+=1.0;
+}
+
+static inline MagickRealType In(const MagickRealType p,
+ const MagickRealType alpha,const MagickRealType magick_unused(q),
+ const MagickRealType beta)
+{
+ return((1.0-QuantumScale*alpha)*p*(1.0-QuantumScale*beta));
+}
+
+static inline void CompositeIn(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ gamma=(1.0-QuantumScale*alpha)*(1.0-QuantumScale*beta);
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*In(p->red,alpha,q->red,beta);
+ composite->green=gamma*In(p->green,alpha,q->green,beta);
+ composite->blue=gamma*In(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*In(p->index,alpha,q->index,beta);
+}
+
+static inline MagickRealType Lighten(const MagickRealType p,
+ const MagickRealType alpha,const MagickRealType q,const MagickRealType beta)
+{
+ if (p > q)
+ return(MagickOver_(p,alpha,q,beta)); /* src-over */
+ return(MagickOver_(q,beta,p,alpha)); /* dst-over */
+}
+
+static inline void CompositeLighten(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ composite->opacity=QuantumScale*alpha*beta; /* optimized 1-gamma */
+ gamma=1.0-QuantumScale*composite->opacity;
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Lighten(p->red,alpha,q->red,beta);
+ composite->green=gamma*Lighten(p->green,alpha,q->green,beta);
+ composite->blue=gamma*Lighten(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Lighten(p->index,alpha,q->index,beta);
+}
+
+static inline void CompositeLinearDodge(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ /*
+ Operation performed directly - not need for sub-routine.
+ */
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*(p->red*Sa+q->red*Da);
+ composite->green=gamma*(p->green*Sa+q->green*Da);
+ composite->blue=gamma*(p->blue*Sa+q->blue*Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*(p->index*Sa+q->index*Da);
+}
+
+
+static inline MagickRealType LinearBurn(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ /*
+ LinearLight: as defined by Abode Photoshop, according to
+ http://www.simplefilter.de/en/basics/mixmods.html is:
+
+ f(Sc,Dc) = Dc + Sc - 1
+ */
+ return(Sca+Dca-Sa*Da);
+}
+
+static inline void CompositeLinearBurn(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*LinearBurn(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*LinearBurn(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*LinearBurn(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*LinearBurn(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+
+static inline MagickRealType LinearLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+#if 0
+ /*
+ Previous formula, only valid for fully-opaque images.
+ */
+ return(Dca+2*Sca-1.0);
+#else
+ /*
+ LinearLight: as defined by Abode Photoshop, according to
+ http://www.simplefilter.de/en/basics/mixmods.html is:
+
+ f(Sc,Dc) = Dc + 2*Sc - 1
+ */
+ return((Sca-Sa)*Da+Sca+Dca);
+#endif
+}
+
+static inline void CompositeLinearLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*LinearLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*LinearLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*LinearLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*LinearLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static inline MagickRealType Mathematics(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da,
+ const GeometryInfo *geometry_info)
+{
+ /*
+ 'Mathematics' a free form user control mathematical composition is defined
+ as...
+
+ f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
+
+ Where the arguments A,B,C,D are (currently) passed to composite as
+ a command separated 'geometry' string in "compose:args" image artifact.
+
+ A = a->rho, B = a->sigma, C = a->xi, D = a->psi
+
+ Applying the SVG transparency formula (see above), we get...
+
+ Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
+
+ Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
+ Dca*(1.0-Sa)
+ */
+ return(geometry_info->rho*Sca*Dca+geometry_info->sigma*Sca*Da+
+ geometry_info->xi*Dca*Sa+geometry_info->psi*Sa*Da+Sca*(1.0-Da)+
+ Dca*(1.0-Sa));
+}
+
+static inline void CompositeMathematics(const MagickPixelPacket *p,
+ const MagickPixelPacket *q,const GeometryInfo *args,
+ MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*p->opacity;
+ Da=1.0-QuantumScale*q->opacity;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Mathematics(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da,args);
+ composite->green=gamma*Mathematics(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da,args);
+ composite->blue=gamma*Mathematics(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da,args);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Mathematics(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da,args);
+}
+
+static inline MagickRealType Minus(const MagickRealType Sca,
+ const MagickRealType Dca)
+{
+ return(Sca-Dca);
+}
+
+static inline void CompositeMinus(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha;
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa-Da); /* is this correct? - I do not think so! */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Minus(p->red*Sa,q->red*Da);
+ composite->green=gamma*Minus(p->green*Sa,q->green*Da);
+ composite->blue=gamma*Minus(p->blue*Sa,q->blue*Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Minus(p->index*Sa,q->index*Da);
+}
+
+static inline MagickRealType Multiply(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ return(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeMultiply(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha;
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Multiply(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*Multiply(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*Multiply(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Multiply(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static inline MagickRealType Out(const MagickRealType p,
+ const MagickRealType alpha,const MagickRealType magick_unused(q),
+ const MagickRealType beta)
+{
+ return((1.0-QuantumScale*alpha)*p*QuantumScale*beta);
+}
+
+static inline void CompositeOut(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ gamma;
+
+ gamma=(1.0-QuantumScale*alpha)*QuantumScale*beta;
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Out(p->red,alpha,q->red,beta);
+ composite->green=gamma*Out(p->green,alpha,q->green,beta);
+ composite->blue=gamma*Out(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Out(p->index,alpha,q->index,beta);
+}
+
+static inline void CompositeOver(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickPixelCompositeOver(p,alpha,q,beta,composite);
+}
+
+static MagickRealType PegtopLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ /*
+ PegTOP Soft-Light alternative: A continuous version of the Softlight
+ function, producing very similar results however it does not take into
+ account alpha channel.
+
+ f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
+
+ See http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
+ */
+ if (fabs(Da) < MagickEpsilon)
+ return(Sca);
+ return(Dca*Dca*(Sa-2*Sca)/Da+Sca*(2*Dca+1-Da)+Dca*(1-Sa));
+}
+
+static inline void CompositePegtopLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*PegtopLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*PegtopLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*PegtopLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*PegtopLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static MagickRealType PinLight(const MagickRealType Sca,
+ const MagickRealType Sa,const MagickRealType Dca,const MagickRealType Da)
+{
+ /*
+ PinLight: A Photoshop 7 composition method
+ http://www.simplefilter.de/en/basics/mixmods.html
+
+ f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
+ */
+ if (Dca*Sa < Da*(2*Sca-Sa))
+ return(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
+ if ((Dca*Sa) > (2*Sca*Da))
+ return(Sca*Da+Sca+Dca*(1.0-Sa));
+ return(Sca*(1.0-Da)+Dca);
+}
+
+static inline void CompositePinLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*PinLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*PinLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*PinLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*PinLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static inline void CompositePlus(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickPixelCompositePlus(p,alpha,q,beta,composite);
+}
+
+static inline MagickRealType Screen(const MagickRealType Sca,
+ const MagickRealType Dca)
+{
+ return(Sca+Dca-Sca*Dca);
+}
+
+static inline void CompositeScreen(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Screen(QuantumScale*p->red*Sa,QuantumScale*
+ q->red*Da);
+ composite->green=gamma*Screen(QuantumScale*p->green*Sa,QuantumScale*
+ q->green*Da);
+ composite->blue=gamma*Screen(QuantumScale*p->blue*Sa,QuantumScale*
+ q->blue*Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Screen(QuantumScale*p->index*Sa,QuantumScale*
+ q->index*Da);
+}
+
+static MagickRealType SoftLight(const MagickRealType Sca,
+ const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
+{
+#if 0
+ /*
+ Oct 2004 SVG specification -- spec discovered to be incorrect
+ See http://lists.w3.org/Archives/Public/www-svg/2009Feb/0014.html.
+ */
+ if (2.0*Sca < Sa)
+ return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if (8.0*Dca <= Da)
+ return(Dca*(Sa-(1.0-Dca/Da)*(2.0*Sca-Sa)*(3.0-8.0*Dca/Da))+
+ Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return((Dca*Sa+(pow(Dca/Da,0.5)*Da-Dca)*(2.0*Sca-Sa))+Sca*(1.0-Da)+
+ Dca*(1.0-Sa));
+#else
+ MagickRealType
+ alpha,
+ beta;
+
+ /*
+ New specification: March 2009 SVG specification.
+ */
+ alpha=Dca/Da;
+ if ((2.0*Sca) < Sa)
+ return(Dca*(Sa+(2.0*Sca-Sa)*(1.0-alpha))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
+ {
+ beta=Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*alpha*(4.0*alpha+1.0)*(alpha-1.0)+7.0*
+ alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
+ return(beta);
+ }
+ beta=Dca*Sa+Da*(2.0*Sca-Sa)*(pow(alpha,0.5)-alpha)+Sca*(1.0-Da)+Dca*(1.0-Sa);
+ return(beta);
+#endif
+}
+
+static inline void CompositeSoftLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*SoftLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*SoftLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*SoftLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*SoftLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static inline MagickRealType Subtract(const MagickRealType p,
+ const MagickRealType magick_unused(alpha),const MagickRealType q,
+ const MagickRealType magick_unused(beta))
+{
+ MagickRealType
+ pixel;
+
+ pixel=p-q;
+ if (pixel < 0.0)
+ pixel+=(QuantumRange+1.0);
+ return(pixel);
+}
+
+static inline void CompositeSubtract(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ composite->red=Subtract(p->red,alpha,q->red,beta);
+ composite->green=Subtract(p->green,alpha,q->green,beta);
+ composite->blue=Subtract(p->blue,alpha,q->blue,beta);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=Subtract(p->index,alpha,q->index,beta);
+}
+
+static inline MagickRealType Threshold(const MagickRealType p,
+ const MagickRealType magick_unused(alpha),const MagickRealType q,
+ const MagickRealType magick_unused(beta),const MagickRealType threshold,
+ const MagickRealType amount)
+{
+ MagickRealType
+ delta;
+
+ delta=p-q;
+ if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
+ return(q);
+ return(q+delta*amount);
+}
+
+static inline void CompositeThreshold(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,const MagickRealType threshold,
+ const MagickRealType amount,MagickPixelPacket *composite)
+{
+ composite->red=Threshold(p->red,alpha,q->red,beta,threshold,amount);
+ composite->green=Threshold(p->green,alpha,q->green,beta,threshold,amount);
+ composite->blue=Threshold(p->blue,alpha,q->blue,beta,threshold,amount);
+ composite->opacity=(MagickRealType) QuantumRange-
+ Threshold(p->opacity,alpha,q->opacity,beta,threshold,amount);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=Threshold(p->index,alpha,q->index,beta,threshold,amount);
+}
+
+static MagickRealType VividLight(const MagickRealType Sca,
+ const MagickRealType Sa, const MagickRealType Dca, const MagickRealType Da)
+{
+ /*
+ VividLight: A Photoshop 7 composition method. See
+ http://www.simplefilter.de/en/basics/mixmods.html.
+
+ f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
+ */
+ if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
+ return(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ if ((2*Sca) <= Sa)
+ return(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+ return(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
+}
+
+static inline void CompositeVividLight(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=RoundToUnity(Sa+Da-Sa*Da); /* over blend, as per SVG doc */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ gamma=QuantumRange/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*VividLight(QuantumScale*p->red*Sa,Sa,QuantumScale*
+ q->red*Da,Da);
+ composite->green=gamma*VividLight(QuantumScale*p->green*Sa,Sa,QuantumScale*
+ q->green*Da,Da);
+ composite->blue=gamma*VividLight(QuantumScale*p->blue*Sa,Sa,QuantumScale*
+ q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*VividLight(QuantumScale*p->index*Sa,Sa,QuantumScale*
+ q->index*Da,Da);
+}
+
+static MagickRealType Xor(const MagickRealType Sca,const MagickRealType Sa,
+ const MagickRealType Dca,const MagickRealType Da)
+{
+ return(Sca*(1-Da)+Dca*(1-Sa));
+}
+
+static inline void CompositeXor(const MagickPixelPacket *p,
+ const MagickRealType alpha,const MagickPixelPacket *q,
+ const MagickRealType beta,MagickPixelPacket *composite)
+{
+ MagickRealType
+ Da,
+ gamma,
+ Sa;
+
+ Sa=1.0-QuantumScale*alpha; /* simplify and speed up equations */
+ Da=1.0-QuantumScale*beta;
+ gamma=Sa+Da-2*Sa*Da; /* Xor blend mode X=0,Y=1,Z=1 */
+ composite->opacity=(MagickRealType) QuantumRange*(1.0-gamma);
+ /*
+ Optimized by multipling QuantumRange taken from gamma.
+ */
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ composite->red=gamma*Xor(p->red*Sa,Sa,q->red*Da,Da);
+ composite->green=gamma*Xor(p->green*Sa,Sa,q->green*Da,Da);
+ composite->blue=gamma*Xor(p->blue*Sa,Sa,q->blue*Da,Da);
+ if (q->colorspace == CMYKColorspace)
+ composite->index=gamma*Xor(p->index*Sa,Sa,q->index*Da,Da);
+}
+
+static void HSBComposite(const double hue,const double saturation,
+ const double brightness,MagickRealType *red,MagickRealType *green,
+ MagickRealType *blue)
+{
+ MagickRealType
+ f,
+ h,
+ p,
+ q,
+ t;
+
+ /*
+ Convert HSB to RGB colorspace.
+ */
+ assert(red != (MagickRealType *) NULL);
+ assert(green != (MagickRealType *) NULL);
+ assert(blue != (MagickRealType *) NULL);
+ if (saturation == 0.0)
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(*red);
+ *blue=(*red);
+ return;
+ }
+ h=6.0*(hue-floor(hue));
+ f=h-floor((double) h);
+ p=brightness*(1.0-saturation);
+ q=brightness*(1.0-saturation*f);
+ t=brightness*(1.0-saturation*(1.0-f));
+ switch ((int) h)
+ {
+ case 0:
+ default:
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(MagickRealType) QuantumRange*t;
+ *blue=(MagickRealType) QuantumRange*p;
+ break;
+ }
+ case 1:
+ {
+ *red=(MagickRealType) QuantumRange*q;
+ *green=(MagickRealType) QuantumRange*brightness;
+ *blue=(MagickRealType) QuantumRange*p;
+ break;
+ }
+ case 2:
+ {
+ *red=(MagickRealType) QuantumRange*p;
+ *green=(MagickRealType) QuantumRange*brightness;
+ *blue=(MagickRealType) QuantumRange*t;
+ break;
+ }
+ case 3:
+ {
+ *red=(MagickRealType) QuantumRange*p;
+ *green=(MagickRealType) QuantumRange*q;
+ *blue=(MagickRealType) QuantumRange*brightness;
+ break;
+ }
+ case 4:
+ {
+ *red=(MagickRealType) QuantumRange*t;
+ *green=(MagickRealType) QuantumRange*p;
+ *blue=(MagickRealType) QuantumRange*brightness;
+ break;
+ }
+ case 5:
+ {
+ *red=(MagickRealType) QuantumRange*brightness;
+ *green=(MagickRealType) QuantumRange*p;
+ *blue=(MagickRealType) QuantumRange*q;
+ break;
+ }
+ }
+}
+
+MagickExport MagickBooleanType CompositeImage(Image *image,
+ const CompositeOperator compose,const Image *composite_image,
+ const long x_offset,const long y_offset)
+{
+ MagickBooleanType
+ status;
+
+ status=CompositeImageChannel(image,DefaultChannels,compose,composite_image,
+ x_offset,y_offset);
+ return(status);
+}
+
+MagickExport MagickBooleanType CompositeImageChannel(Image *image,
+ const ChannelType magick_unused(channel),const CompositeOperator compose,
+ const Image *composite_image,const long x_offset,const long y_offset)
+{
+#define CompositeImageTag "Composite/Image"
+
+ CacheView
+ *composite_view,
+ *image_view;
+
+ const char
+ *value;
+
+ double
+ sans;
+
+ ExceptionInfo
+ *exception;
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *destination_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ modify_outside_overlay,
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ amount,
+ destination_dissolve,
+ midpoint,
+ percent_brightness,
+ percent_saturation,
+ source_dissolve,
+ threshold;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Prepare composite image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(composite_image != (Image *) NULL);
+ assert(composite_image->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ GetMagickPixelPacket(image,&zero);
+ destination_image=(Image *) NULL;
+ amount=0.5;
+ destination_dissolve=1.0;
+ modify_outside_overlay=MagickFalse;
+ percent_brightness=100.0;
+ percent_saturation=100.0;
+ source_dissolve=1.0;
+ threshold=0.05f;
+ switch (compose)
+ {
+ case ClearCompositeOp:
+ case SrcCompositeOp:
+ case InCompositeOp:
+ case SrcInCompositeOp:
+ case OutCompositeOp:
+ case SrcOutCompositeOp:
+ case DstInCompositeOp:
+ case DstAtopCompositeOp:
+ {
+ /*
+ Modify destination outside the overlaid region.
+ */
+ modify_outside_overlay=MagickTrue;
+ break;
+ }
+ case CopyOpacityCompositeOp:
+ case ChangeMaskCompositeOp:
+ {
+ /*
+ Modify destination outside the overlaid region and require an alpha
+ channel to exist, to add transparency.
+ */
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ modify_outside_overlay=MagickTrue;
+ break;
+ }
+ case BlurCompositeOp:
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ blur_xu,
+ blur_xv,
+ blur_yu,
+ blur_yv;
+
+ ResampleFilter
+ *resample_filter;
+
+ CacheView
+ *composite_view,
+ *dest_view;
+
+ /*
+ Blur Image dictated by an overlay gradient map:
+ X = red_channel; Y = green_channel;
+ compose:args = x_scale[,y_scale[,angle]]
+ */
+ destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (destination_image == (Image *) NULL)
+ return(MagickFalse);
+ /*
+ Determine the horizontal and vertical maximim blur.
+ */
+ SetGeometryInfo(&geometry_info);
+ flags=NoValue;
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ flags=ParseGeometry(value,&geometry_info);
+ if ((flags & WidthValue) == 0 )
+ {
+ destination_image=DestroyImage(destination_image);
+ return(MagickFalse);
+ }
+ blur_xu=geometry_info.rho;
+ blur_yv=geometry_info.sigma;
+ blur_xv=blur_yu = 0.0;
+ if ((flags & HeightValue) == 0)
+ blur_yv=blur_xu;
+ if ((flags & XValue) != 0)
+ {
+ MagickRealType
+ angle,
+ x,
+ y;
+
+ x=blur_xu;
+ y=blur_yv;
+ angle=DegreesToRadians(geometry_info.xi);
+ blur_xu=x*cos(angle);
+ blur_xv=x*sin(angle);
+ blur_yu=(-y*sin(angle));
+ blur_yu=y*cos(angle);
+ }
+ /*
+ Blur Image by resampling;
+ */
+ pixel=zero;
+ exception=(&image->exception);
+ resample_filter=AcquireResampleFilter(image,&image->exception);
+ SetResampleFilter(resample_filter,GaussianFilter,1.0);
+ dest_view=AcquireCacheView(destination_image);
+ composite_view=AcquireCacheView(composite_image);
+ for (y=0; y < (long) composite_image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register PixelPacket
+ *__restrict r;
+
+ register IndexPacket
+ *__restrict destination_indexes;
+
+ register long
+ x;
+
+ if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
+ continue;
+ p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
+ 1,exception);
+ r=QueueCacheViewAuthenticPixels(dest_view,0,y,
+ destination_image->columns,1,&image->exception);
+ if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
+ break;
+ destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
+ for (x=0; x < (long) composite_image->columns; x++)
+ {
+ if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
+ {
+ p++;
+ continue;
+ }
+ ScaleResampleFilter(resample_filter,blur_xu*QuantumScale*p->red,
+ blur_yu*QuantumScale*p->green,blur_xv*QuantumScale*p->red,
+ blur_yv*QuantumScale*p->green);
+ (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
+ (double) y_offset+y,&pixel);
+ SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
+ p++;
+ r++;
+ }
+ sync=SyncCacheViewAuthenticPixels(dest_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ resample_filter=DestroyResampleFilter(resample_filter);
+ composite_view=DestroyCacheView(composite_view);
+ dest_view=DestroyCacheView(dest_view);
+ composite_image=destination_image;
+ break;
+ }
+ case DisplaceCompositeOp:
+ case DistortCompositeOp:
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ horizontal_scale,
+ vertical_scale,
+ x_center,
+ y_center,
+ x_lookup,
+ y_lookup;
+
+ register IndexPacket
+ *__restrict destination_indexes;
+
+ register PixelPacket
+ *__restrict r;
+
+ ResampleFilter
+ *resample_filter;
+
+ CacheView
+ *composite_view,
+ *dest_view;
+
+ /*
+ Displace/Distort based on overlay gradient map:
+ X = red_channel; Y = green_channel;
+ compose:args = x_scale[,y_scale[,x_center,y_center]]
+ */
+ destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (destination_image == (Image *) NULL)
+ return(MagickFalse);
+ SetGeometryInfo(&geometry_info);
+ flags=NoValue;
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ flags=ParseGeometry(value,&geometry_info);
+ if ((flags & (WidthValue|HeightValue)) == 0 )
+ if ((flags & AspectValue) == 0)
+ {
+ horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
+ 2.0;
+ vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
+ }
+ else
+ {
+ horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
+ vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
+ }
+ else
+ {
+ horizontal_scale=geometry_info.rho;
+ vertical_scale=geometry_info.sigma;
+ if ((flags & PercentValue) != 0)
+ {
+ if ((flags & AspectValue) == 0)
+ {
+ horizontal_scale*=(composite_image->columns-1.0)/200.0;
+ vertical_scale*=(composite_image->rows-1.0)/200.0;
+ }
+ else
+ {
+ horizontal_scale*=(image->columns-1.0)/200.0;
+ vertical_scale*=(image->rows-1.0)/200.0;
+ }
+ }
+ if ((flags & HeightValue) == 0)
+ vertical_scale=horizontal_scale;
+ }
+ /*
+ Determine fixed center point for absolute distortion map
+ Absolute distort ==
+ Displace lookup relative to a fixed absolute point
+ Select that point according to +X+Y user inputs.
+ default = center of overlay image
+ flag '!' = locations/percentage relative to background image
+ */
+ x_center=(MagickRealType) x_offset;
+ y_center=(MagickRealType) y_offset;
+ if (compose == DistortCompositeOp)
+ {
+ if ((flags & XValue) == 0)
+ if ((flags & AspectValue) == 0)
+ x_center=(MagickRealType) x_offset+ (composite_image->columns-1)/
+ 2.0;
+ else
+ x_center=((MagickRealType) image->columns-1)/2.0;
+ else
+ if ((flags & AspectValue) == 0)
+ x_center=(MagickRealType) x_offset+geometry_info.xi;
+ else
+ x_center=geometry_info.xi;
+ if ((flags & YValue) == 0)
+ if ((flags & AspectValue) == 0)
+ y_center=(MagickRealType) y_offset+
+ (composite_image->rows-1)/2.0;
+ else
+ y_center=((MagickRealType) image->rows-1)/2.0;
+ else
+ if ((flags & AspectValue) == 0)
+ y_center=(MagickRealType) y_offset+geometry_info.psi;
+ else
+ y_center=geometry_info.psi;
+ }
+ /*
+ Shift the pixel lookup point as defined by the provided,
+ displacement/distortion map. -- Like a lens...
+ */
+ pixel=zero;
+ exception=(&image->exception);
+ resample_filter=AcquireResampleFilter(image,&image->exception);
+ dest_view=AcquireCacheView(destination_image);
+ composite_view=AcquireCacheView(composite_image);
+ for (y=0; y < (long) composite_image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ if (((y+y_offset) < 0) || ((y+y_offset) >= (long) image->rows))
+ continue;
+ p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
+ 1,exception);
+ r=QueueCacheViewAuthenticPixels(dest_view,0,y,
+ destination_image->columns,1,&image->exception);
+ if ((p == (const PixelPacket *) NULL) || (r == (PixelPacket *) NULL))
+ break;
+ destination_indexes=GetCacheViewAuthenticIndexQueue(dest_view);
+ for (x=0; x < (long) composite_image->columns; x++)
+ {
+ if (((x_offset+x) < 0) || ((x_offset+x) >= (long) image->columns))
+ {
+ p++;
+ continue;
+ }
+ /*
+ Displace the lookup.
+ */
+ x_lookup=(horizontal_scale*(p->red-(((MagickRealType) QuantumRange+
+ 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
+ x_center+((compose == DisplaceCompositeOp) ? x : 0);
+ y_lookup=(vertical_scale*(p->green-(((MagickRealType) QuantumRange+
+ 1.0)/2.0)))/(((MagickRealType) QuantumRange+1.0)/2.0)+
+ y_center+((compose == DisplaceCompositeOp) ? y : 0);
+ (void) ResamplePixelColor(resample_filter,(double) x_lookup,
+ (double) y_lookup,&pixel);
+ /*
+ Mask with 'invalid pixel mask' in alpha channel.
+ */
+ pixel.opacity = (MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
+ pixel.opacity)*(1.0-QuantumScale*p->opacity));
+ SetPixelPacket(destination_image,&pixel,r,destination_indexes+x);
+ p++;
+ r++;
+ }
+ sync=SyncCacheViewAuthenticPixels(dest_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ resample_filter=DestroyResampleFilter(resample_filter);
+ composite_view=DestroyCacheView(composite_view);
+ dest_view=DestroyCacheView(dest_view);
+ composite_image=destination_image;
+ break;
+ }
+ case DissolveCompositeOp:
+ {
+ /*
+ Geometry arguments to dissolve factors.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ source_dissolve=geometry_info.rho/100.0;
+ destination_dissolve=1.0;
+ if ((source_dissolve-MagickEpsilon) < 0.0)
+ source_dissolve=0.0;
+ if ((source_dissolve+MagickEpsilon) > 1.0)
+ {
+ destination_dissolve=2.0-source_dissolve;
+ source_dissolve=1.0;
+ }
+ if ((flags & SigmaValue) != 0)
+ destination_dissolve=geometry_info.sigma/100.0;
+ if ((destination_dissolve-MagickEpsilon) < 0.0)
+ destination_dissolve=0.0;
+ modify_outside_overlay=MagickTrue;
+ if ((destination_dissolve+MagickEpsilon) > 1.0 )
+ {
+ destination_dissolve=1.0;
+ modify_outside_overlay=MagickFalse;
+ }
+ }
+ break;
+ }
+ case BlendCompositeOp:
+ {
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ source_dissolve=geometry_info.rho/100.0;
+ destination_dissolve=1.0-source_dissolve;
+ if ((flags & SigmaValue) != 0)
+ destination_dissolve=geometry_info.sigma/100.0;
+ modify_outside_overlay=MagickTrue;
+ if ((destination_dissolve+MagickEpsilon) > 1.0)
+ modify_outside_overlay=MagickFalse;
+ }
+ break;
+ }
+ case MathematicsCompositeOp:
+ {
+ /*
+ Just collect the values from "compose:args", setting.
+ Unused values are set to zero automagically.
+
+ Arguments are normally a comma separated list, so this probably should
+ be changed to some 'general comma list' parser, (with a minimum
+ number of values)
+ */
+ SetGeometryInfo(&geometry_info);
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ (void) ParseGeometry(value,&geometry_info);
+ break;
+ }
+ case ModulateCompositeOp:
+ {
+ /*
+ Determine the brightness and saturation scale.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ percent_brightness=geometry_info.rho;
+ if ((flags & SigmaValue) != 0)
+ percent_saturation=geometry_info.sigma;
+ }
+ break;
+ }
+ case ThresholdCompositeOp:
+ {
+ /*
+ Determine the amount and threshold.
+ */
+ value=GetImageArtifact(composite_image,"compose:args");
+ if (value != (char *) NULL)
+ {
+ flags=ParseGeometry(value,&geometry_info);
+ amount=geometry_info.rho;
+ threshold=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ threshold=0.05f;
+ }
+ threshold*=QuantumRange;
+ break;
+ }
+ default:
+ break;
+ }
+ value=GetImageArtifact(composite_image,"compose:outside-overlay");
+ if (value != (const char *) NULL)
+ modify_outside_overlay=IsMagickTrue(value);
+ /*
+ Composite image.
+ */
+ status=MagickTrue;
+ progress=0;
+ midpoint=((MagickRealType) QuantumRange+1.0)/2;
+ GetMagickPixelPacket(composite_image,&zero);
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ composite_view=AcquireCacheView(composite_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ const PixelPacket
+ *pixels;
+
+ double
+ brightness,
+ hue,
+ saturation;
+
+ MagickPixelPacket
+ composite,
+ destination,
+ source;
+
+ register const IndexPacket
+ *__restrict composite_indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ if (modify_outside_overlay == MagickFalse)
+ {
+ if (y < y_offset)
+ continue;
+ if ((y-y_offset) >= (long) composite_image->rows)
+ continue;
+ }
+ /*
+ If pixels is NULL, y is outside overlay region.
+ */
+ pixels=(PixelPacket *) NULL;
+ p=(PixelPacket *) NULL;
+ if ((y >= y_offset) && ((y-y_offset) < (long) composite_image->rows))
+ {
+ p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
+ composite_image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ pixels=p;
+ if (x_offset < 0)
+ p-=x_offset;
+ }
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ composite_indexes=GetCacheViewVirtualIndexQueue(composite_view);
+ source=zero;
+ destination=zero;
+ hue=0.0;
+ saturation=0.0;
+ brightness=0.0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (modify_outside_overlay == MagickFalse)
+ {
+ if (x < x_offset)
+ {
+ q++;
+ continue;
+ }
+ if ((x-x_offset) >= (long) composite_image->columns)
+ break;
+ }
+ destination.red=(MagickRealType) q->red;
+ destination.green=(MagickRealType) q->green;
+ destination.blue=(MagickRealType) q->blue;
+ if (image->matte != MagickFalse)
+ destination.opacity=(MagickRealType) q->opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ destination.red=(MagickRealType) QuantumRange-destination.red;
+ destination.green=(MagickRealType) QuantumRange-destination.green;
+ destination.blue=(MagickRealType) QuantumRange-destination.blue;
+ destination.index=(MagickRealType) (QuantumRange-indexes[x]);
+ }
+ /*
+ Handle destination modifications outside overlaid region.
+ */
+ composite=destination;
+ if ((pixels == (PixelPacket *) NULL) || (x < x_offset) ||
+ ((x-x_offset) >= (long) composite_image->columns))
+ {
+ switch (compose)
+ {
+ case DissolveCompositeOp:
+ case BlendCompositeOp:
+ {
+ composite.opacity=(MagickRealType) (QuantumRange-
+ destination_dissolve*(QuantumRange-composite.opacity));
+ break;
+ }
+ case ClearCompositeOp:
+ case SrcCompositeOp:
+ {
+ CompositeClear(&destination,&composite);
+ break;
+ }
+ case InCompositeOp:
+ case SrcInCompositeOp:
+ case OutCompositeOp:
+ case SrcOutCompositeOp:
+ case DstInCompositeOp:
+ case DstAtopCompositeOp:
+ case CopyOpacityCompositeOp:
+ case ChangeMaskCompositeOp:
+ {
+ composite.opacity=(MagickRealType) TransparentOpacity;
+ break;
+ }
+ default:
+ {
+ (void) GetOneVirtualMagickPixel(composite_image,x-x_offset,
+ y-y_offset,&composite,exception);
+ break;
+ }
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ composite.red=(MagickRealType) QuantumRange-composite.red;
+ composite.green=(MagickRealType) QuantumRange-composite.green;
+ composite.blue=(MagickRealType) QuantumRange-composite.blue;
+ composite.index=(MagickRealType) QuantumRange-composite.index;
+ }
+ q->red=RoundToQuantum(composite.red);
+ q->green=RoundToQuantum(composite.green);
+ q->blue=RoundToQuantum(composite.blue);
+ if (image->matte != MagickFalse)
+ q->opacity=RoundToQuantum(composite.opacity);
+ if (image->colorspace == CMYKColorspace)
+ indexes[x]=RoundToQuantum(composite.index);
+ q++;
+ continue;
+ }
+ /*
+ Handle normal overlay of source onto destination.
+ */
+ source.red=(MagickRealType) p->red;
+ source.green=(MagickRealType) p->green;
+ source.blue=(MagickRealType) p->blue;
+ if (composite_image->matte != MagickFalse)
+ source.opacity=(MagickRealType) p->opacity;
+ if (composite_image->colorspace == CMYKColorspace)
+ {
+ source.red=(MagickRealType) QuantumRange-source.red;
+ source.green=(MagickRealType) QuantumRange-source.green;
+ source.blue=(MagickRealType) QuantumRange-source.blue;
+ source.index=(MagickRealType) QuantumRange-(MagickRealType)
+ composite_indexes[x-x_offset];
+ }
+ switch (compose)
+ {
+ case AddCompositeOp:
+ {
+ CompositeAdd(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case ClearCompositeOp:
+ {
+ CompositeClear(&destination,&composite);
+ break;
+ }
+ case SrcCompositeOp:
+ case CopyCompositeOp:
+ case ReplaceCompositeOp:
+ {
+ composite=source;
+ break;
+ }
+ case ChangeMaskCompositeOp:
+ {
+ if ((composite.opacity > ((MagickRealType) QuantumRange/2.0)) ||
+ (IsMagickColorSimilar(&source,&destination) != MagickFalse))
+ composite.opacity=(MagickRealType) TransparentOpacity;
+ else
+ composite.opacity=(MagickRealType) OpaqueOpacity;
+ break;
+ }
+ case DivideCompositeOp:
+ {
+ CompositeDivide(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case DstCompositeOp:
+ break;
+ case OverCompositeOp:
+ case SrcOverCompositeOp:
+ {
+ CompositeOver(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case DstOverCompositeOp:
+ {
+ CompositeOver(&destination,destination.opacity,&source,source.opacity,
+ &composite);
+ break;
+ }
+ case SrcInCompositeOp:
+ case InCompositeOp:
+ {
+ CompositeIn(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case DstInCompositeOp:
+ {
+ CompositeIn(&destination,destination.opacity,&source,source.opacity,
+ &composite);
+ break;
+ }
+ case OutCompositeOp:
+ case SrcOutCompositeOp:
+ {
+ CompositeOut(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case DstOutCompositeOp:
+ {
+ CompositeOut(&destination,destination.opacity,&source,source.opacity,
+ &composite);
+ break;
+ }
+ case AtopCompositeOp:
+ case SrcAtopCompositeOp:
+ {
+ CompositeAtop(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case DstAtopCompositeOp:
+ {
+ CompositeAtop(&destination,destination.opacity,&source,source.opacity,
+ &composite);
+ break;
+ }
+ case XorCompositeOp:
+ {
+ CompositeXor(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case PlusCompositeOp:
+ {
+ CompositePlus(&source,source.opacity,&destination,destination.opacity,
+ &composite);
+ break;
+ }
+ case MultiplyCompositeOp:
+ {
+ CompositeMultiply(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case ScreenCompositeOp:
+ {
+ CompositeScreen(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case DarkenCompositeOp:
+ {
+ CompositeDarken(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case LightenCompositeOp:
+ {
+ CompositeLighten(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case ColorDodgeCompositeOp:
+ {
+ CompositeColorDodge(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case ColorBurnCompositeOp:
+ {
+ CompositeColorBurn(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case LinearDodgeCompositeOp:
+ {
+ CompositeLinearDodge(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case LinearBurnCompositeOp:
+ {
+ CompositeLinearBurn(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case HardLightCompositeOp:
+ {
+ CompositeHardLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case OverlayCompositeOp:
+ {
+ /*
+ Reversed HardLight.
+ */
+ CompositeHardLight(&destination,destination.opacity,&source,
+ source.opacity,&composite);
+ break;
+ }
+ case SoftLightCompositeOp:
+ {
+ CompositeSoftLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case LinearLightCompositeOp:
+ {
+ CompositeLinearLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case PegtopLightCompositeOp:
+ {
+ CompositePegtopLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case VividLightCompositeOp:
+ {
+ CompositeVividLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case PinLightCompositeOp:
+ {
+ CompositePinLight(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case DifferenceCompositeOp:
+ {
+ CompositeDifference(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case ExclusionCompositeOp:
+ {
+ CompositeExclusion(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case MinusCompositeOp:
+ {
+ CompositeMinus(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case BumpmapCompositeOp:
+ {
+ if (source.opacity == TransparentOpacity)
+ break;
+ CompositeBumpmap(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case DissolveCompositeOp:
+ {
+ CompositeOver(&source,(MagickRealType) (QuantumRange-source_dissolve*
+ (QuantumRange-source.opacity)),&destination,(MagickRealType)
+ (QuantumRange-destination_dissolve*(QuantumRange-
+ destination.opacity)),&composite);
+ break;
+ }
+ case BlendCompositeOp:
+ {
+ MagickPixelCompositeBlend(&source,source_dissolve,&destination,
+ destination_dissolve,&composite);
+ break;
+ }
+ case MathematicsCompositeOp:
+ {
+ CompositeMathematics(&source,&destination,&geometry_info,&composite);
+ break;
+ }
+ case BlurCompositeOp:
+ case DisplaceCompositeOp:
+ case DistortCompositeOp:
+ {
+ composite=source;
+ break;
+ }
+ case ThresholdCompositeOp:
+ {
+ CompositeThreshold(&source,source.opacity,&destination,
+ destination.opacity,threshold,amount,&composite);
+ break;
+ }
+ case ModulateCompositeOp:
+ {
+ long
+ offset;
+
+ if (source.opacity == TransparentOpacity)
+ break;
+ offset=(long) (MagickPixelIntensityToQuantum(&source)-midpoint);
+ if (offset == 0)
+ break;
+ CompositeHSB(destination.red,destination.green,destination.blue,&hue,
+ &saturation,&brightness);
+ brightness+=(0.01*percent_brightness*offset)/midpoint;
+ saturation*=0.01*percent_saturation;
+ HSBComposite(hue,saturation,brightness,&composite.red,
+ &composite.green,&composite.blue);
+ break;
+ }
+ case HueCompositeOp:
+ {
+ if (source.opacity == TransparentOpacity)
+ break;
+ if (destination.opacity == TransparentOpacity)
+ {
+ composite=source;
+ break;
+ }
+ CompositeHSB(destination.red,destination.green,destination.blue,&hue,
+ &saturation,&brightness);
+ CompositeHSB(source.red,source.green,source.blue,&hue,&sans,&sans);
+ HSBComposite(hue,saturation,brightness,&composite.red,
+ &composite.green,&composite.blue);
+ if (source.opacity < destination.opacity)
+ composite.opacity=source.opacity;
+ break;
+ }
+ case SaturateCompositeOp:
+ {
+ if (source.opacity == TransparentOpacity)
+ break;
+ if (destination.opacity == TransparentOpacity)
+ {
+ composite=source;
+ break;
+ }
+ CompositeHSB(destination.red,destination.green,destination.blue,&hue,
+ &saturation,&brightness);
+ CompositeHSB(source.red,source.green,source.blue,&sans,&saturation,
+ &sans);
+ HSBComposite(hue,saturation,brightness,&composite.red,
+ &composite.green,&composite.blue);
+ if (source.opacity < destination.opacity)
+ composite.opacity=source.opacity;
+ break;
+ }
+ case SubtractCompositeOp:
+ {
+ CompositeSubtract(&source,source.opacity,&destination,
+ destination.opacity,&composite);
+ break;
+ }
+ case LuminizeCompositeOp:
+ {
+ if (source.opacity == TransparentOpacity)
+ break;
+ if (destination.opacity == TransparentOpacity)
+ {
+ composite=source;
+ break;
+ }
+ CompositeHSB(destination.red,destination.green,destination.blue,&hue,
+ &saturation,&brightness);
+ CompositeHSB(source.red,source.green,source.blue,&sans,&sans,
+ &brightness);
+ HSBComposite(hue,saturation,brightness,&composite.red,
+ &composite.green,&composite.blue);
+ if (source.opacity < destination.opacity)
+ composite.opacity=source.opacity;
+ break;
+ }
+ case ColorizeCompositeOp:
+ {
+ if (source.opacity == TransparentOpacity)
+ break;
+ if (destination.opacity == TransparentOpacity)
+ {
+ composite=source;
+ break;
+ }
+ CompositeHSB(destination.red,destination.green,destination.blue,&sans,
+ &sans,&brightness);
+ CompositeHSB(source.red,source.green,source.blue,&hue,&saturation,
+ &sans);
+ HSBComposite(hue,saturation,brightness,&composite.red,
+ &composite.green,&composite.blue);
+ if (source.opacity < destination.opacity)
+ composite.opacity=source.opacity;
+ break;
+ }
+ case CopyRedCompositeOp:
+ case CopyCyanCompositeOp:
+ {
+ composite.red=source.red;
+ break;
+ }
+ case CopyGreenCompositeOp:
+ case CopyMagentaCompositeOp:
+ {
+ composite.green=source.green;
+ break;
+ }
+ case CopyBlueCompositeOp:
+ case CopyYellowCompositeOp:
+ {
+ composite.blue=source.blue;
+ break;
+ }
+ case CopyOpacityCompositeOp:
+ {
+ if (source.matte == MagickFalse)
+ {
+ composite.opacity=(MagickRealType) (QuantumRange-
+ MagickPixelIntensityToQuantum(&source));
+ break;
+ }
+ composite.opacity=source.opacity;
+ break;
+ }
+ case CopyBlackCompositeOp:
+ {
+ if (source.colorspace != CMYKColorspace)
+ ConvertRGBToCMYK(&source);
+ composite.index=source.index;
+ break;
+ }
+ default:
+ break;
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ composite.red=(MagickRealType) QuantumRange-composite.red;
+ composite.green=(MagickRealType) QuantumRange-composite.green;
+ composite.blue=(MagickRealType) QuantumRange-composite.blue;
+ composite.index=(MagickRealType) QuantumRange-composite.index;
+ }
+ q->red=RoundToQuantum(composite.red);
+ q->green=RoundToQuantum(composite.green);
+ q->blue=RoundToQuantum(composite.blue);
+ q->opacity=RoundToQuantum(composite.opacity);
+ if (image->colorspace == CMYKColorspace)
+ indexes[x]=RoundToQuantum(composite.index);
+ p++;
+ if (p >= (pixels+composite_image->columns))
+ p=pixels;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_CompositeImageChannel)
+#endif
+ proceed=SetImageProgress(image,CompositeImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ composite_view=DestroyCacheView(composite_view);
+ image_view=DestroyCacheView(image_view);
+ if (destination_image != (Image * ) NULL)
+ destination_image=DestroyImage(destination_image);
+ return(status);
+}
diff --git a/magick/composite.h b/magick/composite.h
new file mode 100644
index 0000000..e58769e
--- /dev/null
+++ b/magick/composite.h
@@ -0,0 +1,103 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image composite methods.
+*/
+#ifndef _MAGICKCORE_COMPOSITE_H
+#define _MAGICKCORE_COMPOSITE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedCompositeOp,
+ NoCompositeOp,
+ AddCompositeOp,
+ AtopCompositeOp,
+ BlendCompositeOp,
+ BumpmapCompositeOp,
+ ChangeMaskCompositeOp,
+ ClearCompositeOp,
+ ColorBurnCompositeOp,
+ ColorDodgeCompositeOp,
+ ColorizeCompositeOp,
+ CopyBlackCompositeOp,
+ CopyBlueCompositeOp,
+ CopyCompositeOp,
+ CopyCyanCompositeOp,
+ CopyGreenCompositeOp,
+ CopyMagentaCompositeOp,
+ CopyOpacityCompositeOp,
+ CopyRedCompositeOp,
+ CopyYellowCompositeOp,
+ DarkenCompositeOp,
+ DstAtopCompositeOp,
+ DstCompositeOp,
+ DstInCompositeOp,
+ DstOutCompositeOp,
+ DstOverCompositeOp,
+ DifferenceCompositeOp,
+ DisplaceCompositeOp,
+ DissolveCompositeOp,
+ ExclusionCompositeOp,
+ HardLightCompositeOp,
+ HueCompositeOp,
+ InCompositeOp,
+ LightenCompositeOp,
+ LinearLightCompositeOp,
+ LuminizeCompositeOp,
+ MinusCompositeOp,
+ ModulateCompositeOp,
+ MultiplyCompositeOp,
+ OutCompositeOp,
+ OverCompositeOp,
+ OverlayCompositeOp,
+ PlusCompositeOp,
+ ReplaceCompositeOp,
+ SaturateCompositeOp,
+ ScreenCompositeOp,
+ SoftLightCompositeOp,
+ SrcAtopCompositeOp,
+ SrcCompositeOp,
+ SrcInCompositeOp,
+ SrcOutCompositeOp,
+ SrcOverCompositeOp,
+ SubtractCompositeOp,
+ ThresholdCompositeOp,
+ XorCompositeOp,
+ DivideCompositeOp,
+ DistortCompositeOp,
+ BlurCompositeOp,
+ PegtopLightCompositeOp,
+ VividLightCompositeOp,
+ PinLightCompositeOp,
+ LinearDodgeCompositeOp,
+ LinearBurnCompositeOp,
+ MathematicsCompositeOp
+} CompositeOperator;
+
+extern MagickExport MagickBooleanType
+ CompositeImage(Image *,const CompositeOperator,const Image *,const long,
+ const long),
+ CompositeImageChannel(Image *,const ChannelType,const CompositeOperator,
+ const Image *,const long,const long);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/compress.c b/magick/compress.c
new file mode 100644
index 0000000..e025448
--- /dev/null
+++ b/magick/compress.c
@@ -0,0 +1,1478 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO M M PPPP RRRR EEEEE SSSSS SSSSS %
+% C O O MM MM P P R R E SS SS %
+% C O O M M M PPPP RRRR EEE SSS SSS %
+% C O O M M P R R E SS SS %
+% CCCC OOO M M P R R EEEEE SSSSS SSSSS %
+% %
+% %
+% MagickCore Image Compression/Decompression Methods %
+% %
+% Software Design %
+% John Cristy %
+% May 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/color-private.h"
+#include "magick/cache.h"
+#include "magick/compress.h"
+#include "magick/constitute.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/resource_.h"
+#include "magick/string_.h"
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+#if defined(MAGICKCORE_HAVE_TIFFCONF_H)
+#include "tiffconf.h"
+#endif
+#include "tiffio.h"
+#define CCITTParam "-1"
+#else
+#define CCITTParam "0"
+#endif
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+#include "zlib.h"
+#endif
+
+/*
+ Typedef declarations.
+*/
+struct _Ascii85Info
+{
+ long
+ offset,
+ line_break;
+
+ unsigned char
+ buffer[10];
+};
+
+typedef struct HuffmanTable
+{
+ unsigned long
+ id,
+ code,
+ length,
+ count;
+} HuffmanTable;
+
+/*
+ Huffman coding declarations.
+*/
+#define TWId 23
+#define MWId 24
+#define TBId 25
+#define MBId 26
+#define EXId 27
+
+static const HuffmanTable
+ MBTable[]=
+ {
+ { MBId, 0x0f, 10, 64 }, { MBId, 0xc8, 12, 128 },
+ { MBId, 0xc9, 12, 192 }, { MBId, 0x5b, 12, 256 },
+ { MBId, 0x33, 12, 320 }, { MBId, 0x34, 12, 384 },
+ { MBId, 0x35, 12, 448 }, { MBId, 0x6c, 13, 512 },
+ { MBId, 0x6d, 13, 576 }, { MBId, 0x4a, 13, 640 },
+ { MBId, 0x4b, 13, 704 }, { MBId, 0x4c, 13, 768 },
+ { MBId, 0x4d, 13, 832 }, { MBId, 0x72, 13, 896 },
+ { MBId, 0x73, 13, 960 }, { MBId, 0x74, 13, 1024 },
+ { MBId, 0x75, 13, 1088 }, { MBId, 0x76, 13, 1152 },
+ { MBId, 0x77, 13, 1216 }, { MBId, 0x52, 13, 1280 },
+ { MBId, 0x53, 13, 1344 }, { MBId, 0x54, 13, 1408 },
+ { MBId, 0x55, 13, 1472 }, { MBId, 0x5a, 13, 1536 },
+ { MBId, 0x5b, 13, 1600 }, { MBId, 0x64, 13, 1664 },
+ { MBId, 0x65, 13, 1728 }, { MBId, 0x00, 0, 0 }
+ };
+
+static const HuffmanTable
+ EXTable[]=
+ {
+ { EXId, 0x08, 11, 1792 }, { EXId, 0x0c, 11, 1856 },
+ { EXId, 0x0d, 11, 1920 }, { EXId, 0x12, 12, 1984 },
+ { EXId, 0x13, 12, 2048 }, { EXId, 0x14, 12, 2112 },
+ { EXId, 0x15, 12, 2176 }, { EXId, 0x16, 12, 2240 },
+ { EXId, 0x17, 12, 2304 }, { EXId, 0x1c, 12, 2368 },
+ { EXId, 0x1d, 12, 2432 }, { EXId, 0x1e, 12, 2496 },
+ { EXId, 0x1f, 12, 2560 }, { EXId, 0x00, 0, 0 }
+ };
+
+static const HuffmanTable
+ MWTable[]=
+ {
+ { MWId, 0x1b, 5, 64 }, { MWId, 0x12, 5, 128 },
+ { MWId, 0x17, 6, 192 }, { MWId, 0x37, 7, 256 },
+ { MWId, 0x36, 8, 320 }, { MWId, 0x37, 8, 384 },
+ { MWId, 0x64, 8, 448 }, { MWId, 0x65, 8, 512 },
+ { MWId, 0x68, 8, 576 }, { MWId, 0x67, 8, 640 },
+ { MWId, 0xcc, 9, 704 }, { MWId, 0xcd, 9, 768 },
+ { MWId, 0xd2, 9, 832 }, { MWId, 0xd3, 9, 896 },
+ { MWId, 0xd4, 9, 960 }, { MWId, 0xd5, 9, 1024 },
+ { MWId, 0xd6, 9, 1088 }, { MWId, 0xd7, 9, 1152 },
+ { MWId, 0xd8, 9, 1216 }, { MWId, 0xd9, 9, 1280 },
+ { MWId, 0xda, 9, 1344 }, { MWId, 0xdb, 9, 1408 },
+ { MWId, 0x98, 9, 1472 }, { MWId, 0x99, 9, 1536 },
+ { MWId, 0x9a, 9, 1600 }, { MWId, 0x18, 6, 1664 },
+ { MWId, 0x9b, 9, 1728 }, { MWId, 0x00, 0, 0 }
+ };
+
+static const HuffmanTable
+ TBTable[]=
+ {
+ { TBId, 0x37, 10, 0 }, { TBId, 0x02, 3, 1 }, { TBId, 0x03, 2, 2 },
+ { TBId, 0x02, 2, 3 }, { TBId, 0x03, 3, 4 }, { TBId, 0x03, 4, 5 },
+ { TBId, 0x02, 4, 6 }, { TBId, 0x03, 5, 7 }, { TBId, 0x05, 6, 8 },
+ { TBId, 0x04, 6, 9 }, { TBId, 0x04, 7, 10 }, { TBId, 0x05, 7, 11 },
+ { TBId, 0x07, 7, 12 }, { TBId, 0x04, 8, 13 }, { TBId, 0x07, 8, 14 },
+ { TBId, 0x18, 9, 15 }, { TBId, 0x17, 10, 16 }, { TBId, 0x18, 10, 17 },
+ { TBId, 0x08, 10, 18 }, { TBId, 0x67, 11, 19 }, { TBId, 0x68, 11, 20 },
+ { TBId, 0x6c, 11, 21 }, { TBId, 0x37, 11, 22 }, { TBId, 0x28, 11, 23 },
+ { TBId, 0x17, 11, 24 }, { TBId, 0x18, 11, 25 }, { TBId, 0xca, 12, 26 },
+ { TBId, 0xcb, 12, 27 }, { TBId, 0xcc, 12, 28 }, { TBId, 0xcd, 12, 29 },
+ { TBId, 0x68, 12, 30 }, { TBId, 0x69, 12, 31 }, { TBId, 0x6a, 12, 32 },
+ { TBId, 0x6b, 12, 33 }, { TBId, 0xd2, 12, 34 }, { TBId, 0xd3, 12, 35 },
+ { TBId, 0xd4, 12, 36 }, { TBId, 0xd5, 12, 37 }, { TBId, 0xd6, 12, 38 },
+ { TBId, 0xd7, 12, 39 }, { TBId, 0x6c, 12, 40 }, { TBId, 0x6d, 12, 41 },
+ { TBId, 0xda, 12, 42 }, { TBId, 0xdb, 12, 43 }, { TBId, 0x54, 12, 44 },
+ { TBId, 0x55, 12, 45 }, { TBId, 0x56, 12, 46 }, { TBId, 0x57, 12, 47 },
+ { TBId, 0x64, 12, 48 }, { TBId, 0x65, 12, 49 }, { TBId, 0x52, 12, 50 },
+ { TBId, 0x53, 12, 51 }, { TBId, 0x24, 12, 52 }, { TBId, 0x37, 12, 53 },
+ { TBId, 0x38, 12, 54 }, { TBId, 0x27, 12, 55 }, { TBId, 0x28, 12, 56 },
+ { TBId, 0x58, 12, 57 }, { TBId, 0x59, 12, 58 }, { TBId, 0x2b, 12, 59 },
+ { TBId, 0x2c, 12, 60 }, { TBId, 0x5a, 12, 61 }, { TBId, 0x66, 12, 62 },
+ { TBId, 0x67, 12, 63 }, { TBId, 0x00, 0, 0 }
+ };
+
+static const HuffmanTable
+ TWTable[]=
+ {
+ { TWId, 0x35, 8, 0 }, { TWId, 0x07, 6, 1 }, { TWId, 0x07, 4, 2 },
+ { TWId, 0x08, 4, 3 }, { TWId, 0x0b, 4, 4 }, { TWId, 0x0c, 4, 5 },
+ { TWId, 0x0e, 4, 6 }, { TWId, 0x0f, 4, 7 }, { TWId, 0x13, 5, 8 },
+ { TWId, 0x14, 5, 9 }, { TWId, 0x07, 5, 10 }, { TWId, 0x08, 5, 11 },
+ { TWId, 0x08, 6, 12 }, { TWId, 0x03, 6, 13 }, { TWId, 0x34, 6, 14 },
+ { TWId, 0x35, 6, 15 }, { TWId, 0x2a, 6, 16 }, { TWId, 0x2b, 6, 17 },
+ { TWId, 0x27, 7, 18 }, { TWId, 0x0c, 7, 19 }, { TWId, 0x08, 7, 20 },
+ { TWId, 0x17, 7, 21 }, { TWId, 0x03, 7, 22 }, { TWId, 0x04, 7, 23 },
+ { TWId, 0x28, 7, 24 }, { TWId, 0x2b, 7, 25 }, { TWId, 0x13, 7, 26 },
+ { TWId, 0x24, 7, 27 }, { TWId, 0x18, 7, 28 }, { TWId, 0x02, 8, 29 },
+ { TWId, 0x03, 8, 30 }, { TWId, 0x1a, 8, 31 }, { TWId, 0x1b, 8, 32 },
+ { TWId, 0x12, 8, 33 }, { TWId, 0x13, 8, 34 }, { TWId, 0x14, 8, 35 },
+ { TWId, 0x15, 8, 36 }, { TWId, 0x16, 8, 37 }, { TWId, 0x17, 8, 38 },
+ { TWId, 0x28, 8, 39 }, { TWId, 0x29, 8, 40 }, { TWId, 0x2a, 8, 41 },
+ { TWId, 0x2b, 8, 42 }, { TWId, 0x2c, 8, 43 }, { TWId, 0x2d, 8, 44 },
+ { TWId, 0x04, 8, 45 }, { TWId, 0x05, 8, 46 }, { TWId, 0x0a, 8, 47 },
+ { TWId, 0x0b, 8, 48 }, { TWId, 0x52, 8, 49 }, { TWId, 0x53, 8, 50 },
+ { TWId, 0x54, 8, 51 }, { TWId, 0x55, 8, 52 }, { TWId, 0x24, 8, 53 },
+ { TWId, 0x25, 8, 54 }, { TWId, 0x58, 8, 55 }, { TWId, 0x59, 8, 56 },
+ { TWId, 0x5a, 8, 57 }, { TWId, 0x5b, 8, 58 }, { TWId, 0x4a, 8, 59 },
+ { TWId, 0x4b, 8, 60 }, { TWId, 0x32, 8, 61 }, { TWId, 0x33, 8, 62 },
+ { TWId, 0x34, 8, 63 }, { TWId, 0x00, 0, 0 }
+ };
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A S C I I 8 5 E n c o d e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ASCII85Encode() encodes data in ASCII base-85 format. ASCII base-85
+% encoding produces five ASCII printing characters from every four bytes of
+% binary data.
+%
+% The format of the ASCII85Encode method is:
+%
+% void Ascii85Encode(Image *image,const unsigned long code)
+%
+% A description of each parameter follows:
+%
+% o code: a binary unsigned char to encode to ASCII 85.
+%
+% o file: write the encoded ASCII character to this file.
+%
+%
+*/
+#define MaxLineExtent 36
+
+static char *Ascii85Tuple(unsigned char *data)
+{
+ static char
+ tuple[6];
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ code,
+ quantum;
+
+ code=((((unsigned long) data[0] << 8) | (unsigned long) data[1]) << 16) |
+ ((unsigned long) data[2] << 8) | (unsigned long) data[3];
+ if (code == 0L)
+ {
+ tuple[0]='z';
+ tuple[1]='\0';
+ return(tuple);
+ }
+ quantum=85UL*85UL*85UL*85UL;
+ for (i=0; i < 4; i++)
+ {
+ x=(long) (code/quantum);
+ code-=quantum*x;
+ tuple[i]=(char) (x+(int) '!');
+ quantum/=85L;
+ }
+ tuple[4]=(char) ((code % 85L)+(int) '!');
+ tuple[5]='\0';
+ return(tuple);
+}
+
+MagickExport void Ascii85Initialize(Image *image)
+{
+ /*
+ Allocate image structure.
+ */
+ if (image->ascii85 == (Ascii85Info *) NULL)
+ image->ascii85=(Ascii85Info *) AcquireMagickMemory(sizeof(*image->ascii85));
+ if (image->ascii85 == (Ascii85Info *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(image->ascii85,0,sizeof(*image->ascii85));
+ image->ascii85->line_break=MaxLineExtent << 1;
+ image->ascii85->offset=0;
+}
+
+MagickExport void Ascii85Flush(Image *image)
+{
+ register char
+ *tuple;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(image->ascii85 != (Ascii85Info *) NULL);
+ if (image->ascii85->offset > 0)
+ {
+ image->ascii85->buffer[image->ascii85->offset]='\0';
+ image->ascii85->buffer[image->ascii85->offset+1]='\0';
+ image->ascii85->buffer[image->ascii85->offset+2]='\0';
+ tuple=Ascii85Tuple(image->ascii85->buffer);
+ (void) WriteBlob(image,(size_t) image->ascii85->offset+1,
+ (const unsigned char *) (*tuple == 'z' ? "!!!!" : tuple));
+ }
+ (void) WriteBlobByte(image,'~');
+ (void) WriteBlobByte(image,'>');
+ (void) WriteBlobByte(image,'\n');
+}
+
+MagickExport void Ascii85Encode(Image *image,const unsigned char code)
+{
+ long
+ n;
+
+ register char
+ *q;
+
+ register unsigned char
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image->ascii85 != (Ascii85Info *) NULL);
+ image->ascii85->buffer[image->ascii85->offset]=code;
+ image->ascii85->offset++;
+ if (image->ascii85->offset < 4)
+ return;
+ p=image->ascii85->buffer;
+ for (n=image->ascii85->offset; n >= 4; n-=4)
+ {
+ for (q=Ascii85Tuple(p); *q != '\0'; q++)
+ {
+ image->ascii85->line_break--;
+ if ((image->ascii85->line_break < 0) && (*q != '%'))
+ {
+ (void) WriteBlobByte(image,'\n');
+ image->ascii85->line_break=2*MaxLineExtent;
+ }
+ (void) WriteBlobByte(image,(unsigned char) *q);
+ }
+ p+=8;
+ }
+ image->ascii85->offset=n;
+ p-=4;
+ for (n=0; n < 4; n++)
+ image->ascii85->buffer[n]=(*p++);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H u f f m a n D e c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% HuffmanDecodeImage() uncompresses an image via Huffman-coding.
+%
+% The format of the HuffmanDecodeImage method is:
+%
+% MagickBooleanType HuffmanDecodeImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static inline size_t MagickMax(const size_t x,const size_t y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType HuffmanDecodeImage(Image *image)
+{
+#define HashSize 1021
+#define MBHashA 293
+#define MBHashB 2695
+#define MWHashA 3510
+#define MWHashB 1178
+
+#define InitializeHashTable(hash,table,a,b) \
+{ \
+ entry=table; \
+ while (entry->code != 0) \
+ { \
+ hash[((entry->length+a)*(entry->code+b)) % HashSize]=(HuffmanTable *) entry; \
+ entry++; \
+ } \
+}
+
+#define InputBit(bit) \
+{ \
+ if ((mask & 0xff) == 0) \
+ { \
+ byte=ReadBlobByte(image); \
+ if (byte == EOF) \
+ break; \
+ mask=0x80; \
+ } \
+ runlength++; \
+ bit=(unsigned long) ((byte & mask) != 0 ? 0x01 : 0x00); \
+ mask>>=1; \
+ if (bit != 0) \
+ runlength=0; \
+}
+
+ const HuffmanTable
+ *entry;
+
+ ExceptionInfo
+ *exception;
+
+ HuffmanTable
+ **mb_hash,
+ **mw_hash;
+
+ IndexPacket
+ index;
+
+ int
+ byte;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ i;
+
+ register unsigned char
+ *p;
+
+ ssize_t
+ count;
+
+ unsigned char
+ *scanline;
+
+ unsigned int
+ bail,
+ color;
+
+ unsigned long
+ bit,
+ code,
+ mask,
+ length,
+ null_lines,
+ runlength;
+
+ /*
+ Allocate buffers.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ mb_hash=(HuffmanTable **) AcquireQuantumMemory(HashSize,sizeof(*mb_hash));
+ mw_hash=(HuffmanTable **) AcquireQuantumMemory(HashSize,sizeof(*mw_hash));
+ scanline=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,
+ sizeof(*scanline));
+ if ((mb_hash == (HuffmanTable **) NULL) ||
+ (mw_hash == (HuffmanTable **) NULL) ||
+ (scanline == (unsigned char *) NULL))
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ /*
+ Initialize Huffman tables.
+ */
+ for (i=0; i < HashSize; i++)
+ {
+ mb_hash[i]=(HuffmanTable *) NULL;
+ mw_hash[i]=(HuffmanTable *) NULL;
+ }
+ InitializeHashTable(mw_hash,TWTable,MWHashA,MWHashB);
+ InitializeHashTable(mw_hash,MWTable,MWHashA,MWHashB);
+ InitializeHashTable(mw_hash,EXTable,MWHashA,MWHashB);
+ InitializeHashTable(mb_hash,TBTable,MBHashA,MBHashB);
+ InitializeHashTable(mb_hash,MBTable,MBHashA,MBHashB);
+ InitializeHashTable(mb_hash,EXTable,MBHashA,MBHashB);
+ /*
+ Uncompress 1D Huffman to runlength encoded pixels.
+ */
+ byte=0;
+ mask=0;
+ null_lines=0;
+ runlength=0;
+ while (runlength < 11)
+ InputBit(bit);
+ do { InputBit(bit); } while ((int) bit == 0);
+ image->x_resolution=204.0;
+ image->y_resolution=196.0;
+ image->units=PixelsPerInchResolution;
+ exception=(&image->exception);
+ for (y=0; ((y < (long) image->rows) && (null_lines < 3)); )
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Initialize scanline to white.
+ */
+ p=scanline;
+ for (x=0; x < (long) image->columns; x++)
+ *p++=(unsigned char) 0;
+ /*
+ Decode Huffman encoded scanline.
+ */
+ color=MagickTrue;
+ code=0;
+ count=0;
+ length=0;
+ runlength=0;
+ x=0;
+ for ( ; ; )
+ {
+ if (byte == EOF)
+ break;
+ if (x >= (long) image->columns)
+ {
+ while (runlength < 11)
+ InputBit(bit);
+ do { InputBit(bit); } while ((int) bit == 0);
+ break;
+ }
+ bail=MagickFalse;
+ do
+ {
+ if (runlength < 11)
+ InputBit(bit)
+ else
+ {
+ InputBit(bit);
+ if ((int) bit != 0)
+ {
+ null_lines++;
+ if (x != 0)
+ null_lines=0;
+ bail=MagickTrue;
+ break;
+ }
+ }
+ code=(code << 1)+(unsigned long) bit;
+ length++;
+ } while (code == 0);
+ if (bail != MagickFalse)
+ break;
+ if (length > 13)
+ {
+ while (runlength < 11)
+ InputBit(bit);
+ do { InputBit(bit); } while ((int) bit == 0);
+ break;
+ }
+ if (color != MagickFalse)
+ {
+ if (length < 4)
+ continue;
+ entry=mw_hash[((length+MWHashA)*(code+MWHashB)) % HashSize];
+ }
+ else
+ {
+ if (length < 2)
+ continue;
+ entry=mb_hash[((length+MBHashA)*(code+MBHashB)) % HashSize];
+ }
+ if (entry == (const HuffmanTable *) NULL)
+ continue;
+ if ((entry->length != length) || (entry->code != code))
+ continue;
+ switch (entry->id)
+ {
+ case TWId:
+ case TBId:
+ {
+ count+=entry->count;
+ if ((x+count) > (long) image->columns)
+ count=(ssize_t) image->columns-x;
+ if (count > 0)
+ {
+ if (color != MagickFalse)
+ {
+ x+=count;
+ count=0;
+ }
+ else
+ for ( ; count > 0; count--)
+ scanline[x++]=(unsigned char) 1;
+ }
+ color=(unsigned int)
+ ((color == MagickFalse) ? MagickTrue : MagickFalse);
+ break;
+ }
+ case MWId:
+ case MBId:
+ case EXId:
+ {
+ count+=entry->count;
+ break;
+ }
+ default:
+ break;
+ }
+ code=0;
+ length=0;
+ }
+ /*
+ Transfer scanline to image pixels.
+ */
+ p=scanline;
+ q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ index=(IndexPacket) (*p++);
+ indexes[x]=index;
+ *q++=image->colormap[(long) index];
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,LoadImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ y++;
+ }
+ image->rows=(unsigned long) MagickMax((size_t) y-3,1);
+ image->compression=FaxCompression;
+ /*
+ Free decoder memory.
+ */
+ mw_hash=(HuffmanTable **) RelinquishMagickMemory(mw_hash);
+ mb_hash=(HuffmanTable **) RelinquishMagickMemory(mb_hash);
+ scanline=(unsigned char *) RelinquishMagickMemory(scanline);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H u f f m a n E n c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% HuffmanEncodeImage() compresses an image via Huffman-coding.
+%
+% The format of the HuffmanEncodeImage method is:
+%
+% MagickBooleanType HuffmanEncodeImage(const ImageInfo *image_info,
+% Image *image,Image *inject_image)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info..
+%
+% o image: the image.
+%
+% o inject_image: inject into the image stream.
+%
+*/
+MagickExport MagickBooleanType HuffmanEncodeImage(const ImageInfo *image_info,
+ Image *image,Image *inject_image)
+{
+#define HuffmanOutputCode(entry) \
+{ \
+ mask=1 << (entry->length-1); \
+ while (mask != 0) \
+ { \
+ OutputBit(((entry->code & mask) != 0 ? 1 : 0)); \
+ mask>>=1; \
+ } \
+}
+
+#define OutputBit(count) \
+{ \
+ if (count > 0) \
+ byte=byte | bit; \
+ bit>>=1; \
+ if ((int) (bit & 0xff) == 0) \
+ { \
+ if (LocaleCompare(image_info->magick,"FAX") == 0) \
+ (void) WriteBlobByte(image,(unsigned char) byte); \
+ else \
+ Ascii85Encode(image,byte); \
+ byte='\0'; \
+ bit=(unsigned char) 0x80; \
+ } \
+}
+
+ const HuffmanTable
+ *entry;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ k,
+ runlength;
+
+ long
+ n,
+ y;
+
+ Image
+ *huffman_image;
+
+ MagickBooleanType
+ proceed;
+
+ register long
+ i,
+ x;
+
+ register const PixelPacket
+ *p;
+
+ register unsigned char
+ *q;
+
+ unsigned char
+ byte,
+ bit,
+ *scanline;
+
+ unsigned long
+ mask,
+ width;
+
+ /*
+ Allocate scanline buffer.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(inject_image != (Image *) NULL);
+ assert(inject_image->signature == MagickSignature);
+ width=inject_image->columns;
+ if (LocaleCompare(image_info->magick,"FAX") == 0)
+ width=(unsigned long) MagickMax(inject_image->columns,1728);
+ scanline=(unsigned char *) AcquireQuantumMemory((size_t) width+1UL,
+ sizeof(*scanline));
+ if (scanline == (unsigned char *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ inject_image->filename);
+ (void) ResetMagickMemory(scanline,0,width*sizeof(*scanline));
+ huffman_image=CloneImage(inject_image,0,0,MagickTrue,&image->exception);
+ if (huffman_image == (Image *) NULL)
+ {
+ scanline=(unsigned char *) RelinquishMagickMemory(scanline);
+ return(MagickFalse);
+ }
+ (void) SetImageType(huffman_image,BilevelType);
+ byte='\0';
+ bit=(unsigned char) 0x80;
+ if (LocaleCompare(image_info->magick,"FAX") != 0)
+ Ascii85Initialize(image);
+ else
+ {
+ /*
+ End of line.
+ */
+ for (k=0; k < 11; k++)
+ OutputBit(0);
+ OutputBit(1);
+ }
+ /*
+ Compress to 1D Huffman pixels.
+ */
+ exception=(&huffman_image->exception);
+ q=scanline;
+ for (y=0; y < (long) huffman_image->rows; y++)
+ {
+ p=GetVirtualPixels(huffman_image,0,y,huffman_image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) huffman_image->columns; x++)
+ {
+ *q++=(unsigned char) (PixelIntensity(p) >= ((MagickRealType)
+ QuantumRange/2.0) ? 0 : 1);
+ p++;
+ }
+ /*
+ Huffman encode scanline.
+ */
+ q=scanline;
+ for (n=(long) width; n > 0; )
+ {
+ /*
+ Output white run.
+ */
+ for (runlength=0; ((n > 0) && (*q == 0)); n--)
+ {
+ q++;
+ runlength++;
+ }
+ if (runlength >= 64)
+ {
+ if (runlength < 1792)
+ entry=MWTable+((runlength/64)-1);
+ else
+ entry=EXTable+(MagickMin((size_t) runlength,2560)-1792)/64;
+ runlength-=entry->count;
+ HuffmanOutputCode(entry);
+ }
+ entry=TWTable+MagickMin((size_t) runlength,63);
+ HuffmanOutputCode(entry);
+ if (n != 0)
+ {
+ /*
+ Output black run.
+ */
+ for (runlength=0; ((*q != 0) && (n > 0)); n--)
+ {
+ q++;
+ runlength++;
+ }
+ if (runlength >= 64)
+ {
+ entry=MBTable+((runlength/64)-1);
+ if (runlength >= 1792)
+ entry=EXTable+(MagickMin((size_t) runlength,2560)-1792)/64;
+ runlength-=entry->count;
+ HuffmanOutputCode(entry);
+ }
+ entry=TBTable+MagickMin((size_t) runlength,63);
+ HuffmanOutputCode(entry);
+ }
+ }
+ /*
+ End of line.
+ */
+ for (k=0; k < 11; k++)
+ OutputBit(0);
+ OutputBit(1);
+ q=scanline;
+ if (GetPreviousImageInList(huffman_image) == (Image *) NULL)
+ {
+ proceed=SetImageProgress(huffman_image,LoadImageTag,y,
+ huffman_image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ }
+ /*
+ End of page.
+ */
+ for (i=0; i < 6; i++)
+ {
+ for (k=0; k < 11; k++)
+ OutputBit(0);
+ OutputBit(1);
+ }
+ /*
+ Flush bits.
+ */
+ if (((int) bit != 0x80) != 0)
+ {
+ if (LocaleCompare(image_info->magick,"FAX") == 0)
+ (void) WriteBlobByte(image,byte);
+ else
+ Ascii85Encode(image,byte);
+ }
+ if (LocaleCompare(image_info->magick,"FAX") != 0)
+ Ascii85Flush(image);
+ huffman_image=DestroyImage(huffman_image);
+ scanline=(unsigned char *) RelinquishMagickMemory(scanline);
+ return(MagickTrue);
+}
+
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H u f f m a n 2 D E n c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Huffman2DEncodeImage() compresses an image via two-dimensional
+% Huffman-coding.
+%
+% The format of the Huffman2DEncodeImage method is:
+%
+% MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
+% Image *image,Image *inject_image)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o inject_image: inject into the image stream.
+%
+*/
+MagickExport MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
+ Image *image,Image *inject_image)
+{
+ char
+ filename[MaxTextExtent];
+
+ FILE
+ *file;
+
+ Image
+ *huffman_image;
+
+ ImageInfo
+ *write_info;
+
+ int
+ unique_file;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ ssize_t
+ count;
+
+ TIFF
+ *tiff;
+
+ uint16
+ fillorder;
+
+ uint32
+ *byte_count,
+ strip_size;
+
+ unsigned char
+ *buffer;
+
+ /*
+ Write image as CCITTFax4 TIFF image to a temporary file.
+ */
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(inject_image != (Image *) NULL);
+ assert(inject_image->signature == MagickSignature);
+ huffman_image=CloneImage(inject_image,0,0,MagickTrue,&image->exception);
+ if (huffman_image == (Image *) NULL)
+ return(MagickFalse);
+ file=(FILE *) NULL;
+ unique_file=AcquireUniqueFileResource(filename);
+ if (unique_file != -1)
+ file=fdopen(unique_file,"wb");
+ if ((unique_file == -1) || (file == (FILE *) NULL))
+ {
+ ThrowFileException(&image->exception,FileOpenError,
+ "UnableToCreateTemporaryFile",filename);
+ return(MagickFalse);
+ }
+ (void) FormatMagickString(huffman_image->filename,MaxTextExtent,"tiff:%s",
+ filename);
+ write_info=CloneImageInfo(image_info);
+ SetImageInfoFile(write_info,file);
+ write_info->compression=Group4Compression;
+ (void) SetImageOption(write_info,"quantum:polarity","min-is-white");
+ status=WriteImage(write_info,huffman_image);
+ (void) fflush(file);
+ write_info=DestroyImageInfo(write_info);
+ if (status == MagickFalse)
+ return(MagickFalse);
+ tiff=TIFFOpen(filename,"rb");
+ if (tiff == (TIFF *) NULL)
+ {
+ huffman_image=DestroyImage(huffman_image);
+ (void) fclose(file);
+ (void) RelinquishUniqueFileResource(filename);
+ ThrowFileException(&image->exception,FileOpenError,"UnableToOpenFile",
+ image_info->filename);
+ return(MagickFalse);
+ }
+ /*
+ Allocate raw strip buffer.
+ */
+ byte_count=0;
+ (void) TIFFGetField(tiff,TIFFTAG_STRIPBYTECOUNTS,&byte_count);
+ strip_size=byte_count[0];
+ for (i=1; i < (long) TIFFNumberOfStrips(tiff); i++)
+ if (byte_count[i] > strip_size)
+ strip_size=byte_count[i];
+ buffer=(unsigned char *) AcquireQuantumMemory((size_t) strip_size,
+ sizeof(*buffer));
+ if (buffer == (unsigned char *) NULL)
+ {
+ TIFFClose(tiff);
+ huffman_image=DestroyImage(huffman_image);
+ (void) fclose(file);
+ (void) RelinquishUniqueFileResource(filename);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image_info->filename);
+ }
+ /*
+ Compress runlength encoded to 2D Huffman pixels.
+ */
+ fillorder=FILLORDER_LSB2MSB;
+ (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_FILLORDER,&fillorder);
+ for (i=0; i < (long) TIFFNumberOfStrips(tiff); i++)
+ {
+ count=(ssize_t) TIFFReadRawStrip(tiff,(uint32) i,buffer,(long)
+ byte_count[i]);
+ if (fillorder == FILLORDER_LSB2MSB)
+ TIFFReverseBits(buffer,(unsigned long) count);
+ (void) WriteBlob(image,(size_t) count,buffer);
+ }
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ TIFFClose(tiff);
+ huffman_image=DestroyImage(huffman_image);
+ (void) fclose(file);
+ (void) RelinquishUniqueFileResource(filename);
+ return(MagickTrue);
+}
+#else
+MagickExport MagickBooleanType Huffman2DEncodeImage(const ImageInfo *image_info,
+ Image *image,Image *inject_image)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ (void) inject_image;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (TIFF)",
+ image->filename);
+ return(MagickFalse);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L Z W E n c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LZWEncodeImage() compresses an image via LZW-coding specific to Postscript
+% Level II or Portable Document Format.
+%
+% The format of the LZWEncodeImage method is:
+%
+% MagickBooleanType LZWEncodeImage(Image *image,const size_t length,
+% unsigned char *pixels)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o length: A value that specifies the number of pixels to compress.
+%
+% o pixels: the address of an unsigned array of characters containing the
+% pixels to compress.
+%
+*/
+MagickExport MagickBooleanType LZWEncodeImage(Image *image,const size_t length,
+ unsigned char *pixels)
+{
+#define LZWClr 256UL /* Clear Table Marker */
+#define LZWEod 257UL /* End of Data marker */
+#define OutputCode(code) \
+{ \
+ accumulator+=code << (32-code_width-number_bits); \
+ number_bits+=code_width; \
+ while (number_bits >= 8) \
+ { \
+ (void) WriteBlobByte(image,(unsigned char) (accumulator >> 24)); \
+ accumulator=accumulator << 8; \
+ number_bits-=8; \
+ } \
+}
+
+ typedef struct _TableType
+ {
+ long
+ prefix,
+ suffix,
+ next;
+ } TableType;
+
+ long
+ index;
+
+ register long
+ i;
+
+ TableType
+ *table;
+
+ unsigned long
+ accumulator,
+ number_bits,
+ code_width,
+ last_code,
+ next_index;
+
+ /*
+ Allocate string table.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(pixels != (unsigned char *) NULL);
+ table=(TableType *) AcquireQuantumMemory(1UL << 12,sizeof(*table));
+ if (table == (TableType *) NULL)
+ return(MagickFalse);
+ /*
+ Initialize variables.
+ */
+ accumulator=0;
+ code_width=9;
+ number_bits=0;
+ last_code=0;
+ OutputCode(LZWClr);
+ for (index=0; index < 256; index++)
+ {
+ table[index].prefix=(-1);
+ table[index].suffix=(short) index;
+ table[index].next=(-1);
+ }
+ next_index=LZWEod+1;
+ code_width=9;
+ last_code=(unsigned long) pixels[0];
+ for (i=1; i < (long) length; i++)
+ {
+ /*
+ Find string.
+ */
+ index=(long) last_code;
+ while (index != -1)
+ if ((table[index].prefix != (long) last_code) ||
+ (table[index].suffix != (long) pixels[i]))
+ index=table[index].next;
+ else
+ {
+ last_code=(unsigned long) index;
+ break;
+ }
+ if (last_code != (unsigned long) index)
+ {
+ /*
+ Add string.
+ */
+ OutputCode(last_code);
+ table[next_index].prefix=(long) last_code;
+ table[next_index].suffix=(short) pixels[i];
+ table[next_index].next=table[last_code].next;
+ table[last_code].next=(long) next_index;
+ next_index++;
+ /*
+ Did we just move up to next bit width?
+ */
+ if ((next_index >> code_width) != 0)
+ {
+ code_width++;
+ if (code_width > 12)
+ {
+ /*
+ Did we overflow the max bit width?
+ */
+ code_width--;
+ OutputCode(LZWClr);
+ for (index=0; index < 256; index++)
+ {
+ table[index].prefix=(-1);
+ table[index].suffix=index;
+ table[index].next=(-1);
+ }
+ next_index=LZWEod+1;
+ code_width=9;
+ }
+ }
+ last_code=(unsigned long) pixels[i];
+ }
+ }
+ /*
+ Flush tables.
+ */
+ OutputCode(last_code);
+ OutputCode(LZWEod);
+ if (number_bits != 0)
+ (void) WriteBlobByte(image,(unsigned char) (accumulator >> 24));
+ table=(TableType *) RelinquishMagickMemory(table);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a c k b i t s E n c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PackbitsEncodeImage() compresses an image via Macintosh Packbits encoding
+% specific to Postscript Level II or Portable Document Format. To ensure
+% portability, the binary Packbits bytes are encoded as ASCII Base-85.
+%
+% The format of the PackbitsEncodeImage method is:
+%
+% MagickBooleanType PackbitsEncodeImage(Image *image,const size_t length,
+% unsigned char *pixels)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o length: A value that specifies the number of pixels to compress.
+%
+% o pixels: the address of an unsigned array of characters containing the
+% pixels to compress.
+%
+*/
+MagickExport MagickBooleanType PackbitsEncodeImage(Image *image,
+ const size_t length,unsigned char *pixels)
+{
+ int
+ count;
+
+ register long
+ i,
+ j;
+
+ unsigned char
+ *packbits;
+
+ /*
+ Compress pixels with Packbits encoding.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(pixels != (unsigned char *) NULL);
+ packbits=(unsigned char *) AcquireQuantumMemory(128UL,sizeof(*packbits));
+ if (packbits == (unsigned char *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ for (i=(long) length; i != 0; )
+ {
+ switch (i)
+ {
+ case 1:
+ {
+ i--;
+ (void) WriteBlobByte(image,(unsigned char) 0);
+ (void) WriteBlobByte(image,*pixels);
+ break;
+ }
+ case 2:
+ {
+ i-=2;
+ (void) WriteBlobByte(image,(unsigned char) 1);
+ (void) WriteBlobByte(image,*pixels);
+ (void) WriteBlobByte(image,pixels[1]);
+ break;
+ }
+ case 3:
+ {
+ i-=3;
+ if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
+ {
+ (void) WriteBlobByte(image,(unsigned char) ((256-3)+1));
+ (void) WriteBlobByte(image,*pixels);
+ break;
+ }
+ (void) WriteBlobByte(image,(unsigned char) 2);
+ (void) WriteBlobByte(image,*pixels);
+ (void) WriteBlobByte(image,pixels[1]);
+ (void) WriteBlobByte(image,pixels[2]);
+ break;
+ }
+ default:
+ {
+ if ((*pixels == *(pixels+1)) && (*(pixels+1) == *(pixels+2)))
+ {
+ /*
+ Packed run.
+ */
+ count=3;
+ while (((long) count < i) && (*pixels == *(pixels+count)))
+ {
+ count++;
+ if (count >= 127)
+ break;
+ }
+ i-=count;
+ (void) WriteBlobByte(image,(unsigned char) ((256-count)+1));
+ (void) WriteBlobByte(image,*pixels);
+ pixels+=count;
+ break;
+ }
+ /*
+ Literal run.
+ */
+ count=0;
+ while ((*(pixels+count) != *(pixels+count+1)) ||
+ (*(pixels+count+1) != *(pixels+count+2)))
+ {
+ packbits[count+1]=pixels[count];
+ count++;
+ if (((long) count >= (i-3)) || (count >= 127))
+ break;
+ }
+ i-=count;
+ *packbits=(unsigned char) (count-1);
+ for (j=0; j <= (long) count; j++)
+ (void) WriteBlobByte(image,packbits[j]);
+ pixels+=count;
+ break;
+ }
+ }
+ }
+ (void) WriteBlobByte(image,(unsigned char) 128); /* EOD marker */
+ packbits=(unsigned char *) RelinquishMagickMemory(packbits);
+ return(MagickTrue);
+}
+
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Z L I B E n c o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ZLIBEncodeImage compresses an image via ZLIB-coding specific to
+% Postscript Level II or Portable Document Format.
+%
+% The format of the ZLIBEncodeImage method is:
+%
+% MagickBooleanType ZLIBEncodeImage(Image *image,const size_t length,
+% unsigned char *pixels)
+%
+% A description of each parameter follows:
+%
+% o file: the address of a structure of type FILE. ZLIB encoded pixels
+% are written to this file.
+%
+% o length: A value that specifies the number of pixels to compress.
+%
+% o pixels: the address of an unsigned array of characters containing the
+% pixels to compress.
+%
+*/
+
+static voidpf AcquireZIPMemory(voidpf context,unsigned int items,
+ unsigned int size)
+{
+ (void) context;
+ return((voidpf) AcquireQuantumMemory(items,size));
+}
+
+static void RelinquishZIPMemory(voidpf context,voidpf memory)
+{
+ (void) context;
+ memory=RelinquishMagickMemory(memory);
+}
+
+MagickExport MagickBooleanType ZLIBEncodeImage(Image *image,const size_t length,
+ unsigned char *pixels)
+{
+ int
+ status;
+
+ register long
+ i;
+
+ size_t
+ compress_packets;
+
+ unsigned char
+ *compress_pixels;
+
+ z_stream
+ stream;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ compress_packets=(size_t) (1.001*length+12);
+ compress_pixels=(unsigned char *) AcquireQuantumMemory(compress_packets,
+ sizeof(*compress_pixels));
+ if (compress_pixels == (unsigned char *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ stream.next_in=pixels;
+ stream.avail_in=(unsigned int) length;
+ stream.next_out=compress_pixels;
+ stream.avail_out=(unsigned int) compress_packets;
+ stream.zalloc=AcquireZIPMemory;
+ stream.zfree=RelinquishZIPMemory;
+ stream.opaque=(voidpf) NULL;
+ status=deflateInit(&stream,(int) (image->quality ==
+ UndefinedCompressionQuality ? 7 : MagickMin(image->quality/10,9)));
+ if (status == Z_OK)
+ {
+ status=deflate(&stream,Z_FINISH);
+ if (status == Z_STREAM_END)
+ status=deflateEnd(&stream);
+ else
+ (void) deflateEnd(&stream);
+ compress_packets=(size_t) stream.total_out;
+ }
+ if (status != Z_OK)
+ ThrowBinaryException(CoderError,"UnableToZipCompressImage",image->filename)
+ else
+ for (i=0; i < (long) compress_packets; i++)
+ (void) WriteBlobByte(image,compress_pixels[i]);
+ compress_pixels=(unsigned char *) RelinquishMagickMemory(compress_pixels);
+ return(status == Z_OK ? MagickTrue : MagickFalse);
+}
+#else
+MagickExport MagickBooleanType ZLIBEncodeImage(Image *image,
+ const size_t magick_unused(length),unsigned char *magick_unused(pixels))
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (ZIP)",
+ image->filename);
+ return(MagickFalse);
+}
+#endif
diff --git a/magick/compress.h b/magick/compress.h
new file mode 100644
index 0000000..786292a
--- /dev/null
+++ b/magick/compress.h
@@ -0,0 +1,68 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image compression/decompression methods.
+*/
+#ifndef _MAGICKCORE_COMPRESS_H
+#define _MAGICKCORE_COMPRESS_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedCompression,
+ NoCompression,
+ BZipCompression,
+ DXT1Compression,
+ DXT3Compression,
+ DXT5Compression,
+ FaxCompression,
+ Group4Compression,
+ JPEGCompression,
+ JPEG2000Compression,
+ LosslessJPEGCompression,
+ LZWCompression,
+ RLECompression,
+ ZipCompression,
+ ZipSCompression,
+ PizCompression,
+ Pxr24Compression,
+ B44Compression,
+ B44ACompression
+} CompressionType;
+
+typedef struct _Ascii85Info
+ Ascii85Info;
+
+extern MagickExport MagickBooleanType
+ HuffmanDecodeImage(Image *),
+ HuffmanEncodeImage(const ImageInfo *,Image *,Image *),
+ Huffman2DEncodeImage(const ImageInfo *,Image *,Image *),
+ LZWEncodeImage(Image *,const size_t,unsigned char *),
+ PackbitsEncodeImage(Image *,const size_t,unsigned char *),
+ ZLIBEncodeImage(Image *,const size_t,unsigned char *);
+
+extern MagickExport void
+ Ascii85Encode(Image *,const unsigned char),
+ Ascii85Flush(Image *),
+ Ascii85Initialize(Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/config.h_vms b/magick/config.h_vms
new file mode 100644
index 0000000..ebeb2c0
--- /dev/null
+++ b/magick/config.h_vms
@@ -0,0 +1,284 @@
+/* magick/config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef MAGICKCORE_HAVE_DOPRNT
+
+/* Define if you have a working `mmap' system call. */
+#define MAGICKCORE_HAVE_MMAP 1
+
+/* Define if you have the vprintf function. */
+#define MAGICKCORE_HAVE_VPRINTF 1
+
+/* Define as __inline if that's what the C compiler calls it. */
+#define MAGICKCORE_inline __inline
+
+/* Define as the return type of signal handlers (int or void). */
+#define MAGICKCORE_RETSIGTYPE void
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef MAGICKCORE_STAT_MACROS_BROKEN
+
+/* Define if you have the ANSI C header files. */
+#define MAGICKCORE_STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define MAGICKCORE_TIME_WITH_SYS_TIME 1
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef MAGICKCORE_WORDS_BIGENDIAN
+
+/* Define if the X Window System is missing or not being used. */
+#undef MAGICKCORE_X_DISPLAY_MISSING
+
+/* The number of bytes in a int. */
+#define MAGICKCORE_SIZEOF_INT 4
+
+/* The number of bytes in a long long. */
+#define MAGICKCORE_SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* Define if you have the getcwd function. */
+#define MAGICKCORE_HAVE_GETCWD 1
+
+/* Define if you have the getexecname function. */
+#undef MAGICKCORE_HAVE_GETEXECNAME
+
+/* Define if you have the getpagesize function. */
+#define MAGICKCORE_HAVE_GETPAGESIZE 1
+
+/* Define if you have the gettimeofday function. */
+#define MAGICKCORE_HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the mkdir function. */
+#define MAGICKCORE_HAVE_MKDIR 1
+
+/* Define if you have the poll function. */
+#undef MAGICKCORE_HAVE_POLL
+
+/* Define if you have the select function. */
+#undef MAGICKCORE_HAVE_SELECT
+
+/* Define if you have the snprintf function. */
+#undef MAGICKCORE_HAVE_SNPRINTF
+
+/* Define if you have the strchr function. */
+#define MAGICKCORE_HAVE_STRCHR 1
+
+/* Define if you have the strerror function. */
+#define MAGICKCORE_HAVE_STRERROR 1
+
+/* Define if you have the strtol function. */
+#define MAGICKCORE_HAVE_STRTOL 1
+
+/* Define if you have the sysconf function. */
+#define MAGICKCORE_HAVE_SYSCONF 1
+
+/* Define if you have the tempnam function. */
+#define MAGICKCORE_HAVE_TEMPNAM 1
+
+/* Define if you have the vsnprintf function. */
+#undef MAGICKCORE_HAVE_VSNPRINTF
+
+/* Define if you have the <dirent.h> header file. */
+#define MAGICKCORE_HAVE_DIRENT_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define MAGICKCORE_HAVE_ERRNO_H 1
+
+/* Define if you have the <fcntl.h> header file. */
+#define MAGICKCORE_HAVE_FCNTL_H 1
+
+/* Define if you have the <ft2build.h> header file. */
+#define MAGICKCORE_HAVE_FT2BUILD_H 1
+
+/* Define if you have the <freetype/freetype.h> header file. */
+#define MAGICKCORE_HAVE_FREETYPE_FREETYPE_H 1
+
+/* Define if you have the <hdf.h> header file. */
+#define MAGICKCORE_HAVE_HDF_H 1
+
+/* Define if you have the <hdf/hdf.h> header file. */
+#undef MAGICKCORE_HAVE_HDF_HDF_H
+
+/* Define if you have the <libxml/xml-error.h> header file. */
+#undef MAGICKCORE_HAVE_LIBXML_XML_ERROR_H
+
+/* Define if you have the <libxml/xmlerror.h> header file. */
+#undef MAGICKCORE_HAVE_LIBXML_XMLERROR_H
+
+/* Define if you have the <jp2conf.h> header file. */
+#undef MAGICKCORE_HAVE_JP2CONF_H
+
+/* Define if you have the <malloc.h> header file. */
+#undef MAGICKCORE_HAVE_MALLOC_H
+
+/* Define if you have the <math.h> header file. */
+#define MAGICKCORE_HAVE_MATH_H 1
+
+/* Define if you have the <memory.h> header file. */
+#define MAGICKCORE_HAVE_MEMORY_H 1
+
+/* Define if you have the <ndir.h> header file. */
+#undef MAGICKCORE_HAVE_NDIR_H
+
+/* Define if you have the <pwd.h> header file. */
+#define MAGICKCORE_HAVE_PWD_H 1
+
+/* Define if you have the <stdarg.h> header file. */
+#define MAGICKCORE_HAVE_STDARG_H 1
+
+/* Define if you have the <stdint.h> header file. */
+#undef MAGICKCORE_HAVE_STDINT_H
+
+/* Define if you have the <string.h> header file. */
+#define MAGICKCORE_HAVE_STRING_H 1
+
+/* Define if you have the <sys/dir.h> header file. */
+#undef MAGICKCORE_HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file. */
+#undef MAGICKCORE_HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/select.h> header file. */
+#define MAGICKCORE_HAVE_SYS_SELECT_H 1
+
+/* Define if you have the <sys/stat.h> header file. */
+#define MAGICKCORE_HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#define MAGICKCORE_HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/types.h> header file. */
+#define MAGICKCORE_HAVE_SYS_TYPES_H 1
+
+/* Define if you have the <tiffconf.h> header file. */
+#define MAGICKCORE_HAVE_TIFFCONF_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define MAGICKCORE_HAVE_UNISTD_H 1
+
+/* Define if you have the <varargs.h> header file. */
+#undef MAGICKCORE_HAVE_VARARGS_H
+
+/* Define if using libltdl to create dynamically loadable modules */
+#undef MAGICKCORE_LTDL_DELEGATE
+
+/* Location of coder modules */
+#undef MAGICKCORE_MagickModulesPath
+
+/* Number of bits in a pixel Quantum (8 or 16) */
+#define MAGICKCORE_QUANTUM_DEPTH 16
+
+/* Pixel cache threshold (default 2047MB) */
+#undef MAGICKCORE_PixelCacheThreshold
+
+/* Define to specify default TrueType font path. */
+#undef MAGICKCORE_TT_FONT_PATH
+
+/* Location of X11 configure files */
+#undef MAGICKCORE_X11ConfigurePath
+
+/* Define if you have Posix thread methods. */
+#undef MAGICKCORE_HasPTHREADS
+
+/* Define if you have zlib compression library */
+#define MAGICKCORE_ZLIB_DELEGATE 1
+
+/* Define if you have the bzip2 library */
+#define MAGICKCORE_BZLIB_DELEGATE 1
+
+/* Define if you have X11 library */
+#define MAGICKCORE_X11_DELEGATE 1
+
+/* X11 server supports shared memory extension */
+#undef MAGICKCORE_HAVE_SHARED_MEMORY
+
+/* X11 server supports shape extension */
+#undef MAGICKCORE_HAVE_SHAPE
+
+/* Define if you have Display Postscript */
+#undef MAGICKCORE_DPS_DELEGATE
+
+/* Define if you have FlashPIX library */
+#undef MAGICKCORE_FPX_DELEGATE
+
+/* Define if you have LCMS library */
+#undef MAGICKCORE_LCMS_DELEGATE
+
+/* Define if you have PNG library */
+#define MAGICKCORE_PNG_DELEGATE 1
+
+/* Define if you have JPEG library */
+#define MAGICKCORE_JPEG_DELEGATE 1
+
+/* Define if you have JPEG version 2 Jasper library */
+#define MAGICKCORE_JP2_DELEGATE 1
+
+/* Define if you have Ghostscript library */
+#undef MAGICKCORE_GS_DELEGATE
+
+/* Define if you have FreeType (TrueType font) library */
+#define MAGICKCORE_FREETYPE_DELEGATE 1
+
+/* Define if you have TIFF library */
+#define MAGICKCORE_TIFF_DELEGATE 1
+
+/* Define if you have HDF4 library */
+#define MAGICKCORE_HDF_DELEGATE 1
+
+/* Define if you have JBIG library */
+#define MAGICKCORE_JBIG_DELEGATE 1
+
+/* Define if you have XML library */
+#undef MAGICKCORE_XML_DELEGATE
+
+/* Define if you have WMF library */
+#undef MAGICKCORE_WMF_DELEGATE
+
+/* Define if you have sys_errlist in libc */
+#undef MAGICKCORE_HAVE_SYS_ERRLIST
+
+/* Define directory where ImageMagick/delegates.xml lives. */
+#undef MAGICKCORE_MagickConfigurePath
+
+/* define MAGICKCORE_if bool is a built-in type */
+#define MAGICKCORE_HAVE_BOOL 1
+
+/* define MAGICKCORE_if the compiler supports const_cast<> */
+#undef MAGICKCORE_HAVE_CONST_CAST
+
+/* define MAGICKCORE_if the compiler supports default template parameters */
+#undef MAGICKCORE_HAVE_DEFAULT_TEMPLATE_PARAMETERS
+
+/* define MAGICKCORE_if the compiler supports exceptions */
+#undef MAGICKCORE_HAVE_EXCEPTIONS
+
+/* define MAGICKCORE_if the compiler implements namespaces */
+#undef MAGICKCORE_HAVE_NAMESPACES
+
+/* define MAGICKCORE_if the compiler supports the explicit keyword */
+#undef MAGICKCORE_HAVE_EXPLICIT
+
+/* define MAGICKCORE_if the compiler supports ISO C++ standard library */
+#define MAGICKCORE_HAVE_STD 1
+
+/* define MAGICKCORE_if the compiler supports Standard Template Library */
+#define MAGICKCORE_HAVE_STL 1
+
+/* define MAGICKCORE_if the compiler supports the mutable keyword */
+#undef MAGICKCORE_HAVE_MUTABLE
+
+/* define MAGICKCORE_if the compiler accepts the new for scoping rules */
+#undef MAGICKCORE_HAVE_NEW_FOR_SCOPING
+
+/* define MAGICKCORE_if the compiler supports static_cast<> */
+#undef MAGICKCORE_HAVE_STATIC_CAST
+
+/* define MAGICKCORE_if the compiler supports basic templates */
+#undef MAGICKCORE_HAVE_TEMPLATES
+
+#define MAGICKCORE_LIBRARY_RELATIVE_PATH "/"
+
+#define MAGICKCORE_HAVE_MKSTEMP 1
+
+#define round nint
diff --git a/magick/configure.c b/magick/configure.c
new file mode 100644
index 0000000..610c597
--- /dev/null
+++ b/magick/configure.c
@@ -0,0 +1,1194 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO N N FFFFF IIIII GGGG U U RRRR EEEEE %
+% C O O NN N F I G U U R R E %
+% C O O N N N FFF I G GG U U RRRR EEE %
+% C O O N NN F I G G U U R R E %
+% CCCC OOO N N F IIIII GGG UUU R R EEEEE %
+% %
+% %
+% MagickCore Image Configure Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define ConfigureFilename "configure.xml"
+
+/*
+ Static declarations.
+*/
+static const char
+ *ConfigureMap = (char *)
+ "<?xml version=\"1.0\"?>"
+ "<configuremap>"
+ " <configure stealth=\"True\" />"
+ "</configuremap>";
+
+static LinkedListInfo
+ *configure_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *configure_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_configure = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeConfigureList(ExceptionInfo *),
+ LoadConfigureLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C o n f i g u r e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyConfigureList() deallocates memory associated with the configure list.
+%
+% The format of the DestroyConfigureList method is:
+%
+% DestroyConfigureList(void)
+%
+*/
+
+static void *DestroyConfigureElement(void *configure_info)
+{
+ register ConfigureInfo
+ *p;
+
+ p=(ConfigureInfo *) configure_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->value != (char *) NULL)
+ p->value=DestroyString(p->value);
+ p=(ConfigureInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyConfigureList(void)
+{
+ AcquireSemaphoreInfo(&configure_semaphore);
+ if (configure_list != (LinkedListInfo *) NULL)
+ configure_list=DestroyLinkedList(configure_list,DestroyConfigureElement);
+ configure_list=(LinkedListInfo *) NULL;
+ instantiate_configure=MagickFalse;
+ RelinquishSemaphoreInfo(configure_semaphore);
+ DestroySemaphoreInfo(&configure_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y C o n f i g u r e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyConfigureOptions() releases memory associated with an configure
+% options.
+%
+% The format of the DestroyProfiles method is:
+%
+% LinkedListInfo *DestroyConfigureOptions(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static void *DestroyOptions(void *option)
+{
+ return(DestroyStringInfo((StringInfo *) option));
+}
+
+MagickExport LinkedListInfo *DestroyConfigureOptions(LinkedListInfo *options)
+{
+ assert(options != (LinkedListInfo *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(DestroyLinkedList(options,DestroyOptions));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C o n f i g u r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureInfo() searches the configure list for the specified name and if
+% found returns attributes for that element.
+%
+% The format of the GetConfigureInfo method is:
+%
+% const ConfigureInfo *GetConfigureInfo(const char *name,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o configure_info: GetConfigureInfo() searches the configure list for the
+% specified name and if found returns attributes for that element.
+%
+% o name: the configure name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const ConfigureInfo *GetConfigureInfo(const char *name,
+ ExceptionInfo *exception)
+{
+ register const ConfigureInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((configure_list == (LinkedListInfo *) NULL) ||
+ (instantiate_configure == MagickFalse))
+ if (InitializeConfigureList(exception) == MagickFalse)
+ return((const ConfigureInfo *) NULL);
+ if ((configure_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(configure_list) != MagickFalse))
+ return((const ConfigureInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ return((const ConfigureInfo *) GetValueFromLinkedList(configure_list,0));
+ /*
+ Search for configure tag.
+ */
+ AcquireSemaphoreInfo(&configure_semaphore);
+ ResetLinkedListIterator(configure_list);
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ while (p != (const ConfigureInfo *) NULL)
+ {
+ if (LocaleCompare(name,p->name) == 0)
+ break;
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ }
+ if (p != (ConfigureInfo *) NULL)
+ (void) InsertValueInLinkedList(configure_list,0,
+ RemoveElementByValueFromLinkedList(configure_list,p));
+ RelinquishSemaphoreInfo(configure_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureInfoList() returns any configure options that match the
+% specified pattern.
+%
+% The format of the GetConfigureInfoList function is:
+%
+% const ConfigureInfo **GetConfigureInfoList(const char *pattern,
+% unsigned long *number_options,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_options: This integer returns the number of configure options in
+% the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ConfigureInfoCompare(const void *x,const void *y)
+{
+ const ConfigureInfo
+ **p,
+ **q;
+
+ p=(const ConfigureInfo **) x,
+ q=(const ConfigureInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const ConfigureInfo **GetConfigureInfoList(const char *pattern,
+ unsigned long *number_options,ExceptionInfo *exception)
+{
+ const ConfigureInfo
+ **options;
+
+ register const ConfigureInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate configure list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_options != (unsigned long *) NULL);
+ *number_options=0;
+ p=GetConfigureInfo("*",exception);
+ if (p == (const ConfigureInfo *) NULL)
+ return((const ConfigureInfo **) NULL);
+ options=(const ConfigureInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(configure_list)+1UL,sizeof(*options));
+ if (options == (const ConfigureInfo **) NULL)
+ return((const ConfigureInfo **) NULL);
+ /*
+ Generate configure list.
+ */
+ AcquireSemaphoreInfo(&configure_semaphore);
+ ResetLinkedListIterator(configure_list);
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ for (i=0; p != (const ConfigureInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ options[i++]=p;
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ }
+ RelinquishSemaphoreInfo(configure_semaphore);
+ qsort((void *) options,(size_t) i,sizeof(*options),ConfigureInfoCompare);
+ options[i]=(ConfigureInfo *) NULL;
+ *number_options=(unsigned long) i;
+ return(options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureList() returns any configure options that match the specified
+% pattern.
+%
+% The format of the GetConfigureList function is:
+%
+% char **GetConfigureList(const char *pattern,
+% unsigned long *number_options,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_options: This integer returns the number of options in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ConfigureCompare(const void *x,const void *y)
+{
+ register char
+ **p,
+ **q;
+
+ p=(char **) x;
+ q=(char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetConfigureList(const char *pattern,
+ unsigned long *number_options,ExceptionInfo *exception)
+{
+ char
+ **options;
+
+ register const ConfigureInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate configure list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_options != (unsigned long *) NULL);
+ *number_options=0;
+ p=GetConfigureInfo("*",exception);
+ if (p == (const ConfigureInfo *) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&configure_semaphore);
+ RelinquishSemaphoreInfo(configure_semaphore);
+ options=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(configure_list)+1UL,sizeof(*options));
+ if (options == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&configure_semaphore);
+ ResetLinkedListIterator(configure_list);
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ for (i=0; p != (const ConfigureInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ options[i++]=ConstantString(p->name);
+ p=(const ConfigureInfo *) GetNextValueInLinkedList(configure_list);
+ }
+ RelinquishSemaphoreInfo(configure_semaphore);
+ qsort((void *) options,(size_t) i,sizeof(*options),ConfigureCompare);
+ options[i]=(char *) NULL;
+ *number_options=(unsigned long) i;
+ return(options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureOption() returns the value associated with the configure option.
+%
+% The format of the GetConfigureOption method is:
+%
+% char *GetConfigureOption(const char *option)
+%
+% A description of each parameter follows:
+%
+% o configure_info: The configure info.
+%
+*/
+MagickExport char *GetConfigureOption(const char *option)
+{
+ const char
+ *value;
+
+ const ConfigureInfo
+ *configure_info;
+
+ ExceptionInfo
+ *exception;
+
+ assert(option != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",option);
+ exception=AcquireExceptionInfo();
+ configure_info=GetConfigureInfo(option,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (configure_info == (ConfigureInfo *) NULL)
+ return((char *) NULL);
+ value=GetConfigureValue(configure_info);
+ if ((value == (const char *) NULL) || (*value == '\0'))
+ return((char *) NULL);
+ return(ConstantString(value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureOptions() returns any Magick configuration options associated
+% with the specified filename.
+%
+% The format of the GetConfigureOptions method is:
+%
+% LinkedListInfo *GetConfigureOptions(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the configure file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport LinkedListInfo *GetConfigureOptions(const char *filename,
+ ExceptionInfo *exception)
+{
+ char
+ path[MaxTextExtent];
+
+ const char
+ *element;
+
+ LinkedListInfo
+ *options,
+ *paths;
+
+ StringInfo
+ *xml;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) CopyMagickString(path,filename,MaxTextExtent);
+ /*
+ Load XML from configuration files to linked-list.
+ */
+ options=NewLinkedList(0);
+ paths=GetConfigurePaths(filename,exception);
+ if (paths != (LinkedListInfo *) NULL)
+ {
+ ResetLinkedListIterator(paths);
+ element=(const char *) GetNextValueInLinkedList(paths);
+ while (element != (const char *) NULL)
+ {
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",element,filename);
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Searching for configure file: \"%s\"",path);
+ xml=ConfigureFileToStringInfo(path);
+ if (xml != (StringInfo *) NULL)
+ (void) AppendValueToLinkedList(options,xml);
+ element=(const char *) GetNextValueInLinkedList(paths);
+ }
+ paths=DestroyLinkedList(paths,RelinquishMagickMemory);
+ }
+#if defined(__WINDOWS__)
+ {
+ char
+ *blob;
+
+ blob=(char *) NTResourceToBlob(filename);
+ if (blob != (char *) NULL)
+ {
+ xml=StringToStringInfo(blob);
+ SetStringInfoPath(xml,filename);
+ (void) AppendValueToLinkedList(options,xml);
+ blob=DestroyString(blob);
+ }
+ }
+#endif
+ if (GetNumberOfElementsInLinkedList(options) == 0)
+ (void) ThrowMagickException(exception,GetMagickModule(),ConfigureWarning,
+ "UnableToOpenConfigureFile","`%s'",filename);
+ ResetLinkedListIterator(options);
+ return(options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e P a t h s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigurePaths() returns any Magick configuration paths associated
+% with the specified filename.
+%
+% The format of the GetConfigurePaths method is:
+%
+% LinkedListInfo *GetConfigurePaths(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the configure file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport LinkedListInfo *GetConfigurePaths(const char *filename,
+ ExceptionInfo *exception)
+{
+ char
+ path[MaxTextExtent];
+
+ LinkedListInfo
+ *paths;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) CopyMagickString(path,filename,MaxTextExtent);
+ paths=NewLinkedList(0);
+ {
+ char
+ *configure_path;
+
+ /*
+ Search $MAGICK_CONFIGURE_PATH.
+ */
+ configure_path=GetEnvironmentValue("MAGICK_CONFIGURE_PATH");
+ if (configure_path != (char *) NULL)
+ {
+ register char
+ *p,
+ *q;
+
+ for (p=configure_path-1; p != (char *) NULL; )
+ {
+ (void) CopyMagickString(path,p+1,MaxTextExtent);
+ q=strchr(path,DirectoryListSeparator);
+ if (q != (char *) NULL)
+ *q='\0';
+ q=path+strlen(path)-1;
+ if ((q >= path) && (*q != *DirectorySeparator))
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ p=strchr(p+1,DirectoryListSeparator);
+ }
+ configure_path=DestroyString(configure_path);
+ }
+ }
+#if defined(MAGICKCORE_INSTALLED_SUPPORT)
+#if defined(MAGICKCORE_SHARE_CONFIGURE_PATH)
+ (void) AppendValueToLinkedList(paths,ConstantString(
+ MAGICKCORE_SHARE_CONFIGURE_PATH));
+#endif
+#if defined(MAGICKCORE_CONFIGURE_PATH)
+ (void) AppendValueToLinkedList(paths,ConstantString(
+ MAGICKCORE_CONFIGURE_PATH));
+#endif
+#if defined(MAGICKCORE_DOCUMENTATION_PATH)
+ (void) AppendValueToLinkedList(paths,ConstantString(
+ MAGICKCORE_DOCUMENTATION_PATH));
+#endif
+#if defined(MAGICKCORE_SHARE_PATH)
+ (void) AppendValueToLinkedList(paths,ConstantString(MAGICKCORE_SHARE_PATH));
+#endif
+#if defined(__WINDOWS__) && !(defined(MAGICKCORE_CONFIGURE_PATH) || defined(MAGICKCORE_SHARE_CONFIGURE_PATH))
+ {
+ char
+ *registry_key;
+
+ unsigned char
+ *key_value;
+
+ /*
+ Locate file via registry key.
+ */
+ registry_key="ConfigurePath";
+ key_value=NTRegistryKeyLookup(registry_key);
+ if (key_value != (unsigned char *) NULL)
+ {
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",(char *) key_value,
+ DirectorySeparator);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ key_value=(unsigned char *) RelinquishMagickMemory(key_value);
+ }
+ }
+#endif
+#else
+ {
+ char
+ *home;
+
+ /*
+ Search under MAGICK_HOME.
+ */
+ home=GetEnvironmentValue("MAGICK_HOME");
+ if (home != (char *) NULL)
+ {
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",home,
+ DirectorySeparator);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+#else
+ (void) FormatMagickString(path,MaxTextExtent,"%s/lib/%s/",home,
+ MAGICKCORE_CONFIGURE_RELATIVE_PATH);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ (void) FormatMagickString(path,MaxTextExtent,"%s/share/%s/",home,
+ MAGICKCORE_SHARE_CONFIGURE_RELATIVE_PATH);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+#endif
+ home=DestroyString(home);
+ }
+ }
+ if (*GetClientPath() != '\0')
+ {
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",GetClientPath(),
+ DirectorySeparator);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+#else
+ char
+ prefix[MaxTextExtent];
+
+ /*
+ Search based on executable directory if directory is known.
+ */
+ (void) CopyMagickString(prefix,GetClientPath(),MaxTextExtent);
+ ChopPathComponents(prefix,1);
+ (void) FormatMagickString(path,MaxTextExtent,"%s/share/%s/",prefix,
+ MAGICKCORE_SHARE_CONFIGURE_RELATIVE_PATH);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ (void) FormatMagickString(path,MaxTextExtent,"%s/lib/%s/",prefix,
+ MAGICKCORE_CONFIGURE_RELATIVE_PATH);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+#endif
+ }
+#endif
+ {
+ char
+ *home;
+
+ home=GetEnvironmentValue("HOME");
+ if (home == (char *) NULL)
+ home=GetEnvironmentValue("USERPROFILE");
+ if (home != (char *) NULL)
+ {
+ /*
+ Search $HOME/.magick.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s.magick%s",home,
+ DirectorySeparator,DirectorySeparator);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ home=DestroyString(home);
+ }
+ }
+#if defined(__WINDOWS__)
+ {
+ char
+ module_path[MaxTextExtent];
+
+ if ((NTGetModulePath("CORE_RL_magick_.dll",module_path) != MagickFalse) ||
+ (NTGetModulePath("CORE_DB_magick_.dll",module_path) != MagickFalse))
+ {
+ char
+ *element;
+
+ /*
+ Search module path.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",module_path,
+ DirectorySeparator);
+ element=(char *) RemoveElementByValueFromLinkedList(paths,path);
+ if (element != (char *) NULL)
+ element=DestroyString(element);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ }
+ if (NTGetModulePath("Magick.dll",module_path) != MagickFalse)
+ {
+ /*
+ Search PerlMagick module path.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",module_path,
+ DirectorySeparator);
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",module_path,
+ "\\inc\\lib\\auto\\Image\\Magick\\");
+ (void) AppendValueToLinkedList(paths,ConstantString(path));
+ }
+ }
+#endif
+ /*
+ Search current directory.
+ */
+ (void) AppendValueToLinkedList(paths,ConstantString(""));
+ return(paths);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureValue() returns the value associated with the configure info.
+%
+% The format of the GetConfigureValue method is:
+%
+% const char *GetConfigureValue(const ConfigureInfo *configure_info)
+%
+% A description of each parameter follows:
+%
+% o configure_info: The configure info.
+%
+*/
+MagickExport const char *GetConfigureValue(const ConfigureInfo *configure_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(configure_info != (ConfigureInfo *) NULL);
+ assert(configure_info->signature == MagickSignature);
+ return(configure_info->value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e C o n f i g u r e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeConfigureList() initializes the configure list.
+%
+% The format of the InitializeConfigureList method is:
+%
+% MagickBooleanType InitializeConfigureList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeConfigureList(ExceptionInfo *exception)
+{
+ if ((configure_list == (LinkedListInfo *) NULL) &&
+ (instantiate_configure == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&configure_semaphore);
+ if ((configure_list == (LinkedListInfo *) NULL) &&
+ (instantiate_configure == MagickFalse))
+ {
+ (void) LoadConfigureLists(ConfigureFilename,exception);
+ instantiate_configure=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(configure_semaphore);
+ }
+ return(configure_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t C o n f i g u r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListConfigureInfo() lists the configure info to a file.
+%
+% The format of the ListConfigureInfo method is:
+%
+% MagickBooleanType ListConfigureInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListConfigureInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const char
+ *name,
+ *path,
+ *value;
+
+ const ConfigureInfo
+ **configure_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_options;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ configure_info=GetConfigureInfoList("*",&number_options,exception);
+ if (configure_info == (const ConfigureInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_options; i++)
+ {
+ if (configure_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,configure_info[i]->path) != 0))
+ {
+ if (configure_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",configure_info[i]->path);
+ (void) fprintf(file,"Name Value\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=configure_info[i]->path;
+ name="unknown";
+ if (configure_info[i]->name != (char *) NULL)
+ name=configure_info[i]->name;
+ (void) fprintf(file,"%s",name);
+ for (j=(long) strlen(name); j <= 12; j++)
+ (void) fprintf(file," ");
+ (void) fprintf(file," ");
+ value="unknown";
+ if (configure_info[i]->value != (char *) NULL)
+ value=configure_info[i]->value;
+ (void) fprintf(file,"%s",value);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ configure_info=(const ConfigureInfo **)
+ RelinquishMagickMemory((void *) configure_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d C o n f i g u r e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadConfigureList() loads the configure configuration file which provides a
+% mapping between configure attributes and a configure name.
+%
+% The format of the LoadConfigureList method is:
+%
+% MagickBooleanType LoadConfigureList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The configure list in XML format.
+%
+% o filename: The configure list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadConfigureList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ ConfigureInfo
+ *configure_info;
+
+ const char
+ *q;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Load the configure map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading configure file \"%s\" ...",filename);
+ if (configure_list == (LinkedListInfo *) NULL)
+ {
+ configure_list=NewLinkedList(0);
+ if (configure_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ configure_info=(ConfigureInfo *) NULL;
+ if (xml == (char *) NULL)
+ token=AcquireString(ConfigureMap);
+ else
+ token=AcquireString((char *) xml);
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadConfigureList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<configure") == 0)
+ {
+ /*
+ Configure element.
+ */
+ configure_info=(ConfigureInfo *) AcquireMagickMemory(
+ sizeof(*configure_info));
+ if (configure_info == (ConfigureInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(configure_info,0,sizeof(*configure_info));
+ configure_info->path=ConstantString(filename);
+ configure_info->signature=MagickSignature;
+ continue;
+ }
+ if (configure_info == (ConfigureInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AppendValueToLinkedList(configure_list,configure_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ configure_info->name);
+ configure_info=(ConfigureInfo *) NULL;
+ }
+ /*
+ Parse configure element.
+ */
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ configure_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ configure_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ case 'V':
+ case 'v':
+ {
+ if (LocaleCompare((char *) keyword,"value") == 0)
+ {
+ configure_info->value=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d C o n f i g u r e L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadConfigureList() loads one or more configure configuration files which
+% provides a mapping between configure attributes and a configure name.
+%
+% The format of the LoadConfigureLists method is:
+%
+% MagickBooleanType LoadConfigureLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadConfigureLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadConfigureList(ConfigureMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadConfigureList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((configure_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(configure_list) != MagickFalse))
+ status|=LoadConfigureList(ConfigureMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/configure.h b/magick/configure.h
new file mode 100644
index 0000000..68e2356
--- /dev/null
+++ b/magick/configure.h
@@ -0,0 +1,71 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore configure methods.
+*/
+#ifndef _MAGICKCORE_CONFIGURE_H
+#define _MAGICKCORE_CONFIGURE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/hashmap.h"
+
+typedef struct _ConfigureInfo
+{
+ char
+ *path,
+ *name,
+ *value;
+
+ MagickBooleanType
+ stealth;
+
+ struct _ConfigureInfo
+ *previous,
+ *next; /* deprecated, use GetConfigureInfoList() */
+
+ unsigned long
+ signature;
+} ConfigureInfo;
+
+extern MagickExport char
+ **GetConfigureList(const char *,unsigned long *,ExceptionInfo *),
+ *GetConfigureOption(const char *);
+
+extern MagickExport const char
+ *GetConfigureValue(const ConfigureInfo *);
+
+extern MagickExport const ConfigureInfo
+ *GetConfigureInfo(const char *,ExceptionInfo *),
+ **GetConfigureInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport LinkedListInfo
+ *DestroyConfigureOptions(LinkedListInfo *),
+ *GetConfigurePaths(const char *,ExceptionInfo *),
+ *GetConfigureOptions(const char *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ ListConfigureInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyConfigureList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/constitute.c b/magick/constitute.c
new file mode 100644
index 0000000..3ddfc51
--- /dev/null
+++ b/magick/constitute.c
@@ -0,0 +1,1250 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% CCCC OOO N N SSSSS TTTTT IIIII TTTTT U U TTTTT EEEEE %
+% C O O NN N SS T I T U U T E %
+% C O O N N N ESSS T I T U U T EEE %
+% C O O N NN SS T I T U U T E %
+% CCCC OOO N N SSSSS T IIIII T UUU T EEEEE %
+% %
+% %
+% MagickCore Methods to Consitute an Image %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/cache.h"
+#include "magick/client.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/geometry.h"
+#include "magick/identify.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/pixel.h"
+#include "magick/policy.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/statistic.h"
+#include "magick/stream.h"
+#include "magick/string_.h"
+#include "magick/timer.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+
+static SemaphoreInfo
+ *constitute_semaphore = (SemaphoreInfo *) NULL;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n s t i t u t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConstituteImage() returns an image from the pixel data you supply.
+% The pixel data must be in scanline order top-to-bottom. The data can be
+% char, short int, int, float, or double. Float and double require the
+% pixels to be normalized [0..1], otherwise [0..QuantumRange]. For example, to
+% create a 640x480 image from unsigned red-green-blue character data, use:
+%
+% image = ConstituteImage(640,480,"RGB",CharPixel,pixels,&exception);
+%
+% The format of the ConstituteImage method is:
+%
+% Image *ConstituteImage(const unsigned long columns,
+% const unsigned long rows,const char *map,const StorageType storage,
+% const void *pixels,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o columns: width in pixels of the image.
+%
+% o rows: height in pixels of the image.
+%
+% o map: This string reflects the expected ordering of the pixel array.
+% It can be any combination or order of R = red, G = green, B = blue,
+% A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
+% Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
+% P = pad.
+%
+% o storage: Define the data type of the pixels. Float and double types are
+% expected to be normalized [0..1] otherwise [0..QuantumRange]. Choose
+% from these types: CharPixel, DoublePixel, FloatPixel, IntegerPixel,
+% LongPixel, QuantumPixel, or ShortPixel.
+%
+% o pixels: This array of values contain the pixel components as defined by
+% map and type. You must preallocate this array where the expected
+% length varies depending on the values of width, height, map, and type.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ConstituteImage(const unsigned long columns,
+ const unsigned long rows,const char *map,const StorageType storage,
+ const void *pixels,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Allocate image structure.
+ */
+ assert(map != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",map);
+ assert(pixels != (void *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ image=AcquireImage((ImageInfo *) NULL);
+ if (image == (Image *) NULL)
+ return((Image *) NULL);
+ if ((columns == 0) || (rows == 0))
+ ThrowImageException(OptionError,"NonZeroWidthAndHeightRequired");
+ image->columns=columns;
+ image->rows=rows;
+ (void) SetImageBackgroundColor(image);
+ status=ImportImagePixels(image,0,0,columns,rows,map,storage,pixels);
+ if (status == MagickFalse)
+ {
+ InheritException(exception,&image->exception);
+ image=DestroyImage(image);
+ }
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C o n s t i t u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyConstitute() destroys the constitute environment.
+%
+% The format of the DestroyConstitute method is:
+%
+% DestroyConstitute(void)
+%
+*/
+MagickExport void DestroyConstitute(void)
+{
+ if (constitute_semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&constitute_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P i n g I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PingImage() returns all the properties of an image or image sequence
+% except for the pixels. It is much faster and consumes far less memory
+% than ReadImage(). On failure, a NULL image is returned and exception
+% describes the reason for the failure.
+%
+% The format of the PingImage method is:
+%
+% Image *PingImage(const ImageInfo *image_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: Ping the image defined by the file or filename members of
+% this structure.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static size_t PingStream(const Image *magick_unused(image),
+ const void *magick_unused(pixels),const size_t columns)
+{
+ return(columns);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport Image *PingImage(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ ImageInfo
+ *ping_info;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ ping_info=CloneImageInfo(image_info);
+ ping_info->ping=MagickTrue;
+ image=ReadStream(ping_info,&PingStream,exception);
+ if (image != (Image *) NULL)
+ {
+ ResetTimer(&image->timer);
+ if (ping_info->verbose != MagickFalse)
+ (void) IdentifyImage(image,stdout,MagickFalse);
+ }
+ ping_info=DestroyImageInfo(ping_info);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P i n g I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PingImages() pings one or more images and returns them as an image list.
+%
+% The format of the PingImage method is:
+%
+% Image *PingImages(const ImageInfo *image_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *PingImages(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent];
+
+ Image
+ *image,
+ *images;
+
+ ImageInfo
+ *read_info;
+
+ /*
+ Ping image list from a file.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) InterpretImageFilename(image_info,(Image *) NULL,image_info->filename,
+ (int) image_info->scene,filename);
+ if (LocaleCompare(filename,image_info->filename) != 0)
+ {
+ ExceptionInfo
+ *sans;
+
+ long
+ extent,
+ scene;
+
+ /*
+ Images of the form image-%d.png[1-5].
+ */
+ read_info=CloneImageInfo(image_info);
+ sans=AcquireExceptionInfo();
+ (void) SetImageInfo(read_info,MagickFalse,sans);
+ sans=DestroyExceptionInfo(sans);
+ (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
+ images=NewImageList();
+ extent=(long) (read_info->scene+read_info->number_scenes);
+ for (scene=(long) read_info->scene; scene < (long) extent; scene++)
+ {
+ (void) InterpretImageFilename(image_info,(Image *) NULL,filename,(int)
+ scene,read_info->filename);
+ image=PingImage(read_info,exception);
+ if (image == (Image *) NULL)
+ continue;
+ AppendImageToList(&images,image);
+ }
+ read_info=DestroyImageInfo(read_info);
+ return(images);
+ }
+ return(PingImage(image_info,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadImage() reads an image or image sequence from a file or file handle.
+% The method returns a NULL if there is a memory shortage or if the image
+% cannot be read. On failure, a NULL image is returned and exception
+% describes the reason for the failure.
+%
+% The format of the ReadImage method is:
+%
+% Image *ReadImage(const ImageInfo *image_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: Read the image defined by the file or filename members of
+% this structure.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ReadImage(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent],
+ magick[MaxTextExtent],
+ magick_filename[MaxTextExtent];
+
+ const char
+ *value;
+
+ const DelegateInfo
+ *delegate_info;
+
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *sans_exception;
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *image,
+ *next;
+
+ ImageInfo
+ *read_info;
+
+ MagickStatusType
+ flags,
+ thread_support;
+
+ PolicyDomain
+ domain;
+
+ PolicyRights
+ rights;
+
+ /*
+ Determine image type from filename prefix or suffix (e.g. image.jpg).
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image_info->filename != (char *) NULL);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ read_info=CloneImageInfo(image_info);
+ (void) CopyMagickString(magick_filename,read_info->filename,MaxTextExtent);
+ (void) SetImageInfo(read_info,MagickFalse,exception);
+ (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
+ (void) CopyMagickString(magick,read_info->magick,MaxTextExtent);
+ domain=CoderPolicyDomain;
+ rights=ReadPolicyRights;
+ if (IsRightsAuthorized(domain,rights,read_info->magick) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",read_info->filename);
+ return((Image *) NULL);
+ }
+ /*
+ Call appropriate image reader based on image type.
+ */
+ sans_exception=AcquireExceptionInfo();
+ magick_info=GetMagickInfo(read_info->magick,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ if (magick_info != (const MagickInfo *) NULL)
+ {
+ if (GetMagickEndianSupport(magick_info) == MagickFalse)
+ read_info->endian=UndefinedEndian;
+ else
+ if ((image_info->endian == UndefinedEndian) &&
+ (GetMagickRawSupport(magick_info) != MagickFalse))
+ {
+ unsigned long
+ lsb_first;
+
+ lsb_first=1;
+ read_info->endian=(*(char *) &lsb_first) == 1 ? LSBEndian :
+ MSBEndian;
+ }
+ }
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (GetMagickSeekableStream(magick_info) != MagickFalse))
+ {
+ MagickBooleanType
+ status;
+
+ image=AcquireImage(read_info);
+ (void) CopyMagickString(image->filename,read_info->filename,
+ MaxTextExtent);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ {
+ read_info=DestroyImageInfo(read_info);
+ image=DestroyImage(image);
+ return((Image *) NULL);
+ }
+ if (IsBlobSeekable(image) == MagickFalse)
+ {
+ /*
+ Coder requires a seekable stream.
+ */
+ *read_info->filename='\0';
+ status=ImageToFile(image,read_info->filename,exception);
+ if (status == MagickFalse)
+ {
+ (void) CloseBlob(image);
+ read_info=DestroyImageInfo(read_info);
+ image=DestroyImage(image);
+ return((Image *) NULL);
+ }
+ read_info->temporary=MagickTrue;
+ }
+ (void) CloseBlob(image);
+ image=DestroyImage(image);
+ }
+ image=NewImageList();
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (GetImageDecoder(magick_info) != (DecodeImageHandler *) NULL))
+ {
+ thread_support=GetMagickThreadSupport(magick_info);
+ if ((thread_support & DecoderThreadSupport) == 0)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ image=GetImageDecoder(magick_info)(read_info,exception);
+ if ((thread_support & DecoderThreadSupport) == 0)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ }
+ else
+ {
+ delegate_info=GetDelegateInfo(read_info->magick,(char *) NULL,exception);
+ if (delegate_info == (const DelegateInfo *) NULL)
+ {
+ if (IsPathAccessible(read_info->filename) != MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
+ read_info->filename);
+ if (read_info->temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(read_info->filename);
+ read_info=DestroyImageInfo(read_info);
+ return((Image *) NULL);
+ }
+ /*
+ Let our decoding delegate process the image.
+ */
+ image=AcquireImage(read_info);
+ if (image == (Image *) NULL)
+ {
+ read_info=DestroyImageInfo(read_info);
+ return((Image *) NULL);
+ }
+ (void) CopyMagickString(image->filename,read_info->filename,
+ MaxTextExtent);
+ *read_info->filename='\0';
+ if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ (void) InvokeDelegate(read_info,image,read_info->magick,(char *) NULL,
+ exception);
+ if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ image=DestroyImageList(image);
+ read_info->temporary=MagickTrue;
+ (void) SetImageInfo(read_info,MagickFalse,exception);
+ magick_info=GetMagickInfo(read_info->magick,exception);
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (GetImageDecoder(magick_info) == (DecodeImageHandler *) NULL))
+ {
+ if (IsPathAccessible(read_info->filename) != MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateError,"NoDecodeDelegateForThisImageFormat","`%s'",
+ read_info->filename);
+ else
+ ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
+ read_info->filename);
+ read_info=DestroyImageInfo(read_info);
+ return((Image *) NULL);
+ }
+ thread_support=GetMagickThreadSupport(magick_info);
+ if ((thread_support & DecoderThreadSupport) == 0)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ image=(Image *) (GetImageDecoder(magick_info))(read_info,exception);
+ if ((thread_support & DecoderThreadSupport) == 0)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ }
+ if (read_info->temporary != MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(read_info->filename);
+ read_info->temporary=MagickFalse;
+ if (image != (Image *) NULL)
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ }
+ if (image == (Image *) NULL)
+ {
+ read_info=DestroyImageInfo(read_info);
+ return(image);
+ }
+ if (exception->severity >= ErrorException)
+ (void) LogMagickEvent(ExceptionEvent,GetMagickModule(),
+ "Coder (%s) generated an image despite an error (%d), "
+ "notify the developers",image->magick,exception->severity);
+ if (IsBlobTemporary(image) != MagickFalse)
+ (void) RelinquishUniqueFileResource(read_info->filename);
+ if ((GetNextImageInList(image) != (Image *) NULL) &&
+ (IsSceneGeometry(read_info->scenes,MagickFalse) != MagickFalse))
+ {
+ Image
+ *clones;
+
+ clones=CloneImages(image,read_info->scenes,exception);
+ if (clones == (Image *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "SubimageSpecificationReturnsNoImages","`%s'",read_info->filename);
+ else
+ {
+ image=DestroyImageList(image);
+ image=GetFirstImageInList(clones);
+ }
+ }
+ if (GetBlobError(image) != MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,
+ "AnErrorHasOccurredReadingFromFile",read_info->filename);
+ image=DestroyImageList(image);
+ read_info=DestroyImageInfo(read_info);
+ return((Image *) NULL);
+ }
+ for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ char
+ timestamp[MaxTextExtent];
+
+ const StringInfo
+ *profile;
+
+ next->taint=MagickFalse;
+ if (next->magick_columns == 0)
+ next->magick_columns=next->columns;
+ if (next->magick_rows == 0)
+ next->magick_rows=next->rows;
+ if ((LocaleCompare(magick,"HTTP") != 0) &&
+ (LocaleCompare(magick,"FTP") != 0))
+ (void) CopyMagickString(next->magick,magick,MaxTextExtent);
+ (void) CopyMagickString(next->magick_filename,magick_filename,
+ MaxTextExtent);
+ if (IsBlobTemporary(image) != MagickFalse)
+ (void) CopyMagickString(next->filename,filename,MaxTextExtent);
+ value=GetImageProperty(next,"tiff:Orientation");
+ if (value == (char *) NULL)
+ value=GetImageProperty(next,"exif:Orientation");
+ if (value != (char *) NULL)
+ {
+ next->orientation=(OrientationType) atol(value);
+ (void) DeleteImageProperty(next,"tiff:Orientation");
+ (void) DeleteImageProperty(next,"exif:Orientation");
+ }
+ value=GetImageProperty(next,"tiff:XResolution");
+ if (value == (char *) NULL)
+ value=GetImageProperty(next,"exif:XResolution");
+ if (value != (char *) NULL)
+ {
+ geometry_info.rho=next->x_resolution;
+ geometry_info.sigma=1.0;
+ flags=ParseGeometry(value,&geometry_info);
+ if (geometry_info.sigma != 0)
+ next->x_resolution=geometry_info.rho/geometry_info.sigma;
+ (void) DeleteImageProperty(next,"exif:XResolution");
+ (void) DeleteImageProperty(next,"tiff:XResolution");
+ }
+ value=GetImageProperty(next,"tiff:YResolution");
+ if (value == (char *) NULL)
+ value=GetImageProperty(next,"exif:YResolution");
+ if (value != (char *) NULL)
+ {
+ geometry_info.rho=next->y_resolution;
+ geometry_info.sigma=1.0;
+ flags=ParseGeometry(value,&geometry_info);
+ if (geometry_info.sigma != 0)
+ next->y_resolution=geometry_info.rho/geometry_info.sigma;
+ (void) DeleteImageProperty(next,"exif:YResolution");
+ (void) DeleteImageProperty(next,"tiff:YResolution");
+ }
+ value=GetImageProperty(next,"tiff:ResolutionUnit");
+ if (value == (char *) NULL)
+ value=GetImageProperty(next,"exif:ResolutionUnit");
+ if (value != (char *) NULL)
+ {
+ next->units=(ResolutionType) (atoi(value)-1);
+ (void) DeleteImageProperty(next,"exif:ResolutionUnit");
+ (void) DeleteImageProperty(next,"tiff:ResolutionUnit");
+ }
+ if (next->page.width == 0)
+ next->page.width=next->columns;
+ if (next->page.height == 0)
+ next->page.height=next->rows;
+ if (*read_info->filename != '\0')
+ {
+ char
+ *property;
+
+ const char
+ *option;
+
+ option=GetImageOption(read_info,"caption");
+ if (option != (const char *) NULL)
+ {
+ property=InterpretImageProperties(read_info,next,option);
+ (void) SetImageProperty(next,"caption",property);
+ property=DestroyString(property);
+ }
+ option=GetImageOption(read_info,"comment");
+ if (option != (const char *) NULL)
+ {
+ property=InterpretImageProperties(read_info,next,option);
+ (void) SetImageProperty(next,"comment",property);
+ property=DestroyString(property);
+ }
+ option=GetImageOption(read_info,"label");
+ if (option != (const char *) NULL)
+ {
+ property=InterpretImageProperties(read_info,next,option);
+ (void) SetImageProperty(next,"label",property);
+ property=DestroyString(property);
+ }
+ }
+ if (LocaleCompare(next->magick,"TEXT") == 0)
+ (void) ParseAbsoluteGeometry("0x0+0+0",&next->page);
+ if ((read_info->extract != (char *) NULL) &&
+ (read_info->stream == (StreamHandler) NULL))
+ {
+ RectangleInfo
+ geometry;
+
+ flags=ParseAbsoluteGeometry(read_info->extract,&geometry);
+ if ((next->columns != geometry.width) ||
+ (next->rows != geometry.height))
+ {
+ if (((flags & XValue) != 0) || ((flags & YValue) != 0))
+ {
+ Image
+ *crop_image;
+
+ crop_image=CropImage(next,&geometry,exception);
+ if (crop_image != (Image *) NULL)
+ ReplaceImageInList(&next,crop_image);
+ }
+ else
+ if (((flags & WidthValue) != 0) || ((flags & HeightValue) != 0))
+ {
+ Image
+ *size_image;
+
+ flags=ParseRegionGeometry(next,read_info->extract,&geometry,
+ exception);
+ size_image=ResizeImage(next,geometry.width,geometry.height,
+ next->filter,next->blur,exception);
+ if (size_image != (Image *) NULL)
+ ReplaceImageInList(&next,size_image);
+ }
+ }
+ }
+ profile=GetImageProfile(next,"icc");
+ if (profile == (const StringInfo *) NULL)
+ profile=GetImageProfile(next,"icm");
+ if (profile != (const StringInfo *) NULL)
+ {
+ next->color_profile.length=GetStringInfoLength(profile);
+ next->color_profile.info=GetStringInfoDatum(profile);
+ }
+ profile=GetImageProfile(next,"iptc");
+ if (profile == (const StringInfo *) NULL)
+ profile=GetImageProfile(next,"8bim");
+ if (profile != (const StringInfo *) NULL)
+ {
+ next->iptc_profile.length=GetStringInfoLength(profile);
+ next->iptc_profile.info=GetStringInfoDatum(profile);
+ }
+ (void) FormatMagickTime(GetBlobProperties(next)->st_mtime,MaxTextExtent,
+ timestamp);
+ (void) SetImageProperty(next,"date:modify",timestamp);
+ (void) FormatMagickTime(GetBlobProperties(next)->st_ctime,MaxTextExtent,
+ timestamp);
+ (void) SetImageProperty(next,"date:create",timestamp);
+ if (read_info->verbose != MagickFalse)
+ (void) IdentifyImage(next,stdout,MagickFalse);
+ image=next;
+ }
+ read_info=DestroyImageInfo(read_info);
+ return(GetFirstImageInList(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadImages() reads one or more images and returns them as an image list.
+%
+% The format of the ReadImage method is:
+%
+% Image *ReadImages(const ImageInfo *image_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ReadImages(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent];
+
+ Image
+ *image,
+ *images;
+
+ ImageInfo
+ *read_info;
+
+ /*
+ Read image list from a file.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) InterpretImageFilename(image_info,(Image *) NULL,image_info->filename,
+ (int) image_info->scene,filename);
+ if (LocaleCompare(filename,image_info->filename) != 0)
+ {
+ ExceptionInfo
+ *sans;
+
+ long
+ extent,
+ scene;
+
+ /*
+ Images of the form image-%d.png[1-5].
+ */
+ read_info=CloneImageInfo(image_info);
+ sans=AcquireExceptionInfo();
+ (void) SetImageInfo(read_info,MagickFalse,sans);
+ sans=DestroyExceptionInfo(sans);
+ (void) CopyMagickString(filename,read_info->filename,MaxTextExtent);
+ images=NewImageList();
+ extent=(long) (read_info->scene+read_info->number_scenes);
+ for (scene=(long) read_info->scene; scene < (long) extent; scene++)
+ {
+ (void) InterpretImageFilename(image_info,(Image *) NULL,filename,(int)
+ scene,read_info->filename);
+ image=ReadImage(read_info,exception);
+ if (image == (Image *) NULL)
+ continue;
+ AppendImageToList(&images,image);
+ }
+ read_info=DestroyImageInfo(read_info);
+ return(images);
+ }
+ return(ReadImage(image_info,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e a d I n l i n e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadInlineImage() reads a Base64-encoded inline image or image sequence.
+% The method returns a NULL if there is a memory shortage or if the image
+% cannot be read. On failure, a NULL image is returned and exception
+% describes the reason for the failure.
+%
+% The format of the ReadInlineImage method is:
+%
+% Image *ReadInlineImage(const ImageInfo *image_info,const char *content,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o content: the image encoded in Base64.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ReadInlineImage(const ImageInfo *image_info,
+ const char *content,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ ImageInfo
+ *read_info;
+
+ unsigned char
+ *blob;
+
+ size_t
+ length;
+
+ register const char
+ *p;
+
+ image=NewImageList();
+ for (p=content; (*p != ',') && (*p != '\0'); p++) ;
+ if (*p == '\0')
+ ThrowReaderException(CorruptImageError,"CorruptImage");
+ p++;
+ length=0;
+ blob=Base64Decode(p,&length);
+ if (length == 0)
+ ThrowReaderException(CorruptImageError,"CorruptImage");
+ read_info=CloneImageInfo(image_info);
+ (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
+ (void *) NULL);
+ image=BlobToImage(read_info,blob,length,exception);
+ blob=(unsigned char *) RelinquishMagickMemory(blob);
+ read_info=DestroyImageInfo(read_info);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W r i t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteImage() writes an image or an image sequence to a file or filehandle.
+% If writing to a file on disk, the name is defined by the filename member of
+% the image structure. Write() returns MagickFalse is these is a memory
+% shortage or if the image cannot be written. Check the exception member of
+% image to determine the cause for any failure.
+%
+% The format of the WriteImage method is:
+%
+% MagickBooleanType WriteImage(const ImageInfo *image_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType WriteImage(const ImageInfo *image_info,
+ Image *image)
+{
+ char
+ filename[MaxTextExtent];
+
+ const DelegateInfo
+ *delegate_info;
+
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *sans_exception;
+
+ ImageInfo
+ *write_info;
+
+ MagickBooleanType
+ status,
+ temporary;
+
+ MagickStatusType
+ thread_support;
+
+ PolicyDomain
+ domain;
+
+ PolicyRights
+ rights;
+
+ /*
+ Determine image type from filename prefix or suffix (e.g. image.jpg).
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ sans_exception=AcquireExceptionInfo();
+ write_info=CloneImageInfo(image_info);
+ (void) CopyMagickString(write_info->filename,image->filename,MaxTextExtent);
+ if (*write_info->magick == '\0')
+ (void) CopyMagickString(write_info->magick,image->magick,MaxTextExtent);
+ (void) SetImageInfo(write_info,MagickTrue,sans_exception);
+ if (LocaleCompare(write_info->magick,"clipmask") == 0)
+ {
+ if (image->clip_mask == (Image *) NULL)
+ {
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ OptionError,"NoClipPathDefined","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ image=image->clip_mask;
+ (void) SetImageInfo(write_info,MagickTrue,sans_exception);
+ }
+ (void) CopyMagickString(filename,image->filename,MaxTextExtent);
+ (void) CopyMagickString(image->filename,write_info->filename,MaxTextExtent);
+ domain=CoderPolicyDomain;
+ rights=WritePolicyRights;
+ if (IsRightsAuthorized(domain,rights,write_info->magick) == MagickFalse)
+ {
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ ThrowBinaryException(PolicyError,"NotAuthorized",filename);
+ }
+ magick_info=GetMagickInfo(write_info->magick,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ if (magick_info != (const MagickInfo *) NULL)
+ {
+ if (GetMagickEndianSupport(magick_info) == MagickFalse)
+ image->endian=UndefinedEndian;
+ else
+ if ((image_info->endian == UndefinedEndian) &&
+ (GetMagickRawSupport(magick_info) != MagickFalse))
+ {
+ unsigned long
+ lsb_first;
+
+ lsb_first=1;
+ image->endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
+ }
+ }
+ (void) SyncImageProfiles(image);
+ if ((write_info->page == (char *) NULL) &&
+ (GetPreviousImageInList(image) == (Image *) NULL) &&
+ (GetNextImageInList(image) == (Image *) NULL) &&
+ (IsTaintImage(image) == MagickFalse))
+ {
+ delegate_info=GetDelegateInfo(image->magick,write_info->magick,
+ &image->exception);
+ if ((delegate_info != (const DelegateInfo *) NULL) &&
+ (GetDelegateMode(delegate_info) == 0) &&
+ (IsPathAccessible(image->magick_filename) != MagickFalse))
+ {
+ /*
+ Process image with bi-modal delegate.
+ */
+ (void) CopyMagickString(image->filename,image->magick_filename,
+ MaxTextExtent);
+ status=InvokeDelegate(write_info,image,image->magick,
+ write_info->magick,&image->exception);
+ write_info=DestroyImageInfo(write_info);
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ return(status);
+ }
+ }
+ status=MagickFalse;
+ temporary=MagickFalse;
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (GetMagickSeekableStream(magick_info) != MagickFalse))
+ {
+ char
+ filename[MaxTextExtent];
+
+ (void) CopyMagickString(filename,image->filename,MaxTextExtent);
+ status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ if (status != MagickFalse)
+ {
+ if (IsBlobSeekable(image) == MagickFalse)
+ {
+ /*
+ A seekable stream is required by the encoder.
+ */
+ (void) CopyMagickString(write_info->filename,image->filename,
+ MaxTextExtent);
+ (void) AcquireUniqueFilename(image->filename);
+ temporary=MagickTrue;
+ }
+ (void) CloseBlob(image);
+ }
+ }
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (GetImageEncoder(magick_info) != (EncodeImageHandler *) NULL))
+ {
+ /*
+ Call appropriate image writer based on image type.
+ */
+ thread_support=GetMagickThreadSupport(magick_info);
+ if ((thread_support & EncoderThreadSupport) == 0)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ status=GetImageEncoder(magick_info)(write_info,image);
+ if ((thread_support & EncoderThreadSupport) == 0)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ }
+ else
+ {
+ delegate_info=GetDelegateInfo((char *) NULL,write_info->magick,
+ &image->exception);
+ if (delegate_info != (DelegateInfo *) NULL)
+ {
+ /*
+ Process the image with delegate.
+ */
+ *write_info->filename='\0';
+ if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ status=InvokeDelegate(write_info,image,(char *) NULL,
+ write_info->magick,&image->exception);
+ if (GetDelegateThreadSupport(delegate_info) == MagickFalse)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ }
+ else
+ {
+ sans_exception=AcquireExceptionInfo();
+ magick_info=GetMagickInfo(write_info->magick,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ if ((write_info->affirm == MagickFalse) &&
+ (magick_info == (const MagickInfo *) NULL))
+ {
+ (void) CopyMagickString(write_info->magick,image->magick,
+ MaxTextExtent);
+ magick_info=GetMagickInfo(write_info->magick,&image->exception);
+ }
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (GetImageEncoder(magick_info) == (EncodeImageHandler *) NULL))
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"NoEncodeDelegateForThisImageFormat","`%s'",
+ image->filename);
+ else
+ {
+ /*
+ Call appropriate image writer based on image type.
+ */
+ thread_support=GetMagickThreadSupport(magick_info);
+ if ((thread_support & EncoderThreadSupport) == 0)
+ AcquireSemaphoreInfo(&constitute_semaphore);
+ status=GetImageEncoder(magick_info)(write_info,image);
+ if ((thread_support & EncoderThreadSupport) == 0)
+ RelinquishSemaphoreInfo(constitute_semaphore);
+ }
+ }
+ }
+ if (GetBlobError(image) != MagickFalse)
+ ThrowFileException(&image->exception,FileOpenError,
+ "AnErrorHasOccurredWritingToFile",image->filename);
+ if (temporary == MagickTrue)
+ {
+ /*
+ Copy temporary image file to permanent.
+ */
+ status=OpenBlob(write_info,image,ReadBinaryBlobMode,&image->exception);
+ if (status != MagickFalse)
+ status=ImageToFile(image,write_info->filename,&image->exception);
+ (void) RelinquishUniqueFileResource(image->filename);
+ (void) CopyMagickString(image->filename,write_info->filename,
+ MaxTextExtent);
+ (void) CloseBlob(image);
+ }
+ if ((LocaleCompare(write_info->magick,"info") != 0) &&
+ (write_info->verbose != MagickFalse))
+ (void) IdentifyImage(image,stdout,MagickFalse);
+ write_info=DestroyImageInfo(write_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W r i t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteImages() writes an image sequence.
+%
+% The format of the WriteImages method is:
+%
+% MagickBooleanType WriteImages(const ImageInfo *image_info,Image *images,
+% const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o images: the image list.
+%
+% o filename: the image filename.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType WriteImages(const ImageInfo *image_info,
+ Image *images,const char *filename,ExceptionInfo *exception)
+{
+ BlobInfo
+ *blob;
+
+ ExceptionInfo
+ *sans_exception;
+
+ ImageInfo
+ *write_info;
+
+ MagickStatusType
+ status;
+
+ register Image
+ *p;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ write_info=CloneImageInfo(image_info);
+ images=GetFirstImageInList(images);
+ blob=CloneBlobInfo(images->blob); /* thread specific I/O handler */
+ DestroyBlob(images);
+ images->blob=blob;
+ if (filename != (const char *) NULL)
+ for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
+ (void) CopyMagickString(p->filename,filename,MaxTextExtent);
+ (void) CopyMagickString(write_info->filename,images->filename,MaxTextExtent);
+ if (*write_info->magick == '\0')
+ (void) CopyMagickString(write_info->magick,images->magick,MaxTextExtent);
+ sans_exception=AcquireExceptionInfo();
+ (void) SetImageInfo(write_info,MagickTrue,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ p=images;
+ for ( ; GetNextImageInList(p) != (Image *) NULL; p=GetNextImageInList(p))
+ if (p->scene >= GetNextImageInList(p)->scene)
+ {
+ register long
+ i;
+
+ /*
+ Generate consistent scene numbers.
+ */
+ i=(long) images->scene;
+ for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
+ p->scene=(unsigned long) i++;
+ break;
+ }
+ /*
+ Write images.
+ */
+ status=MagickTrue;
+ for (p=images; p != (Image *) NULL; p=GetNextImageInList(p))
+ {
+ status&=WriteImage(write_info,p);
+ GetImageException(p,exception);
+ if (write_info->adjoin != MagickFalse)
+ break;
+ }
+ write_info=DestroyImageInfo(write_info);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
diff --git a/magick/constitute.h b/magick/constitute.h
new file mode 100644
index 0000000..7058f9f
--- /dev/null
+++ b/magick/constitute.h
@@ -0,0 +1,57 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image constitute methods.
+*/
+#ifndef _MAGICKCORE_CONSTITUTE_H
+#define _MAGICKCORE_CONSTITUTE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedPixel,
+ CharPixel,
+ DoublePixel,
+ FloatPixel,
+ IntegerPixel,
+ LongPixel,
+ QuantumPixel,
+ ShortPixel
+} StorageType;
+
+extern MagickExport Image
+ *ConstituteImage(const unsigned long,const unsigned long,const char *,
+ const StorageType,const void *,ExceptionInfo *),
+ *PingImage(const ImageInfo *,ExceptionInfo *),
+ *PingImages(const ImageInfo *,ExceptionInfo *),
+ *ReadImage(const ImageInfo *,ExceptionInfo *),
+ *ReadImages(const ImageInfo *,ExceptionInfo *),
+ *ReadInlineImage(const ImageInfo *,const char *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ WriteImage(const ImageInfo *,Image *),
+ WriteImages(const ImageInfo *,Image *,const char *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyConstitute(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/decorate.c b/magick/decorate.c
new file mode 100644
index 0000000..d89b6df
--- /dev/null
+++ b/magick/decorate.c
@@ -0,0 +1,886 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% DDDD EEEEE CCCC OOO RRRR AAA TTTTT EEEEE %
+% D D E C O O R R A A T E %
+% D D EEE C O O RRRR AAAAA T EEE %
+% D D E C O O R R A A T E %
+% DDDD EEEEE CCCC OOO R R A A T EEEEE %
+% %
+% %
+% MagickCore Image Decoration Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache-view.h"
+#include "magick/color-private.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/decorate.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/transform.h"
+
+/*
+ Define declarations.
+*/
+#define AccentuateModulate ScaleCharToQuantum(80)
+#define HighlightModulate ScaleCharToQuantum(125)
+#define ShadowModulate ScaleCharToQuantum(135)
+#define DepthModulate ScaleCharToQuantum(185)
+#define TroughModulate ScaleCharToQuantum(110)
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B o r d e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BorderImage() surrounds the image with a border of the color defined by
+% the bordercolor member of the image structure. The width and height
+% of the border are defined by the corresponding members of the border_info
+% structure.
+%
+% The format of the BorderImage method is:
+%
+% Image *BorderImage(const Image *image,const RectangleInfo *border_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o border_info: Define the width and height of the border.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *BorderImage(const Image *image,
+ const RectangleInfo *border_info,ExceptionInfo *exception)
+{
+ Image
+ *border_image;
+
+ /*
+ Decorate the image with a border.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(border_info != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ border_image=CloneImage(image,image->columns+2*border_info->width,
+ image->rows+2*border_info->height,MagickTrue,exception);
+ if (border_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(border_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&border_image->exception);
+ border_image=DestroyImage(border_image);
+ return((Image *) NULL);
+ }
+ border_image->background_color=border_image->border_color;
+ if (border_image->background_color.opacity != OpaqueOpacity)
+ border_image->matte=MagickTrue;
+ (void) SetImageBackgroundColor(border_image);
+ (void) CompositeImage(border_image,image->compose,image,(long)
+ border_info->width,(long) border_info->height);
+ border_image->background_color=image->background_color;
+ return(border_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F r a m e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FrameImage() adds a simulated three-dimensional border around the image.
+% The color of the border is defined by the matte_color member of image.
+% Members width and height of frame_info specify the border width of the
+% vertical and horizontal sides of the frame. Members inner and outer
+% indicate the width of the inner and outer shadows of the frame.
+%
+% The format of the FrameImage method is:
+%
+% Image *FrameImage(const Image *image,const FrameInfo *frame_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o frame_info: Define the width and height of the frame and its bevels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *FrameImage(const Image *image,const FrameInfo *frame_info,
+ ExceptionInfo *exception)
+{
+#define FrameImageTag "Frame/Image"
+
+ Image
+ *frame_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ accentuate,
+ border,
+ highlight,
+ interior,
+ matte,
+ shadow,
+ trough;
+
+ register long
+ x;
+
+ unsigned long
+ bevel_width,
+ height,
+ width;
+
+ CacheView
+ *frame_view;
+
+ /*
+ Check frame geometry.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(frame_info != (FrameInfo *) NULL);
+ if ((frame_info->outer_bevel < 0) || (frame_info->inner_bevel < 0))
+ ThrowImageException(OptionError,"FrameIsLessThanImageSize");
+ bevel_width=(unsigned long) (frame_info->outer_bevel+frame_info->inner_bevel);
+ width=frame_info->width-frame_info->x-bevel_width;
+ height=frame_info->height-frame_info->y-bevel_width;
+ if ((width < image->columns) || (height < image->rows))
+ ThrowImageException(OptionError,"FrameIsLessThanImageSize");
+ /*
+ Initialize framed image attributes.
+ */
+ frame_image=CloneImage(image,frame_info->width,frame_info->height,MagickTrue,
+ exception);
+ if (frame_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(frame_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&frame_image->exception);
+ frame_image=DestroyImage(frame_image);
+ return((Image *) NULL);
+ }
+ if (frame_image->matte_color.opacity != OpaqueOpacity)
+ frame_image->matte=MagickTrue;
+ frame_image->page=image->page;
+ if ((image->page.width != 0) && (image->page.height != 0))
+ {
+ frame_image->page.width+=frame_image->columns-image->columns;
+ frame_image->page.height+=frame_image->rows-image->rows;
+ }
+ /*
+ Initialize 3D effects color.
+ */
+ GetMagickPixelPacket(frame_image,&interior);
+ SetMagickPixelPacket(frame_image,&image->border_color,(IndexPacket *) NULL,
+ &interior);
+ GetMagickPixelPacket(frame_image,&matte);
+ matte.colorspace=RGBColorspace;
+ SetMagickPixelPacket(frame_image,&image->matte_color,(IndexPacket *) NULL,
+ &matte);
+ GetMagickPixelPacket(frame_image,&border);
+ border.colorspace=RGBColorspace;
+ SetMagickPixelPacket(frame_image,&image->border_color,(IndexPacket *) NULL,
+ &border);
+ GetMagickPixelPacket(frame_image,&accentuate);
+ accentuate.red=(MagickRealType) (QuantumScale*((QuantumRange-
+ AccentuateModulate)*matte.red+(QuantumRange*AccentuateModulate)));
+ accentuate.green=(MagickRealType) (QuantumScale*((QuantumRange-
+ AccentuateModulate)*matte.green+(QuantumRange*AccentuateModulate)));
+ accentuate.blue=(MagickRealType) (QuantumScale*((QuantumRange-
+ AccentuateModulate)*matte.blue+(QuantumRange*AccentuateModulate)));
+ accentuate.opacity=matte.opacity;
+ GetMagickPixelPacket(frame_image,&highlight);
+ highlight.red=(MagickRealType) (QuantumScale*((QuantumRange-
+ HighlightModulate)*matte.red+(QuantumRange*HighlightModulate)));
+ highlight.green=(MagickRealType) (QuantumScale*((QuantumRange-
+ HighlightModulate)*matte.green+(QuantumRange*HighlightModulate)));
+ highlight.blue=(MagickRealType) (QuantumScale*((QuantumRange-
+ HighlightModulate)*matte.blue+(QuantumRange*HighlightModulate)));
+ highlight.opacity=matte.opacity;
+ GetMagickPixelPacket(frame_image,&shadow);
+ shadow.red=QuantumScale*matte.red*ShadowModulate;
+ shadow.green=QuantumScale*matte.green*ShadowModulate;
+ shadow.blue=QuantumScale*matte.blue*ShadowModulate;
+ shadow.opacity=matte.opacity;
+ GetMagickPixelPacket(frame_image,&trough);
+ trough.red=QuantumScale*matte.red*TroughModulate;
+ trough.green=QuantumScale*matte.green*TroughModulate;
+ trough.blue=QuantumScale*matte.blue*TroughModulate;
+ trough.opacity=matte.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ ConvertRGBToCMYK(&interior);
+ ConvertRGBToCMYK(&matte);
+ ConvertRGBToCMYK(&border);
+ ConvertRGBToCMYK(&accentuate);
+ ConvertRGBToCMYK(&highlight);
+ ConvertRGBToCMYK(&shadow);
+ ConvertRGBToCMYK(&trough);
+ }
+ status=MagickTrue;
+ progress=0;
+ frame_view=AcquireCacheView(frame_image);
+ height=(unsigned long) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
+ frame_info->inner_bevel);
+ if (height != 0)
+ {
+ register IndexPacket
+ *__restrict frame_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Draw top of ornamental border.
+ */
+ q=QueueCacheViewAuthenticPixels(frame_view,0,0,frame_image->columns,
+ height,exception);
+ frame_indexes=GetCacheViewAuthenticIndexQueue(frame_view);
+ if (q != (PixelPacket *) NULL)
+ {
+ /*
+ Draw top of ornamental border.
+ */
+ for (y=0; y < (long) frame_info->outer_bevel; y++)
+ {
+ for (x=0; x < (long) (frame_image->columns-y); x++)
+ {
+ if (x < y)
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ else
+ SetPixelPacket(frame_image,&accentuate,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for ( ; x < (long) frame_image->columns; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ for (y=0; y < (long) (frame_info->y-bevel_width); y++)
+ {
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=frame_image->columns-2*frame_info->outer_bevel;
+ for (x=0; x < (long) width; x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ for (y=0; y < (long) frame_info->inner_bevel; y++)
+ {
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) (frame_info->x-bevel_width); x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=image->columns+((unsigned long) frame_info->inner_bevel << 1)-
+ y;
+ for (x=0; x < (long) width; x++)
+ {
+ if (x < y)
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ else
+ SetPixelPacket(frame_image,&trough,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for ( ; x < (long) (image->columns+2*frame_info->inner_bevel); x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=frame_info->width-frame_info->x-image->columns-bevel_width;
+ for (x=0; x < (long) width; x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ (void) SyncCacheViewAuthenticPixels(frame_view,exception);
+ }
+ }
+ /*
+ Draw sides of ornamental border.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict frame_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Initialize scanline with matte color.
+ */
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(frame_view,0,frame_info->y+y,
+ frame_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ frame_indexes=GetCacheViewAuthenticIndexQueue(frame_view);
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) (frame_info->x-bevel_width); x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->inner_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ /*
+ Set frame interior to interior color.
+ */
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetPixelPacket(frame_image,&interior,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->inner_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=frame_info->width-frame_info->x-image->columns-bevel_width;
+ for (x=0; x < (long) width; x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ if (SyncCacheViewAuthenticPixels(frame_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_FrameImage)
+#endif
+ proceed=SetImageProgress(image,FrameImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ height=(unsigned long) (frame_info->inner_bevel+frame_info->height-
+ frame_info->y-image->rows-bevel_width+frame_info->outer_bevel);
+ if (height != 0)
+ {
+ register IndexPacket
+ *__restrict frame_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Draw bottom of ornamental border.
+ */
+ q=QueueCacheViewAuthenticPixels(frame_view,0,(long) (frame_image->rows-
+ height),frame_image->columns,height,exception);
+ if (q != (PixelPacket *) NULL)
+ {
+ /*
+ Draw bottom of ornamental border.
+ */
+ frame_indexes=GetCacheViewAuthenticIndexQueue(frame_view);
+ for (y=frame_info->inner_bevel-1; y >= 0; y--)
+ {
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) (frame_info->x-bevel_width); x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < y; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for ( ; x < (long) (image->columns+2*frame_info->inner_bevel); x++)
+ {
+ if (x >= (long) (image->columns+2*frame_info->inner_bevel-y))
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ else
+ SetPixelPacket(frame_image,&accentuate,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=frame_info->width-frame_info->x-image->columns-bevel_width;
+ for (x=0; x < (long) width; x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ height=frame_info->height-frame_info->y-image->rows-bevel_width;
+ for (y=0; y < (long) height; y++)
+ {
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ width=frame_image->columns-2*frame_info->outer_bevel;
+ for (x=0; x < (long) width; x++)
+ {
+ SetPixelPacket(frame_image,&matte,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for (x=0; x < (long) frame_info->outer_bevel; x++)
+ {
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ for (y=frame_info->outer_bevel-1; y >= 0; y--)
+ {
+ for (x=0; x < y; x++)
+ {
+ SetPixelPacket(frame_image,&highlight,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ for ( ; x < (long) frame_image->columns; x++)
+ {
+ if (x >= (long) (frame_image->columns-y))
+ SetPixelPacket(frame_image,&shadow,q,frame_indexes);
+ else
+ SetPixelPacket(frame_image,&trough,q,frame_indexes);
+ q++;
+ frame_indexes++;
+ }
+ }
+ (void) SyncCacheViewAuthenticPixels(frame_view,exception);
+ }
+ }
+ frame_view=DestroyCacheView(frame_view);
+ x=(long) (frame_info->outer_bevel+(frame_info->x-bevel_width)+
+ frame_info->inner_bevel);
+ y=(long) (frame_info->outer_bevel+(frame_info->y-bevel_width)+
+ frame_info->inner_bevel);
+ (void) CompositeImage(frame_image,image->compose,image,x,y);
+ return(frame_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R a i s e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RaiseImage() creates a simulated three-dimensional button-like effect
+% by lightening and darkening the edges of the image. Members width and
+% height of raise_info define the width of the vertical and horizontal
+% edge of the effect.
+%
+% The format of the RaiseImage method is:
+%
+% MagickBooleanType RaiseImage(const Image *image,
+% const RectangleInfo *raise_info,const MagickBooleanType raise)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o raise_info: Define the width and height of the raise area.
+%
+% o raise: A value other than zero creates a 3-D raise effect,
+% otherwise it has a lowered effect.
+%
+*/
+MagickExport MagickBooleanType RaiseImage(Image *image,
+ const RectangleInfo *raise_info,const MagickBooleanType raise)
+{
+#define AccentuateFactor ScaleCharToQuantum(135)
+#define HighlightFactor ScaleCharToQuantum(190)
+#define ShadowFactor ScaleCharToQuantum(190)
+#define RaiseImageTag "Raise/Image"
+#define TroughFactor ScaleCharToQuantum(135)
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ Quantum
+ foreground,
+ background;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(raise_info != (RectangleInfo *) NULL);
+ if ((image->columns <= (raise_info->width << 1)) ||
+ (image->rows <= (raise_info->height << 1)))
+ ThrowBinaryException(OptionError,"ImageSizeMustExceedBevelWidth",
+ image->filename);
+ foreground=(Quantum) QuantumRange;
+ background=(Quantum) 0;
+ if (raise == MagickFalse)
+ {
+ foreground=(Quantum) 0;
+ background=(Quantum) QuantumRange;
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Raise image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) raise_info->height; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < y; x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q++;
+ }
+ for ( ; x < (long) (image->columns-y); x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*
+ AccentuateFactor+(MagickRealType) foreground*(QuantumRange-
+ AccentuateFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ AccentuateFactor+(MagickRealType) foreground*(QuantumRange-
+ AccentuateFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ AccentuateFactor+(MagickRealType) foreground*(QuantumRange-
+ AccentuateFactor)));
+ q++;
+ }
+ for ( ; x < (long) image->columns; x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*ShadowFactor+
+ (MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=(long) raise_info->height; y < (long) (image->rows-raise_info->height); y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) raise_info->width; x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q++;
+ }
+ for ( ; x < (long) (image->columns-raise_info->width); x++)
+ q++;
+ for ( ; x < (long) image->columns; x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*ShadowFactor+
+ (MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=(long) (image->rows-raise_info->height); y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) (image->rows-y); x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ HighlightFactor+(MagickRealType) foreground*(QuantumRange-
+ HighlightFactor)));
+ q++;
+ }
+ for ( ; x < (long) (image->columns-(image->rows-y)); x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*TroughFactor+
+ (MagickRealType) background*(QuantumRange-TroughFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ TroughFactor+(MagickRealType) background*(QuantumRange-TroughFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ TroughFactor+(MagickRealType) background*(QuantumRange-TroughFactor)));
+ q++;
+ }
+ for ( ; x < (long) image->columns; x++)
+ {
+ q->red=RoundToQuantum(QuantumScale*((MagickRealType) q->red*ShadowFactor+
+ (MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->green=RoundToQuantum(QuantumScale*((MagickRealType) q->green*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q->blue=RoundToQuantum(QuantumScale*((MagickRealType) q->blue*
+ ShadowFactor+(MagickRealType) background*(QuantumRange-ShadowFactor)));
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RaiseImage)
+#endif
+ proceed=SetImageProgress(image,RaiseImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
diff --git a/magick/decorate.h b/magick/decorate.h
new file mode 100644
index 0000000..194e274
--- /dev/null
+++ b/magick/decorate.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image decorate methods.
+*/
+#ifndef _MAGICKCORE_DECORATE_H
+#define _MAGICKCORE_DECORATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _FrameInfo
+{
+ unsigned long
+ width,
+ height;
+
+ long
+ x,
+ y,
+ inner_bevel,
+ outer_bevel;
+} FrameInfo;
+
+extern MagickExport Image
+ *BorderImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *FrameImage(const Image *,const FrameInfo *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ RaiseImage(Image *,const RectangleInfo *,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/delegate-private.h b/magick/delegate-private.h
new file mode 100644
index 0000000..e0994c0
--- /dev/null
+++ b/magick/delegate-private.h
@@ -0,0 +1,66 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore delegates private methods.
+*/
+#ifndef _MAGICKCORE_DELEGATE_PRIVATE_H
+#define _MAGICKCORE_DELEGATE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_GS_DELEGATE)
+#include "ghostscript/iapi.h"
+#include "ghostscript/ierrors.h"
+#endif
+
+#ifndef gs_main_instance_DEFINED
+# define gs_main_instance_DEFINED
+typedef struct gs_main_instance_s
+ gs_main_instance;
+#endif
+
+#if !defined(MagickDLLCall)
+# if defined(__WINDOWS__)
+# define MagickDLLCall __stdcall
+# else
+# define MagickDLLCall
+# endif
+#endif
+
+typedef struct _GhostscriptVectors
+{
+ int
+ (MagickDLLCall *exit)(gs_main_instance *);
+
+ int
+ (MagickDLLCall *init_with_args)(gs_main_instance *,int,char **);
+
+ int
+ (MagickDLLCall *new_instance)(gs_main_instance **,void *);
+
+ int
+ (MagickDLLCall *run_string)(gs_main_instance *,const char *,int,int *);
+
+ void
+ (MagickDLLCall *delete_instance)(gs_main_instance *);
+} GhostscriptVectors;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/delegate.c b/magick/delegate.c
new file mode 100644
index 0000000..b9f9a94
--- /dev/null
+++ b/magick/delegate.c
@@ -0,0 +1,1468 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% DDDD EEEEE L EEEEE GGGG AAA TTTTT EEEEE %
+% D D E L E G A A T E %
+% D D EEE L EEE G GG AAAAA T EEE %
+% D D E L E G G A A T E %
+% DDDD EEEEE LLLLL EEEEE GGG A A T EEEEE %
+% %
+% %
+% MagickCore Methods to Read/Write/Invoke Delegates %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The Delegates methods associate a set of commands with a particular
+% image format. ImageMagick uses delegates for formats it does not handle
+% directly.
+%
+% Thanks to Bob Friesenhahn for the initial inspiration and design of the
+% delegates methods.
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/policy.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define DelegateFilename "delegates.xml"
+
+/*
+ Declare delegate map.
+*/
+static const char
+ *DelegateMap = (const char *)
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ "<delegatemap>"
+ " <delegate decode=\"autotrace\" stealth=\"True\" command=\""autotrace" -output-format svg -output-file "%o" "%i"\"/>"
+ " <delegate decode=\"avi:decode\" stealth=\"True\" command=\""mplayer" "%i" -really-quiet -ao null -vo png:z=3\"/>"
+ " <delegate decode=\"browse\" stealth=\"True\" spawn=\"True\" command=\""xdg-open" http://www.imagemagick.org/\"/>"
+ " <delegate decode=\"cgm\" thread-support=\"False\" command=\""ralcgm" -d ps -oC < "%i" > "%o" 2> "%u"\"/>"
+ " <delegate decode=\"dng:decode\" command=\""/usr/bin/ufraw-batch" --silent --wb=camera --black-point=auto --exposure=auto --create-id=also --out-type=ppm16 "--output=%u.pnm" "%i"\"/>"
+ " <delegate decode=\"edit\" stealth=\"True\" command=\""xterm" -title "Edit Image Comment" -e vi "%o"\"/>"
+ " <delegate decode=\"eps\" encode=\"pdf\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"eps\" encode=\"ps\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pswrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"fig\" command=\""fig2dev" -L ps "%i" "%o"\"/>"
+ " <delegate decode=\"gplt\" command=\""echo" "set size 1.25,0.62 set terminal postscript portrait color solid; set output "%o"; load "%i"" > "%u";"gnuplot" "%u"\"/>"
+ " <delegate decode=\"hdr\" command=\""ra_pfm" "%i" "%o"\"/>"
+ " <delegate decode=\"hpg\" command=\""hp2xx" -q -m eps -f `basename "%o"` "%i" mv -f `basename "%o"` "%o"\"/>"
+ " <delegate decode=\"hpgl\" command=\"if [ -e hp2xx -o -e /usr/bin/hp2xx ]; then hp2xx -q -m eps -f `basename "%o"` "%i" mv -f `basename "%o"` "%o else echo "You need to install hp2xx to use HPGL files with ImageMagick." exit 1 fi\"/>"
+ " <delegate decode=\"htm\" command=\""html2ps" -U -o "%o" "%i"\"/>"
+ " <delegate decode=\"html\" command=\""html2ps" -U -o "%o" "%i"\"/>"
+ " <delegate decode=\"https\" command=\""wget" -q -O "%o" "https:%M"\"/>"
+ " <delegate decode=\"ilbm\" command=\""ilbmtoppm" "%i" > "%o"\"/>"
+ " <delegate decode=\"man\" command=\""groff" -man -Tps "%i" > "%o"\"/>"
+ " <delegate decode=\"mpeg:decode\" stealth=\"True\" command=\""ffmpeg" --i "%i" -vcodec pam -an -f rawvideo -y "%u0.pam" 2;> "%Z"\"/>"
+ " <delegate decode=\"null\" encode=\"mpeg:encode\" stealth=\"True\" command=\""ffmpeg" "%M%%d.jpg" "%u.%m" 2;> "%Z"\"/>"
+ " <delegate decode=\"pcl:color\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=ppmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
+ " <delegate decode=\"pcl:cmyk\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=bmpsep8" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
+ " <delegate decode=\"pcl:mono\" stealth=\"True\" command=\""pcl6" -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pbmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "%s"\"/>"
+ " <delegate decode=\"pdf\" encode=\"eps\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=epswrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"pdf\" encode=\"ps\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pswrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"pic\" command=\""ra_pfm" "%i" "%o"\"/>"
+ " <delegate decode=\"pnm\" encode=\"ilbm\" mode=\"encode\" command=\""ppmtoilbm" -24if "%i" > "%o"\"/>"
+ " <delegate decode=\"pnm\" encode=\"launch\" mode=\"encode\" command=\""gimp" "%i"\"/>"
+ " <delegate decode=\"pov\" command=\""povray" "+i"%i"" -D0 +o"%o" +fn%q +w%w +h%h +a -q9 -kfi"%s" -kff"%n" "convert" -concatenate "%o*.png" "%o"\"/>"
+ " <delegate decode=\"ps\" encode=\"eps\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=epswrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"ps\" encode=\"pdf\" mode=\"bi\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pdfwrite" "-sOutputFile=%o" "-f%i"\"/>"
+ " <delegate decode=\"ps\" encode=\"print\" mode=\"encode\" command=\"lpr "%i"\"/>"
+ " <delegate decode=\"ps:alpha\" stealth=\"True\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pngalpha" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
+ " <delegate decode=\"ps:bbox\" stealth=\"True\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=bbox" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
+ " <delegate decode=\"ps:cmyk\" stealth=\"True\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pam" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
+ " <delegate decode=\"ps:color\" stealth=\"True\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pnmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
+ " <delegate decode=\"ps:mono\" stealth=\"True\" command=\""gs" -q -dQUIET -dSAFER -dPARANOIDSAFE -dBATCH -dNOPAUSE -dNOPROMPT -dMaxBitmap=500000000 -dAlignToPixels=0 -dGridFitTT=0 "-sDEVICE=pnmraw" -dTextAlphaBits=%u -dGraphicsAlphaBits=%u "-r%s" %s "-sOutputFile=%s" "-f%s" "-f%s"\"/>"
+ " <delegate decode=\"rad\" command=\""ra_pfm" "%i" "%o"\"/>"
+ " <delegate decode=\"rgba\" encode=\"rle\" mode=\"encode\" command=\""rawtorle" -o "%o" -v "%i"\"/>"
+ " <delegate decode=\"scan\" command=\""scanimage" -d "%i" > "%o"\"/>"
+ " <delegate encode=\"show\" stealth=\"True\" spawn=\"True\" command=\""/usr/local/bin/display" -immutable -delay 0 -window-group %g -title "%l of %f" "temporary:%i"\"/>"
+ " <delegate decode=\"shtml\" command=\""html2ps" -U -o "%o" "%i"\"/>"
+ " <delegate decode=\"svg\" command=\""wmf2eps" -o "%o" "%i"\"/>"
+ " <delegate decode=\"txt\" encode=\"ps\" mode=\"bi\" command=\""enscript" -o "%o" "%i"\"/>"
+ " <delegate encode=\"win\" stealth=\"True\" spawn=\"True\" command=\""/usr/local/bin/display" -immutable -delay 0 -window-group %g -title "%l of %f" "temporary:%i"\"/>"
+ " <delegate decode=\"wmf\" command=\""wmf2eps" -o "%o" "%i"\"/>"
+ "</delegatemap>";
+
+/*
+ Global declaractions.
+*/
+static LinkedListInfo
+ *delegate_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *delegate_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_delegate = MagickFalse;
+
+/*
+ Forward declaractions.
+*/
+static MagickBooleanType
+ InitializeDelegateList(ExceptionInfo *),
+ LoadDelegateLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y D e l e g a t e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyDelegateList() deallocates memory associated with the delegates list.
+%
+% The format of the DestroyDelegateList method is:
+%
+% DestroyDelegateList(void)
+%
+*/
+
+static void *DestroyDelegate(void *delegate_info)
+{
+ register DelegateInfo
+ *p;
+
+ p=(DelegateInfo *) delegate_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->decode != (char *) NULL)
+ p->decode=DestroyString(p->decode);
+ if (p->encode != (char *) NULL)
+ p->encode=DestroyString(p->encode);
+ if (p->commands != (char *) NULL)
+ p->commands=DestroyString(p->commands);
+ p=(DelegateInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+
+MagickExport void DestroyDelegateList(void)
+{
+ AcquireSemaphoreInfo(&delegate_semaphore);
+ if (delegate_list != (LinkedListInfo *) NULL)
+ delegate_list=DestroyLinkedList(delegate_list,DestroyDelegate);
+ instantiate_delegate=MagickFalse;
+ RelinquishSemaphoreInfo(delegate_semaphore);
+ DestroySemaphoreInfo(&delegate_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateCommand() replaces any embedded formatting characters with the
+% appropriate image attribute and returns the resulting command.
+%
+% The format of the GetDelegateCommand method is:
+%
+% char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
+% const char *decode,const char *encode,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o command: Method GetDelegateCommand returns the command associated
+% with specified delegate tag.
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o decode: Specifies the decode delegate we are searching for as a
+% character string.
+%
+% o encode: Specifies the encode delegate we are searching for as a
+% character string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport char *GetDelegateCommand(const ImageInfo *image_info,Image *image,
+ const char *decode,const char *encode,ExceptionInfo *exception)
+{
+ char
+ *command,
+ **commands;
+
+ const DelegateInfo
+ *delegate_info;
+
+ register long
+ i;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ delegate_info=GetDelegateInfo(decode,encode,exception);
+ if (delegate_info == (const DelegateInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
+ "NoTagFound","`%s'",decode ? decode : encode);
+ return((char *) NULL);
+ }
+ commands=StringToList(delegate_info->commands);
+ if (commands == (char **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ decode ? decode : encode);
+ return((char *) NULL);
+ }
+ command=InterpretImageProperties(image_info,image,commands[0]);
+ if (command == (char *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
+ "MemoryAllocationFailed","`%s'",commands[0]);
+ /*
+ Relinquish resources.
+ */
+ for (i=0; commands[i] != (char *) NULL; i++)
+ commands[i]=DestroyString(commands[i]);
+ commands=(char **) RelinquishMagickMemory(commands);
+ return(command);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e C o m m a n d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateCommands() returns the commands associated with a delegate.
+%
+% The format of the GetDelegateCommands method is:
+%
+% const char *GetDelegateCommands(const DelegateInfo *delegate_info)
+%
+% A description of each parameter follows:
+%
+% o delegate_info: The delegate info.
+%
+*/
+MagickExport const char *GetDelegateCommands(const DelegateInfo *delegate_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(delegate_info != (DelegateInfo *) NULL);
+ assert(delegate_info->signature == MagickSignature);
+ return(delegate_info->commands);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateInfo() returns any delegates associated with the specified tag.
+%
+% The format of the GetDelegateInfo method is:
+%
+% const DelegateInfo *GetDelegateInfo(const char *decode,
+% const char *encode,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o decode: Specifies the decode delegate we are searching for as a
+% character string.
+%
+% o encode: Specifies the encode delegate we are searching for as a
+% character string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const DelegateInfo *GetDelegateInfo(const char *decode,
+ const char *encode,ExceptionInfo *exception)
+{
+ register const DelegateInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((delegate_list == (LinkedListInfo *) NULL) ||
+ (instantiate_delegate == MagickFalse))
+ if (InitializeDelegateList(exception) == MagickFalse)
+ return((const DelegateInfo *) NULL);
+ if ((delegate_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(delegate_list) != MagickFalse))
+ return((const DelegateInfo *) NULL);
+ if ((LocaleCompare(decode,"*") == 0) && (LocaleCompare(encode,"*") == 0))
+ return((const DelegateInfo *) GetValueFromLinkedList(delegate_list,0));
+ /*
+ Search for named delegate.
+ */
+ AcquireSemaphoreInfo(&delegate_semaphore);
+ ResetLinkedListIterator(delegate_list);
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ while (p != (const DelegateInfo *) NULL)
+ {
+ if (p->mode > 0)
+ {
+ if (LocaleCompare(p->decode,decode) == 0)
+ break;
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ continue;
+ }
+ if (p->mode < 0)
+ {
+ if (LocaleCompare(p->encode,encode) == 0)
+ break;
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ continue;
+ }
+ if (LocaleCompare(decode,p->decode) == 0)
+ if (LocaleCompare(encode,p->encode) == 0)
+ break;
+ if (LocaleCompare(decode,"*") == 0)
+ if (LocaleCompare(encode,p->encode) == 0)
+ break;
+ if (LocaleCompare(decode,p->decode) == 0)
+ if (LocaleCompare(encode,"*") == 0)
+ break;
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ }
+ if (p != (const DelegateInfo *) NULL)
+ (void) InsertValueInLinkedList(delegate_list,0,
+ RemoveElementByValueFromLinkedList(delegate_list,p));
+ RelinquishSemaphoreInfo(delegate_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateInfoList() returns any delegates that match the specified pattern.
+%
+% The delegate of the GetDelegateInfoList function is:
+%
+% const DelegateInfo **GetDelegateInfoList(const char *pattern,
+% unsigned long *number_delegates,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_delegates: This integer returns the number of delegates in the
+% list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int DelegateInfoCompare(const void *x,const void *y)
+{
+ const DelegateInfo
+ **p,
+ **q;
+
+ p=(const DelegateInfo **) x,
+ q=(const DelegateInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ {
+ if ((*p)->decode == (char *) NULL)
+ if (((*p)->encode != (char *) NULL) &&
+ ((*q)->encode != (char *) NULL))
+ return(strcmp((*p)->encode,(*q)->encode));
+ if (((*p)->decode != (char *) NULL) &&
+ ((*q)->decode != (char *) NULL))
+ return(strcmp((*p)->decode,(*q)->decode));
+ }
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const DelegateInfo **GetDelegateInfoList(const char *pattern,
+ unsigned long *number_delegates,ExceptionInfo *exception)
+{
+ const DelegateInfo
+ **delegates;
+
+ register const DelegateInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate delegate list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_delegates != (unsigned long *) NULL);
+ *number_delegates=0;
+ p=GetDelegateInfo("*","*",exception);
+ if (p == (const DelegateInfo *) NULL)
+ return((const DelegateInfo **) NULL);
+ delegates=(const DelegateInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(delegate_list)+1UL,sizeof(*delegates));
+ if (delegates == (const DelegateInfo **) NULL)
+ return((const DelegateInfo **) NULL);
+ /*
+ Generate delegate list.
+ */
+ AcquireSemaphoreInfo(&delegate_semaphore);
+ ResetLinkedListIterator(delegate_list);
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ for (i=0; p != (const DelegateInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ ((GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse) ||
+ (GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse)))
+ delegates[i++]=p;
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ }
+ RelinquishSemaphoreInfo(delegate_semaphore);
+ qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateInfoCompare);
+ delegates[i]=(DelegateInfo *) NULL;
+ *number_delegates=(unsigned long) i;
+ return(delegates);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateList() returns any image format delegates that match the
+% specified pattern.
+%
+% The format of the GetDelegateList function is:
+%
+% char **GetDelegateList(const char *pattern,
+% unsigned long *number_delegates,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_delegates: This integer returns the number of delegates
+% in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int DelegateCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetDelegateList(const char *pattern,
+ unsigned long *number_delegates,ExceptionInfo *exception)
+{
+ char
+ **delegates;
+
+ register const DelegateInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate delegate list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_delegates != (unsigned long *) NULL);
+ *number_delegates=0;
+ p=GetDelegateInfo("*","*",exception);
+ if (p == (const DelegateInfo *) NULL)
+ return((char **) NULL);
+ delegates=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(delegate_list)+1UL,sizeof(*delegates));
+ if (delegates == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&delegate_semaphore);
+ ResetLinkedListIterator(delegate_list);
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ for (i=0; p != (const DelegateInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->decode,pattern,MagickFalse) != MagickFalse))
+ delegates[i++]=ConstantString(p->decode);
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->encode,pattern,MagickFalse) != MagickFalse))
+ delegates[i++]=ConstantString(p->encode);
+ p=(const DelegateInfo *) GetNextValueInLinkedList(delegate_list);
+ }
+ RelinquishSemaphoreInfo(delegate_semaphore);
+ qsort((void *) delegates,(size_t) i,sizeof(*delegates),DelegateCompare);
+ delegates[i]=(char *) NULL;
+ *number_delegates=(unsigned long) i;
+ return(delegates);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t D e l e g a t e M o d e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateMode() returns the mode of the delegate.
+%
+% The format of the GetDelegateMode method is:
+%
+% long GetDelegateMode(const DelegateInfo *delegate_info)
+%
+% A description of each parameter follows:
+%
+% o delegate_info: The delegate info.
+%
+*/
+MagickExport long GetDelegateMode(const DelegateInfo *delegate_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(delegate_info != (DelegateInfo *) NULL);
+ assert(delegate_info->signature == MagickSignature);
+ return(delegate_info->mode);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t D e l e g a t e T h r e a d S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDelegateThreadSupport() returns MagickTrue if the delegate supports
+% threads.
+%
+% The format of the GetDelegateThreadSupport method is:
+%
+% MagickBooleanType GetDelegateThreadSupport(
+% const DelegateInfo *delegate_info)
+%
+% A description of each parameter follows:
+%
+% o delegate_info: The delegate info.
+%
+*/
+MagickExport MagickBooleanType GetDelegateThreadSupport(
+ const DelegateInfo *delegate_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(delegate_info != (DelegateInfo *) NULL);
+ assert(delegate_info->signature == MagickSignature);
+ return(delegate_info->thread_support);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e D e l e g a t e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeDelegateList() initializes the delegate list.
+%
+% The format of the InitializeDelegateList method is:
+%
+% MagickBooleanType InitializeDelegateList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeDelegateList(ExceptionInfo *exception)
+{
+ if ((delegate_list == (LinkedListInfo *) NULL) &&
+ (instantiate_delegate == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&delegate_semaphore);
+ if ((delegate_list == (LinkedListInfo *) NULL) &&
+ (instantiate_delegate == MagickFalse))
+ {
+ (void) LoadDelegateLists(DelegateFilename,exception);
+ instantiate_delegate=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(delegate_semaphore);
+ }
+ return(delegate_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n v o k e D e l e g a t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InvokeDelegate replaces any embedded formatting characters with the
+% appropriate image attribute and executes the resulting command. MagickFalse
+% is returned if the commands execute with success otherwise MagickTrue.
+%
+% The format of the InvokeDelegate method is:
+%
+% MagickBooleanType InvokeDelegate(ImageInfo *image_info,Image *image,
+% const char *decode,const char *encode,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the imageInfo.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static MagickBooleanType CopyDelegateFile(const char *source,
+ const char *destination)
+{
+ int
+ destination_file,
+ source_file;
+
+ MagickBooleanType
+ status;
+
+ register size_t
+ i;
+
+ size_t
+ length,
+ quantum;
+
+ ssize_t
+ count;
+
+ struct stat
+ attributes;
+
+ unsigned char
+ *buffer;
+
+ /*
+ Return if destination file already exists and is not empty.
+ */
+ assert(source != (const char *) NULL);
+ assert(destination != (char *) NULL);
+ status=GetPathAttributes(destination,&attributes);
+ if ((status != MagickFalse) && (attributes.st_size != 0))
+ return(MagickTrue);
+ /*
+ Copy source file to destination.
+ */
+ destination_file=open(destination,O_WRONLY | O_BINARY | O_CREAT,S_MODE);
+ if (destination_file == -1)
+ return(MagickFalse);
+ source_file=open(source,O_RDONLY | O_BINARY);
+ if (source_file == -1)
+ {
+ (void) close(destination_file);
+ return(MagickFalse);
+ }
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
+ quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
+ buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
+ if (buffer == (unsigned char *) NULL)
+ {
+ (void) close(source_file);
+ (void) close(destination_file);
+ return(MagickFalse);
+ }
+ length=0;
+ for (i=0; ; i+=count)
+ {
+ count=(ssize_t) read(source_file,buffer,quantum);
+ if (count <= 0)
+ break;
+ length=(size_t) count;
+ count=(ssize_t) write(destination_file,buffer,length);
+ if ((size_t) count != length)
+ break;
+ }
+ (void) close(destination_file);
+ (void) close(source_file);
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ return(i != 0 ? MagickTrue : MagickFalse);
+}
+
+MagickExport MagickBooleanType InvokeDelegate(ImageInfo *image_info,
+ Image *image,const char *decode,const char *encode,ExceptionInfo *exception)
+{
+ char
+ *command,
+ **commands,
+ input_filename[MaxTextExtent],
+ output_filename[MaxTextExtent];
+
+ const DelegateInfo
+ *delegate_info;
+
+ MagickBooleanType
+ status,
+ temporary;
+
+ register long
+ i;
+
+ PolicyRights
+ rights;
+
+ /*
+ Get delegate.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ rights=ExecutePolicyRights;
+ if (IsRightsAuthorized(DelegatePolicyDomain,rights,decode) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",decode);
+ return(MagickFalse);
+ }
+ if (IsRightsAuthorized(DelegatePolicyDomain,rights,encode) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",encode);
+ return(MagickFalse);
+ }
+ temporary=(*image->filename == '\0') ? MagickTrue : MagickFalse;
+ if (temporary != MagickFalse)
+ if (AcquireUniqueFilename(image->filename) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image->filename);
+ return(MagickFalse);
+ }
+ delegate_info=GetDelegateInfo(decode,encode,exception);
+ if (delegate_info == (DelegateInfo *) NULL)
+ {
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
+ "NoTagFound","`%s'",decode ? decode : encode);
+ return(MagickFalse);
+ }
+ if (*image_info->filename == '\0')
+ {
+ if (AcquireUniqueFilename(image_info->filename) == MagickFalse)
+ {
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image_info->filename);
+ return(MagickFalse);
+ }
+ image_info->temporary=MagickTrue;
+ }
+ if ((delegate_info->mode != 0) &&
+ (((decode != (const char *) NULL) &&
+ (delegate_info->encode != (char *) NULL)) ||
+ ((encode != (const char *) NULL) &&
+ (delegate_info->decode != (char *) NULL))))
+ {
+ char
+ *magick;
+
+ ImageInfo
+ *clone_info;
+
+ register Image
+ *p;
+
+ /*
+ Delegate requires a particular image format.
+ */
+ if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image_info->unique);
+ return(MagickFalse);
+ }
+ if (AcquireUniqueFilename(image_info->zero) == MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(image_info->zero);
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image_info->zero);
+ return(MagickFalse);
+ }
+ magick=InterpretImageProperties(image_info,image,decode != (char *) NULL ?
+ delegate_info->encode : delegate_info->decode);
+ if (magick == (char *) NULL)
+ {
+ (void) RelinquishUniqueFileResource(image_info->unique);
+ (void) RelinquishUniqueFileResource(image_info->zero);
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
+ return(MagickFalse);
+ }
+ LocaleUpper(magick);
+ clone_info=CloneImageInfo(image_info);
+ (void) CopyMagickString((char *) clone_info->magick,magick,
+ MaxTextExtent);
+ if (LocaleCompare(magick,"NULL") != 0)
+ (void) CopyMagickString(image->magick,magick,MaxTextExtent);
+ magick=DestroyString(magick);
+ (void) FormatMagickString(clone_info->filename,MaxTextExtent,"%s:",
+ delegate_info->decode);
+ (void) SetImageInfo(clone_info,MagickTrue,exception);
+ (void) CopyMagickString(clone_info->filename,image_info->filename,
+ MaxTextExtent);
+ (void) CopyMagickString(image_info->filename,image->filename,
+ MaxTextExtent);
+ for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
+ {
+ (void) FormatMagickString(p->filename,MaxTextExtent,"%s:%s",
+ delegate_info->decode,clone_info->filename);
+ status=WriteImage(clone_info,p);
+ if (status == MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(image_info->unique);
+ (void) RelinquishUniqueFileResource(image_info->zero);
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ clone_info=DestroyImageInfo(clone_info);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ DelegateError,"DelegateFailed","`%s'",decode ? decode : encode);
+ return(MagickFalse);
+ }
+ if (clone_info->adjoin != MagickFalse)
+ break;
+ }
+ (void) RelinquishUniqueFileResource(image_info->unique);
+ (void) RelinquishUniqueFileResource(image_info->zero);
+ clone_info=DestroyImageInfo(clone_info);
+ }
+ /*
+ Invoke delegate.
+ */
+ commands=StringToList(delegate_info->commands);
+ if (commands == (char **) NULL)
+ {
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ decode ? decode : encode);
+ return(MagickFalse);
+ }
+ command=(char *) NULL;
+ status=MagickFalse;
+ (void) CopyMagickString(output_filename,image_info->filename,MaxTextExtent);
+ (void) CopyMagickString(input_filename,image->filename,MaxTextExtent);
+ for (i=0; commands[i] != (char *) NULL; i++)
+ {
+ status=AcquireUniqueSymbolicLink(output_filename,image_info->filename);
+ if (AcquireUniqueFilename(image_info->unique) == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image_info->unique);
+ break;
+ }
+ if (AcquireUniqueFilename(image_info->zero) == MagickFalse)
+ {
+ (void) RelinquishUniqueFileResource(image_info->unique);
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",image_info->zero);
+ break;
+ }
+ if (LocaleCompare(decode,"SCAN") != 0)
+ {
+ status=AcquireUniqueSymbolicLink(input_filename,image->filename);
+ if (status == MagickFalse)
+ {
+ ThrowFileException(exception,FileOpenError,
+ "UnableToCreateTemporaryFile",input_filename);
+ break;
+ }
+ }
+ status=MagickFalse;
+ command=InterpretImageProperties(image_info,image,commands[i]);
+ if (command != (char *) NULL)
+ {
+ /*
+ Execute delegate.
+ */
+ if (delegate_info->spawn != MagickFalse)
+ (void) ConcatenateString(&command," &");
+ status=SystemCommand(image_info->verbose,command) != 0 ? MagickTrue :
+ MagickFalse;
+ if (delegate_info->spawn != MagickFalse)
+ (void) sleep(2);
+ command=DestroyString(command);
+ }
+ if (LocaleCompare(decode,"SCAN") != 0)
+ {
+ if (CopyDelegateFile(image->filename,input_filename) == MagickFalse)
+ (void) RelinquishUniqueFileResource(input_filename);
+ }
+ if (CopyDelegateFile(image_info->filename,output_filename) == MagickFalse)
+ (void) RelinquishUniqueFileResource(output_filename);
+ if (image_info->temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image_info->filename);
+ (void) RelinquishUniqueFileResource(image_info->unique);
+ (void) RelinquishUniqueFileResource(image_info->zero);
+ (void) RelinquishUniqueFileResource(image_info->filename);
+ (void) RelinquishUniqueFileResource(image->filename);
+ if (status != MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
+ "DelegateFailed","`%s'",commands[i]);
+ break;
+ }
+ commands[i]=DestroyString(commands[i]);
+ }
+ (void) CopyMagickString(image_info->filename,output_filename,MaxTextExtent);
+ (void) CopyMagickString(image->filename,input_filename,MaxTextExtent);
+ /*
+ Relinquish resources.
+ */
+ for ( ; commands[i] != (char *) NULL; i++)
+ commands[i]=DestroyString(commands[i]);
+ commands=(char **) RelinquishMagickMemory(commands);
+ if (temporary != MagickFalse)
+ (void) RelinquishUniqueFileResource(image->filename);
+ return(status == MagickFalse ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t D e l e g a t e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListDelegateInfo() lists the image formats to a file.
+%
+% The format of the ListDelegateInfo method is:
+%
+% MagickBooleanType ListDelegateInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListDelegateInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const DelegateInfo
+ **delegate_info;
+
+ char
+ **commands,
+ delegate[MaxTextExtent];
+
+ const char
+ *path;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_delegates;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ delegate_info=GetDelegateInfoList("*",&number_delegates,exception);
+ if (delegate_info == (const DelegateInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_delegates; i++)
+ {
+ if (delegate_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,delegate_info[i]->path) != 0))
+ {
+ if (delegate_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",delegate_info[i]->path);
+ (void) fprintf(file,"Delegate Command\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=delegate_info[i]->path;
+ *delegate='\0';
+ if (delegate_info[i]->encode != (char *) NULL)
+ (void) CopyMagickString(delegate,delegate_info[i]->encode,MaxTextExtent);
+ (void) ConcatenateMagickString(delegate," ",MaxTextExtent);
+ delegate[8]='\0';
+ commands=StringToList(delegate_info[i]->commands);
+ if (commands == (char **) NULL)
+ continue;
+ (void) fprintf(file,"%11s%c=%c%s ",delegate_info[i]->decode ?
+ delegate_info[i]->decode : "",delegate_info[i]->mode <= 0 ? '<' : ' ',
+ delegate_info[i]->mode >= 0 ? '>' : ' ',delegate);
+ StripString(commands[0]);
+ (void) fprintf(file,"\"%s\"\n",commands[0]);
+ for (j=1; commands[j] != (char *) NULL; j++)
+ {
+ StripString(commands[j]);
+ (void) fprintf(file," \"%s\"\n",commands[j]);
+ }
+ for (j=0; commands[j] != (char *) NULL; j++)
+ commands[j]=DestroyString(commands[j]);
+ commands=(char **) RelinquishMagickMemory(commands);
+ }
+ (void) fflush(file);
+ delegate_info=(const DelegateInfo **)
+ RelinquishMagickMemory((void *) delegate_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d D e l e g a t e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadDelegateList() loads the delegate configuration file which provides a
+% mapping between delegate attributes and a delegate name.
+%
+% The format of the LoadDelegateList method is:
+%
+% MagickBooleanType LoadDelegateList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The delegate list in XML format.
+%
+% o filename: The delegate list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadDelegateList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ DelegateInfo
+ *delegate_info;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Load the delegate map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading delegate configuration file \"%s\" ...",filename);
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (delegate_list == (LinkedListInfo *) NULL)
+ {
+ delegate_list=NewLinkedList(0);
+ if (delegate_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ delegate_info=(DelegateInfo *) NULL;
+ token=AcquireString(xml);
+ for (q=(const char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadDelegateList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<delegate") == 0)
+ {
+ /*
+ Delegate element.
+ */
+ delegate_info=(DelegateInfo *) AcquireMagickMemory(
+ sizeof(*delegate_info));
+ if (delegate_info == (DelegateInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(delegate_info,0,sizeof(*delegate_info));
+ delegate_info->path=ConstantString(filename);
+ delegate_info->signature=MagickSignature;
+ continue;
+ }
+ if (delegate_info == (DelegateInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AppendValueToLinkedList(delegate_list,delegate_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ delegate_info->commands);
+ delegate_info=(DelegateInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'C':
+ case 'c':
+ {
+ if (LocaleCompare((char *) keyword,"command") == 0)
+ {
+ char
+ *commands;
+
+ commands=AcquireString(token);
+#if defined(__WINDOWS__)
+ if (strchr(commands,'@') != (char *) NULL)
+ {
+ char
+ path[MaxTextExtent];
+
+ NTGhostscriptEXE(path,MaxTextExtent);
+ (void) SubstituteString((char **) &commands,"@PSDelegate@",
+ path);
+ (void) SubstituteString((char **) &commands,"\\","/");
+ }
+#endif
+ (void) SubstituteString((char **) &commands,"&","&");
+ (void) SubstituteString((char **) &commands,""","\"");
+ (void) SubstituteString((char **) &commands,">",">");
+ (void) SubstituteString((char **) &commands,"<","<");
+ delegate_info->commands=commands;
+ break;
+ }
+ break;
+ }
+ case 'D':
+ case 'd':
+ {
+ if (LocaleCompare((char *) keyword,"decode") == 0)
+ {
+ delegate_info->decode=ConstantString(token);
+ delegate_info->mode=1;
+ break;
+ }
+ break;
+ }
+ case 'E':
+ case 'e':
+ {
+ if (LocaleCompare((char *) keyword,"encode") == 0)
+ {
+ delegate_info->encode=ConstantString(token);
+ delegate_info->mode=(-1);
+ break;
+ }
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if (LocaleCompare((char *) keyword,"mode") == 0)
+ {
+ delegate_info->mode=1;
+ if (LocaleCompare(token,"bi") == 0)
+ delegate_info->mode=0;
+ else
+ if (LocaleCompare(token,"encode") == 0)
+ delegate_info->mode=(-1);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"spawn") == 0)
+ {
+ delegate_info->spawn=IsMagickTrue(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ delegate_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if (LocaleCompare((char *) keyword,"thread-support") == 0)
+ {
+ delegate_info->thread_support=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d D e l e g a t e L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadDelegateList() loads one or more delegate configuration file which
+% provides a mapping between delegate attributes and a delegate name.
+%
+% The format of the LoadDelegateLists method is:
+%
+% MagickBooleanType LoadDelegateLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadDelegateLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadDelegateList(DelegateMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadDelegateList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((delegate_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(delegate_list) != MagickFalse))
+ status|=LoadDelegateList(DelegateMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/delegate.h b/magick/delegate.h
new file mode 100644
index 0000000..16061fb
--- /dev/null
+++ b/magick/delegate.h
@@ -0,0 +1,76 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore delegates methods.
+*/
+#ifndef _MAGICKCORE_DELEGATE_H
+#define _MAGICKCORE_DELEGATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _DelegateInfo
+{
+ char
+ *path,
+ *decode,
+ *encode,
+ *commands;
+
+ long
+ mode;
+
+ MagickBooleanType
+ thread_support,
+ spawn,
+ stealth;
+
+ struct _DelegateInfo
+ *previous,
+ *next; /* deprecated, use GetDelegateInfoList() */
+
+ unsigned long
+ signature;
+} DelegateInfo;
+
+extern MagickExport char
+ *GetDelegateCommand(const ImageInfo *,Image *,const char *,const char *,
+ ExceptionInfo *),
+ **GetDelegateList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const char
+ *GetDelegateCommands(const DelegateInfo *);
+
+extern MagickExport const DelegateInfo
+ *GetDelegateInfo(const char *,const char *,ExceptionInfo *exception),
+ **GetDelegateInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport long
+ GetDelegateMode(const DelegateInfo *);
+
+extern MagickExport MagickBooleanType
+ GetDelegateThreadSupport(const DelegateInfo *),
+ InvokeDelegate(ImageInfo *,Image *,const char *,const char *,ExceptionInfo *),
+ ListDelegateInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyDelegateList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/deprecate.c b/magick/deprecate.c
new file mode 100644
index 0000000..7874b71
--- /dev/null
+++ b/magick/deprecate.c
@@ -0,0 +1,6344 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% DDDD EEEEE PPPP RRRR EEEEE CCCC AAA TTTTT EEEEE %
+% D D E P P R R E C A A T E %
+% D D EEE PPPPP RRRR EEE C AAAAA T EEE %
+% D D E P R R E C A A T E %
+% DDDD EEEEE P R R EEEEE CCCC A A T EEEEE %
+% %
+% %
+% MagickCore Deprecated Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/deprecate.h"
+#include "magick/draw.h"
+#include "magick/draw-private.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/identify.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/magick.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/paint.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/quantize.h"
+#include "magick/random_.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/segment.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/threshold.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+
+#if !defined(MAGICKCORE_EXCLUDE_DEPRECATED)
+/*
+ Global declarations.
+*/
+static MonitorHandler
+ monitor_handler = (MonitorHandler) NULL;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e C a c h e V i e w I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireCacheViewIndexes() returns the indexes associated with the specified
+% view.
+%
+% The format of the AcquireCacheViewIndexes method is:
+%
+% const IndexPacket *AcquireCacheViewIndexes(const CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport const IndexPacket *AcquireCacheViewIndexes(
+ const CacheView *cache_view)
+{
+ return(GetCacheViewVirtualIndexQueue(cache_view));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e C a c h e V i e w P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireCacheViewPixels() gets pixels from the in-memory or disk pixel cache
+% as defined by the geometry parameters. A pointer to the pixels is returned
+% if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the AcquireCacheViewPixels method is:
+%
+% const PixelPacket *AcquireCacheViewPixels(const CacheView *cache_view,
+% const long x,const long y,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const PixelPacket *AcquireCacheViewPixels(
+ const CacheView *cache_view,const long x,const long y,
+ const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
+{
+ return(GetCacheViewVirtualPixels(cache_view,x,y,columns,rows,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireImagePixels() returns an immutable pixel region. If the
+% region is successfully accessed, a pointer to it is returned, otherwise
+% NULL is returned. The returned pointer may point to a temporary working
+% copy of the pixels or it may point to the original pixels in memory.
+% Performance is maximized if the selected region is part of one row, or one
+% or more full rows, since there is opportunity to access the pixels in-place
+% (without a copy) if the image is in RAM, or in a memory-mapped file. The
+% returned pointer should *never* be deallocated by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or the storage class is PseudoClass,
+% call GetAuthenticIndexQueue() after invoking GetAuthenticPixels() to access the
+% black color component or to obtain the colormap indexes (of type IndexPacket)
+% corresponding to the region.
+%
+% If you plan to modify the pixels, use GetAuthenticPixels() instead.
+%
+% Note, the AcquireImagePixels() and GetAuthenticPixels() methods are not
+% thread-safe. In a threaded environment, use GetCacheViewVirtualPixels() or
+% GetCacheViewAuthenticPixels() instead.
+%
+% The format of the AcquireImagePixels() method is:
+%
+% const PixelPacket *AcquireImagePixels(const Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const PixelPacket *AcquireImagePixels(const Image *image,
+ const long x,const long y,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+ return(GetVirtualPixels(image,x,y,columns,rows,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireIndexes() returns the black channel or the colormap indexes
+% associated with the last call to QueueAuthenticPixels() or GetVirtualPixels().
+% NULL is returned if the black channel or colormap indexes are not available.
+%
+% The format of the AcquireIndexes() method is:
+%
+% const IndexPacket *AcquireIndexes(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o indexes: AcquireIndexes() returns the indexes associated with the last
+% call to QueueAuthenticPixels() or GetVirtualPixels().
+%
+% o image: the image.
+%
+*/
+MagickExport const IndexPacket *AcquireIndexes(const Image *image)
+{
+ return(GetVirtualIndexQueue(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireMemory() returns a pointer to a block of memory at least size bytes
+% suitably aligned for any use.
+%
+% The format of the AcquireMemory method is:
+%
+% void *AcquireMemory(const size_t size)
+%
+% A description of each parameter follows:
+%
+% o size: the size of the memory in bytes to allocate.
+%
+*/
+MagickExport void *AcquireMemory(const size_t size)
+{
+ void
+ *allocation;
+
+ assert(size != 0);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ allocation=malloc(size);
+ return(allocation);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e O n e C a c h e V i e w P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireOneCacheViewPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs. If
+% you plan to modify the pixel, use GetOneCacheViewAuthenticPixel() instead.
+%
+% The format of the AcquireOneCacheViewPixel method is:
+%
+% MagickBooleanType AcquireOneCacheViewPixel(const CacheView *cache_view,
+% const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y: These values define the offset of the pixel.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType AcquireOneCacheViewPixel(
+ const CacheView *cache_view,const long x,const long y,PixelPacket *pixel,
+ ExceptionInfo *exception)
+{
+ return(GetOneCacheViewVirtualPixel(cache_view,x,y,pixel,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e O n e C a c h e V i e w V i r t u a l P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireOneCacheViewVirtualPixel() returns a single pixel at the specified
+% (x,y) location. The image background color is returned if an error occurs.
+% If you plan to modify the pixel, use GetOneCacheViewAuthenticPixel() instead.
+%
+% The format of the AcquireOneCacheViewPixel method is:
+%
+% MagickBooleanType AcquireOneCacheViewVirtualPixel(
+% const CacheView *cache_view,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y: These values define the offset of the pixel.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType AcquireOneCacheViewVirtualPixel(
+ const CacheView *cache_view,const VirtualPixelMethod virtual_pixel_method,
+ const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetOneCacheViewVirtualMethodPixel(cache_view,virtual_pixel_method,
+ x,y,pixel,exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e O n e M a g i c k P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireOneMagickPixel() returns a single pixel at the specified (x,y)
+% location. The image background color is returned if an error occurs. If
+% you plan to modify the pixel, use GetOnePixel() instead.
+%
+% The format of the AcquireOneMagickPixel() method is:
+%
+% MagickPixelPacket AcquireOneMagickPixel(const Image image,const long x,
+% const long y,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickPixelPacket AcquireOneMagickPixel(const Image *image,
+ const long x,const long y,ExceptionInfo *exception)
+{
+ MagickPixelPacket
+ pixel;
+
+ (void) GetOneVirtualMagickPixel(image,x,y,&pixel,exception);
+ return(pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e O n e P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireOnePixel() returns a single pixel at the specified (x,y) location.
+% The image background color is returned if an error occurs. If you plan to
+% modify the pixel, use GetOnePixel() instead.
+%
+% The format of the AcquireOnePixel() method is:
+%
+% PixelPacket AcquireOnePixel(const Image image,const long x,
+% const long y,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket AcquireOnePixel(const Image *image,const long x,
+ const long y,ExceptionInfo *exception)
+{
+ PixelPacket
+ pixel;
+
+ (void) GetOneVirtualPixel(image,x,y,&pixel,exception);
+ return(pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e O n e V i r t u a l P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireOneVirtualPixel() returns a single pixel at the specified (x,y)
+% location as defined by specified pixel method. The image background color
+% is returned if an error occurs. If you plan to modify the pixel, use
+% GetOnePixel() instead.
+%
+% The format of the AcquireOneVirtualPixel() method is:
+%
+% PixelPacket AcquireOneVirtualPixel(const Image image,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport PixelPacket AcquireOneVirtualPixel(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ ExceptionInfo *exception)
+{
+ PixelPacket
+ pixel;
+
+ (void) GetOneVirtualMethodPixel(image,virtual_pixel_method,x,y,&pixel,
+ exception);
+ return(pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquirePixels() returns the pixels associated with the last call to
+% QueueAuthenticPixels() or GetVirtualPixels().
+%
+% The format of the AcquirePixels() method is:
+%
+% const PixelPacket *AcquirePixels(const Image image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport const PixelPacket *AcquirePixels(const Image *image)
+{
+ return(GetVirtualPixelQueue(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A f f i n i t y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AffinityImage() replaces the colors of an image with the closest color from
+% a reference image.
+%
+% The format of the AffinityImage method is:
+%
+% MagickBooleanType AffinityImage(const QuantizeInfo *quantize_info,
+% Image *image,const Image *affinity_image)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o image: the image.
+%
+% o affinity_image: the reference image.
+%
+*/
+MagickExport MagickBooleanType AffinityImage(const QuantizeInfo *quantize_info,
+ Image *image,const Image *affinity_image)
+{
+ return(RemapImage(quantize_info,image,affinity_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A f f i n i t y I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AffinityImages() replaces the colors of a sequence of images with the
+% closest color from a reference image.
+%
+% The format of the AffinityImage method is:
+%
+% MagickBooleanType AffinityImages(const QuantizeInfo *quantize_info,
+% Image *images,Image *affinity_image)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o images: the image sequence.
+%
+% o affinity_image: the reference image.
+%
+*/
+MagickExport MagickBooleanType AffinityImages(const QuantizeInfo *quantize_info,
+ Image *images,const Image *affinity_image)
+{
+ return(RemapImages(quantize_info,images,affinity_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A l l o c a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AllocateImage() returns a pointer to an image structure initialized to
+% default values.
+%
+% The format of the AllocateImage method is:
+%
+% Image *AllocateImage(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: Many of the image default values are set from this
+% structure. For example, filename, compression, depth, background color,
+% and others.
+%
+*/
+MagickExport Image *AllocateImage(const ImageInfo *image_info)
+{
+ return(AcquireImage(image_info));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A l l o c a t e I m a g e C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AllocateImageColormap() allocates an image colormap and initializes
+% it to a linear gray colorspace. If the image already has a colormap,
+% it is replaced. AllocateImageColormap() returns MagickTrue if successful,
+% otherwise MagickFalse if there is not enough memory.
+%
+% The format of the AllocateImageColormap method is:
+%
+% MagickBooleanType AllocateImageColormap(Image *image,
+% const unsigned long colors)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colors: the number of colors in the image colormap.
+%
+*/
+MagickExport MagickBooleanType AllocateImageColormap(Image *image,
+ const unsigned long colors)
+{
+ return(AcquireImageColormap(image,colors));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A l l o c a t e N e x t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AllocateNextImage() initializes the next image in a sequence to
+% default values. The next member of image points to the newly allocated
+% image. If there is a memory shortage, next is assigned NULL.
+%
+% The format of the AllocateNextImage method is:
+%
+% void AllocateNextImage(const ImageInfo *image_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o image_info: Many of the image default values are set from this
+% structure. For example, filename, compression, depth, background color,
+% and others.
+%
+% o image: the image.
+%
+*/
+MagickExport void AllocateNextImage(const ImageInfo *image_info,Image *image)
+{
+ AcquireNextImage(image_info,image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AllocateString() allocates memory for a string and copies the source string
+% to that memory location (and returns it).
+%
+% The format of the AllocateString method is:
+%
+% char *AllocateString(const char *source)
+%
+% A description of each parameter follows:
+%
+% o source: A character string.
+%
+*/
+MagickExport char *AllocateString(const char *source)
+{
+ char
+ *destination;
+
+ size_t
+ length;
+
+ assert(source != (const char *) NULL);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ length=strlen(source)+MaxTextExtent+1;
+ destination=(char *) AcquireQuantumMemory(length,sizeof(*destination));
+ if (destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ *destination='\0';
+ if (source != (char *) NULL)
+ (void) CopyMagickString(destination,source,length);
+ return(destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C h a n n e l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Extract a channel from the image. A channel is a particular color component
+% of each pixel in the image.
+%
+% The format of the ChannelImage method is:
+%
+% unsigned int ChannelImage(Image *image,const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: Identify which channel to extract: RedChannel, GreenChannel,
+% BlueChannel, OpacityChannel, CyanChannel, MagentaChannel, YellowChannel,
+% or BlackChannel.
+%
+*/
+MagickExport unsigned int ChannelImage(Image *image,const ChannelType channel)
+{
+ return(SeparateImageChannel(image,channel));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C h a n n e l T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ChannelThresholdImage() changes the value of individual pixels based on
+% the intensity of each pixel channel. The result is a high-contrast image.
+%
+% The format of the ChannelThresholdImage method is:
+%
+% unsigned int ChannelThresholdImage(Image *image,const char *level)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o level: define the threshold values.
+%
+*/
+MagickExport unsigned int ChannelThresholdImage(Image *image,const char *level)
+{
+ MagickPixelPacket
+ threshold;
+
+ GeometryInfo
+ geometry_info;
+
+ unsigned int
+ flags,
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (level == (char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(level,&geometry_info);
+ threshold.red=geometry_info.rho;
+ threshold.green=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ threshold.green=threshold.red;
+ threshold.blue=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ threshold.blue=threshold.red;
+ status=BilevelImageChannel(image,RedChannel,threshold.red);
+ status|=BilevelImageChannel(image,GreenChannel,threshold.green);
+ status|=BilevelImageChannel(image,BlueChannel,threshold.blue);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l i p I m a g e P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClipPathImage() sets the image clip mask based any clipping path information
+% if it exists.
+%
+% The format of the ClipImage method is:
+%
+% MagickBooleanType ClipPathImage(Image *image,const char *pathname,
+% const MagickBooleanType inside)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o pathname: name of clipping path resource. If name is preceded by #, use
+% clipping path numbered by name.
+%
+% o inside: if non-zero, later operations take effect inside clipping path.
+% Otherwise later operations take effect outside clipping path.
+%
+*/
+MagickExport MagickBooleanType ClipPathImage(Image *image,const char *pathname,
+ const MagickBooleanType inside)
+{
+ return(ClipImagePath(image,pathname,inside));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e A t t r i b u t e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageAttributes() clones one or more image attributes.
+%
+% The format of the CloneImageAttributes method is:
+%
+% MagickBooleanType CloneImageAttributes(Image *image,
+% const Image *clone_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clone_image: the clone image.
+%
+*/
+MagickExport MagickBooleanType CloneImageAttributes(Image *image,
+ const Image *clone_image)
+{
+ return(CloneImageProperties(image,clone_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneMemory() copies size bytes from memory area source to the destination.
+% Copying between objects that overlap will take place correctly. It returns
+% destination.
+%
+% The format of the CloneMemory method is:
+%
+% void *CloneMemory(void *destination,const void *source,
+% const size_t size)
+%
+% A description of each parameter follows:
+%
+% o destination: the destination.
+%
+% o source: the source.
+%
+% o size: the size of the memory in bytes to allocate.
+%
+*/
+MagickExport void *CloneMemory(void *destination,const void *source,
+ const size_t size)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned char
+ *q;
+
+ register long
+ i;
+
+ assert(destination != (void *) NULL);
+ assert(source != (const void *) NULL);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ p=(const unsigned char *) source;
+ q=(unsigned char *) destination;
+ if ((p <= q) || ((p+size) >= q))
+ return(CopyMagickMemory(destination,source,size));
+ /*
+ Overlap, copy backwards.
+ */
+ p+=size;
+ q+=size;
+ for (i=(long) (size-1); i >= 0; i--)
+ *--q=(*--p);
+ return(destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o s e C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloseCacheView() closes the specified view returned by a previous call to
+% OpenCacheView().
+%
+% The format of the CloseCacheView method is:
+%
+% CacheView *CloseCacheView(CacheView *view_info)
+%
+% A description of each parameter follows:
+%
+% o view_info: the address of a structure of type CacheView.
+%
+*/
+MagickExport CacheView *CloseCacheView(CacheView *view_info)
+{
+ return(DestroyCacheView(view_info));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o l o r F l o o d f i l l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ColorFloodfill() changes the color value of any pixel that matches
+% target and is an immediate neighbor. If the method FillToBorderMethod is
+% specified, the color value is changed for any neighbor pixel that does not
+% match the bordercolor member of image.
+%
+% By default target must match a particular pixel color exactly.
+% However, in many cases two colors may differ by a small amount. The
+% fuzz member of image defines how much tolerance is acceptable to
+% consider two colors as the same. For example, set fuzz to 10 and the
+% color red at intensities of 100 and 102 respectively are now
+% interpreted as the same color for the purposes of the floodfill.
+%
+% The format of the ColorFloodfillImage method is:
+%
+% MagickBooleanType ColorFloodfillImage(Image *image,
+% const DrawInfo *draw_info,const PixelPacket target,
+% const long x_offset,const long y_offset,const PaintMethod method)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o target: the RGB value of the target color.
+%
+% o x,y: the starting location of the operation.
+%
+% o method: Choose either FloodfillMethod or FillToBorderMethod.
+%
+*/
+
+#define MaxStacksize (1UL << 15)
+#define PushSegmentStack(up,left,right,delta) \
+{ \
+ if (s >= (segment_stack+MaxStacksize)) \
+ ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
+ else \
+ { \
+ if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (long) image->rows)) \
+ { \
+ s->x1=(double) (left); \
+ s->y1=(double) (up); \
+ s->x2=(double) (right); \
+ s->y2=(double) (delta); \
+ s++; \
+ } \
+ } \
+}
+
+MagickExport MagickBooleanType ColorFloodfillImage(Image *image,
+ const DrawInfo *draw_info,const PixelPacket target,const long x_offset,
+ const long y_offset,const PaintMethod method)
+{
+ Image
+ *floodplane_image;
+
+ long
+ offset,
+ start,
+ x,
+ x1,
+ x2,
+ y;
+
+ MagickBooleanType
+ skip;
+
+ PixelPacket
+ fill_color;
+
+ register SegmentInfo
+ *s;
+
+ SegmentInfo
+ *segment_stack;
+
+ /*
+ Check boundary conditions.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if ((x_offset < 0) || (x_offset >= (long) image->columns))
+ return(MagickFalse);
+ if ((y_offset < 0) || (y_offset >= (long) image->rows))
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ floodplane_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (floodplane_image == (Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
+ /*
+ Set floodfill color.
+ */
+ segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
+ sizeof(*segment_stack));
+ if (segment_stack == (SegmentInfo *) NULL)
+ {
+ floodplane_image=DestroyImage(floodplane_image);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ /*
+ Push initial segment on stack.
+ */
+ x=x_offset;
+ y=y_offset;
+ start=0;
+ s=segment_stack;
+ PushSegmentStack(y,x,x,1);
+ PushSegmentStack(y+1,x,x,-1);
+ while (s > segment_stack)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Pop segment off stack.
+ */
+ s--;
+ x1=(long) s->x1;
+ x2=(long) s->x2;
+ offset=(long) s->y2;
+ y=(long) s->y1+offset;
+ /*
+ Recolor neighboring pixels.
+ */
+ p=GetVirtualPixels(image,0,y,(unsigned long) (x1+1),1,&image->exception);
+ q=GetAuthenticPixels(floodplane_image,0,y,(unsigned long) (x1+1),1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ p+=x1;
+ q+=x1;
+ for (x=x1; x >= 0; x--)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ p--;
+ q--;
+ }
+ if (SyncAuthenticPixels(floodplane_image,&image->exception) == MagickFalse)
+ break;
+ skip=x >= x1 ? MagickTrue : MagickFalse;
+ if (skip == MagickFalse)
+ {
+ start=x+1;
+ if (start < x1)
+ PushSegmentStack(y,start,x1-1,-offset);
+ x=x1+1;
+ }
+ do
+ {
+ if (skip == MagickFalse)
+ {
+ if (x < (long) image->columns)
+ {
+ p=GetVirtualPixels(image,x,y,image->columns-x,1,
+ &image->exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (PixelPacket *) NULL))
+ break;
+ for ( ; x < (long) image->columns; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(floodplane_image,&image->exception) == MagickFalse)
+ break;
+ }
+ PushSegmentStack(y,start,x-1,offset);
+ if (x > (x2+1))
+ PushSegmentStack(y,x2+1,x-1,-offset);
+ }
+ skip=MagickFalse;
+ x++;
+ if (x <= x2)
+ {
+ p=GetVirtualPixels(image,x,y,(unsigned long) (x2-x+1),1,
+ &image->exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,(unsigned long) (x2-x+1),1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for ( ; x <= x2; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ p++;
+ q++;
+ }
+ }
+ start=x;
+ } while (x <= x2);
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Tile fill color onto floodplane.
+ */
+ p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,
+ &image->exception);
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (p->opacity != OpaqueOpacity)
+ {
+ (void) GetFillColor(draw_info,x,y,&fill_color);
+ MagickCompositeOver(&fill_color,(MagickRealType) fill_color.opacity,q,
+ (MagickRealType) q->opacity,q);
+ }
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ }
+ segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
+ floodplane_image=DestroyImage(floodplane_image);
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageAttribute() deletes an attribute from the image.
+%
+% The format of the DeleteImageAttribute method is:
+%
+% MagickBooleanType DeleteImageAttribute(Image *image,const char *key)
+%
+% A description of each parameter follows:
+%
+% o image: the image info.
+%
+% o key: the image key.
+%
+*/
+MagickExport MagickBooleanType DeleteImageAttribute(Image *image,
+ const char *key)
+{
+ return(DeleteImageProperty(image,key));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageList() deletes an image at the specified position in the list.
+%
+% The format of the DeleteImageList method is:
+%
+% unsigned int DeleteImageList(Image *images,const long offset)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o offset: the position within the list.
+%
+*/
+MagickExport unsigned int DeleteImageList(Image *images,const long offset)
+{
+ register long
+ i;
+
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ while (GetPreviousImageInList(images) != (Image *) NULL)
+ images=GetPreviousImageInList(images);
+ for (i=0; i < offset; i++)
+ {
+ if (GetNextImageInList(images) == (Image *) NULL)
+ return(MagickFalse);
+ images=GetNextImageInList(images);
+ }
+ DeleteImageFromList(&images);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e M a g i c k R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteMagickRegistry() deletes an entry in the registry as defined by the id.
+% It returns MagickTrue if the entry is deleted otherwise MagickFalse if no
+% entry is found in the registry that matches the id.
+%
+% The format of the DeleteMagickRegistry method is:
+%
+% MagickBooleanType DeleteMagickRegistry(const long id)
+%
+% A description of each parameter follows:
+%
+% o id: the registry id.
+%
+*/
+MagickExport MagickBooleanType DeleteMagickRegistry(const long id)
+{
+ char
+ key[MaxTextExtent];
+
+ (void) FormatMagickString(key,MaxTextExtent,"%ld\n",id);
+ return(DeleteImageRegistry(key));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M a g i c k R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagickRegistry() deallocates memory associated the magick registry.
+%
+% The format of the DestroyMagickRegistry method is:
+%
+% void DestroyMagickRegistry(void)
+%
+*/
+MagickExport void DestroyMagickRegistry(void)
+{
+ DestroyImageRegistry();
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s c r i b e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DescribeImage() describes an image by printing its attributes to the file.
+% Attributes include the image width, height, size, and others.
+%
+% The format of the DescribeImage method is:
+%
+% MagickBooleanType DescribeImage(Image *image,FILE *file,
+% const MagickBooleanType verbose)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o file: the file, typically stdout.
+%
+% o verbose: A value other than zero prints more detailed information
+% about the image.
+%
+*/
+MagickExport MagickBooleanType DescribeImage(Image *image,FILE *file,
+ const MagickBooleanType verbose)
+{
+ return(IdentifyImage(image,file,verbose));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e A t t r i b u t e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageAttributes() deallocates memory associated with the image
+% attribute list.
+%
+% The format of the DestroyImageAttributes method is:
+%
+% DestroyImageAttributes(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyImageAttributes(Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->attributes != (void *) NULL)
+ image->attributes=(void *) DestroySplayTree((SplayTreeInfo *)
+ image->attributes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImages() destroys an image list.
+%
+% The format of the DestroyImages method is:
+%
+% void DestroyImages(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+*/
+MagickExport void DestroyImages(Image *image)
+{
+ if (image == (Image *) NULL)
+ return;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.4.3");
+ image=DestroyImageList(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y M a g i c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagick() destroys the ImageMagick environment.
+%
+% The format of the DestroyMagick function is:
+%
+% DestroyMagick(void)
+%
+*/
+MagickExport void DestroyMagick(void)
+{
+ MagickCoreTerminus();
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D i s p a t c h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DispatchImage() extracts pixel data from an image and returns it to you.
+% The method returns MagickFalse on success otherwise MagickTrue if an error is
+% encountered. The data is returned as char, short int, int, long, float,
+% or double in the order specified by map.
+%
+% Suppose you want to extract the first scanline of a 640x480 image as
+% character data in red-green-blue order:
+%
+% DispatchImage(image,0,0,640,1,"RGB",CharPixel,pixels,exception);
+%
+% The format of the DispatchImage method is:
+%
+% unsigned int DispatchImage(const Image *image,const long x_offset,
+% const long y_offset,const unsigned long columns,
+% const unsigned long rows,const char *map,const StorageType type,
+% void *pixels,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x_offset, y_offset, columns, rows: These values define the perimeter
+% of a region of pixels you want to extract.
+%
+% o map: This string reflects the expected ordering of the pixel array.
+% It can be any combination or order of R = red, G = green, B = blue,
+% A = alpha, C = cyan, Y = yellow, M = magenta, K = black, or
+% I = intensity (for grayscale).
+%
+% o type: Define the data type of the pixels. Float and double types are
+% normalized to [0..1] otherwise [0..QuantumRange]. Choose from these
+% types: CharPixel, ShortPixel, IntegerPixel, LongPixel, FloatPixel, or
+% DoublePixel.
+%
+% o pixels: This array of values contain the pixel components as defined by
+% map and type. You must preallocate this array where the expected
+% length varies depending on the values of width, height, map, and type.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned int DispatchImage(const Image *image,const long x_offset,
+ const long y_offset,const unsigned long columns,const unsigned long rows,
+ const char *map,const StorageType type,void *pixels,ExceptionInfo *exception)
+{
+ unsigned int
+ status;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.6");
+ status=ExportImagePixels(image,x_offset,y_offset,columns,rows,map,type,pixels,
+ exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x t r a c t S u b i m a g e F r o m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExtractSubimageFromImageImage() extracts a region of the image that most
+% closely resembles the referemce.
+%
+% The format of the ExtractSubimageFromImageImage method is:
+%
+% Image *ExtractSubimageFromImage(const Image *image,const Image *referemce,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o referemce: find an area of the image that closely resembles this image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static double GetSimilarityMetric(const Image *image,const Image *referemce,
+ const long x_offset,const long y_offset,const double similarity_threshold,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ double
+ channels,
+ normalized_similarity,
+ similarity;
+
+ CacheView
+ *image_view,
+ *referemce_view;
+
+ /*
+ Compute the similarity in pixels between two images.
+ */
+ normalized_similarity=1.0;
+ similarity=0.0;
+ channels=3;
+ if ((image->matte != MagickFalse) && (referemce->matte != MagickFalse))
+ channels++;
+ if ((image->colorspace == CMYKColorspace) &&
+ (referemce->colorspace == CMYKColorspace))
+ channels++;
+ image_view=AcquireCacheView(image);
+ referemce_view=AcquireCacheView(referemce);
+ for (y=0; y < (long) referemce->rows; y++)
+ {
+ register const IndexPacket
+ *indexes,
+ *referemce_indexes;
+
+ register const PixelPacket
+ *p,
+ *q;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,x_offset,y_offset+y,
+ referemce->columns,1,exception);
+ q=GetCacheViewVirtualPixels(referemce_view,0,y,referemce->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (const PixelPacket *) NULL))
+ continue;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ referemce_indexes=GetCacheViewVirtualIndexQueue(referemce_view);
+ for (x=0; x < (long) referemce->columns; x++)
+ {
+ MagickRealType
+ pixel;
+
+ pixel=QuantumScale*(p->red-(double) q->red);
+ similarity+=pixel*pixel;
+ pixel=QuantumScale*(p->green-(double) q->green);
+ similarity+=pixel*pixel;
+ pixel=QuantumScale*(p->blue-(double) q->blue);
+ similarity+=pixel*pixel;
+ if ((image->matte != MagickFalse) && (referemce->matte != MagickFalse))
+ {
+ pixel=QuantumScale*(p->opacity-(double) q->opacity);
+ similarity+=pixel*pixel;
+ }
+ if ((image->colorspace == CMYKColorspace) &&
+ (referemce->colorspace == CMYKColorspace))
+ {
+ pixel=QuantumScale*(indexes[x]-(double) referemce_indexes[x]);
+ similarity+=pixel*pixel;
+ }
+ p++;
+ q++;
+ }
+ normalized_similarity=sqrt(similarity)/referemce->columns/referemce->rows/
+ channels;
+ if (normalized_similarity > similarity_threshold)
+ break;
+ }
+ referemce_view=DestroyCacheView(referemce_view);
+ image_view=DestroyCacheView(image_view);
+ return(normalized_similarity);
+}
+
+MagickExport Image *ExtractSubimageFromImage(Image *image,const Image *referemce,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ double
+ similarity_threshold;
+
+ RectangleInfo
+ offset;
+
+ /*
+ Extract referemce from image.
+ */
+ if ((referemce->columns > image->columns) || (referemce->rows > image->rows))
+ return((Image *) NULL);
+ similarity_threshold=HUGE_VAL;
+ SetGeometry(referemce,&offset);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4)
+#endif
+ for (y=0; y < (long) (image->rows-referemce->rows); y++)
+ {
+ double
+ similarity;
+
+ register long
+ x;
+
+ for (x=0; x < (long) (image->columns-referemce->columns); x++)
+ {
+ similarity=GetSimilarityMetric(image,referemce,x,y,similarity_threshold,
+ exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ExtractSubimageFromImage)
+#endif
+ if (similarity < similarity_threshold)
+ {
+ similarity_threshold=similarity;
+ offset.x=x;
+ offset.y=y;
+ }
+ }
+ }
+ if (similarity_threshold > (QuantumScale*referemce->fuzz/100.0))
+ return((Image *) NULL);
+ return(CropImage(image,&offset,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F l a t t e n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FlattenImages() Obsolete Function: Use MergeImageLayers() instead.
+%
+% The format of the FlattenImage method is:
+%
+% Image *FlattenImage(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *FlattenImages(Image *image,ExceptionInfo *exception)
+{
+ return(MergeImageLayers(image,FlattenLayer,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t I m a g e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatImageAttribute() permits formatted key/value pairs to be saved as an
+% image attribute.
+%
+% The format of the FormatImageAttribute method is:
+%
+% MagickBooleanType FormatImageAttribute(Image *image,const char *key,
+% const char *format,...)
+%
+% A description of each parameter follows.
+%
+% o image: The image.
+%
+% o key: The attribute key.
+%
+% o format: A string describing the format to use to write the remaining
+% arguments.
+%
+*/
+
+MagickExport MagickBooleanType FormatImageAttributeList(Image *image,
+ const char *key,const char *format,va_list operands)
+{
+ char
+ value[MaxTextExtent];
+
+ int
+ n;
+
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(value,MaxTextExtent,format,operands);
+#else
+ n=vsprintf(value,format,operands);
+#endif
+ if (n < 0)
+ value[MaxTextExtent-1]='\0';
+ return(SetImageAttribute(image,key,value));
+}
+
+MagickExport MagickBooleanType FormatImageAttribute(Image *image,
+ const char *key,const char *format,...)
+{
+ MagickBooleanType
+ status;
+
+ va_list
+ operands;
+
+ va_start(operands,format);
+ status=FormatImageAttributeList(image,key,format,operands);
+ va_end(operands);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatString() prints formatted output of a variable argument list.
+%
+% The format of the FormatString method is:
+%
+% void FormatString(char *string,const char *format,...)
+%
+% A description of each parameter follows.
+%
+% o string: Method FormatString returns the formatted string in this
+% character buffer.
+%
+% o format: A string describing the format to use to write the remaining
+% arguments.
+%
+*/
+
+MagickExport void FormatStringList(char *string,const char *format,
+ va_list operands)
+{
+ int
+ n;
+
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(string,MaxTextExtent,format,operands);
+#else
+ n=vsprintf(string,format,operands);
+#endif
+ if (n < 0)
+ string[MaxTextExtent-1]='\0';
+}
+
+MagickExport void FormatString(char *string,const char *format,...)
+{
+ va_list
+ operands;
+
+ va_start(operands,format);
+ FormatStringList(string,format,operands);
+ va_end(operands);
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F u z z y C o l o r M a t c h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FuzzyColorMatch() returns true if two pixels are identical in color.
+%
+% The format of the ColorMatch method is:
+%
+% void FuzzyColorMatch(const PixelPacket *p,const PixelPacket *q,
+% const double fuzz)
+%
+% A description of each parameter follows:
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+% o distance: Define how much tolerance is acceptable to consider
+% two colors as the same.
+%
+*/
+MagickExport unsigned int FuzzyColorMatch(const PixelPacket *p,
+ const PixelPacket *q,const double fuzz)
+{
+ MagickPixelPacket
+ pixel;
+
+ register MagickRealType
+ distance;
+
+ if ((fuzz == 0.0) && (p->red == q->red) && (p->green == q->green) &&
+ (p->blue == q->blue))
+ return(MagickTrue);
+ pixel.red=p->red-(MagickRealType) q->red;
+ distance=pixel.red*pixel.red;
+ if (distance > (fuzz*fuzz))
+ return(MagickFalse);
+ pixel.green=p->green-(MagickRealType) q->green;
+ distance+=pixel.green*pixel.green;
+ if (distance > (fuzz*fuzz))
+ return(MagickFalse);
+ pixel.blue=p->blue-(MagickRealType) q->blue;
+ distance+=pixel.blue*pixel.blue;
+ if (distance > (fuzz*fuzz))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F u z z y C o l o r C o m p a r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FuzzyColorCompare() returns MagickTrue if the distance between two colors is
+% less than the specified distance in a linear three dimensional color space.
+% This method is used by ColorFloodFill() and other algorithms which
+% compare two colors.
+%
+% The format of the FuzzyColorCompare method is:
+%
+% void FuzzyColorCompare(const Image *image,const PixelPacket *p,
+% const PixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+*/
+MagickExport MagickBooleanType FuzzyColorCompare(const Image *image,
+ const PixelPacket *p,const PixelPacket *q)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.2.5");
+ return(IsColorSimilar(image,p,q));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F u z z y O p a c i t y C o m p a r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FuzzyOpacityCompare() returns true if the distance between two opacity
+% values is less than the specified distance in a linear color space. This
+% method is used by MatteFloodFill() and other algorithms which compare
+% two opacity values.
+%
+% The format of the FuzzyOpacityCompare method is:
+%
+% void FuzzyOpacityCompare(const Image *image,const PixelPacket *p,
+% const PixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o p: Pixel p.
+%
+% o q: Pixel q.
+%
+*/
+MagickExport MagickBooleanType FuzzyOpacityCompare(const Image *image,
+ const PixelPacket *p,const PixelPacket *q)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.2.5");
+ return(IsOpacitySimilar(image,p,q));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C o n f i g u r e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetConfigureBlob() returns the specified configure file as a blob.
+%
+% The format of the GetConfigureBlob method is:
+%
+% void *GetConfigureBlob(const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the configure file name.
+%
+% o path: return the full path information of the configure file.
+%
+% o length: This pointer to a size_t integer sets the initial length of the
+% blob. On return, it reflects the actual length of the blob.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void *GetConfigureBlob(const char *filename,char *path,
+ size_t *length,ExceptionInfo *exception)
+{
+ void
+ *blob;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ assert(path != (char *) NULL);
+ assert(length != (size_t *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ blob=(void *) NULL;
+ (void) CopyMagickString(path,filename,MaxTextExtent);
+#if defined(MAGICKCORE_INSTALLED_SUPPORT)
+#if defined(MAGICKCORE_LIBRARY_PATH)
+ if (blob == (void *) NULL)
+ {
+ /*
+ Search hard coded paths.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",
+ MAGICKCORE_LIBRARY_PATH,filename);
+ if (IsPathAccessible(path) != MagickFalse)
+ blob=FileToBlob(path,~0,length,exception);
+ }
+#endif
+#if defined(__WINDOWS__) && !(defined(MAGICKCORE_CONFIGURE_PATH) || defined(MAGICKCORE_SHARE_CONFIGURE_PATH))
+ if (blob == (void *) NULL)
+ {
+ char
+ *key_value;
+
+ /*
+ Locate file via registry key.
+ */
+ key_value=NTRegistryKeyLookup("ConfigurePath");
+ if (key_value != (char *) NULL)
+ {
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",key_value,
+ DirectorySeparator,filename);
+ if (IsPathAccessible(path) != MagickFalse)
+ blob=FileToBlob(path,~0,length,exception);
+ }
+ }
+#endif
+#else
+ if (blob == (void *) NULL)
+ {
+ char
+ *home;
+
+ home=GetEnvironmentValue("MAGICK_HOME");
+ if (home != (char *) NULL)
+ {
+ /*
+ Search MAGICK_HOME.
+ */
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",home,
+ DirectorySeparator,filename);
+#else
+ (void) FormatMagickString(path,MaxTextExtent,"%s/lib/%s/%s",home,
+ MAGICKCORE_LIBRARY_RELATIVE_PATH,filename);
+#endif
+ if (IsPathAccessible(path) != MagickFalse)
+ blob=FileToBlob(path,~0,length,exception);
+ home=DestroyString(home);
+ }
+ home=GetEnvironmentValue("HOME");
+ if (home == (char *) NULL)
+ home=GetEnvironmentValue("USERPROFILE");
+ if (home != (char *) NULL)
+ {
+ /*
+ Search $HOME/.magick.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s.magick%s%s",home,
+ DirectorySeparator,DirectorySeparator,filename);
+ if ((IsPathAccessible(path) != MagickFalse) && (blob == (void *) NULL))
+ blob=FileToBlob(path,~0,length,exception);
+ home=DestroyString(home);
+ }
+ }
+ if ((blob == (void *) NULL) && (*GetClientPath() != '\0'))
+ {
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",GetClientPath(),
+ DirectorySeparator,filename);
+#else
+ char
+ prefix[MaxTextExtent];
+
+ /*
+ Search based on executable directory if directory is known.
+ */
+ (void) CopyMagickString(prefix,GetClientPath(),
+ MaxTextExtent);
+ ChopPathComponents(prefix,1);
+ (void) FormatMagickString(path,MaxTextExtent,"%s/lib/%s/%s",prefix,
+ MAGICKCORE_LIBRARY_RELATIVE_PATH,filename);
+#endif
+ if (IsPathAccessible(path) != MagickFalse)
+ blob=FileToBlob(path,~0,length,exception);
+ }
+ /*
+ Search current directory.
+ */
+ if ((blob == (void *) NULL) && (IsPathAccessible(path) != MagickFalse))
+ blob=FileToBlob(path,~0,length,exception);
+#if defined(__WINDOWS__)
+ /*
+ Search Windows registry.
+ */
+ if (blob == (void *) NULL)
+ blob=NTResourceToBlob(filename);
+#endif
+#endif
+ if (blob == (void *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),ConfigureWarning,
+ "UnableToOpenConfigureFile","`%s'",path);
+ return(blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheView() gets pixels from the in-memory or disk pixel cache as
+% defined by the geometry parameters. A pointer to the pixels is returned if
+% the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetCacheView method is:
+%
+% PixelPacket *GetCacheView(CacheView *cache_view,const long x,
+% const long y,const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the address of a structure of type CacheView.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *GetCacheView(CacheView *cache_view,const long x,
+ const long y,const unsigned long columns,const unsigned long rows)
+{
+ PixelPacket
+ *pixels;
+
+ pixels=GetCacheViewAuthenticPixels(cache_view,x,y,columns,rows,
+ GetCacheViewException(cache_view));
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewIndexes() returns the indexes associated with the specified
+% view.
+%
+% The format of the GetCacheViewIndexes method is:
+%
+% IndexPacket *GetCacheViewIndexes(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport IndexPacket *GetCacheViewIndexes(CacheView *cache_view)
+{
+ return(GetCacheViewAuthenticIndexQueue(cache_view));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t C a c h e V i e w P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCacheViewPixels() gets pixels from the in-memory or disk pixel cache as
+% defined by the geometry parameters. A pointer to the pixels is returned if
+% the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the GetCacheViewPixels method is:
+%
+% PixelPacket *GetCacheViewPixels(CacheView *cache_view,const long x,
+% const long y,const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *GetCacheViewPixels(CacheView *cache_view,const long x,
+ const long y,const unsigned long columns,const unsigned long rows)
+{
+ PixelPacket
+ *pixels;
+
+ pixels=GetCacheViewAuthenticPixels(cache_view,x,y,columns,rows,
+ GetCacheViewException(cache_view));
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageAttribute() searches the list of image attributes and returns
+% a pointer to the attribute if it exists otherwise NULL.
+%
+% The format of the GetImageAttribute method is:
+%
+% const ImageAttribute *GetImageAttribute(const Image *image,
+% const char *key)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o key: These character strings are the name of an image attribute to
+% return.
+%
+*/
+
+static void *DestroyAttribute(void *attribute)
+{
+ register ImageAttribute
+ *p;
+
+ p=(ImageAttribute *) attribute;
+ if (p->value != (char *) NULL)
+ p->value=DestroyString(p->value);
+ return(RelinquishMagickMemory(p));
+}
+
+MagickExport const ImageAttribute *GetImageAttribute(const Image *image,
+ const char *key)
+{
+ const char
+ *value;
+
+ ImageAttribute
+ *attribute;
+
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.3.1");
+ value=GetImageProperty(image,key);
+ if (value == (const char *) NULL)
+ return((const ImageAttribute *) NULL);
+ if (image->attributes == (void *) NULL)
+ ((Image *) image)->attributes=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,DestroyAttribute);
+ else
+ {
+ const ImageAttribute
+ *attribute;
+
+ attribute=(const ImageAttribute *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->attributes,key);
+ if (attribute != (const ImageAttribute *) NULL)
+ return(attribute);
+ }
+ attribute=(ImageAttribute *) AcquireMagickMemory(sizeof(*attribute));
+ if (attribute == (ImageAttribute *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(attribute,0,sizeof(*attribute));
+ attribute->key=ConstantString(key);
+ attribute->value=ConstantString(value);
+ (void) AddValueToSplayTree((SplayTreeInfo *) ((Image *) image)->attributes,
+ attribute->key,attribute);
+ return((const ImageAttribute *) attribute);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C l i p p i n g P a t h A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageClippingPathAttribute() searches the list of image attributes and
+% returns a pointer to a clipping path if it exists otherwise NULL.
+%
+% The format of the GetImageClippingPathAttribute method is:
+%
+% const ImageAttribute *GetImageClippingPathAttribute(Image *image)
+%
+% A description of each parameter follows:
+%
+% o attribute: Method GetImageClippingPathAttribute returns the clipping
+% path if it exists otherwise NULL.
+%
+% o image: the image.
+%
+*/
+MagickExport const ImageAttribute *GetImageClippingPathAttribute(Image *image)
+{
+ return(GetImageAttribute(image,"8BIM:1999,2998"));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e F r o m M a g i c k R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageFromMagickRegistry() gets an image from the registry as defined by
+% its name. If the image is not found, a NULL image is returned.
+%
+% The format of the GetImageFromMagickRegistry method is:
+%
+% Image *GetImageFromMagickRegistry(const char *name,long *id,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the name of the image to retrieve from the registry.
+%
+% o id: the registry id.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *GetImageFromMagickRegistry(const char *name,long *id,
+ ExceptionInfo *exception)
+{
+ *id=0L;
+ return((Image *) GetImageRegistry(ImageRegistryType,name,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickRegistry() gets a blob from the registry as defined by the id. If
+% the blob that matches the id is not found, NULL is returned.
+%
+% The format of the GetMagickRegistry method is:
+%
+% const void *GetMagickRegistry(const long id,RegistryType *type,
+% size_t *length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o id: the registry id.
+%
+% o type: the registry type.
+%
+% o length: the blob length in number of bytes.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void *GetMagickRegistry(const long id,RegistryType *type,
+ size_t *length,ExceptionInfo *exception)
+{
+ char
+ key[MaxTextExtent];
+
+ void
+ *blob;
+
+ *type=UndefinedRegistryType;
+ *length=0;
+ (void) FormatMagickString(key,MaxTextExtent,"%ld\n",id);
+ blob=(void *) GetImageRegistry(ImageRegistryType,key,exception);
+ if (blob != (void *) NULL)
+ return(blob);
+ blob=(void *) GetImageRegistry(ImageInfoRegistryType,key,exception);
+ if (blob != (void *) NULL)
+ return(blob);
+ return((void *) GetImageRegistry(UndefinedRegistryType,key,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageGeometry() returns a region as defined by the geometry string with
+% respect to the image and its gravity.
+%
+% The format of the GetImageGeometry method is:
+%
+% int GetImageGeometry(Image *image,const char *geometry,
+% const unsigned int size_to_fit,RectangeInfo *region_info)
+%
+% A description of each parameter follows:
+%
+% o flags: Method GetImageGeometry returns a bitmask that indicates
+% which of the four values were located in the geometry string.
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o size_to_fit: A value other than 0 means to scale the region so it
+% fits within the specified width and height.
+%
+% o region_info: the region as defined by the geometry string with
+% respect to the image and its gravity.
+%
+*/
+MagickExport int GetImageGeometry(Image *image,const char *geometry,
+ const unsigned int size_to_fit,RectangleInfo *region_info)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.4");
+ if (size_to_fit != MagickFalse)
+ return((int) ParseRegionGeometry(image,geometry,region_info,&image->exception));
+ return((int) ParsePageGeometry(image,geometry,region_info,&image->exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageList() returns an image at the specified position in the list.
+%
+% The format of the GetImageList method is:
+%
+% Image *GetImageList(const Image *images,const long offset,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o offset: the position within the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *GetImageList(const Image *images,const long offset,
+ ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ image=CloneImage(GetImageFromList(images,(long) offset),0,0,MagickTrue,
+ exception);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e L i s t I n d e x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageListIndex() returns the position in the list of the specified
+% image.
+%
+% The format of the GetImageListIndex method is:
+%
+% long GetImageListIndex(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport long GetImageListIndex(const Image *images)
+{
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(GetImageIndexInList(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e L i s t S i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageListSize() returns the number of images in the list.
+%
+% The format of the GetImageListSize method is:
+%
+% unsigned long GetImageListSize(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport unsigned long GetImageListSize(const Image *images)
+{
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(GetImageListLength(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImagePixels() obtains a pixel region for read/write access. If the
+% region is successfully accessed, a pointer to a PixelPacket array
+% representing the region is returned, otherwise NULL is returned.
+%
+% The returned pointer may point to a temporary working copy of the pixels
+% or it may point to the original pixels in memory. Performance is maximized
+% if the selected region is part of one row, or one or more full rows, since
+% then there is opportunity to access the pixels in-place (without a copy)
+% if the image is in RAM, or in a memory-mapped file. The returned pointer
+% should *never* be deallocated by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or if the storage class is
+% PseduoClass, call GetAuthenticIndexQueue() after invoking GetImagePixels()
+% to obtain the black color component or colormap indexes (of type IndexPacket)
+% corresponding to the region. Once the PixelPacket (and/or IndexPacket)
+% array has been updated, the changes must be saved back to the underlying
+% image using SyncAuthenticPixels() or they may be lost.
+%
+% The format of the GetImagePixels() method is:
+%
+% PixelPacket *GetImagePixels(Image *image,const long x,const long y,
+% const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *GetImagePixels(Image *image,const long x,const long y,
+ const unsigned long columns,const unsigned long rows)
+{
+ return(GetAuthenticPixels(image,x,y,columns,rows,&image->exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I n d e x e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetIndexes() returns the black channel or the colormap indexes associated
+% with the last call to QueueAuthenticPixels() or GetVirtualPixels(). NULL is
+% returned if the black channel or colormap indexes are not available.
+%
+% The format of the GetIndexes() method is:
+%
+% IndexPacket *GetIndexes(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o indexes: GetIndexes() returns the indexes associated with the last
+% call to QueueAuthenticPixels() or GetAuthenticPixels().
+%
+% o image: the image.
+%
+*/
+MagickExport IndexPacket *GetIndexes(const Image *image)
+{
+ return(GetAuthenticIndexQueue(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickGeometry() is similar to GetGeometry() except the returned
+% geometry is modified as determined by the meta characters: %, !, <, >,
+% and ~.
+%
+% The format of the GetMagickGeometry method is:
+%
+% unsigned int GetMagickGeometry(const char *geometry,long *x,long *y,
+% unsigned long *width,unsigned long *height)
+%
+% A description of each parameter follows:
+%
+% o geometry: Specifies a character string representing the geometry
+% specification.
+%
+% o x,y: A pointer to an integer. The x and y offset as determined by
+% the geometry specification is returned here.
+%
+% o width,height: A pointer to an unsigned integer. The width and height
+% as determined by the geometry specification is returned here.
+%
+*/
+MagickExport unsigned int GetMagickGeometry(const char *geometry,long *x,
+ long *y,unsigned long *width,unsigned long *height)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.3");
+ return(ParseMetaGeometry(geometry,x,y,width,height));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImage() returns the next image in a list.
+%
+% The format of the GetNextImage method is:
+%
+% Image *GetNextImage(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetNextImage(const Image *images)
+{
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(GetNextImageInList(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageAttribute() gets the next image attribute.
+%
+% The format of the GetNextImageAttribute method is:
+%
+% const ImageAttribute *GetNextImageAttribute(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport const ImageAttribute *GetNextImageAttribute(const Image *image)
+{
+ const char
+ *property;
+
+ property=GetNextImageProperty(image);
+ if (property == (const char *) NULL)
+ return((const ImageAttribute *) NULL);
+ return(GetImageAttribute(image,property));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N u m b e r S c e n e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNumberScenes() returns the number of images in the list.
+%
+% The format of the GetNumberScenes method is:
+%
+% unsigned int GetNumberScenes(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport unsigned int GetNumberScenes(const Image *image)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return((unsigned int) GetImageListLength(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O n e P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOnePixel() returns a single pixel at the specified (x,y) location.
+% The image background color is returned if an error occurs.
+%
+% The format of the GetOnePixel() method is:
+%
+% PixelPacket GetOnePixel(const Image image,const long x,const long y)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+*/
+MagickExport PixelPacket GetOnePixel(Image *image,const long x,const long y)
+{
+ PixelPacket
+ pixel;
+
+ (void) GetOneAuthenticPixel(image,x,y,&pixel,&image->exception);
+ return(pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPixels() returns the pixels associated with the last call to
+% QueueAuthenticPixels() or GetAuthenticPixels().
+%
+% The format of the GetPixels() method is:
+%
+% PixelPacket *GetPixels(const Image image)
+%
+% A description of each parameter follows:
+%
+% o pixels: GetPixels() returns the pixels associated with the last call
+% to QueueAuthenticPixels() or GetAuthenticPixels().
+%
+% o image: the image.
+%
+*/
+MagickExport PixelPacket *GetPixels(const Image *image)
+{
+ return(GetAuthenticPixelQueue(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P r e v i o u s I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPreviousImage() returns the previous image in a list.
+%
+% The format of the GetPreviousImage method is:
+%
+% Image *GetPreviousImage(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetPreviousImage(const Image *images)
+{
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(GetPreviousImageInList(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H S L T r a n s f o r m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% HSLTransform() converts a (hue, saturation, lightness) to a (red, green,
+% blue) triple.
+%
+% The format of the HSLTransformImage method is:
+%
+% void HSLTransform(const double hue,const double saturation,
+% const double lightness,Quantum *red,Quantum *green,Quantum *blue)
+%
+% A description of each parameter follows:
+%
+% o hue, saturation, lightness: A double value representing a
+% component of the HSL color space.
+%
+% o red, green, blue: A pointer to a pixel component of type Quantum.
+%
+*/
+
+static inline MagickRealType HueToRGB(MagickRealType m1,MagickRealType m2,
+ MagickRealType hue)
+{
+ if (hue < 0.0)
+ hue+=1.0;
+ if (hue > 1.0)
+ hue-=1.0;
+ if ((6.0*hue) < 1.0)
+ return(m1+6.0*(m2-m1)*hue);
+ if ((2.0*hue) < 1.0)
+ return(m2);
+ if ((3.0*hue) < 2.0)
+ return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
+ return(m1);
+}
+
+MagickExport void HSLTransform(const double hue,const double saturation,
+ const double lightness,Quantum *red,Quantum *green,Quantum *blue)
+{
+ MagickRealType
+ b,
+ g,
+ r,
+ m1,
+ m2;
+
+ /*
+ Convert HSL to RGB colorspace.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ if (lightness <= 0.5)
+ m2=lightness*(saturation+1.0);
+ else
+ m2=lightness+saturation-lightness*saturation;
+ m1=2.0*lightness-m2;
+ r=HueToRGB(m1,m2,hue+1.0/3.0);
+ g=HueToRGB(m1,m2,hue);
+ b=HueToRGB(m1,m2,hue-1.0/3.0);
+ *red=RoundToQuantum((MagickRealType) QuantumRange*r);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*g);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*b);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I d e n t i t y A f f i n e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IdentityAffine() initializes the affine transform to the identity matrix.
+%
+% The format of the IdentityAffine method is:
+%
+% IdentityAffine(AffineMatrix *affine)
+%
+% A description of each parameter follows:
+%
+% o affine: A pointer the affine transform of type AffineMatrix.
+%
+*/
+MagickExport void IdentityAffine(AffineMatrix *affine)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ assert(affine != (AffineMatrix *) NULL);
+ (void) ResetMagickMemory(affine,0,sizeof(AffineMatrix));
+ affine->sx=1.0;
+ affine->sy=1.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n i t i a l i z e M a g i c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeMagick() initializes the ImageMagick environment.
+%
+% The format of the InitializeMagick function is:
+%
+% InitializeMagick(const char *path)
+%
+% A description of each parameter follows:
+%
+% o path: the execution path of the current ImageMagick client.
+%
+*/
+MagickExport void InitializeMagick(const char *path)
+{
+ MagickCoreGenesis(path,MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n t e r p o l a t e P i x e l C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InterpolatePixelColor() applies bi-linear or tri-linear interpolation
+% between a pixel and it's neighbors.
+%
+% The format of the InterpolatePixelColor method is:
+%
+% MagickPixelPacket InterpolatePixelColor(const Image *image,
+% CacheView *view_info,InterpolatePixelMethod method,const double x,
+% const double y,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o image_view: the image cache view.
+%
+% o type: the type of pixel color interpolation.
+%
+% o x,y: A double representing the current (x,y) position of the pixel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static void BicubicInterpolate(const MagickPixelPacket *pixels,const double dx,
+ MagickPixelPacket *pixel)
+{
+ MagickRealType
+ dx2,
+ p,
+ q,
+ r,
+ s;
+
+ dx2=dx*dx;
+ p=(pixels[3].red-pixels[2].red)-(pixels[0].red-pixels[1].red);
+ q=(pixels[0].red-pixels[1].red)-p;
+ r=pixels[2].red-pixels[0].red;
+ s=pixels[1].red;
+ pixel->red=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].green-pixels[2].green)-(pixels[0].green-pixels[1].green);
+ q=(pixels[0].green-pixels[1].green)-p;
+ r=pixels[2].green-pixels[0].green;
+ s=pixels[1].green;
+ pixel->green=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].blue-pixels[2].blue)-(pixels[0].blue-pixels[1].blue);
+ q=(pixels[0].blue-pixels[1].blue)-p;
+ r=pixels[2].blue-pixels[0].blue;
+ s=pixels[1].blue;
+ pixel->blue=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].opacity-pixels[2].opacity)-(pixels[0].opacity-pixels[1].opacity);
+ q=(pixels[0].opacity-pixels[1].opacity)-p;
+ r=pixels[2].opacity-pixels[0].opacity;
+ s=pixels[1].opacity;
+ pixel->opacity=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ if (pixel->colorspace == CMYKColorspace)
+ {
+ p=(pixels[3].index-pixels[2].index)-(pixels[0].index-pixels[1].index);
+ q=(pixels[0].index-pixels[1].index)-p;
+ r=pixels[2].index-pixels[0].index;
+ s=pixels[1].index;
+ pixel->index=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ }
+}
+
+static inline MagickRealType CubicWeightingFunction(const MagickRealType x)
+{
+ MagickRealType
+ alpha,
+ gamma;
+
+ alpha=MagickMax(x+2.0,0.0);
+ gamma=1.0*alpha*alpha*alpha;
+ alpha=MagickMax(x+1.0,0.0);
+ gamma-=4.0*alpha*alpha*alpha;
+ alpha=MagickMax(x+0.0,0.0);
+ gamma+=6.0*alpha*alpha*alpha;
+ alpha=MagickMax(x-1.0,0.0);
+ gamma-=4.0*alpha*alpha*alpha;
+ return(gamma/6.0);
+}
+
+static inline double MeshInterpolate(const PointInfo *delta,const double p,
+ const double x,const double y)
+{
+ return(delta->x*x+delta->y*y+(1.0-delta->x-delta->y)*p);
+}
+
+static inline long NearestNeighbor(MagickRealType x)
+{
+ if (x >= 0.0)
+ return((long) (x+0.5));
+ return((long) (x-0.5));
+}
+
+MagickExport MagickPixelPacket InterpolatePixelColor(const Image *image,
+ CacheView *image_view,const InterpolatePixelMethod method,const double x,
+ const double y,ExceptionInfo *exception)
+{
+ MagickPixelPacket
+ pixel;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(image_view != (CacheView *) NULL);
+ GetMagickPixelPacket(image,&pixel);
+ switch (method)
+ {
+ case AverageInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[16];
+
+ MagickRealType
+ alpha[16],
+ gamma;
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x)-1,(long) floor(y)-1,
+ 4,4,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (i=0; i < 16L; i++)
+ {
+ GetMagickPixelPacket(image,pixels+i);
+ SetMagickPixelPacket(image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ gamma=alpha[i];
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red+=gamma*0.0625*pixels[i].red;
+ pixel.green+=gamma*0.0625*pixels[i].green;
+ pixel.blue+=gamma*0.0625*pixels[i].blue;
+ pixel.opacity+=0.0625*pixels[i].opacity;
+ if (image->colorspace == CMYKColorspace)
+ pixel.index+=gamma*0.0625*pixels[i].index;
+ p++;
+ }
+ break;
+ }
+ case BicubicInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[16],
+ u[4];
+
+ MagickRealType
+ alpha[16];
+
+ PointInfo
+ delta;
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x)-1,(long) floor(y)-1,
+ 4,4,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (i=0; i < 16L; i++)
+ {
+ GetMagickPixelPacket(image,pixels+i);
+ SetMagickPixelPacket(image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ p++;
+ }
+ delta.x=x-floor(x);
+ for (i=0; i < 4L; i++)
+ BicubicInterpolate(pixels+4*i,delta.x,u+i);
+ delta.y=y-floor(y);
+ BicubicInterpolate(u,delta.y,&pixel);
+ break;
+ }
+ case BilinearInterpolatePixel:
+ default:
+ {
+ MagickPixelPacket
+ pixels[16];
+
+ MagickRealType
+ alpha[16],
+ gamma;
+
+ PointInfo
+ delta;
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x),(long) floor(y),2,2,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (i=0; i < 4L; i++)
+ {
+ GetMagickPixelPacket(image,pixels+i);
+ SetMagickPixelPacket(image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ p++;
+ }
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ gamma=(((1.0-delta.y)*((1.0-delta.x)*alpha[0]+delta.x*alpha[1])+delta.y*
+ ((1.0-delta.x)*alpha[2]+delta.x*alpha[3])));
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].red+delta.x*
+ pixels[1].red)+delta.y*((1.0-delta.x)*pixels[2].red+delta.x*
+ pixels[3].red));
+ pixel.green=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].green+delta.x*
+ pixels[1].green)+delta.y*((1.0-delta.x)*pixels[2].green+
+ delta.x*pixels[3].green));
+ pixel.blue=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].blue+delta.x*
+ pixels[1].blue)+delta.y*((1.0-delta.x)*pixels[2].blue+delta.x*
+ pixels[3].blue));
+ pixel.opacity=((1.0-delta.y)*((1.0-delta.x)*pixels[0].opacity+delta.x*
+ pixels[1].opacity)+delta.y*((1.0-delta.x)*pixels[2].opacity+delta.x*
+ pixels[3].opacity));
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=gamma*((1.0-delta.y)*((1.0-delta.x)*pixels[0].index+delta.x*
+ pixels[1].index)+delta.y*((1.0-delta.x)*pixels[2].index+delta.x*
+ pixels[3].index));
+ break;
+ }
+ case FilterInterpolatePixel:
+ {
+ Image
+ *excerpt_image,
+ *filter_image;
+
+ MagickPixelPacket
+ pixels[1];
+
+ RectangleInfo
+ geometry;
+
+ geometry.width=4L;
+ geometry.height=4L;
+ geometry.x=(long) floor(x)-1L;
+ geometry.y=(long) floor(y)-1L;
+ excerpt_image=ExcerptImage(image,&geometry,exception);
+ if (excerpt_image == (Image *) NULL)
+ break;
+ filter_image=ResizeImage(excerpt_image,1,1,image->filter,image->blur,
+ exception);
+ excerpt_image=DestroyImage(excerpt_image);
+ if (filter_image == (Image *) NULL)
+ break;
+ p=GetVirtualPixels(filter_image,0,0,1,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ filter_image=DestroyImage(filter_image);
+ break;
+ }
+ indexes=GetVirtualIndexQueue(filter_image);
+ GetMagickPixelPacket(image,pixels);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ filter_image=DestroyImage(filter_image);
+ break;
+ }
+ case IntegerInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[1];
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x),(long) floor(y),1,1,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ GetMagickPixelPacket(image,pixels);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ break;
+ }
+ case MeshInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[4];
+
+ MagickRealType
+ alpha[4],
+ gamma;
+
+ PointInfo
+ delta,
+ luminance;
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x),(long) floor(y),
+ 2,2,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (i=0; i < 4L; i++)
+ {
+ GetMagickPixelPacket(image,pixels+i);
+ SetMagickPixelPacket(image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ p++;
+ }
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ luminance.x=MagickPixelLuminance(pixels+0)-MagickPixelLuminance(pixels+3);
+ luminance.y=MagickPixelLuminance(pixels+1)-MagickPixelLuminance(pixels+2);
+ if (fabs(luminance.x) < fabs(luminance.y))
+ {
+ /*
+ Diagonal 0-3 NW-SE.
+ */
+ if (delta.x <= delta.y)
+ {
+ /*
+ Bottom-left triangle (pixel:2, diagonal: 0-3).
+ */
+ delta.y=1.0-delta.y;
+ gamma=MeshInterpolate(&delta,alpha[2],alpha[3],alpha[0]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red=gamma*MeshInterpolate(&delta,pixels[2].red,
+ pixels[3].red,pixels[0].red);
+ pixel.green=gamma*MeshInterpolate(&delta,pixels[2].green,
+ pixels[3].green,pixels[0].green);
+ pixel.blue=gamma*MeshInterpolate(&delta,pixels[2].blue,
+ pixels[3].blue,pixels[0].blue);
+ pixel.opacity=gamma*MeshInterpolate(&delta,pixels[2].opacity,
+ pixels[3].opacity,pixels[0].opacity);
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=gamma*MeshInterpolate(&delta,pixels[2].index,
+ pixels[3].index,pixels[0].index);
+ }
+ else
+ {
+ /*
+ Top-right triangle (pixel:1, diagonal: 0-3).
+ */
+ delta.x=1.0-delta.x;
+ gamma=MeshInterpolate(&delta,alpha[1],alpha[0],alpha[3]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red=gamma*MeshInterpolate(&delta,pixels[1].red,
+ pixels[0].red,pixels[3].red);
+ pixel.green=gamma*MeshInterpolate(&delta,pixels[1].green,
+ pixels[0].green,pixels[3].green);
+ pixel.blue=gamma*MeshInterpolate(&delta,pixels[1].blue,
+ pixels[0].blue,pixels[3].blue);
+ pixel.opacity=gamma*MeshInterpolate(&delta,pixels[1].opacity,
+ pixels[0].opacity,pixels[3].opacity);
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=gamma*MeshInterpolate(&delta,pixels[1].index,
+ pixels[0].index,pixels[3].index);
+ }
+ }
+ else
+ {
+ /*
+ Diagonal 1-2 NE-SW.
+ */
+ if (delta.x <= (1.0-delta.y))
+ {
+ /*
+ Top-left triangle (pixel 0, diagonal: 1-2).
+ */
+ gamma=MeshInterpolate(&delta,alpha[0],alpha[1],alpha[2]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red=gamma*MeshInterpolate(&delta,pixels[0].red,
+ pixels[1].red,pixels[2].red);
+ pixel.green=gamma*MeshInterpolate(&delta,pixels[0].green,
+ pixels[1].green,pixels[2].green);
+ pixel.blue=gamma*MeshInterpolate(&delta,pixels[0].blue,
+ pixels[1].blue,pixels[2].blue);
+ pixel.opacity=gamma*MeshInterpolate(&delta,pixels[0].opacity,
+ pixels[1].opacity,pixels[2].opacity);
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=gamma*MeshInterpolate(&delta,pixels[0].index,
+ pixels[1].index,pixels[2].index);
+ }
+ else
+ {
+ /*
+ Bottom-right triangle (pixel: 3, diagonal: 1-2).
+ */
+ delta.x=1.0-delta.x;
+ delta.y=1.0-delta.y;
+ gamma=MeshInterpolate(&delta,alpha[3],alpha[2],alpha[1]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red=gamma*MeshInterpolate(&delta,pixels[3].red,
+ pixels[2].red,pixels[1].red);
+ pixel.green=gamma*MeshInterpolate(&delta,pixels[3].green,
+ pixels[2].green,pixels[1].green);
+ pixel.blue=gamma*MeshInterpolate(&delta,pixels[3].blue,
+ pixels[2].blue,pixels[1].blue);
+ pixel.opacity=gamma*MeshInterpolate(&delta,pixels[3].opacity,
+ pixels[2].opacity,pixels[1].opacity);
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=gamma*MeshInterpolate(&delta,pixels[3].index,
+ pixels[2].index,pixels[1].index);
+ }
+ }
+ break;
+ }
+ case NearestNeighborInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[1];
+
+ p=GetCacheViewVirtualPixels(image_view,NearestNeighbor(x),NearestNeighbor(y),
+ 1,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ GetMagickPixelPacket(image,pixels);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ break;
+ }
+ case SplineInterpolatePixel:
+ {
+ long
+ j,
+ n;
+
+ MagickPixelPacket
+ pixels[16];
+
+ MagickRealType
+ alpha[16],
+ dx,
+ dy,
+ gamma;
+
+ PointInfo
+ delta;
+
+ p=GetCacheViewVirtualPixels(image_view,(long) floor(x)-1,(long) floor(y)-1,
+ 4,4,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ n=0;
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ for (i=(-1); i < 3L; i++)
+ {
+ dy=CubicWeightingFunction((MagickRealType) i-delta.y);
+ for (j=(-1); j < 3L; j++)
+ {
+ GetMagickPixelPacket(image,pixels+n);
+ SetMagickPixelPacket(image,p,indexes+n,pixels+n);
+ alpha[n]=1.0;
+ if (image->matte != MagickFalse)
+ {
+ alpha[n]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[n].red*=alpha[n];
+ pixels[n].green*=alpha[n];
+ pixels[n].blue*=alpha[n];
+ if (image->colorspace == CMYKColorspace)
+ pixels[n].index*=alpha[n];
+ }
+ dx=CubicWeightingFunction(delta.x-(MagickRealType) j);
+ gamma=alpha[n];
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel.red+=gamma*dx*dy*pixels[n].red;
+ pixel.green+=gamma*dx*dy*pixels[n].green;
+ pixel.blue+=gamma*dx*dy*pixels[n].blue;
+ if (image->matte != MagickFalse)
+ pixel.opacity+=dx*dy*pixels[n].opacity;
+ if (image->colorspace == CMYKColorspace)
+ pixel.index+=gamma*dx*dy*pixels[n].index;
+ n++;
+ p++;
+ }
+ }
+ break;
+ }
+ }
+ return(pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n t e r p r e t I m a g e A t t r i b u t e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InterpretImageAttributes() replaces any embedded formatting characters with
+% the appropriate image attribute and returns the translated text.
+%
+% The format of the InterpretImageAttributes method is:
+%
+% char *InterpretImageAttributes(const ImageInfo *image_info,Image *image,
+% const char *embed_text)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o embed_text: the address of a character string containing the embedded
+% formatting characters.
+%
+*/
+MagickExport char *InterpretImageAttributes(const ImageInfo *image_info,
+ Image *image,const char *embed_text)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.3.1");
+ return(InterpretImageProperties(image_info,image,embed_text));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s S u b i m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsSubimage() returns MagickTrue if the geometry is a valid subimage
+% specification (e.g. [1], [1-9], [1,7,4]).
+%
+% The format of the IsSubimage method is:
+%
+% unsigned int IsSubimage(const char *geometry,const unsigned int pedantic)
+%
+% A description of each parameter follows:
+%
+% o geometry: This string is the geometry specification.
+%
+% o pedantic: A value other than 0 invokes a more restrictive set of
+% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
+%
+*/
+MagickExport unsigned int IsSubimage(const char *geometry,
+ const unsigned int pedantic)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (geometry == (const char *) NULL)
+ return(MagickFalse);
+ if ((strchr(geometry,'x') != (char *) NULL) ||
+ (strchr(geometry,'X') != (char *) NULL))
+ return(MagickFalse);
+ if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i b e r a t e M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LiberateMemory() frees memory that has already been allocated, and NULL's
+% the pointer to it.
+%
+% The format of the LiberateMemory method is:
+%
+% void LiberateMemory(void **memory)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a block of memory to free for reuse.
+%
+*/
+MagickExport void LiberateMemory(void **memory)
+{
+ assert(memory != (void **) NULL);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (*memory == (void *) NULL)
+ return;
+ free(*memory);
+ *memory=(void *) NULL;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i b e r a t e S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LiberateSemaphoreInfo() relinquishes a semaphore.
+%
+% The format of the LiberateSemaphoreInfo method is:
+%
+% LiberateSemaphoreInfo(void **semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport void LiberateSemaphoreInfo(SemaphoreInfo **semaphore_info)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ RelinquishSemaphoreInfo(*semaphore_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k I n c a r n a t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickIncarnate() initializes the ImageMagick environment.
+%
+% The format of the MagickIncarnate function is:
+%
+% MagickIncarnate(const char *path)
+%
+% A description of each parameter follows:
+%
+% o path: the execution path of the current ImageMagick client.
+%
+*/
+
+MagickExport void MagickIncarnate(const char *path)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.1");
+ InitializeMagick(path);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k M o n i t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickMonitor() calls the monitor handler method with a text string that
+% describes the task and a measure of completion. The method returns
+% MagickTrue on success otherwise MagickFalse if an error is encountered, e.g.
+% if there was a user interrupt.
+%
+% The format of the MagickMonitor method is:
+%
+% MagickBooleanType MagickMonitor(const char *text,
+% const MagickOffsetType offset,const MagickSizeType span,
+% void *client_data)
+%
+% A description of each parameter follows:
+%
+% o offset: the position relative to the span parameter which represents
+% how much progress has been made toward completing a task.
+%
+% o span: the span relative to completing a task.
+%
+% o client_data: the client data.
+%
+*/
+MagickExport MagickBooleanType MagickMonitor(const char *text,
+ const MagickOffsetType offset,const MagickSizeType span,
+ void *magick_unused(client_data))
+{
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ assert(text != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",text);
+ ProcessPendingEvents(text);
+ status=MagickTrue;
+ exception=AcquireExceptionInfo();
+ if (monitor_handler != (MonitorHandler) NULL)
+ status=(*monitor_handler)(text,offset,span,exception);
+ exception=DestroyExceptionInfo(exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MapImage() replaces the colors of an image with the closest color from a
+% reference image.
+%
+% The format of the MapImage method is:
+%
+% MagickBooleanType MapImage(Image *image,const Image *map_image,
+% const MagickBooleanType dither)
+%
+% A description of each parameter follows:
+%
+% o image: Specifies a pointer to an Image structure.
+%
+% o map_image: the image. Reduce image to a set of colors represented by
+% this image.
+%
+% o dither: Set this integer value to something other than zero to
+% dither the mapped image.
+%
+*/
+MagickExport MagickBooleanType MapImage(Image *image,const Image *map_image,
+ const MagickBooleanType dither)
+{
+ QuantizeInfo
+ quantize_info;
+
+ /*
+ Initialize color cube.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(map_image != (Image *) NULL);
+ assert(map_image->signature == MagickSignature);
+ GetQuantizeInfo(&quantize_info);
+ quantize_info.dither=dither;
+ return(RemapImage(&quantize_info,image,map_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a p I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MapImages() replaces the colors of a sequence of images with the closest
+% color from a reference image.
+%
+% The format of the MapImage method is:
+%
+% MagickBooleanType MapImages(Image *images,Image *map_image,
+% const MagickBooleanType dither)
+%
+% A description of each parameter follows:
+%
+% o image: Specifies a pointer to a set of Image structures.
+%
+% o map_image: the image. Reduce image to a set of colors represented by
+% this image.
+%
+% o dither: Set this integer value to something other than zero to
+% dither the quantized image.
+%
+*/
+MagickExport MagickBooleanType MapImages(Image *images,const Image *map_image,
+ const MagickBooleanType dither)
+{
+ QuantizeInfo
+ quantize_info;
+
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ GetQuantizeInfo(&quantize_info);
+ quantize_info.dither=dither;
+ return(RemapImages(&quantize_info,images,map_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a t t e F l o o d f i l l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MatteFloodfill() changes the transparency value of any pixel that matches
+% target and is an immediate neighbor. If the method FillToBorderMethod
+% is specified, the transparency value is changed for any neighbor pixel
+% that does not match the bordercolor member of image.
+%
+% By default target must match a particular pixel transparency exactly.
+% However, in many cases two transparency values may differ by a
+% small amount. The fuzz member of image defines how much tolerance is
+% acceptable to consider two transparency values as the same. For example,
+% set fuzz to 10 and the opacity values of 100 and 102 respectively are
+% now interpreted as the same value for the purposes of the floodfill.
+%
+% The format of the MatteFloodfillImage method is:
+%
+% MagickBooleanType MatteFloodfillImage(Image *image,
+% const PixelPacket target,const Quantum opacity,const long x_offset,
+% const long y_offset,const PaintMethod method)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target: the RGB value of the target color.
+%
+% o opacity: the level of transparency: 0 is fully opaque and QuantumRange is
+% fully transparent.
+%
+% o x,y: the starting location of the operation.
+%
+% o method: Choose either FloodfillMethod or FillToBorderMethod.
+%
+*/
+MagickExport MagickBooleanType MatteFloodfillImage(Image *image,
+ const PixelPacket target,const Quantum opacity,const long x_offset,
+ const long y_offset,const PaintMethod method)
+{
+ Image
+ *floodplane_image;
+
+ long
+ offset,
+ start,
+ x,
+ x1,
+ x2,
+ y;
+
+ MagickBooleanType
+ skip;
+
+ register SegmentInfo
+ *s;
+
+ SegmentInfo
+ *segment_stack;
+
+ /*
+ Check boundary conditions.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((x_offset < 0) || (x_offset >= (long) image->columns))
+ return(MagickFalse);
+ if ((y_offset < 0) || (y_offset >= (long) image->rows))
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ floodplane_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (floodplane_image == (Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
+ /*
+ Set floodfill color.
+ */
+ segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
+ sizeof(*segment_stack));
+ if (segment_stack == (SegmentInfo *) NULL)
+ {
+ floodplane_image=DestroyImage(floodplane_image);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ /*
+ Push initial segment on stack.
+ */
+ x=x_offset;
+ y=y_offset;
+ start=0;
+ s=segment_stack;
+ PushSegmentStack(y,x,x,1);
+ PushSegmentStack(y+1,x,x,-1);
+ while (s > segment_stack)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Pop segment off stack.
+ */
+ s--;
+ x1=(long) s->x1;
+ x2=(long) s->x2;
+ offset=(long) s->y2;
+ y=(long) s->y1+offset;
+ /*
+ Recolor neighboring pixels.
+ */
+ p=GetVirtualPixels(image,0,y,(unsigned long) (x1+1),1,&image->exception);
+ q=GetAuthenticPixels(floodplane_image,0,y,(unsigned long) (x1+1),1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ p+=x1;
+ q+=x1;
+ for (x=x1; x >= 0; x--)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ q--;
+ p--;
+ }
+ if (SyncAuthenticPixels(floodplane_image,&image->exception) == MagickFalse)
+ break;
+ skip=x >= x1 ? MagickTrue : MagickFalse;
+ if (skip == MagickFalse)
+ {
+ start=x+1;
+ if (start < x1)
+ PushSegmentStack(y,start,x1-1,-offset);
+ x=x1+1;
+ }
+ do
+ {
+ if (skip == MagickFalse)
+ {
+ if (x < (long) image->columns)
+ {
+ p=GetVirtualPixels(image,x,y,image->columns-x,1,
+ &image->exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (PixelPacket *) NULL))
+ break;
+ for ( ; x < (long) image->columns; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ q++;
+ p++;
+ }
+ if (SyncAuthenticPixels(floodplane_image,&image->exception) == MagickFalse)
+ break;
+ }
+ PushSegmentStack(y,start,x-1,offset);
+ if (x > (x2+1))
+ PushSegmentStack(y,x2+1,x-1,-offset);
+ }
+ skip=MagickFalse;
+ x++;
+ if (x <= x2)
+ {
+ p=GetVirtualPixels(image,x,y,(unsigned long) (x2-x+1),1,
+ &image->exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,(unsigned long) (x2-x+1),1,
+ &image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for ( ; x <= x2; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ if (method == FloodfillMethod)
+ {
+ if (IsColorSimilar(image,p,&target) != MagickFalse)
+ break;
+ }
+ else
+ if (IsColorSimilar(image,p,&target) == MagickFalse)
+ break;
+ p++;
+ q++;
+ }
+ }
+ start=x;
+ } while (x <= x2);
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Tile fill color onto floodplane.
+ */
+ p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,
+ &image->exception);
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (p->opacity != OpaqueOpacity)
+ q->opacity=opacity;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ }
+ segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
+ floodplane_image=DestroyImage(floodplane_image);
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o s a i c I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MosaicImages() Obsolete Function: Use MergeImageLayers() instead.
+%
+% The format of the MosaicImage method is:
+%
+% Image *MosaicImages(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image list to be composited together
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MosaicImages(Image *image,ExceptionInfo *exception)
+{
+ return(MergeImageLayers(image,MosaicLayer,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p a q u e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpaqueImage() changes any pixel that matches color with the color
+% defined by fill.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the OpaqueImage method is:
+%
+% MagickBooleanType OpaqueImage(Image *image,
+% const PixelPacket *target,const PixelPacket fill)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target: the RGB value of the target color.
+%
+% o fill: the replacement color.
+%
+*/
+MagickExport MagickBooleanType OpaqueImage(Image *image,
+ const PixelPacket target,const PixelPacket fill)
+{
+#define OpaqueImageTag "Opaque/Image"
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ register long
+ i;
+
+ /*
+ Make image color opaque.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.1.0");
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ switch (image->storage_class)
+ {
+ case DirectClass:
+ default:
+ {
+ /*
+ Make DirectClass image opaque.
+ */
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsColorSimilar(image,q,&target) != MagickFalse)
+ *q=fill;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,OpaqueImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case PseudoClass:
+ {
+ /*
+ Make PseudoClass image opaque.
+ */
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (IsColorSimilar(image,&image->colormap[i],&target) != MagickFalse)
+ image->colormap[i]=fill;
+ }
+ if (fill.opacity != OpaqueOpacity)
+ {
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsColorSimilar(image,q,&target) != MagickFalse)
+ q->opacity=fill.opacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ }
+ }
+ (void) SyncImage(image);
+ break;
+ }
+ }
+ if (fill.opacity != OpaqueOpacity)
+ image->matte=MagickTrue;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p e n C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenCacheView() opens a view into the pixel cache, using the
+% VirtualPixelMethod that is defined within the given image itself.
+%
+% The format of the OpenCacheView method is:
+%
+% CacheView *OpenCacheView(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport CacheView *OpenCacheView(const Image *image)
+{
+ return(AcquireCacheView(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a i n t F l o o d f i l l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PaintFloodfill() changes the color value of any pixel that matches
+% target and is an immediate neighbor. If the method FillToBorderMethod is
+% specified, the color value is changed for any neighbor pixel that does not
+% match the bordercolor member of image.
+%
+% By default target must match a particular pixel color exactly.
+% However, in many cases two colors may differ by a small amount. The
+% fuzz member of image defines how much tolerance is acceptable to
+% consider two colors as the same. For example, set fuzz to 10 and the
+% color red at intensities of 100 and 102 respectively are now
+% interpreted as the same color for the purposes of the floodfill.
+%
+% The format of the PaintFloodfillImage method is:
+%
+% MagickBooleanType PaintFloodfillImage(Image *image,
+% const ChannelType channel,const MagickPixelPacket target,const long x,
+% const long y,const DrawInfo *draw_info,const PaintMethod method)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel(s).
+%
+% o target: the RGB value of the target color.
+%
+% o x,y: the starting location of the operation.
+%
+% o draw_info: the draw info.
+%
+% o method: Choose either FloodfillMethod or FillToBorderMethod.
+%
+*/
+MagickExport MagickBooleanType PaintFloodfillImage(Image *image,
+ const ChannelType channel,const MagickPixelPacket *target,const long x,
+ const long y,const DrawInfo *draw_info,const PaintMethod method)
+{
+ MagickBooleanType
+ status;
+
+ status=FloodfillPaintImage(image,channel,draw_info,target,x,y,
+ method == FloodfillMethod ? MagickFalse : MagickTrue);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% P a i n t O p a q u e I m a g e %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PaintOpaqueImage() changes any pixel that matches color with the color
+% defined by fill.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the PaintOpaqueImage method is:
+%
+% MagickBooleanType PaintOpaqueImage(Image *image,
+% const PixelPacket *target,const PixelPacket *fill)
+% MagickBooleanType PaintOpaqueImageChannel(Image *image,
+% const ChannelType channel,const PixelPacket *target,
+% const PixelPacket *fill)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel(s).
+%
+% o target: the RGB value of the target color.
+%
+% o fill: the replacement color.
+%
+*/
+
+MagickExport MagickBooleanType PaintOpaqueImage(Image *image,
+ const MagickPixelPacket *target,const MagickPixelPacket *fill)
+{
+ return(PaintOpaqueImageChannel(image,DefaultChannels,target,fill));
+}
+
+MagickExport MagickBooleanType PaintOpaqueImageChannel(Image *image,
+ const ChannelType channel,const MagickPixelPacket *target,
+ const MagickPixelPacket *fill)
+{
+ return(OpaquePaintImageChannel(image,channel,target,fill,MagickFalse));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a i n t T r a n s p a r e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PaintTransparentImage() changes the opacity value associated with any pixel
+% that matches color to the value defined by opacity.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the PaintTransparentImage method is:
+%
+% MagickBooleanType PaintTransparentImage(Image *image,
+% const MagickPixelPacket *target,const Quantum opacity)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target: the RGB value of the target color.
+%
+% o opacity: the replacement opacity value.
+%
+*/
+MagickExport MagickBooleanType PaintTransparentImage(Image *image,
+ const MagickPixelPacket *target,const Quantum opacity)
+{
+ return(TransparentPaintImage(image,target,opacity,MagickFalse));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P a r s e I m a g e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseImageGeometry() is similar to GetGeometry() except the returned
+% geometry is modified as determined by the meta characters: %, !, <,
+% and >.
+%
+% The format of the ParseImageGeometry method is:
+%
+% int ParseImageGeometry(char *geometry,long *x,long *y,
+% unsigned long *width,unsigned long *height)
+%
+% A description of each parameter follows:
+%
+% o flags: Method ParseImageGeometry returns a bitmask that indicates
+% which of the four values were located in the geometry string.
+%
+% o image_geometry: Specifies a character string representing the geometry
+% specification.
+%
+% o x,y: A pointer to an integer. The x and y offset as determined by
+% the geometry specification is returned here.
+%
+% o width,height: A pointer to an unsigned integer. The width and height
+% as determined by the geometry specification is returned here.
+%
+*/
+MagickExport int ParseImageGeometry(const char *geometry,long *x,long *y,
+ unsigned long *width,unsigned long *height)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.1");
+ return((int) ParseMetaGeometry(geometry,x,y,width,height));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e S i z e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseSizeGeometry() returns a region as defined by the geometry string with
+% respect to the image dimensions and aspect ratio.
+%
+% The format of the ParseSizeGeometry method is:
+%
+% MagickStatusType ParseSizeGeometry(const Image *image,
+% const char *geometry,RectangeInfo *region_info)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o region_info: the region as defined by the geometry string.
+%
+*/
+MagickExport MagickStatusType ParseSizeGeometry(const Image *image,
+ const char *geometry,RectangleInfo *region_info)
+{
+ MagickStatusType
+ flags;
+
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.4.7");
+ SetGeometry(image,region_info);
+ flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
+ ®ion_info->width,®ion_info->height);
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P o p I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PopImageList() removes the last image in the list.
+%
+% The format of the PopImageList method is:
+%
+% Image *PopImageList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *PopImageList(Image **images)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(RemoveLastImageFromList(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P o p I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PopImagePixels() transfers one or more pixel components from the image pixel
+% cache to a user supplied buffer. The pixels are returned in network byte
+% order. MagickTrue is returned if the pixels are successfully transferred,
+% otherwise MagickFalse.
+%
+% The format of the PopImagePixels method is:
+%
+% size_t PopImagePixels(Image *,const QuantumType quantum,
+% unsigned char *destination)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum: Declare which pixel components to transfer (RGB, RGBA, etc).
+%
+% o destination: The components are transferred to this buffer.
+%
+*/
+MagickExport size_t PopImagePixels(Image *image,const QuantumType quantum,
+ unsigned char *destination)
+{
+ QuantumInfo
+ *quantum_info;
+
+ size_t
+ length;
+
+ quantum_info=AcquireQuantumInfo((const ImageInfo *) NULL,image);
+ if (quantum_info == (QuantumInfo *) NULL)
+ return(0);
+ length=ExportQuantumPixels(image,(const CacheView *) NULL,quantum_info,
+ quantum,destination,&image->exception);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ return(length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P o s t s c r i p t G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PostscriptGeometry() replaces any page mneumonic with the equivalent size in
+% picas.
+%
+% The format of the PostscriptGeometry method is:
+%
+% char *PostscriptGeometry(const char *page)
+%
+% A description of each parameter follows.
+%
+% o page: Specifies a pointer to an array of characters.
+% The string is either a Postscript page name (e.g. A4) or a postscript
+% page geometry (e.g. 612x792+36+36).
+%
+*/
+MagickExport char *PostscriptGeometry(const char *page)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.1");
+ return(GetPageGeometry(page));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P u s h I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PushImageList() adds an image to the end of the list.
+%
+% The format of the PushImageList method is:
+%
+% unsigned int PushImageList(Image *images,const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned int PushImageList(Image **images,const Image *image,
+ ExceptionInfo *exception)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ AppendImageToList(images,CloneImageList(image,exception));
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P u s h I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PushImagePixels() transfers one or more pixel components from a user
+% supplied buffer into the image pixel cache of an image. The pixels are
+% expected in network byte order. It returns MagickTrue if the pixels are
+% successfully transferred, otherwise MagickFalse.
+%
+% The format of the PushImagePixels method is:
+%
+% size_t PushImagePixels(Image *image,const QuantumType quantum,
+% const unsigned char *source)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum: Declare which pixel components to transfer (red, green, blue,
+% opacity, RGB, or RGBA).
+%
+% o source: The pixel components are transferred from this buffer.
+%
+*/
+MagickExport size_t PushImagePixels(Image *image,const QuantumType quantum,
+ const unsigned char *source)
+{
+ QuantumInfo
+ *quantum_info;
+
+ size_t
+ length;
+
+ quantum_info=AcquireQuantumInfo((const ImageInfo *) NULL,image);
+ if (quantum_info == (QuantumInfo *) NULL)
+ return(0);
+ length=ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,quantum,
+ source,&image->exception);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ return(length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u a n t i z a t i o n E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QuantizationError() measures the difference between the original and
+% quantized images. This difference is the total quantization error. The
+% error is computed by summing over all pixels in an image the distance
+% squared in RGB space between each reference pixel value and its quantized
+% value. These values are computed:
+%
+% o mean_error_per_pixel: This value is the mean error for any single
+% pixel in the image.
+%
+% o normalized_mean_square_error: This value is the normalized mean
+% quantization error for any single pixel in the image. This distance
+% measure is normalized to a range between 0 and 1. It is independent
+% of the range of red, green, and blue values in the image.
+%
+% o normalized_maximum_square_error: Thsi value is the normalized
+% maximum quantization error for any single pixel in the image. This
+% distance measure is normalized to a range between 0 and 1. It is
+% independent of the range of red, green, and blue values in your image.
+%
+%
+% The format of the QuantizationError method is:
+%
+% unsigned int QuantizationError(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: Specifies a pointer to an Image structure; returned from
+% ReadImage.
+%
+*/
+MagickExport unsigned int QuantizationError(Image *image)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.3");
+ return(GetImageQuantizeError(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% R a n d o m C h a n n e l T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RandomChannelThresholdImage() changes the value of individual pixels based
+% on the intensity of each pixel compared to a random threshold. The result
+% is a low-contrast, two color image.
+%
+% The format of the RandomChannelThresholdImage method is:
+%
+% unsigned int RandomChannelThresholdImage(Image *image,
+% const char *channel, const char *thresholds,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o thresholds: a geometry string containing LOWxHIGH thresholds.
+% If the string contains 2x2, 3x3, or 4x4, then an ordered
+% dither of order 2, 3, or 4 will be performed instead.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned int RandomChannelThresholdImage(Image *image,const char
+ *channel,const char *thresholds,ExceptionInfo *exception)
+{
+#define RandomChannelThresholdImageText " RandomChannelThreshold image... "
+
+ double
+ lower_threshold,
+ upper_threshold;
+
+ long
+ count,
+ y;
+
+ RandomInfo
+ *random_info;
+
+ static MagickRealType
+ o2[4]={0.2f, 0.6f, 0.8f, 0.4f},
+ o3[9]={0.1f, 0.6f, 0.3f, 0.7f, 0.5f, 0.8f, 0.4f, 0.9f, 0.2f},
+ o4[16]={0.1f, 0.7f, 1.1f, 0.3f, 1.0f, 0.5f, 1.5f, 0.8f, 1.4f, 1.6f, 0.6f,
+ 1.2f, 0.4f, 0.9f, 1.3f, 0.2f},
+ threshold=128;
+
+ unsigned int
+ status;
+
+ unsigned long
+ order;
+
+ /*
+ Threshold image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (thresholds == (const char *) NULL)
+ return(MagickTrue);
+ if (LocaleCompare(thresholds,"2x2") == 0)
+ order=2;
+ else
+ if (LocaleCompare(thresholds,"3x3") == 0)
+ order=3;
+ else
+ if (LocaleCompare(thresholds,"4x4") == 0)
+ order=4;
+ else
+ {
+ order=1;
+ lower_threshold=0;
+ upper_threshold=0;
+ count=sscanf(thresholds,"%lf[/x%%]%lf",&lower_threshold,
+ &upper_threshold);
+ if (strchr(thresholds,'%') != (char *) NULL)
+ {
+ upper_threshold*=(.01*QuantumRange);
+ lower_threshold*=(.01*QuantumRange);
+ }
+ if (count == 1)
+ upper_threshold=(MagickRealType) QuantumRange-lower_threshold;
+ }
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " RandomChannelThresholdImage: channel type=%s",channel);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " Thresholds: %s (%fx%f)",thresholds,lower_threshold,upper_threshold);
+ if (LocaleCompare(channel,"all") == 0 ||
+ LocaleCompare(channel,"intensity") == 0)
+ if (AcquireImageColormap(image,2) == MagickFalse)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ random_info=AcquireRandomInfo();
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register IndexPacket
+ index,
+ *__restrict indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ if (LocaleCompare(channel,"all") == 0 ||
+ LocaleCompare(channel,"intensity") == 0)
+ {
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ intensity;
+
+ intensity=(MagickRealType) PixelIntensityToQuantum(q);
+ if (order == 1)
+ {
+ if (intensity < lower_threshold)
+ threshold=lower_threshold;
+ else if (intensity > upper_threshold)
+ threshold=upper_threshold;
+ else
+ threshold=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info));
+ }
+ else if (order == 2)
+ threshold=(MagickRealType) QuantumRange*o2[(x%2)+2*(y%2)];
+ else if (order == 3)
+ threshold=(MagickRealType) QuantumRange*o3[(x%3)+3*(y%3)];
+ else if (order == 4)
+ threshold=(MagickRealType) QuantumRange*o4[(x%4)+4*(y%4)];
+ q->red=q->green=q->blue=(Quantum) (intensity <=
+ threshold ? 0 : QuantumRange);
+ index=(IndexPacket) (intensity <= threshold ? 0 : 1);
+ *indexes++=index;
+ q->red=q->green=q->blue=image->colormap[(long) index].red;
+ q++;
+ }
+ }
+ if (LocaleCompare(channel,"opacity") == 0 ||
+ LocaleCompare(channel,"all") == 0 ||
+ LocaleCompare(channel,"matte") == 0)
+ {
+ if (image->matte != MagickFalse)
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (order == 1)
+ {
+ if ((MagickRealType) q->opacity < lower_threshold)
+ threshold=lower_threshold;
+ else if ((MagickRealType) q->opacity > upper_threshold)
+ threshold=upper_threshold;
+ else
+ threshold=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info));
+ }
+ else if (order == 2)
+ threshold=(MagickRealType) QuantumRange*o2[(x%2)+2*(y%2)];
+ else if (order == 3)
+ threshold=(MagickRealType) QuantumRange*o3[(x%3)+3*(y%3)];
+ else if (order == 4)
+ threshold=(MagickRealType) QuantumRange*o4[(x%4)+4*(y%4)]/1.7;
+ q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
+ 0 : QuantumRange);
+ q++;
+ }
+ }
+ else
+ {
+ /* To Do: red, green, blue, cyan, magenta, yellow, black */
+ if (LocaleCompare(channel,"intensity") != 0)
+ ThrowBinaryException(OptionError,"UnrecognizedChannelType",
+ image->filename);
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ if (QuantumTick(y,image->rows) != MagickFalse)
+ {
+ status=MagickMonitor(RandomChannelThresholdImageText,y,image->rows,
+ exception);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ random_info=DestroyRandomInfo(random_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a c q u i r e M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReacquireMemory() changes the size of the memory and returns a pointer to
+% the (possibly moved) block. The contents will be unchanged up to the
+% lesser of the new and old sizes.
+%
+% The format of the ReacquireMemory method is:
+%
+% void ReacquireMemory(void **memory,const size_t size)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a memory allocation. On return the pointer
+% may change but the contents of the original allocation will not.
+%
+% o size: the new size of the allocated memory.
+%
+*/
+MagickExport void ReacquireMemory(void **memory,const size_t size)
+{
+ void
+ *allocation;
+
+ assert(memory != (void **) NULL);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (*memory == (void *) NULL)
+ {
+ *memory=AcquireMagickMemory(size);
+ return;
+ }
+ allocation=realloc(*memory,size);
+ if (allocation == (void *) NULL)
+ *memory=RelinquishMagickMemory(*memory);
+ *memory=allocation;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e A t t r i b u t e I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageAttributeIterator() resets the image attributes iterator. Use it
+% in conjunction with GetNextImageAttribute() to iterate over all the values
+% associated with an image.
+%
+% The format of the ResetImageAttributeIterator method is:
+%
+% ResetImageAttributeIterator(const ImageInfo *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void ResetImageAttributeIterator(const Image *image)
+{
+ ResetImagePropertyIterator(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t C a c h e V i e w P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetCacheViewPixels() gets pixels from the in-memory or disk pixel cache as
+% defined by the geometry parameters. A pointer to the pixels is returned
+% if the pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the SetCacheViewPixels method is:
+%
+% PixelPacket *SetCacheViewPixels(CacheView *cache_view,const long x,
+% const long y,const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *SetCacheViewPixels(CacheView *cache_view,const long x,
+ const long y,const unsigned long columns,const unsigned long rows)
+{
+ PixelPacket
+ *pixels;
+
+ pixels=QueueCacheViewAuthenticPixels(cache_view,x,y,columns,rows,
+ GetCacheViewException(cache_view));
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t C a c h e T h e s h o l d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetCacheThreshold() sets the amount of free memory allocated for the pixel
+% cache. Once this threshold is exceeded, all subsequent pixels cache
+% operations are to/from disk.
+%
+% The format of the SetCacheThreshold() method is:
+%
+% void SetCacheThreshold(const size_t threshold)
+%
+% A description of each parameter follows:
+%
+% o threshold: the number of megabytes of memory available to the pixel
+% cache.
+%
+*/
+MagickExport void SetCacheThreshold(const unsigned long size)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.1");
+ (void) SetMagickResourceLimit(MemoryResource,size*1024*1024);
+ (void) SetMagickResourceLimit(MapResource,2*size*1024*1024);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t E x c e p t i o n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetExceptionInfo() sets the exception severity.
+%
+% The format of the SetExceptionInfo method is:
+%
+% MagickBooleanType SetExceptionInfo(ExceptionInfo *exception,
+% ExceptionType severity)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+% o severity: the exception severity.
+%
+*/
+MagickExport MagickBooleanType SetExceptionInfo(ExceptionInfo *exception,
+ ExceptionType severity)
+{
+ assert(exception != (ExceptionInfo *) NULL);
+ ClearMagickException(exception);
+ exception->severity=severity;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImage() sets the red, green, and blue components of each pixel to
+% the image background color and the opacity component to the specified
+% level of transparency. The background color is defined by the
+% background_color member of the image.
+%
+% The format of the SetImage method is:
+%
+% void SetImage(Image *image,const Quantum opacity)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o opacity: Set each pixel to this level of transparency.
+%
+*/
+MagickExport void SetImage(Image *image,const Quantum opacity)
+{
+ long
+ y;
+
+ PixelPacket
+ background_color;
+
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.2.0");
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ background_color=image->background_color;
+ if (opacity != OpaqueOpacity)
+ background_color.opacity=opacity;
+ if (background_color.opacity != OpaqueOpacity)
+ {
+ (void) SetImageStorageClass(image,DirectClass);
+ image->matte=MagickTrue;
+ }
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ {
+ /*
+ Set colormapped or CMYK image.
+ */
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ *q++=background_color;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ indexes[x]=(IndexPacket) 0;
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ }
+ return;
+ }
+ /*
+ Set DirectClass image.
+ */
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ *q++=background_color;
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageAttribute() searches the list of image attributes and replaces the
+% attribute value. If it is not found in the list, the attribute name
+% and value is added to the list.
+%
+% The format of the SetImageAttribute method is:
+%
+% MagickBooleanType SetImageAttribute(Image *image,const char *key,
+% const char *value)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o key: the key.
+%
+% o value: the value.
+%
+*/
+MagickExport MagickBooleanType SetImageAttribute(Image *image,const char *key,
+ const char *value)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.3.1");
+ return(SetImageProperty(image,key,value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageList() inserts an image into the list at the specified position.
+%
+% The format of the SetImageList method is:
+%
+% unsigned int SetImageList(Image *images,const Image *image,
+% const long offset,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o image: the image.
+%
+% o offset: the position within the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned int SetImageList(Image **images,const Image *image,
+ const long offset,ExceptionInfo *exception)
+{
+ Image
+ *clone;
+
+ register long
+ i;
+
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ clone=CloneImageList(image,exception);
+ while (GetPreviousImageInList(*images) != (Image *) NULL)
+ (*images)=GetPreviousImageInList(*images);
+ for (i=0; i < offset; i++)
+ {
+ if (GetNextImageInList(*images) == (Image *) NULL)
+ return(MagickFalse);
+ (*images)=GetNextImageInList(*images);
+ }
+ InsertImageInList(images,clone);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImagePixels() queues a mutable pixel region.
+% If the region is successfully intialized a pointer to a PixelPacket
+% array representing the region is returned, otherwise NULL is returned.
+% The returned pointer may point to a temporary working buffer for the
+% pixels or it may point to the final location of the pixels in memory.
+%
+% Write-only access means that any existing pixel values corresponding to
+% the region are ignored. This useful while the initial image is being
+% created from scratch, or if the existing pixel values are to be
+% completely replaced without need to refer to their pre-existing values.
+% The application is free to read and write the pixel buffer returned by
+% SetImagePixels() any way it pleases. SetImagePixels() does not initialize
+% the pixel array values. Initializing pixel array values is the
+% application's responsibility.
+%
+% Performance is maximized if the selected region is part of one row, or
+% one or more full rows, since then there is opportunity to access the
+% pixels in-place (without a copy) if the image is in RAM, or in a
+% memory-mapped file. The returned pointer should *never* be deallocated
+% by the user.
+%
+% Pixels accessed via the returned pointer represent a simple array of type
+% PixelPacket. If the image type is CMYK or the storage class is PseudoClass,
+% call GetAuthenticIndexQueue() after invoking GetAuthenticPixels() to obtain
+% the black color component or the colormap indexes (of type IndexPacket)
+% corresponding to the region. Once the PixelPacket (and/or IndexPacket)
+% array has been updated, the changes must be saved back to the underlying
+% image using SyncAuthenticPixels() or they may be lost.
+%
+% The format of the SetImagePixels() method is:
+%
+% PixelPacket *SetImagePixels(Image *image,const long x,const long y,
+% const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o pixels: SetImagePixels returns a pointer to the pixels if they are
+% transferred, otherwise a NULL is returned.
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+MagickExport PixelPacket *SetImagePixels(Image *image,const long x,const long y,
+ const unsigned long columns,const unsigned long rows)
+{
+ return(QueueAuthenticPixels(image,x,y,columns,rows,&image->exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t M a g i c k R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetMagickRegistry() sets a blob into the registry and returns a unique ID.
+% If an error occurs, -1 is returned.
+%
+% The format of the SetMagickRegistry method is:
+%
+% long SetMagickRegistry(const RegistryType type,const void *blob,
+% const size_t length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o type: the registry type.
+%
+% o blob: the address of a Binary Large OBject.
+%
+% o length: For a registry type of ImageRegistryType use sizeof(Image)
+% otherise the blob length in number of bytes.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport long SetMagickRegistry(const RegistryType type,const void *blob,
+ const size_t magick_unused(length),ExceptionInfo *exception)
+{
+ char
+ key[MaxTextExtent];
+
+ MagickBooleanType
+ status;
+
+ static long
+ id = 0;
+
+ (void) FormatMagickString(key,MaxTextExtent,"%ld\n",id);
+ status=SetImageRegistry(type,key,blob,exception);
+ if (status == MagickFalse)
+ return(-1);
+ return(id++);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t M o n i t o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetMonitorHandler() sets the monitor handler to the specified method
+% and returns the previous monitor handler.
+%
+% The format of the SetMonitorHandler method is:
+%
+% MonitorHandler SetMonitorHandler(MonitorHandler handler)
+%
+% A description of each parameter follows:
+%
+% o handler: Specifies a pointer to a method to handle monitors.
+%
+*/
+
+MagickExport MonitorHandler GetMonitorHandler(void)
+{
+ return(monitor_handler);
+}
+
+MagickExport MonitorHandler SetMonitorHandler(MonitorHandler handler)
+{
+ MonitorHandler
+ previous_handler;
+
+ previous_handler=monitor_handler;
+ monitor_handler=handler;
+ return(previous_handler);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h i f t I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ShiftImageList() removes an image from the beginning of the list.
+%
+% The format of the ShiftImageList method is:
+%
+% Image *ShiftImageList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *ShiftImageList(Image **images)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ return(RemoveFirstImageFromList(images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S i z e B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SizeBlob() returns the current length of the image file or blob.
+%
+% The format of the SizeBlob method is:
+%
+% off_t SizeBlob(Image *image)
+%
+% A description of each parameter follows:
+%
+% o size: Method SizeBlob returns the current length of the image file
+% or blob.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickOffsetType SizeBlob(Image *image)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.4.3");
+ return((MagickOffsetType) GetBlobSize(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l i c e I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SpliceImageList() removes the images designated by offset and length from
+% the list and replaces them with the specified list.
+%
+% The format of the SpliceImageList method is:
+%
+% Image *SpliceImageList(Image *images,const long offset,
+% const unsigned long length,const Image *splices,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o offset: the position within the list.
+%
+% o length: the length of the image list to remove.
+%
+% o splice: Replace the removed image list with this list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SpliceImageList(Image *images,const long offset,
+ const unsigned long length,const Image *splices,ExceptionInfo *exception)
+{
+ Image
+ *clone;
+
+ register long
+ i;
+
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ clone=CloneImageList(splices,exception);
+ while (GetPreviousImageInList(images) != (Image *) NULL)
+ images=GetPreviousImageInList(images);
+ for (i=0; i < offset; i++)
+ {
+ if (GetNextImageInList(images) == (Image *) NULL)
+ return((Image *) NULL);
+ images=GetNextImageInList(images);
+ }
+ (void) SpliceImageIntoList(&images,length,clone);
+ return(images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Strip() strips any whitespace or quotes from the beginning and end of a
+% string of characters.
+%
+% The format of the Strip method is:
+%
+% void Strip(char *message)
+%
+% A description of each parameter follows:
+%
+% o message: Specifies an array of characters.
+%
+*/
+MagickExport void Strip(char *message)
+{
+ register char
+ *p,
+ *q;
+
+ assert(message != (char *) NULL);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (*message == '\0')
+ return;
+ if (strlen(message) == 1)
+ return;
+ p=message;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((*p == '\'') || (*p == '"'))
+ p++;
+ q=message+strlen(message)-1;
+ while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
+ q--;
+ if (q > p)
+ if ((*q == '\'') || (*q == '"'))
+ q--;
+ (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
+ message[q-p+1]='\0';
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c C a c h e V i e w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncCacheView() saves the cache view pixels to the in-memory or disk
+% cache. It returns MagickTrue if the pixel region is synced, otherwise
+% MagickFalse.
+%
+% The format of the SyncCacheView method is:
+%
+% MagickBooleanType SyncCacheView(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+*/
+MagickExport MagickBooleanType SyncCacheView(CacheView *cache_view)
+{
+ MagickBooleanType
+ status;
+
+ status=SyncCacheViewAuthenticPixels(cache_view,
+ GetCacheViewException(cache_view));
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c C a c h e V i e w P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncCacheViewPixels() saves the cache view pixels to the in-memory
+% or disk cache. It returns MagickTrue if the pixel region is flushed,
+% otherwise MagickFalse.
+%
+% The format of the SyncCacheViewPixels method is:
+%
+% MagickBooleanType SyncCacheViewPixels(CacheView *cache_view)
+%
+% A description of each parameter follows:
+%
+% o cache_view: the cache view.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType SyncCacheViewPixels(CacheView *cache_view)
+{
+ MagickBooleanType
+ status;
+
+ status=SyncCacheViewAuthenticPixels(cache_view,
+ GetCacheViewException(cache_view));
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncImagePixels() saves the image pixels to the in-memory or disk cache.
+% The method returns MagickTrue if the pixel region is synced, otherwise
+% MagickFalse.
+%
+% The format of the SyncImagePixels() method is:
+%
+% MagickBooleanType SyncImagePixels(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType SyncImagePixels(Image *image)
+{
+ return(SyncAuthenticPixels(image,&image->exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T e m p o r a r y F i l e n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TemporaryFilename() replaces the contents of path by a unique path name.
+%
+% The format of the TemporaryFilename method is:
+%
+% void TemporaryFilename(char *path)
+%
+% A description of each parameter follows.
+%
+% o path: Specifies a pointer to an array of characters. The unique path
+% name is returned in this array.
+%
+*/
+MagickExport void TemporaryFilename(char *path)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.6");
+ (void) AcquireUniqueFilename(path);
+ (void) RelinquishUniqueFileResource(path);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ThresholdImage() changes the value of individual pixels based on
+% the intensity of each pixel compared to threshold. The result is a
+% high-contrast, two color image.
+%
+% The format of the ThresholdImage method is:
+%
+% unsigned int ThresholdImage(Image *image,const double threshold)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o threshold: Define the threshold value
+%
+*/
+MagickExport unsigned int ThresholdImage(Image *image,const double threshold)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ IndexPacket
+ index;
+
+ long
+ y;
+
+ /*
+ Threshold image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.7");
+ if (!AcquireImageColormap(image,2))
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ "UnableToThresholdImage");
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ index=(IndexPacket) ((MagickRealType)
+ PixelIntensityToQuantum(q) <= threshold ? 0 : 1);
+ indexes[x]=index;
+ q->red=image->colormap[(long) index].red;
+ q->green=image->colormap[(long) index].green;
+ q->blue=image->colormap[(long) index].blue;
+ q++;
+ }
+ if (!SyncAuthenticPixels(image,&image->exception))
+ break;
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T h r e s h o l d I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ThresholdImageChannel() changes the value of individual pixels based on
+% the intensity of each pixel channel. The result is a high-contrast image.
+%
+% The format of the ThresholdImageChannel method is:
+%
+% unsigned int ThresholdImageChannel(Image *image,const char *threshold)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o threshold: define the threshold values.
+%
+*/
+MagickExport unsigned int ThresholdImageChannel(Image *image,
+ const char *threshold)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ MagickPixelPacket
+ pixel;
+
+ GeometryInfo
+ geometry_info;
+
+ IndexPacket
+ index;
+
+ long
+ y;
+
+ unsigned int
+ flags;
+
+ /*
+ Threshold image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (threshold == (const char *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ flags=ParseGeometry(threshold,&geometry_info);
+ pixel.red=geometry_info.rho;
+ if (flags & SigmaValue)
+ pixel.green=geometry_info.sigma;
+ else
+ pixel.green=pixel.red;
+ if (flags & XiValue)
+ pixel.blue=geometry_info.xi;
+ else
+ pixel.blue=pixel.red;
+ if (flags & PsiValue)
+ pixel.opacity=geometry_info.psi;
+ else
+ pixel.opacity=(MagickRealType) OpaqueOpacity;
+ if (flags & PercentValue)
+ {
+ pixel.red*=QuantumRange/100.0f;
+ pixel.green*=QuantumRange/100.0f;
+ pixel.blue*=QuantumRange/100.0f;
+ pixel.opacity*=QuantumRange/100.0f;
+ }
+ if (!(flags & SigmaValue))
+ {
+ if (!AcquireImageColormap(image,2))
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ "UnableToThresholdImage");
+ if (pixel.red == 0)
+ (void) GetImageDynamicThreshold(image,2.0,2.0,&pixel,&image->exception);
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ if (IsMagickGray(&pixel) != MagickFalse)
+ for (x=0; x < (long) image->columns; x++)
+ {
+ index=(IndexPacket) ((MagickRealType)
+ PixelIntensityToQuantum(q) <= pixel.red ? 0 : 1);
+ indexes[x]=index;
+ q->red=image->colormap[(long) index].red;
+ q->green=image->colormap[(long) index].green;
+ q->blue=image->colormap[(long) index].blue;
+ q++;
+ }
+ else
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=(Quantum) ((MagickRealType)
+ q->red <= pixel.red ? 0 : QuantumRange);
+ q->green=(Quantum) ((MagickRealType)
+ q->green <= pixel.green ? 0 : QuantumRange);
+ q->blue=(Quantum) ((MagickRealType)
+ q->blue <= pixel.blue ? 0 : QuantumRange);
+ q->opacity=(Quantum) ((MagickRealType)
+ q->opacity <= pixel.opacity ? 0 : QuantumRange);
+ q++;
+ }
+ if (!SyncAuthenticPixels(image,&image->exception))
+ break;
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ T r a n s f o r m C o l o r s p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformColorspace() converts the image to a specified colorspace.
+% If the image is already in the requested colorspace, no work is performed.
+% Note that the current colorspace is stored in the image colorspace member.
+% The transformation matrices are not necessarily the standard ones: the
+% weights are rescaled to normalize the range of the transformed values to
+% be [0..QuantumRange].
+%
+% The format of the TransformColorspace method is:
+%
+% unsigned int (void) TransformColorspace(Image *image,
+% const ColorspaceType colorspace)
+%
+% A description of each parameter follows:
+%
+% o image: the image to transform
+%
+% o colorspace: the desired colorspace.
+%
+*/
+MagickExport unsigned int TransformColorspace(Image *image,
+ const ColorspaceType colorspace)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.6");
+ return(TransformImageColorspace(image,colorspace));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s f o r m H S L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformHSL() converts a (red, green, blue) to a (hue, saturation,
+% lightness) triple.
+%
+% The format of the TransformHSL method is:
+%
+% void TransformHSL(const Quantum red,const Quantum green,
+% const Quantum blue,double *hue,double *saturation,double *lightness)
+%
+% A description of each parameter follows:
+%
+% o red, green, blue: A Quantum value representing the red, green, and
+% blue component of a pixel..
+%
+% o hue, saturation, lightness: A pointer to a double value representing a
+% component of the HSL color space.
+%
+*/
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport void TransformHSL(const Quantum red,const Quantum green,
+ const Quantum blue,double *hue,double *saturation,double *lightness)
+{
+ MagickRealType
+ b,
+ delta,
+ g,
+ max,
+ min,
+ r;
+
+ /*
+ Convert RGB to HSL colorspace.
+ */
+ assert(hue != (double *) NULL);
+ assert(saturation != (double *) NULL);
+ assert(lightness != (double *) NULL);
+ r=QuantumScale*red;
+ g=QuantumScale*green;
+ b=QuantumScale*blue;
+ max=MagickMax(r,MagickMax(g,b));
+ min=MagickMin(r,MagickMin(g,b));
+ *hue=0.0;
+ *saturation=0.0;
+ *lightness=(double) ((min+max)/2.0);
+ delta=max-min;
+ if (delta == 0.0)
+ return;
+ *saturation=(double) (delta/((*lightness < 0.5) ? (min+max) :
+ (2.0-max-min)));
+ if (r == max)
+ *hue=(double) (g == min ? 5.0+(max-b)/delta : 1.0-(max-g)/delta);
+ else
+ if (g == max)
+ *hue=(double) (b == min ? 1.0+(max-r)/delta : 3.0-(max-b)/delta);
+ else
+ *hue=(double) (r == min ? 3.0+(max-g)/delta : 5.0-(max-r)/delta);
+ *hue/=6.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s l a t e T e x t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TranslateText() replaces any embedded formatting characters with the
+% appropriate image attribute and returns the translated text.
+%
+% The format of the TranslateText method is:
+%
+% char *TranslateText(const ImageInfo *image_info,Image *image,
+% const char *embed_text)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o embed_text: the address of a character string containing the embedded
+% formatting characters.
+%
+*/
+MagickExport char *TranslateText(const ImageInfo *image_info,Image *image,
+ const char *embed_text)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.2.6");
+ return(InterpretImageProperties(image_info,image,embed_text));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s p a r e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransparentImage() changes the opacity value associated with any pixel
+% that matches color to the value defined by opacity.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the TransparentImage method is:
+%
+% MagickBooleanType TransparentImage(Image *image,
+% const PixelPacket target,const Quantum opacity)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target: the RGB value of the target color.
+%
+% o opacity: the replacement opacity value.
+%
+*/
+MagickExport MagickBooleanType TransparentImage(Image *image,
+ const PixelPacket target,const Quantum opacity)
+{
+#define TransparentImageTag "Transparent/Image"
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ /*
+ Make image color transparent.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v6.1.0");
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,&image->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsColorSimilar(image,q,&target) != MagickFalse)
+ q->opacity=opacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,&image->exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,TransparentImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n s h i f t I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnshiftImageList() adds the image to the beginning of the list.
+%
+% The format of the UnshiftImageList method is:
+%
+% unsigned int UnshiftImageList(Image *images,const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport unsigned int UnshiftImageList(Image **images,const Image *image,
+ ExceptionInfo *exception)
+{
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.5.2");
+ PrependImageToList(images,CloneImageList(image,exception));
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ V a l i d a t e C o l o r m a p I n d e x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ValidateColormapIndex() validates the colormap index. If the index does
+% not range from 0 to the number of colors in the colormap an exception
+% issued and 0 is returned.
+%
+% The format of the ValidateColormapIndex method is:
+%
+% IndexPacket ValidateColormapIndex(Image *image,const unsigned int index)
+%
+% A description of each parameter follows:
+%
+% o index: Method ValidateColormapIndex returns colormap index if it is
+% valid other an exception issued and 0 is returned.
+%
+% o image: the image.
+%
+% o index: This integer is the colormap index.
+%
+*/
+
+MagickExport IndexPacket ValidateColormapIndex(Image *image,
+ const unsigned long index)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DeprecateEvent,GetMagickModule(),"last use: v5.4.4");
+ return(ConstrainColormapIndex(image,index));
+}
+#endif
diff --git a/magick/deprecate.h b/magick/deprecate.h
new file mode 100644
index 0000000..b48eb40
--- /dev/null
+++ b/magick/deprecate.h
@@ -0,0 +1,286 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore deprecated methods.
+*/
+#ifndef _MAGICKCORE_DEPRECATE_H
+#define _MAGICKCORE_DEPRECATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if !defined(MAGICKCORE_EXCLUDE_DEPRECATED)
+
+#include <stdarg.h>
+#include "magick/blob.h"
+#include "magick/cache-view.h"
+#include "magick/draw.h"
+#include "magick/constitute.h"
+#include "magick/magick-config.h"
+#include "magick/pixel.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/registry.h"
+#include "magick/semaphore.h"
+
+#if !defined(magick_attribute)
+# if !defined(__GNUC__)
+# define magick_attribute(x) /*nothing*/
+# else
+# define magick_attribute __attribute__
+# endif
+#endif
+
+#define Downscale(quantum) ScaleQuantumToChar(quantum)
+#define LABColorspace LabColorspace
+#define Intensity(color) PixelIntensityToQuantum(color)
+#define LiberateUniqueFileResource(resource) \
+ RelinquishUniqueFileResource(resource)
+#define LiberateMagickResource(resource) RelinquishMagickResource(resource)
+#define LiberateSemaphore(semaphore) RelinquishSemaphore(semaphore)
+#define QuantumDepth MAGICKCORE_QUANTUM_DEPTH
+#define RunlengthEncodedCompression RLECompression
+#define Upscale(value) ScaleCharToQuantum(value)
+#define XDownscale(value) ScaleShortToQuantum(value)
+#define XUpscale(quantum) ScaleQuantumToShort(quantum)
+
+typedef struct _DoublePixelPacket
+{
+ double
+ red,
+ green,
+ blue,
+ opacity,
+ index;
+} DoublePixelPacket;
+
+typedef enum
+{
+ UndefinedMagickLayerMethod
+} MagickLayerMethod;
+
+typedef MagickOffsetType ExtendedSignedIntegralType;
+typedef MagickSizeType ExtendedUnsignedIntegralType;
+typedef MagickRealType ExtendedRationalType;
+typedef struct _ViewInfo ViewInfo;
+
+typedef MagickBooleanType
+ (*MonitorHandler)(const char *,const MagickOffsetType,const MagickSizeType,
+ ExceptionInfo *);
+
+typedef struct _ImageAttribute
+{
+ char
+ *key,
+ *value;
+
+ MagickBooleanType
+ compression;
+
+ struct _ImageAttribute
+ *previous,
+ *next; /* deprecated */
+} ImageAttribute;
+
+extern MagickExport char
+ *AllocateString(const char *),
+ *InterpretImageAttributes(const ImageInfo *,Image *,const char *),
+ *PostscriptGeometry(const char *),
+ *TranslateText(const ImageInfo *,Image *,const char *);
+
+extern MagickExport const ImageAttribute
+ *GetImageAttribute(const Image *,const char *),
+ *GetImageClippingPathAttribute(Image *),
+ *GetNextImageAttribute(const Image *);
+
+extern MagickExport const IndexPacket
+ *AcquireCacheViewIndexes(const CacheView *),
+ *AcquireIndexes(const Image *);
+
+extern MagickExport const PixelPacket
+ *AcquirePixels(const Image *),
+ *AcquireCacheViewPixels(const CacheView *,const long,const long,
+ const unsigned long,const unsigned long,ExceptionInfo *),
+ *AcquireImagePixels(const Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *);
+
+extern MagickExport Image
+ *AllocateImage(const ImageInfo *),
+ *ExtractSubimageFromImage(Image *,const Image *,ExceptionInfo *),
+ *GetImageFromMagickRegistry(const char *,long *id,ExceptionInfo *),
+ *GetImageList(const Image *,const long,ExceptionInfo *),
+ *GetNextImage(const Image *),
+ *GetPreviousImage(const Image *),
+ *FlattenImages(Image *,ExceptionInfo *),
+ *MosaicImages(Image *,ExceptionInfo *),
+ *PopImageList(Image **),
+ *ShiftImageList(Image **),
+ *SpliceImageList(Image *,const long,const unsigned long,const Image *,
+ ExceptionInfo *);
+
+extern MagickExport IndexPacket
+ *GetCacheViewIndexes(CacheView *),
+ *GetIndexes(const Image *),
+ ValidateColormapIndex(Image *,const unsigned long);
+
+extern MagickExport int
+ GetImageGeometry(Image *,const char *,const unsigned int,RectangleInfo *),
+ ParseImageGeometry(const char *,long *,long *,unsigned long *,
+ unsigned long *);
+
+extern MagickExport long
+ GetImageListIndex(const Image *),
+ SetMagickRegistry(const RegistryType,const void *,const size_t,
+ ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ AcquireOneCacheViewPixel(const CacheView *,const long,const long,
+ PixelPacket *,ExceptionInfo *),
+ AcquireOneCacheViewVirtualPixel(const CacheView *,const VirtualPixelMethod,
+ const long,const long,PixelPacket *,ExceptionInfo *),
+ AffinityImage(const QuantizeInfo *,Image *,const Image *),
+ AffinityImages(const QuantizeInfo *,Image *,const Image *),
+ AllocateImageColormap(Image *,const unsigned long),
+ ClipPathImage(Image *,const char *,const MagickBooleanType),
+ CloneImageAttributes(Image *,const Image *),
+ ColorFloodfillImage(Image *,const DrawInfo *,const PixelPacket,const long,
+ const long,const PaintMethod),
+ DeleteImageAttribute(Image *,const char *),
+ DeleteMagickRegistry(const long),
+ DescribeImage(Image *,FILE *,const MagickBooleanType),
+ FormatImageAttribute(Image *,const char *,const char *,...)
+ magick_attribute((format (printf,3,4))),
+ FormatImageAttributeList(Image *,const char *,const char *,va_list)
+ magick_attribute((format (printf,3,0))),
+ FuzzyColorCompare(const Image *,const PixelPacket *,const PixelPacket *),
+ FuzzyOpacityCompare(const Image *,const PixelPacket *,const PixelPacket *),
+ MagickMonitor(const char *,const MagickOffsetType,const MagickSizeType,
+ void *),
+ MapImage(Image *,const Image *,const MagickBooleanType),
+ MapImages(Image *,const Image *,const MagickBooleanType),
+ MatteFloodfillImage(Image *,const PixelPacket,const Quantum,const long,
+ const long,const PaintMethod),
+ OpaqueImage(Image *,const PixelPacket,const PixelPacket),
+ PaintFloodfillImage(Image *,const ChannelType,const MagickPixelPacket *,
+ const long,const long,const DrawInfo *,const PaintMethod),
+ PaintOpaqueImage(Image *,const MagickPixelPacket *,const MagickPixelPacket *),
+ PaintOpaqueImageChannel(Image *,const ChannelType,const MagickPixelPacket *,
+ const MagickPixelPacket *),
+ PaintTransparentImage(Image *,const MagickPixelPacket *,const Quantum),
+ SetExceptionInfo(ExceptionInfo *,ExceptionType),
+ SetImageAttribute(Image *,const char *,const char *),
+ SyncCacheViewPixels(CacheView *),
+ SyncImagePixels(Image *),
+ TransparentImage(Image *,const PixelPacket,const Quantum);
+
+extern MagickExport MagickPixelPacket
+ AcquireOneMagickPixel(const Image *,const long,const long,ExceptionInfo *);
+
+extern MagickExport MonitorHandler
+ GetMonitorHandler(void),
+ SetMonitorHandler(MonitorHandler);
+
+extern MagickExport MagickOffsetType
+ SizeBlob(Image *image);
+
+extern MagickExport MagickPixelPacket
+ InterpolatePixelColor(const Image *,CacheView *,const InterpolatePixelMethod,
+ const double,const double,ExceptionInfo *);
+
+extern MagickExport MagickStatusType
+ ParseSizeGeometry(const Image *,const char *,RectangleInfo *);
+
+extern MagickExport PixelPacket
+ AcquireOnePixel(const Image *,const long,const long,ExceptionInfo *),
+ AcquireOneVirtualPixel(const Image *,const VirtualPixelMethod,const long,
+ const long,ExceptionInfo *),
+ *GetCacheView(CacheView *,const long,const long,const unsigned long,
+ const unsigned long),
+ *GetCacheViewPixels(CacheView *,const long,const long,const unsigned long,
+ const unsigned long),
+ *GetImagePixels(Image *,const long,const long,const unsigned long,
+ const unsigned long),
+ GetOnePixel(Image *,const long,const long),
+ *GetPixels(const Image *),
+ *SetCacheViewPixels(CacheView *,const long,const long,const unsigned long,
+ const unsigned long),
+ *SetImagePixels(Image *,const long,const long,const unsigned long,
+ const unsigned long);
+
+extern MagickExport size_t
+ PopImagePixels(Image *,const QuantumType,unsigned char *),
+ PushImagePixels(Image *,const QuantumType,const unsigned char *);
+
+extern MagickExport unsigned int
+ ChannelImage(Image *,const ChannelType),
+ ChannelThresholdImage(Image *,const char *),
+ DispatchImage(const Image *,const long,const long,const unsigned long,
+ const unsigned long,const char *,const StorageType,void *,ExceptionInfo *),
+ FuzzyColorMatch(const PixelPacket *,const PixelPacket *,const double),
+ GetNumberScenes(const Image *),
+ GetMagickGeometry(const char *,long *,long *,unsigned long *,unsigned long *),
+ IsSubimage(const char *,const unsigned int),
+ PushImageList(Image **,const Image *,ExceptionInfo *),
+ QuantizationError(Image *),
+ RandomChannelThresholdImage(Image *,const char *,const char *,
+ ExceptionInfo *),
+ SetImageList(Image **,const Image *,const long,ExceptionInfo *),
+ TransformColorspace(Image *,const ColorspaceType),
+ ThresholdImage(Image *,const double),
+ ThresholdImageChannel(Image *,const char *),
+ UnshiftImageList(Image **,const Image *,ExceptionInfo *);
+
+extern MagickExport unsigned long
+ GetImageListSize(const Image *);
+
+extern MagickExport CacheView
+ *CloseCacheView(CacheView *),
+ *OpenCacheView(const Image *);
+
+extern MagickExport void
+ *AcquireMemory(const size_t),
+ AllocateNextImage(const ImageInfo *,Image *),
+ *CloneMemory(void *,const void *,const size_t),
+ DestroyImageAttributes(Image *),
+ DestroyImages(Image *),
+ DestroyMagick(void),
+ DestroyMagickRegistry(void),
+ *GetConfigureBlob(const char *,char *,size_t *,ExceptionInfo *),
+ *GetMagickRegistry(const long,RegistryType *,size_t *,ExceptionInfo *),
+ IdentityAffine(AffineMatrix *),
+ LiberateMemory(void **),
+ LiberateSemaphoreInfo(SemaphoreInfo **),
+ FormatString(char *,const char *,...) magick_attribute((format (printf,2,3))),
+ FormatStringList(char *,const char *,va_list)
+ magick_attribute((format (printf,2,0))),
+ HSLTransform(const double,const double,const double,Quantum *,Quantum *,
+ Quantum *),
+ InitializeMagick(const char *),
+ ReacquireMemory(void **,const size_t),
+ ResetImageAttributeIterator(const Image *),
+ SetCacheThreshold(const unsigned long),
+ SetImage(Image *,const Quantum),
+ Strip(char *),
+ TemporaryFilename(char *),
+ TransformHSL(const Quantum,const Quantum,const Quantum,double *,double *,
+ double *);
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/display-private.h b/magick/display-private.h
new file mode 100644
index 0000000..0664fa4
--- /dev/null
+++ b/magick/display-private.h
@@ -0,0 +1,40 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore methods to interactively display and edit an image.
+*/
+#ifndef _MAGICKCORE_DISPLAY_PRIVATE_H
+#define _MAGICKCORE_DISPLAY_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+#include "magick/xwindow-private.h"
+
+extern MagickExport Image
+ *XDisplayImage(Display *,XResourceInfo *,char **,int,Image **,
+ unsigned long *);
+
+extern MagickExport MagickBooleanType XDisplayBackgroundImage(Display *,
+ XResourceInfo *,Image *);
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/display.c b/magick/display.c
new file mode 100644
index 0000000..04346c4
--- /dev/null
+++ b/magick/display.c
@@ -0,0 +1,16000 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% DDDD IIIII SSSSS PPPP L AAA Y Y %
+% D D I SS P P L A A Y Y %
+% D D I SSS PPPP L AAAAA Y %
+% D D I SS P L A A Y %
+% DDDD IIIII SSSSS P LLLLL A A Y %
+% %
+% %
+% MagickCore Methods to Interactively Display and Edit an Image %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/colorspace.h"
+#include "magick/composite.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/delegate.h"
+#include "magick/display.h"
+#include "magick/display-private.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/paint.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/PreRvIcccm.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/shear.h"
+#include "magick/segment.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+#include "magick/threshold.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#include "magick/widget.h"
+#include "magick/xwindow-private.h"
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+/*
+ Define declarations.
+*/
+#define MaxColors MagickMin(windows->visual_info->colormap_size,256L)
+
+/*
+ Constant declarations.
+*/
+static const unsigned char
+ HighlightBitmap[8] =
+ {
+ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
+ },
+ ShadowBitmap[8] =
+ {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+static const char
+ *PageSizes[] =
+ {
+ "Letter",
+ "Tabloid",
+ "Ledger",
+ "Legal",
+ "Statement",
+ "Executive",
+ "A3",
+ "A4",
+ "A5",
+ "B4",
+ "B5",
+ "Folio",
+ "Quarto",
+ "10x14",
+ (char *) NULL
+ };
+
+/*
+ Help widget declarations.
+*/
+static const char
+ *ImageAnnotateHelp[] =
+ {
+ "In annotate mode, the Command widget has these options:",
+ "",
+ " Font Name",
+ " fixed",
+ " variable",
+ " 5x8",
+ " 6x10",
+ " 7x13bold",
+ " 8x13bold",
+ " 9x15bold",
+ " 10x20",
+ " 12x24",
+ " Browser...",
+ " Font Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " transparent",
+ " Browser...",
+ " Font Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " transparent",
+ " Browser...",
+ " Rotate Text",
+ " -90",
+ " -45",
+ " -30",
+ " 0",
+ " 30",
+ " 45",
+ " 90",
+ " 180",
+ " Dialog...",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a font name from the Font Name sub-menu. Additional",
+ "font names can be specified with the font browser. You can",
+ "change the menu names by setting the X resources font1",
+ "through font9.",
+ "",
+ "Choose a font color from the Font Color sub-menu.",
+ "Additional font colors can be specified with the color",
+ "browser. You can change the menu colors by setting the X",
+ "resources pen1 through pen9.",
+ "",
+ "If you select the color browser and press Grab, you can",
+ "choose the font color by moving the pointer to the desired",
+ "color on the screen and press any button.",
+ "",
+ "If you choose to rotate the text, choose Rotate Text from the",
+ "menu and select an angle. Typically you will only want to",
+ "rotate one line of text at a time. Depending on the angle you",
+ "choose, subsequent lines may end up overwriting each other.",
+ "",
+ "Choosing a font and its color is optional. The default font",
+ "is fixed and the default color is black. However, you must",
+ "choose a location to begin entering text and press button 1.",
+ "An underscore character will appear at the location of the",
+ "pointer. The cursor changes to a pencil to indicate you are",
+ "in text mode. To exit immediately, press Dismiss.",
+ "",
+ "In text mode, any key presses will display the character at",
+ "the location of the underscore and advance the underscore",
+ "cursor. Enter your text and once completed press Apply to",
+ "finish your image annotation. To correct errors press BACK",
+ "SPACE. To delete an entire line of text, press DELETE. Any",
+ "text that exceeds the boundaries of the image window is",
+ "automagically continued onto the next line.",
+ "",
+ "The actual color you request for the font is saved in the",
+ "image. However, the color that appears in your image window",
+ "may be different. For example, on a monochrome screen the",
+ "text will appear black or white even if you choose the color",
+ "red as the font color. However, the image saved to a file",
+ "with -write is written with red lettering. To assure the",
+ "correct color text in the final image, any PseudoClass image",
+ "is promoted to DirectClass (see miff(5)). To force a",
+ "PseudoClass image to remain PseudoClass, use -colors.",
+ (char *) NULL,
+ },
+ *ImageChopHelp[] =
+ {
+ "In chop mode, the Command widget has these options:",
+ "",
+ " Direction",
+ " horizontal",
+ " vertical",
+ " Help",
+ " Dismiss",
+ "",
+ "If the you choose the horizontal direction (this the",
+ "default), the area of the image between the two horizontal",
+ "endpoints of the chop line is removed. Otherwise, the area",
+ "of the image between the two vertical endpoints of the chop",
+ "line is removed.",
+ "",
+ "Select a location within the image window to begin your chop,",
+ "press and hold any button. Next, move the pointer to",
+ "another location in the image. As you move a line will",
+ "connect the initial location and the pointer. When you",
+ "release the button, the area within the image to chop is",
+ "determined by which direction you choose from the Command",
+ "widget.",
+ "",
+ "To cancel the image chopping, move the pointer back to the",
+ "starting point of the line and release the button.",
+ (char *) NULL,
+ },
+ *ImageColorEditHelp[] =
+ {
+ "In color edit mode, the Command widget has these options:",
+ "",
+ " Method",
+ " point",
+ " replace",
+ " floodfill",
+ " filltoborder",
+ " reset",
+ " Pixel Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " Browser...",
+ " Border Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " Browser...",
+ " Fuzz",
+ " 0%",
+ " 2%",
+ " 5%",
+ " 10%",
+ " 15%",
+ " Dialog...",
+ " Undo",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a color editing method from the Method sub-menu",
+ "of the Command widget. The point method recolors any pixel",
+ "selected with the pointer until the button is released. The",
+ "replace method recolors any pixel that matches the color of",
+ "the pixel you select with a button press. Floodfill recolors",
+ "any pixel that matches the color of the pixel you select with",
+ "a button press and is a neighbor. Whereas filltoborder recolors",
+ "any neighbor pixel that is not the border color. Finally reset",
+ "changes the entire image to the designated color.",
+ "",
+ "Next, choose a pixel color from the Pixel Color sub-menu.",
+ "Additional pixel colors can be specified with the color",
+ "browser. You can change the menu colors by setting the X",
+ "resources pen1 through pen9.",
+ "",
+ "Now press button 1 to select a pixel within the image window",
+ "to change its color. Additional pixels may be recolored as",
+ "prescribed by the method you choose.",
+ "",
+ "If the Magnify widget is mapped, it can be helpful in positioning",
+ "your pointer within the image (refer to button 2).",
+ "",
+ "The actual color you request for the pixels is saved in the",
+ "image. However, the color that appears in your image window",
+ "may be different. For example, on a monochrome screen the",
+ "pixel will appear black or white even if you choose the",
+ "color red as the pixel color. However, the image saved to a",
+ "file with -write is written with red pixels. To assure the",
+ "correct color text in the final image, any PseudoClass image",
+ "is promoted to DirectClass (see miff(5)). To force a",
+ "PseudoClass image to remain PseudoClass, use -colors.",
+ (char *) NULL,
+ },
+ *ImageCompositeHelp[] =
+ {
+ "First a widget window is displayed requesting you to enter an",
+ "image name. Press Composite, Grab or type a file name.",
+ "Press Cancel if you choose not to create a composite image.",
+ "When you choose Grab, move the pointer to the desired window",
+ "and press any button.",
+ "",
+ "If the Composite image does not have any matte information,",
+ "you are informed and the file browser is displayed again.",
+ "Enter the name of a mask image. The image is typically",
+ "grayscale and the same size as the composite image. If the",
+ "image is not grayscale, it is converted to grayscale and the",
+ "resulting intensities are used as matte information.",
+ "",
+ "A small window appears showing the location of the cursor in",
+ "the image window. You are now in composite mode. To exit",
+ "immediately, press Dismiss. In composite mode, the Command",
+ "widget has these options:",
+ "",
+ " Operators",
+ " Over",
+ " In",
+ " Out",
+ " Atop",
+ " Xor",
+ " Plus",
+ " Minus",
+ " Add",
+ " Subtract",
+ " Difference",
+ " Multiply",
+ " Bumpmap",
+ " Copy",
+ " CopyRed",
+ " CopyGreen",
+ " CopyBlue",
+ " CopyOpacity",
+ " Clear",
+ " Dissolve",
+ " Displace",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a composite operation from the Operators sub-menu of",
+ "the Command widget. How each operator behaves is described",
+ "below. Image window is the image currently displayed on",
+ "your X server and image is the image obtained with the File",
+ "Browser widget.",
+ "",
+ "Over The result is the union of the two image shapes,",
+ " with image obscuring image window in the region of",
+ " overlap.",
+ "",
+ "In The result is simply image cut by the shape of",
+ " image window. None of the image data of image",
+ " window is in the result.",
+ "",
+ "Out The resulting image is image with the shape of",
+ " image window cut out.",
+ "",
+ "Atop The result is the same shape as image image window,",
+ " with image obscuring image window where the image",
+ " shapes overlap. Note this differs from over",
+ " because the portion of image outside image window's",
+ " shape does not appear in the result.",
+ "",
+ "Xor The result is the image data from both image and",
+ " image window that is outside the overlap region.",
+ " The overlap region is blank.",
+ "",
+ "Plus The result is just the sum of the image data.",
+ " Output values are cropped to QuantumRange (no overflow).",
+ "",
+ "Minus The result of image - image window, with underflow",
+ " cropped to zero.",
+ "",
+ "Add The result of image + image window, with overflow",
+ " wrapping around (mod 256).",
+ "",
+ "Subtract The result of image - image window, with underflow",
+ " wrapping around (mod 256). The add and subtract",
+ " operators can be used to perform reversible",
+ " transformations.",
+ "",
+ "Difference",
+ " The result of abs(image - image window). This",
+ " useful for comparing two very similar images.",
+ "",
+ "Multiply",
+ " The result of image * image window. This",
+ " useful for the creation of drop-shadows.",
+ "",
+ "Bumpmap The result of surface normals from image * image",
+ " window.",
+ "",
+ "Copy The resulting image is image window replaced with",
+ " image. Here the matte information is ignored.",
+ "",
+ "CopyRed The red layer of the image window is replace with",
+ " the red layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyGreen",
+ " The green layer of the image window is replace with",
+ " the green layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyBlue The blue layer of the image window is replace with",
+ " the blue layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyOpacity",
+ " The matte layer of the image window is replace with",
+ " the matte layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "The image compositor requires a matte, or alpha channel in",
+ "the image for some operations. This extra channel usually",
+ "defines a mask which represents a sort of a cookie-cutter",
+ "for the image. This the case when matte is opaque (full",
+ "coverage) for pixels inside the shape, zero outside, and",
+ "between 0 and QuantumRange on the boundary. If image does not",
+ "have a matte channel, it is initialized with 0 for any pixel",
+ "matching in color to pixel location (0,0), otherwise QuantumRange.",
+ "",
+ "If you choose Dissolve, the composite operator becomes Over. The",
+ "image matte channel percent transparency is initialized to factor.",
+ "The image window is initialized to (100-factor). Where factor is the",
+ "value you specify in the Dialog widget.",
+ "",
+ "Displace shifts the image pixels as defined by a displacement",
+ "map. With this option, image is used as a displacement map.",
+ "Black, within the displacement map, is a maximum positive",
+ "displacement. White is a maximum negative displacement and",
+ "middle gray is neutral. The displacement is scaled to determine",
+ "the pixel shift. By default, the displacement applies in both the",
+ "horizontal and vertical directions. However, if you specify a mask,",
+ "image is the horizontal X displacement and mask the vertical Y",
+ "displacement.",
+ "",
+ "Note that matte information for image window is not retained",
+ "for colormapped X server visuals (e.g. StaticColor,",
+ "StaticColor, GrayScale, PseudoColor). Correct compositing",
+ "behavior may require a TrueColor or DirectColor visual or a",
+ "Standard Colormap.",
+ "",
+ "Choosing a composite operator is optional. The default",
+ "operator is replace. However, you must choose a location to",
+ "composite your image and press button 1. Press and hold the",
+ "button before releasing and an outline of the image will",
+ "appear to help you identify your location.",
+ "",
+ "The actual colors of the composite image is saved. However,",
+ "the color that appears in image window may be different.",
+ "For example, on a monochrome screen image window will appear",
+ "black or white even though your composited image may have",
+ "many colors. If the image is saved to a file it is written",
+ "with the correct colors. To assure the correct colors are",
+ "saved in the final image, any PseudoClass image is promoted",
+ "to DirectClass (see miff(5)). To force a PseudoClass image",
+ "to remain PseudoClass, use -colors.",
+ (char *) NULL,
+ },
+ *ImageCutHelp[] =
+ {
+ "In cut mode, the Command widget has these options:",
+ "",
+ " Help",
+ " Dismiss",
+ "",
+ "To define a cut region, press button 1 and drag. The",
+ "cut region is defined by a highlighted rectangle that",
+ "expands or contracts as it follows the pointer. Once you",
+ "are satisfied with the cut region, release the button.",
+ "You are now in rectify mode. In rectify mode, the Command",
+ "widget has these options:",
+ "",
+ " Cut",
+ " Help",
+ " Dismiss",
+ "",
+ "You can make adjustments by moving the pointer to one of the",
+ "cut rectangle corners, pressing a button, and dragging.",
+ "Finally, press Cut to commit your copy region. To",
+ "exit without cutting the image, press Dismiss.",
+ (char *) NULL,
+ },
+ *ImageCopyHelp[] =
+ {
+ "In copy mode, the Command widget has these options:",
+ "",
+ " Help",
+ " Dismiss",
+ "",
+ "To define a copy region, press button 1 and drag. The",
+ "copy region is defined by a highlighted rectangle that",
+ "expands or contracts as it follows the pointer. Once you",
+ "are satisfied with the copy region, release the button.",
+ "You are now in rectify mode. In rectify mode, the Command",
+ "widget has these options:",
+ "",
+ " Copy",
+ " Help",
+ " Dismiss",
+ "",
+ "You can make adjustments by moving the pointer to one of the",
+ "copy rectangle corners, pressing a button, and dragging.",
+ "Finally, press Copy to commit your copy region. To",
+ "exit without copying the image, press Dismiss.",
+ (char *) NULL,
+ },
+ *ImageCropHelp[] =
+ {
+ "In crop mode, the Command widget has these options:",
+ "",
+ " Help",
+ " Dismiss",
+ "",
+ "To define a cropping region, press button 1 and drag. The",
+ "cropping region is defined by a highlighted rectangle that",
+ "expands or contracts as it follows the pointer. Once you",
+ "are satisfied with the cropping region, release the button.",
+ "You are now in rectify mode. In rectify mode, the Command",
+ "widget has these options:",
+ "",
+ " Crop",
+ " Help",
+ " Dismiss",
+ "",
+ "You can make adjustments by moving the pointer to one of the",
+ "cropping rectangle corners, pressing a button, and dragging.",
+ "Finally, press Crop to commit your cropping region. To",
+ "exit without cropping the image, press Dismiss.",
+ (char *) NULL,
+ },
+ *ImageDrawHelp[] =
+ {
+ "The cursor changes to a crosshair to indicate you are in",
+ "draw mode. To exit immediately, press Dismiss. In draw mode,",
+ "the Command widget has these options:",
+ "",
+ " Element",
+ " point",
+ " line",
+ " rectangle",
+ " fill rectangle",
+ " circle",
+ " fill circle",
+ " ellipse",
+ " fill ellipse",
+ " polygon",
+ " fill polygon",
+ " Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " transparent",
+ " Browser...",
+ " Stipple",
+ " Brick",
+ " Diagonal",
+ " Scales",
+ " Vertical",
+ " Wavy",
+ " Translucent",
+ " Opaque",
+ " Open...",
+ " Width",
+ " 1",
+ " 2",
+ " 4",
+ " 8",
+ " 16",
+ " Dialog...",
+ " Undo",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a drawing primitive from the Element sub-menu.",
+ "",
+ "Choose a color from the Color sub-menu. Additional",
+ "colors can be specified with the color browser.",
+ "",
+ "If you choose the color browser and press Grab, you can",
+ "select the color by moving the pointer to the desired",
+ "color on the screen and press any button. The transparent",
+ "color updates the image matte channel and is useful for",
+ "image compositing.",
+ "",
+ "Choose a stipple, if appropriate, from the Stipple sub-menu.",
+ "Additional stipples can be specified with the file browser.",
+ "Stipples obtained from the file browser must be on disk in the",
+ "X11 bitmap format.",
+ "",
+ "Choose a width, if appropriate, from the Width sub-menu. To",
+ "choose a specific width select the Dialog widget.",
+ "",
+ "Choose a point in the Image window and press button 1 and",
+ "hold. Next, move the pointer to another location in the",
+ "image. As you move, a line connects the initial location and",
+ "the pointer. When you release the button, the image is",
+ "updated with the primitive you just drew. For polygons, the",
+ "image is updated when you press and release the button without",
+ "moving the pointer.",
+ "",
+ "To cancel image drawing, move the pointer back to the",
+ "starting point of the line and release the button.",
+ (char *) NULL,
+ },
+ *DisplayHelp[] =
+ {
+ "BUTTONS",
+ " The effects of each button press is described below. Three",
+ " buttons are required. If you have a two button mouse,",
+ " button 1 and 3 are returned. Press ALT and button 3 to",
+ " simulate button 2.",
+ "",
+ " 1 Press this button to map or unmap the Command widget.",
+ "",
+ " 2 Press and drag to define a region of the image to",
+ " magnify.",
+ "",
+ " 3 Press and drag to choose from a select set of commands.",
+ " This button behaves differently if the image being",
+ " displayed is a visual image directory. Here, choose a",
+ " particular tile of the directory and press this button and",
+ " drag to select a command from a pop-up menu. Choose from",
+ " these menu items:",
+ "",
+ " Open",
+ " Next",
+ " Former",
+ " Delete",
+ " Update",
+ "",
+ " If you choose Open, the image represented by the tile is",
+ " displayed. To return to the visual image directory, choose",
+ " Next from the Command widget. Next and Former moves to the",
+ " next or former image respectively. Choose Delete to delete",
+ " a particular image tile. Finally, choose Update to",
+ " synchronize all the image tiles with their respective",
+ " images.",
+ "",
+ "COMMAND WIDGET",
+ " The Command widget lists a number of sub-menus and commands.",
+ " They are",
+ "",
+ " File",
+ " Open...",
+ " Next",
+ " Former",
+ " Select...",
+ " Save...",
+ " Print...",
+ " Delete...",
+ " New...",
+ " Visual Directory...",
+ " Quit",
+ " Edit",
+ " Undo",
+ " Redo",
+ " Cut",
+ " Copy",
+ " Paste",
+ " View",
+ " Half Size",
+ " Original Size",
+ " Double Size",
+ " Resize...",
+ " Apply",
+ " Refresh",
+ " Restore",
+ " Transform",
+ " Crop",
+ " Chop",
+ " Flop",
+ " Flip",
+ " Rotate Right",
+ " Rotate Left",
+ " Rotate...",
+ " Shear...",
+ " Roll...",
+ " Trim Edges",
+ " Enhance",
+ " Brightness...",
+ " Saturation...",
+ " Hue...",
+ " Gamma...",
+ " Sharpen...",
+ " Dull",
+ " Contrast Stretch...",
+ " Sigmoidal Contrast...",
+ " Normalize",
+ " Equalize",
+ " Negate",
+ " Grayscale",
+ " Map...",
+ " Quantize...",
+ " Effects",
+ " Despeckle",
+ " Emboss",
+ " Reduce Noise",
+ " Add Noise",
+ " Sharpen...",
+ " Blur...",
+ " Threshold...",
+ " Edge Detect...",
+ " Spread...",
+ " Shade...",
+ " Painting...",
+ " Segment...",
+ " F/X",
+ " Solarize...",
+ " Sepia Tone...",
+ " Swirl...",
+ " Implode...",
+ " Vignette...",
+ " Wave...",
+ " Oil Painting...",
+ " Charcoal Drawing...",
+ " Image Edit",
+ " Annotate...",
+ " Draw...",
+ " Color...",
+ " Matte...",
+ " Composite...",
+ " Add Border...",
+ " Add Frame...",
+ " Comment...",
+ " Launch...",
+ " Region of Interest...",
+ " Miscellany",
+ " Image Info",
+ " Zoom Image",
+ " Show Preview...",
+ " Show Histogram",
+ " Show Matte",
+ " Background...",
+ " Slide Show",
+ " Preferences...",
+ " Help",
+ " Overview",
+ " Browse Documentation",
+ " About Display",
+ "",
+ " Menu items with a indented triangle have a sub-menu. They",
+ " are represented above as the indented items. To access a",
+ " sub-menu item, move the pointer to the appropriate menu and",
+ " press a button and drag. When you find the desired sub-menu",
+ " item, release the button and the command is executed. Move",
+ " the pointer away from the sub-menu if you decide not to",
+ " execute a particular command.",
+ "",
+ "KEYBOARD ACCELERATORS",
+ " Accelerators are one or two key presses that effect a",
+ " particular command. The keyboard accelerators that",
+ " display(1) understands is:",
+ "",
+ " Ctl+O Press to open an image from a file.",
+ "",
+ " space Press to display the next image.",
+ "",
+ " If the image is a multi-paged document such as a Postscript",
+ " document, you can skip ahead several pages by preceding",
+ " this command with a number. For example to display the",
+ " third page beyond the current page, press 3<space>.",
+ "",
+ " backspace Press to display the former image.",
+ "",
+ " If the image is a multi-paged document such as a Postscript",
+ " document, you can skip behind several pages by preceding",
+ " this command with a number. For example to display the",
+ " third page preceding the current page, press 3<backspace>.",
+ "",
+ " Ctl+S Press to write the image to a file.",
+ "",
+ " Ctl+P Press to print the image to a Postscript printer.",
+ "",
+ " Ctl+D Press to delete an image file.",
+ "",
+ " Ctl+N Press to create a blank canvas.",
+ "",
+ " Ctl+Q Press to discard all images and exit program.",
+ "",
+ " Ctl+Z Press to undo last image transformation.",
+ "",
+ " Ctl+R Press to redo last image transformation.",
+ "",
+ " Ctl+X Press to cut a region of the image.",
+ "",
+ " Ctl+C Press to copy a region of the image.",
+ "",
+ " Ctl+V Press to paste a region to the image.",
+ "",
+ " < Press to half the image size.",
+ "",
+ " - Press to return to the original image size.",
+ "",
+ " > Press to double the image size.",
+ "",
+ " % Press to resize the image to a width and height you",
+ " specify.",
+ "",
+ "Cmd-A Press to make any image transformations permanent."
+ "",
+ " By default, any image size transformations are applied",
+ " to the original image to create the image displayed on",
+ " the X server. However, the transformations are not",
+ " permanent (i.e. the original image does not change",
+ " size only the X image does). For example, if you",
+ " press > the X image will appear to double in size,",
+ " but the original image will in fact remain the same size.",
+ " To force the original image to double in size, press >",
+ " followed by Cmd-A.",
+ "",
+ " @ Press to refresh the image window.",
+ "",
+ " C Press to cut out a rectangular region of the image.",
+ "",
+ " [ Press to chop the image.",
+ "",
+ " H Press to flop image in the horizontal direction.",
+ "",
+ " V Press to flip image in the vertical direction.",
+ "",
+ " / Press to rotate the image 90 degrees clockwise.",
+ "",
+ " \\ Press to rotate the image 90 degrees counter-clockwise.",
+ "",
+ " * Press to rotate the image the number of degrees you",
+ " specify.",
+ "",
+ " S Press to shear the image the number of degrees you",
+ " specify.",
+ "",
+ " R Press to roll the image.",
+ "",
+ " T Press to trim the image edges.",
+ "",
+ " Shft-H Press to vary the image hue.",
+ "",
+ " Shft-S Press to vary the color saturation.",
+ "",
+ " Shft-L Press to vary the color brightness.",
+ "",
+ " Shft-G Press to gamma correct the image.",
+ "",
+ " Shft-C Press to sharpen the image contrast.",
+ "",
+ " Shft-Z Press to dull the image contrast.",
+ "",
+ " = Press to perform histogram equalization on the image.",
+ "",
+ " Shft-N Press to perform histogram normalization on the image.",
+ "",
+ " Shft-~ Press to negate the colors of the image.",
+ "",
+ " . Press to convert the image colors to gray.",
+ "",
+ " Shft-# Press to set the maximum number of unique colors in the",
+ " image.",
+ "",
+ " F2 Press to reduce the speckles in an image.",
+ "",
+ " F3 Press to eliminate peak noise from an image.",
+ "",
+ " F4 Press to add noise to an image.",
+ "",
+ " F5 Press to sharpen an image.",
+ "",
+ " F6 Press to delete an image file.",
+ "",
+ " F7 Press to threshold the image.",
+ "",
+ " F8 Press to detect edges within an image.",
+ "",
+ " F9 Press to emboss an image.",
+ "",
+ " F10 Press to displace pixels by a random amount.",
+ "",
+ " F11 Press to negate all pixels above the threshold level.",
+ "",
+ " F12 Press to shade the image using a distant light source.",
+ "",
+ " F13 Press to lighten or darken image edges to create a 3-D effect.",
+ "",
+ " F14 Press to segment the image by color.",
+ "",
+ " Meta-S Press to swirl image pixels about the center.",
+ "",
+ " Meta-I Press to implode image pixels about the center.",
+ "",
+ " Meta-W Press to alter an image along a sine wave.",
+ "",
+ " Meta-P Press to simulate an oil painting.",
+ "",
+ " Meta-C Press to simulate a charcoal drawing.",
+ "",
+ " Alt-A Press to annotate the image with text.",
+ "",
+ " Alt-D Press to draw on an image.",
+ "",
+ " Alt-P Press to edit an image pixel color.",
+ "",
+ " Alt-M Press to edit the image matte information.",
+ "",
+ " Alt-V Press to composite the image with another.",
+ "",
+ " Alt-B Press to add a border to the image.",
+ "",
+ " Alt-F Press to add an ornamental border to the image.",
+ "",
+ " Alt-Shft-!",
+ " Press to add an image comment.",
+ "",
+ " Ctl-A Press to apply image processing techniques to a region",
+ " of interest.",
+ "",
+ " Shft-? Press to display information about the image.",
+ "",
+ " Shft-+ Press to map the zoom image window.",
+ "",
+ " Shft-P Press to preview an image enhancement, effect, or f/x.",
+ "",
+ " F1 Press to display helpful information about display(1).",
+ "",
+ " Find Press to browse documentation about ImageMagick.",
+ "",
+ " 1-9 Press to change the level of magnification.",
+ "",
+ " Use the arrow keys to move the image one pixel up, down,",
+ " left, or right within the magnify window. Be sure to first",
+ " map the magnify window by pressing button 2.",
+ "",
+ " Press ALT and one of the arrow keys to trim off one pixel",
+ " from any side of the image.",
+ (char *) NULL,
+ },
+ *ImageMatteEditHelp[] =
+ {
+ "Matte information within an image is useful for some",
+ "operations such as image compositing (See IMAGE",
+ "COMPOSITING). This extra channel usually defines a mask",
+ "which represents a sort of a cookie-cutter for the image.",
+ "This the case when matte is opaque (full coverage) for",
+ "pixels inside the shape, zero outside, and between 0 and",
+ "QuantumRange on the boundary.",
+ "",
+ "A small window appears showing the location of the cursor in",
+ "the image window. You are now in matte edit mode. To exit",
+ "immediately, press Dismiss. In matte edit mode, the Command",
+ "widget has these options:",
+ "",
+ " Method",
+ " point",
+ " replace",
+ " floodfill",
+ " filltoborder",
+ " reset",
+ " Border Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " Browser...",
+ " Fuzz",
+ " 0%",
+ " 2%",
+ " 5%",
+ " 10%",
+ " 15%",
+ " Dialog...",
+ " Matte",
+ " Opaque",
+ " Transparent",
+ " Dialog...",
+ " Undo",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a matte editing method from the Method sub-menu of",
+ "the Command widget. The point method changes the matte value",
+ "of any pixel selected with the pointer until the button is",
+ "is released. The replace method changes the matte value of",
+ "any pixel that matches the color of the pixel you select with",
+ "a button press. Floodfill changes the matte value of any pixel",
+ "that matches the color of the pixel you select with a button",
+ "press and is a neighbor. Whereas filltoborder changes the matte",
+ "value any neighbor pixel that is not the border color. Finally",
+ "reset changes the entire image to the designated matte value.",
+ "",
+ "Choose Matte Value and pick Opaque or Transarent. For other values",
+ "select the Dialog entry. Here a dialog appears requesting a matte",
+ "value. The value you select is assigned as the opacity value of the",
+ "selected pixel or pixels.",
+ "",
+ "Now, press any button to select a pixel within the image",
+ "window to change its matte value.",
+ "",
+ "If the Magnify widget is mapped, it can be helpful in positioning",
+ "your pointer within the image (refer to button 2).",
+ "",
+ "Matte information is only valid in a DirectClass image.",
+ "Therefore, any PseudoClass image is promoted to DirectClass",
+ "(see miff(5)). Note that matte information for PseudoClass",
+ "is not retained for colormapped X server visuals (e.g.",
+ "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
+ "immediately save your image to a file (refer to Write).",
+ "Correct matte editing behavior may require a TrueColor or",
+ "DirectColor visual or a Standard Colormap.",
+ (char *) NULL,
+ },
+ *ImagePanHelp[] =
+ {
+ "When an image exceeds the width or height of the X server",
+ "screen, display maps a small panning icon. The rectangle",
+ "within the panning icon shows the area that is currently",
+ "displayed in the image window. To pan about the image,",
+ "press any button and drag the pointer within the panning",
+ "icon. The pan rectangle moves with the pointer and the",
+ "image window is updated to reflect the location of the",
+ "rectangle within the panning icon. When you have selected",
+ "the area of the image you wish to view, release the button.",
+ "",
+ "Use the arrow keys to pan the image one pixel up, down,",
+ "left, or right within the image window.",
+ "",
+ "The panning icon is withdrawn if the image becomes smaller",
+ "than the dimensions of the X server screen.",
+ (char *) NULL,
+ },
+ *ImagePasteHelp[] =
+ {
+ "A small window appears showing the location of the cursor in",
+ "the image window. You are now in paste mode. To exit",
+ "immediately, press Dismiss. In paste mode, the Command",
+ "widget has these options:",
+ "",
+ " Operators",
+ " over",
+ " in",
+ " out",
+ " atop",
+ " xor",
+ " plus",
+ " minus",
+ " add",
+ " subtract",
+ " difference",
+ " replace",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a composite operation from the Operators sub-menu of",
+ "the Command widget. How each operator behaves is described",
+ "below. Image window is the image currently displayed on",
+ "your X server and image is the image obtained with the File",
+ "Browser widget.",
+ "",
+ "Over The result is the union of the two image shapes,",
+ " with image obscuring image window in the region of",
+ " overlap.",
+ "",
+ "In The result is simply image cut by the shape of",
+ " image window. None of the image data of image",
+ " window is in the result.",
+ "",
+ "Out The resulting image is image with the shape of",
+ " image window cut out.",
+ "",
+ "Atop The result is the same shape as image image window,",
+ " with image obscuring image window where the image",
+ " shapes overlap. Note this differs from over",
+ " because the portion of image outside image window's",
+ " shape does not appear in the result.",
+ "",
+ "Xor The result is the image data from both image and",
+ " image window that is outside the overlap region.",
+ " The overlap region is blank.",
+ "",
+ "Plus The result is just the sum of the image data.",
+ " Output values are cropped to QuantumRange (no overflow).",
+ " This operation is independent of the matte",
+ " channels.",
+ "",
+ "Minus The result of image - image window, with underflow",
+ " cropped to zero.",
+ "",
+ "Add The result of image + image window, with overflow",
+ " wrapping around (mod 256).",
+ "",
+ "Subtract The result of image - image window, with underflow",
+ " wrapping around (mod 256). The add and subtract",
+ " operators can be used to perform reversible",
+ " transformations.",
+ "",
+ "Difference",
+ " The result of abs(image - image window). This",
+ " useful for comparing two very similar images.",
+ "",
+ "Copy The resulting image is image window replaced with",
+ " image. Here the matte information is ignored.",
+ "",
+ "CopyRed The red layer of the image window is replace with",
+ " the red layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyGreen",
+ " The green layer of the image window is replace with",
+ " the green layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyBlue The blue layer of the image window is replace with",
+ " the blue layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "CopyOpacity",
+ " The matte layer of the image window is replace with",
+ " the matte layer of the image. The other layers are",
+ " untouched.",
+ "",
+ "The image compositor requires a matte, or alpha channel in",
+ "the image for some operations. This extra channel usually",
+ "defines a mask which represents a sort of a cookie-cutter",
+ "for the image. This the case when matte is opaque (full",
+ "coverage) for pixels inside the shape, zero outside, and",
+ "between 0 and QuantumRange on the boundary. If image does not",
+ "have a matte channel, it is initialized with 0 for any pixel",
+ "matching in color to pixel location (0,0), otherwise QuantumRange.",
+ "",
+ "Note that matte information for image window is not retained",
+ "for colormapped X server visuals (e.g. StaticColor,",
+ "StaticColor, GrayScale, PseudoColor). Correct compositing",
+ "behavior may require a TrueColor or DirectColor visual or a",
+ "Standard Colormap.",
+ "",
+ "Choosing a composite operator is optional. The default",
+ "operator is replace. However, you must choose a location to",
+ "paste your image and press button 1. Press and hold the",
+ "button before releasing and an outline of the image will",
+ "appear to help you identify your location.",
+ "",
+ "The actual colors of the pasted image is saved. However,",
+ "the color that appears in image window may be different.",
+ "For example, on a monochrome screen image window will appear",
+ "black or white even though your pasted image may have",
+ "many colors. If the image is saved to a file it is written",
+ "with the correct colors. To assure the correct colors are",
+ "saved in the final image, any PseudoClass image is promoted",
+ "to DirectClass (see miff(5)). To force a PseudoClass image",
+ "to remain PseudoClass, use -colors.",
+ (char *) NULL,
+ },
+ *ImageROIHelp[] =
+ {
+ "In region of interest mode, the Command widget has these",
+ "options:",
+ "",
+ " Help",
+ " Dismiss",
+ "",
+ "To define a region of interest, press button 1 and drag.",
+ "The region of interest is defined by a highlighted rectangle",
+ "that expands or contracts as it follows the pointer. Once",
+ "you are satisfied with the region of interest, release the",
+ "button. You are now in apply mode. In apply mode the",
+ "Command widget has these options:",
+ "",
+ " File",
+ " Save...",
+ " Print...",
+ " Edit",
+ " Undo",
+ " Redo",
+ " Transform",
+ " Flop",
+ " Flip",
+ " Rotate Right",
+ " Rotate Left",
+ " Enhance",
+ " Hue...",
+ " Saturation...",
+ " Brightness...",
+ " Gamma...",
+ " Spiff",
+ " Dull",
+ " Contrast Stretch",
+ " Sigmoidal Contrast...",
+ " Normalize",
+ " Equalize",
+ " Negate",
+ " Grayscale",
+ " Map...",
+ " Quantize...",
+ " Effects",
+ " Despeckle",
+ " Emboss",
+ " Reduce Noise",
+ " Sharpen...",
+ " Blur...",
+ " Threshold...",
+ " Edge Detect...",
+ " Spread...",
+ " Shade...",
+ " Raise...",
+ " Segment...",
+ " F/X",
+ " Solarize...",
+ " Sepia Tone...",
+ " Swirl...",
+ " Implode...",
+ " Vignette...",
+ " Wave...",
+ " Oil Painting...",
+ " Charcoal Drawing...",
+ " Miscellany",
+ " Image Info",
+ " Zoom Image",
+ " Show Preview...",
+ " Show Histogram",
+ " Show Matte",
+ " Help",
+ " Dismiss",
+ "",
+ "You can make adjustments to the region of interest by moving",
+ "the pointer to one of the rectangle corners, pressing a",
+ "button, and dragging. Finally, choose an image processing",
+ "technique from the Command widget. You can choose more than",
+ "one image processing technique to apply to an area.",
+ "Alternatively, you can move the region of interest before",
+ "applying another image processing technique. To exit, press",
+ "Dismiss.",
+ (char *) NULL,
+ },
+ *ImageRotateHelp[] =
+ {
+ "In rotate mode, the Command widget has these options:",
+ "",
+ " Pixel Color",
+ " black",
+ " blue",
+ " cyan",
+ " green",
+ " gray",
+ " red",
+ " magenta",
+ " yellow",
+ " white",
+ " Browser...",
+ " Direction",
+ " horizontal",
+ " vertical",
+ " Help",
+ " Dismiss",
+ "",
+ "Choose a background color from the Pixel Color sub-menu.",
+ "Additional background colors can be specified with the color",
+ "browser. You can change the menu colors by setting the X",
+ "resources pen1 through pen9.",
+ "",
+ "If you choose the color browser and press Grab, you can",
+ "select the background color by moving the pointer to the",
+ "desired color on the screen and press any button.",
+ "",
+ "Choose a point in the image window and press this button and",
+ "hold. Next, move the pointer to another location in the",
+ "image. As you move a line connects the initial location and",
+ "the pointer. When you release the button, the degree of",
+ "image rotation is determined by the slope of the line you",
+ "just drew. The slope is relative to the direction you",
+ "choose from the Direction sub-menu of the Command widget.",
+ "",
+ "To cancel the image rotation, move the pointer back to the",
+ "starting point of the line and release the button.",
+ (char *) NULL,
+ };
+
+/*
+ Enumeration declarations.
+*/
+typedef enum
+{
+ CopyMode,
+ CropMode,
+ CutMode
+} ClipboardMode;
+
+typedef enum
+{
+ OpenCommand,
+ NextCommand,
+ FormerCommand,
+ SelectCommand,
+ SaveCommand,
+ PrintCommand,
+ DeleteCommand,
+ NewCommand,
+ VisualDirectoryCommand,
+ QuitCommand,
+ UndoCommand,
+ RedoCommand,
+ CutCommand,
+ CopyCommand,
+ PasteCommand,
+ HalfSizeCommand,
+ OriginalSizeCommand,
+ DoubleSizeCommand,
+ ResizeCommand,
+ ApplyCommand,
+ RefreshCommand,
+ RestoreCommand,
+ CropCommand,
+ ChopCommand,
+ FlopCommand,
+ FlipCommand,
+ RotateRightCommand,
+ RotateLeftCommand,
+ RotateCommand,
+ ShearCommand,
+ RollCommand,
+ TrimCommand,
+ HueCommand,
+ SaturationCommand,
+ BrightnessCommand,
+ GammaCommand,
+ SpiffCommand,
+ DullCommand,
+ ContrastStretchCommand,
+ SigmoidalContrastCommand,
+ NormalizeCommand,
+ EqualizeCommand,
+ NegateCommand,
+ GrayscaleCommand,
+ MapCommand,
+ QuantizeCommand,
+ DespeckleCommand,
+ EmbossCommand,
+ ReduceNoiseCommand,
+ AddNoiseCommand,
+ SharpenCommand,
+ BlurCommand,
+ ThresholdCommand,
+ EdgeDetectCommand,
+ SpreadCommand,
+ ShadeCommand,
+ RaiseCommand,
+ SegmentCommand,
+ SolarizeCommand,
+ SepiaToneCommand,
+ SwirlCommand,
+ ImplodeCommand,
+ VignetteCommand,
+ WaveCommand,
+ OilPaintCommand,
+ CharcoalDrawCommand,
+ AnnotateCommand,
+ DrawCommand,
+ ColorCommand,
+ MatteCommand,
+ CompositeCommand,
+ AddBorderCommand,
+ AddFrameCommand,
+ CommentCommand,
+ LaunchCommand,
+ RegionofInterestCommand,
+ ROIHelpCommand,
+ ROIDismissCommand,
+ InfoCommand,
+ ZoomCommand,
+ ShowPreviewCommand,
+ ShowHistogramCommand,
+ ShowMatteCommand,
+ BackgroundCommand,
+ SlideShowCommand,
+ PreferencesCommand,
+ HelpCommand,
+ BrowseDocumentationCommand,
+ VersionCommand,
+ SaveToUndoBufferCommand,
+ FreeBuffersCommand,
+ NullCommand
+} CommandType;
+
+typedef enum
+{
+ AnnotateNameCommand,
+ AnnotateFontColorCommand,
+ AnnotateBackgroundColorCommand,
+ AnnotateRotateCommand,
+ AnnotateHelpCommand,
+ AnnotateDismissCommand,
+ TextHelpCommand,
+ TextApplyCommand,
+ ChopDirectionCommand,
+ ChopHelpCommand,
+ ChopDismissCommand,
+ HorizontalChopCommand,
+ VerticalChopCommand,
+ ColorEditMethodCommand,
+ ColorEditColorCommand,
+ ColorEditBorderCommand,
+ ColorEditFuzzCommand,
+ ColorEditUndoCommand,
+ ColorEditHelpCommand,
+ ColorEditDismissCommand,
+ CompositeOperatorsCommand,
+ CompositeDissolveCommand,
+ CompositeDisplaceCommand,
+ CompositeHelpCommand,
+ CompositeDismissCommand,
+ CropHelpCommand,
+ CropDismissCommand,
+ RectifyCopyCommand,
+ RectifyHelpCommand,
+ RectifyDismissCommand,
+ DrawElementCommand,
+ DrawColorCommand,
+ DrawStippleCommand,
+ DrawWidthCommand,
+ DrawUndoCommand,
+ DrawHelpCommand,
+ DrawDismissCommand,
+ MatteEditMethod,
+ MatteEditBorderCommand,
+ MatteEditFuzzCommand,
+ MatteEditValueCommand,
+ MatteEditUndoCommand,
+ MatteEditHelpCommand,
+ MatteEditDismissCommand,
+ PasteOperatorsCommand,
+ PasteHelpCommand,
+ PasteDismissCommand,
+ RotateColorCommand,
+ RotateDirectionCommand,
+ RotateCropCommand,
+ RotateSharpenCommand,
+ RotateHelpCommand,
+ RotateDismissCommand,
+ HorizontalRotateCommand,
+ VerticalRotateCommand,
+ TileLoadCommand,
+ TileNextCommand,
+ TileFormerCommand,
+ TileDeleteCommand,
+ TileUpdateCommand
+} ModeType;
+
+/*
+ Stipples.
+*/
+#define BricksWidth 20
+#define BricksHeight 20
+#define DiagonalWidth 16
+#define DiagonalHeight 16
+#define HighlightWidth 8
+#define HighlightHeight 8
+#define ScalesWidth 16
+#define ScalesHeight 16
+#define ShadowWidth 8
+#define ShadowHeight 8
+#define VerticalWidth 16
+#define VerticalHeight 16
+#define WavyWidth 16
+#define WavyHeight 16
+
+/*
+ Constant declaration.
+*/
+static const int
+ RoiDelta = 8;
+
+static const unsigned char
+ BricksBitmap[] =
+ {
+ 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
+ 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
+ 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
+ 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
+ 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
+ },
+ DiagonalBitmap[] =
+ {
+ 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
+ 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
+ 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
+ },
+ ScalesBitmap[] =
+ {
+ 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
+ 0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
+ 0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
+ },
+ VerticalBitmap[] =
+ {
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
+ },
+ WavyBitmap[] =
+ {
+ 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
+ 0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
+ 0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
+ };
+
+/*
+ Function prototypes.
+*/
+static CommandType
+ XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
+ const MagickStatusType,KeySym,Image **);
+
+static Image
+ *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
+ Image **),
+ *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
+ *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
+ *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
+
+static MagickBooleanType
+ XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
+ XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
+ XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
+ XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
+
+static void
+ XDrawPanRectangle(Display *,XWindows *),
+ XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
+ XMagnifyImage(Display *,XWindows *,XEvent *),
+ XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
+ XPanImage(Display *,XWindows *,XEvent *),
+ XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
+ const KeySym),
+ XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
+ XScreenEvent(Display *,XWindows *,XEvent *),
+ XTranslateImage(Display *,XWindows *,Image *,const KeySym);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D i s p l a y I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DisplayImages() displays an image sequence to any X window screen. It
+% returns a value other than 0 if successful. Check the exception member
+% of image to determine the reason for any failure.
+%
+% The format of the DisplayImages method is:
+%
+% MagickBooleanType DisplayImages(const ImageInfo *image_info,
+% Image *images)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
+ Image *images)
+{
+ char
+ *argv[1];
+
+ Display
+ *display;
+
+ Image
+ *image;
+
+ register long
+ i;
+
+ unsigned long
+ state;
+
+ XrmDatabase
+ resource_database;
+
+ XResourceInfo
+ resource_info;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ display=XOpenDisplay(image_info->server_name);
+ if (display == (Display *) NULL)
+ {
+ (void) ThrowMagickException(&images->exception,GetMagickModule(),
+ XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
+ image_info->server_name));
+ return(MagickFalse);
+ }
+ if (images->exception.severity != UndefinedException)
+ CatchException(&images->exception);
+ (void) XSetErrorHandler(XError);
+ resource_database=XGetResourceDatabase(display,GetClientName());
+ (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
+ XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
+ if (image_info->page != (char *) NULL)
+ resource_info.image_geometry=AcquireString(image_info->page);
+ resource_info.immutable=MagickTrue;
+ argv[0]=AcquireString(GetClientName());
+ state=DefaultState;
+ for (i=0; (state & ExitState) == 0; i++)
+ {
+ if ((images->iterations != 0) && (i >= (long) images->iterations))
+ break;
+ image=GetImageFromList(images,i % GetImageListLength(images));
+ (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
+ }
+ argv[0]=DestroyString(argv[0]);
+ (void) XCloseDisplay(display);
+ XDestroyResourceInfo(&resource_info);
+ if (images->exception.severity != UndefinedException)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o t e D i s p l a y C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoteDisplayCommand() encourages a remote display program to display the
+% specified image filename.
+%
+% The format of the RemoteDisplayCommand method is:
+%
+% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
+% const char *window,const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o window: Specifies the name or id of an X window.
+%
+% o filename: the name of the image filename to display.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
+ const char *window,const char *filename,ExceptionInfo *exception)
+{
+ Display
+ *display;
+
+ MagickStatusType
+ status;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(filename != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ display=XOpenDisplay(image_info->server_name);
+ if (display == (Display *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
+ "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
+ return(MagickFalse);
+ }
+ (void) XSetErrorHandler(XError);
+ status=XRemoteCommand(display,window,filename);
+ (void) XCloseDisplay(display);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X A n n o t a t e E d i t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XAnnotateEditImage() annotates the image with text.
+%
+% The format of the XAnnotateEditImage method is:
+%
+% MagickBooleanType XAnnotateEditImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline long MagickMin(const long x,const long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static MagickBooleanType XAnnotateEditImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ static const char
+ *AnnotateMenu[] =
+ {
+ "Font Name",
+ "Font Color",
+ "Box Color",
+ "Rotate Text",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ },
+ *TextMenu[] =
+ {
+ "Help",
+ "Apply",
+ (char *) NULL
+ };
+
+ static const ModeType
+ AnnotateCommands[] =
+ {
+ AnnotateNameCommand,
+ AnnotateFontColorCommand,
+ AnnotateBackgroundColorCommand,
+ AnnotateRotateCommand,
+ AnnotateHelpCommand,
+ AnnotateDismissCommand
+ },
+ TextCommands[] =
+ {
+ TextHelpCommand,
+ TextApplyCommand
+ };
+
+ static MagickBooleanType
+ transparent_box = MagickTrue,
+ transparent_pen = MagickFalse;
+
+ static MagickRealType
+ degrees = 0.0;
+
+ static unsigned int
+ box_id = MaxNumberPens-2,
+ font_id = 0,
+ pen_id = 0;
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ const char
+ *ColorMenu[MaxNumberPens+1];
+
+ Cursor
+ cursor;
+
+ GC
+ annotate_context;
+
+ int
+ id,
+ pen_number,
+ status,
+ x,
+ y;
+
+ KeySym
+ key_symbol;
+
+ register char
+ *p;
+
+ register long
+ i;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XAnnotateInfo
+ *annotate_info,
+ *previous_info;
+
+ XColor
+ color;
+
+ XFontStruct
+ *font_info;
+
+ XEvent
+ event,
+ text_event;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Annotate");
+ windows->command.data=4;
+ (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ cursor=XCreateFontCursor(display,XC_left_side);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ x+windows->image.x,y+windows->image.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,AnnotateMenu,&event);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ if (id < 0)
+ continue;
+ switch (AnnotateCommands[id])
+ {
+ case AnnotateNameCommand:
+ {
+ const char
+ *FontMenu[MaxNumberFonts];
+
+ int
+ font_number;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < MaxNumberFonts; i++)
+ FontMenu[i]=resource_info->font_name[i];
+ FontMenu[MaxNumberFonts-2]="Browser...";
+ FontMenu[MaxNumberFonts-1]=(const char *) NULL;
+ /*
+ Select a font name from the pop-up menu.
+ */
+ font_number=XMenuWidget(display,windows,AnnotateMenu[id],
+ (const char **) FontMenu,command);
+ if (font_number < 0)
+ break;
+ if (font_number == (MaxNumberFonts-2))
+ {
+ static char
+ font_name[MaxTextExtent] = "fixed";
+
+ /*
+ Select a font name from a browser.
+ */
+ resource_info->font_name[font_number]=font_name;
+ XFontBrowserWidget(display,windows,"Select",font_name);
+ if (*font_name == '\0')
+ break;
+ }
+ /*
+ Initialize font info.
+ */
+ font_info=XLoadQueryFont(display,resource_info->font_name[
+ font_number]);
+ if (font_info == (XFontStruct *) NULL)
+ {
+ XNoticeWidget(display,windows,"Unable to load font:",
+ resource_info->font_name[font_number]);
+ break;
+ }
+ font_id=(unsigned int) font_number;
+ (void) XFreeFont(display,font_info);
+ break;
+ }
+ case AnnotateFontColorCommand:
+ {
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="transparent";
+ ColorMenu[MaxNumberPens-1]="Browser...";
+ ColorMenu[MaxNumberPens]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
+ MagickFalse;
+ if (transparent_pen != MagickFalse)
+ break;
+ if (pen_number == (MaxNumberPens-1))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set pen color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&color);
+ XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
+ (unsigned int) MaxColors,&color);
+ windows->pixel_info->pen_colors[pen_number]=color;
+ pen_id=(unsigned int) pen_number;
+ break;
+ }
+ case AnnotateBackgroundColorCommand:
+ {
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="transparent";
+ ColorMenu[MaxNumberPens-1]="Browser...";
+ ColorMenu[MaxNumberPens]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
+ MagickFalse;
+ if (transparent_box != MagickFalse)
+ break;
+ if (pen_number == (MaxNumberPens-1))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set pen color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&color);
+ XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
+ (unsigned int) MaxColors,&color);
+ windows->pixel_info->pen_colors[pen_number]=color;
+ box_id=(unsigned int) pen_number;
+ break;
+ }
+ case AnnotateRotateCommand:
+ {
+ int
+ entry;
+
+ static char
+ angle[MaxTextExtent] = "30.0";
+
+ static const char
+ *RotateMenu[] =
+ {
+ "-90",
+ "-45",
+ "-30",
+ "0",
+ "30",
+ "45",
+ "90",
+ "180",
+ "Dialog...",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (entry != 8)
+ {
+ degrees=atof(RotateMenu[entry]);
+ break;
+ }
+ (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
+ angle);
+ if (*angle == '\0')
+ break;
+ degrees=atof(angle);
+ break;
+ }
+ case AnnotateHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Annotation",ImageAnnotateHelp);
+ break;
+ }
+ case AnnotateDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Change to text entering mode.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Annotation",ImageAnnotateHelp);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ /*
+ Set font info and check boundary conditions.
+ */
+ font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
+ if (font_info == (XFontStruct *) NULL)
+ {
+ XNoticeWidget(display,windows,"Unable to load font:",
+ resource_info->font_name[font_id]);
+ font_info=windows->font_info;
+ }
+ if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
+ x=(int) windows->image.width-font_info->max_bounds.width;
+ if (y < (int) (font_info->ascent+font_info->descent))
+ y=(int) font_info->ascent+font_info->descent;
+ if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
+ ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
+ return(MagickFalse);
+ /*
+ Initialize annotate structure.
+ */
+ annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
+ if (annotate_info == (XAnnotateInfo *) NULL)
+ return(MagickFalse);
+ XGetAnnotateInfo(annotate_info);
+ annotate_info->x=x;
+ annotate_info->y=y;
+ if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
+ annotate_info->stencil=OpaqueStencil;
+ else
+ if (transparent_box == MagickFalse)
+ annotate_info->stencil=BackgroundStencil;
+ else
+ annotate_info->stencil=ForegroundStencil;
+ annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
+ annotate_info->degrees=degrees;
+ annotate_info->font_info=font_info;
+ annotate_info->text=(char *) AcquireQuantumMemory((size_t)
+ windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
+ sizeof(*annotate_info->text));
+ if (annotate_info->text == (char *) NULL)
+ return(MagickFalse);
+ /*
+ Create cursor and set graphic context.
+ */
+ cursor=XCreateFontCursor(display,XC_pencil);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ annotate_context=windows->image.annotate_context;
+ (void) XSetFont(display,annotate_context,font_info->fid);
+ (void) XSetBackground(display,annotate_context,
+ windows->pixel_info->pen_colors[box_id].pixel);
+ (void) XSetForeground(display,annotate_context,
+ windows->pixel_info->pen_colors[pen_id].pixel);
+ /*
+ Begin annotating the image with text.
+ */
+ (void) CloneString(&windows->command.name,"Text");
+ windows->command.data=0;
+ (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
+ state=DefaultState;
+ (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
+ text_event.xexpose.width=(int) font_info->max_bounds.width;
+ text_event.xexpose.height=font_info->max_bounds.ascent+
+ font_info->max_bounds.descent;
+ p=annotate_info->text;
+ do
+ {
+ /*
+ Display text cursor.
+ */
+ *p='\0';
+ (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ (void) XSetBackground(display,annotate_context,
+ windows->pixel_info->background_color.pixel);
+ (void) XSetForeground(display,annotate_context,
+ windows->pixel_info->foreground_color.pixel);
+ id=XCommandWidget(display,windows,AnnotateMenu,&event);
+ (void) XSetBackground(display,annotate_context,
+ windows->pixel_info->pen_colors[box_id].pixel);
+ (void) XSetForeground(display,annotate_context,
+ windows->pixel_info->pen_colors[pen_id].pixel);
+ if (id < 0)
+ continue;
+ switch (TextCommands[id])
+ {
+ case TextHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Annotation",ImageAnnotateHelp);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ break;
+ }
+ case TextApplyCommand:
+ {
+ /*
+ Finished annotating.
+ */
+ annotate_info->width=(unsigned int) XTextWidth(font_info,
+ annotate_info->text,(int) strlen(annotate_info->text));
+ XRefreshWindow(display,&windows->image,&text_event);
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ /*
+ Erase text cursor.
+ */
+ text_event.xexpose.x=x;
+ text_event.xexpose.y=y-font_info->max_bounds.ascent;
+ (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
+ (unsigned int) text_event.xexpose.width,(unsigned int)
+ text_event.xexpose.height,MagickFalse);
+ XRefreshWindow(display,&windows->image,&text_event);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.window != windows->image.id)
+ break;
+ if (event.xbutton.button == Button2)
+ {
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ windows->image.id,CurrentTime);
+ break;
+ }
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.count == 0)
+ {
+ XAnnotateInfo
+ *text_info;
+
+ /*
+ Refresh Image window.
+ */
+ XRefreshWindow(display,&windows->image,(XEvent *) NULL);
+ text_info=annotate_info;
+ while (text_info != (XAnnotateInfo *) NULL)
+ {
+ if (annotate_info->stencil == ForegroundStencil)
+ (void) XDrawString(display,windows->image.id,annotate_context,
+ text_info->x,text_info->y,text_info->text,
+ (int) strlen(text_info->text));
+ else
+ (void) XDrawImageString(display,windows->image.id,
+ annotate_context,text_info->x,text_info->y,text_info->text,
+ (int) strlen(text_info->text));
+ text_info=text_info->previous;
+ }
+ (void) XDrawString(display,windows->image.id,annotate_context,
+ x,y,"_",1);
+ }
+ break;
+ }
+ case KeyPress:
+ {
+ int
+ length;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (((event.xkey.state & ControlMask) != 0) ||
+ ((event.xkey.state & Mod1Mask) != 0))
+ state|=ModifierState;
+ if ((state & ModifierState) != 0)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ key_symbol=DeleteCommand;
+ break;
+ }
+ default:
+ break;
+ }
+ switch ((int) key_symbol)
+ {
+ case XK_BackSpace:
+ {
+ /*
+ Erase one character.
+ */
+ if (p == annotate_info->text)
+ {
+ if (annotate_info->previous == (XAnnotateInfo *) NULL)
+ break;
+ else
+ {
+ /*
+ Go to end of the previous line of text.
+ */
+ annotate_info=annotate_info->previous;
+ p=annotate_info->text;
+ x=annotate_info->x+annotate_info->width;
+ y=annotate_info->y;
+ if (annotate_info->width != 0)
+ p+=strlen(annotate_info->text);
+ break;
+ }
+ }
+ p--;
+ x-=XTextWidth(font_info,p,1);
+ text_event.xexpose.x=x;
+ text_event.xexpose.y=y-font_info->max_bounds.ascent;
+ XRefreshWindow(display,&windows->image,&text_event);
+ break;
+ }
+ case XK_bracketleft:
+ {
+ key_symbol=XK_Escape;
+ break;
+ }
+ case DeleteCommand:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ while (p != annotate_info->text)
+ {
+ p--;
+ x-=XTextWidth(font_info,p,1);
+ text_event.xexpose.x=x;
+ XRefreshWindow(display,&windows->image,&text_event);
+ }
+ break;
+ }
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Finished annotating.
+ */
+ annotate_info->width=(unsigned int) XTextWidth(font_info,
+ annotate_info->text,(int) strlen(annotate_info->text));
+ XRefreshWindow(display,&windows->image,&text_event);
+ state|=ExitState;
+ break;
+ }
+ default:
+ {
+ /*
+ Draw a single character on the Image window.
+ */
+ if ((state & ModifierState) != 0)
+ break;
+ if (*command == '\0')
+ break;
+ *p=(*command);
+ if (annotate_info->stencil == ForegroundStencil)
+ (void) XDrawString(display,windows->image.id,annotate_context,
+ x,y,p,1);
+ else
+ (void) XDrawImageString(display,windows->image.id,
+ annotate_context,x,y,p,1);
+ x+=XTextWidth(font_info,p,1);
+ p++;
+ if ((x+font_info->max_bounds.width) < (int) windows->image.width)
+ break;
+ }
+ case XK_Return:
+ case XK_KP_Enter:
+ {
+ /*
+ Advance to the next line of text.
+ */
+ *p='\0';
+ annotate_info->width=(unsigned int) XTextWidth(font_info,
+ annotate_info->text,(int) strlen(annotate_info->text));
+ if (annotate_info->next != (XAnnotateInfo *) NULL)
+ {
+ /*
+ Line of text already exists.
+ */
+ annotate_info=annotate_info->next;
+ x=annotate_info->x;
+ y=annotate_info->y;
+ p=annotate_info->text;
+ break;
+ }
+ annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
+ sizeof(*annotate_info->next));
+ if (annotate_info->next == (XAnnotateInfo *) NULL)
+ return(MagickFalse);
+ *annotate_info->next=(*annotate_info);
+ annotate_info->next->previous=annotate_info;
+ annotate_info=annotate_info->next;
+ annotate_info->text=(char *) AcquireQuantumMemory((size_t)
+ windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
+ sizeof(*annotate_info->text));
+ if (annotate_info->text == (char *) NULL)
+ return(MagickFalse);
+ annotate_info->y+=annotate_info->height;
+ if (annotate_info->y > (int) windows->image.height)
+ annotate_info->y=(int) annotate_info->height;
+ annotate_info->next=(XAnnotateInfo *) NULL;
+ x=annotate_info->x;
+ y=annotate_info->y;
+ p=annotate_info->text;
+ break;
+ }
+ }
+ break;
+ }
+ case KeyRelease:
+ {
+ /*
+ Respond to a user key release.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ state&=(~ModifierState);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,event.xselection.requestor,
+ event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
+ &type,&format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ /*
+ Annotate Image window with primary selection.
+ */
+ for (i=0; i < (long) length; i++)
+ {
+ if ((char) data[i] != '\n')
+ {
+ /*
+ Draw a single character on the Image window.
+ */
+ *p=(char) data[i];
+ (void) XDrawString(display,windows->image.id,annotate_context,
+ x,y,p,1);
+ x+=XTextWidth(font_info,p,1);
+ p++;
+ if ((x+font_info->max_bounds.width) < (int) windows->image.width)
+ continue;
+ }
+ /*
+ Advance to the next line of text.
+ */
+ *p='\0';
+ annotate_info->width=(unsigned int) XTextWidth(font_info,
+ annotate_info->text,(int) strlen(annotate_info->text));
+ if (annotate_info->next != (XAnnotateInfo *) NULL)
+ {
+ /*
+ Line of text already exists.
+ */
+ annotate_info=annotate_info->next;
+ x=annotate_info->x;
+ y=annotate_info->y;
+ p=annotate_info->text;
+ continue;
+ }
+ annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
+ sizeof(*annotate_info->next));
+ if (annotate_info->next == (XAnnotateInfo *) NULL)
+ return(MagickFalse);
+ *annotate_info->next=(*annotate_info);
+ annotate_info->next->previous=annotate_info;
+ annotate_info=annotate_info->next;
+ annotate_info->text=(char *) AcquireQuantumMemory((size_t)
+ windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
+ sizeof(*annotate_info->text));
+ if (annotate_info->text == (char *) NULL)
+ return(MagickFalse);
+ annotate_info->y+=annotate_info->height;
+ if (annotate_info->y > (int) windows->image.height)
+ annotate_info->y=(int) annotate_info->height;
+ annotate_info->next=(XAnnotateInfo *) NULL;
+ x=annotate_info->x;
+ y=annotate_info->y;
+ p=annotate_info->text;
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ (void) XFreeCursor(display,cursor);
+ /*
+ Annotation is relative to image configuration.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ /*
+ Initialize annotated image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ while (annotate_info != (XAnnotateInfo *) NULL)
+ {
+ if (annotate_info->width == 0)
+ {
+ /*
+ No text on this line-- go to the next line of text.
+ */
+ previous_info=annotate_info->previous;
+ annotate_info->text=(char *)
+ RelinquishMagickMemory(annotate_info->text);
+ annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
+ annotate_info=previous_info;
+ continue;
+ }
+ /*
+ Determine pixel index for box and pen color.
+ */
+ windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
+ if (windows->pixel_info->colors != 0)
+ for (i=0; i < (long) windows->pixel_info->colors; i++)
+ if (windows->pixel_info->pixels[i] ==
+ windows->pixel_info->pen_colors[box_id].pixel)
+ {
+ windows->pixel_info->box_index=(unsigned short) i;
+ break;
+ }
+ windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
+ if (windows->pixel_info->colors != 0)
+ for (i=0; i < (long) windows->pixel_info->colors; i++)
+ if (windows->pixel_info->pixels[i] ==
+ windows->pixel_info->pen_colors[pen_id].pixel)
+ {
+ windows->pixel_info->pen_index=(unsigned short) i;
+ break;
+ }
+ /*
+ Define the annotate geometry string.
+ */
+ annotate_info->x=(int)
+ width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
+ annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
+ windows->image.y)/windows->image.ximage->height;
+ (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
+ "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
+ height*annotate_info->height/windows->image.ximage->height,
+ annotate_info->x+x,annotate_info->y+y);
+ /*
+ Annotate image with text.
+ */
+ status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
+ if (status == 0)
+ return(MagickFalse);
+ /*
+ Free up memory.
+ */
+ previous_info=annotate_info->previous;
+ annotate_info->text=DestroyString(annotate_info->text);
+ annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
+ annotate_info=previous_info;
+ }
+ (void) XSetForeground(display,annotate_context,
+ windows->pixel_info->foreground_color.pixel);
+ (void) XSetBackground(display,annotate_context,
+ windows->pixel_info->background_color.pixel);
+ (void) XSetFont(display,annotate_context,windows->font_info->fid);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XFreeFont(display,font_info);
+ /*
+ Update image configuration.
+ */
+ XConfigureImageColormap(display,resource_info,windows,image);
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X B a c k g r o u n d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XBackgroundImage() displays the image in the background of a window.
+%
+% The format of the XBackgroundImage method is:
+%
+% MagickBooleanType XBackgroundImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XBackgroundImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+#define BackgroundImageTag "Background/Image"
+
+ int
+ status;
+
+ static char
+ window_id[MaxTextExtent] = "root";
+
+ XResourceInfo
+ background_resources;
+
+ /*
+ Put image in background.
+ */
+ status=XDialogWidget(display,windows,"Background",
+ "Enter window id (id 0x00 selects window with pointer):",window_id);
+ if (*window_id == '\0')
+ return(MagickFalse);
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XInfoWidget(display,windows,BackgroundImageTag);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ background_resources=(*resource_info);
+ background_resources.window_id=window_id;
+ background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
+ status=XDisplayBackgroundImage(display,&background_resources,*image);
+ if (status != MagickFalse)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_retain_colors,CurrentTime);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C h o p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XChopImage() chops the X image.
+%
+% The format of the XChopImage method is:
+%
+% MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XChopImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+ static const char
+ *ChopMenu[] =
+ {
+ "Direction",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static ModeType
+ direction = HorizontalChopCommand;
+
+ static const ModeType
+ ChopCommands[] =
+ {
+ ChopDirectionCommand,
+ ChopHelpCommand,
+ ChopDismissCommand
+ },
+ DirectionCommands[] =
+ {
+ HorizontalChopCommand,
+ VerticalChopCommand
+ };
+
+ char
+ text[MaxTextExtent];
+
+ Image
+ *chop_image;
+
+ int
+ id,
+ x,
+ y;
+
+ MagickRealType
+ scale_factor;
+
+ RectangleInfo
+ chop_info;
+
+ unsigned int
+ distance,
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XSegment
+ segment_info;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Chop");
+ windows->command.data=1;
+ (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ x+windows->image.x,y+windows->image.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,ChopMenu,&event);
+ if (id < 0)
+ continue;
+ switch (ChopCommands[id])
+ {
+ case ChopDirectionCommand:
+ {
+ char
+ command[MaxTextExtent];
+
+ static const char
+ *Directions[] =
+ {
+ "horizontal",
+ "vertical",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
+ if (id >= 0)
+ direction=DirectionCommands[id];
+ break;
+ }
+ case ChopHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Chop",ImageChopHelp);
+ break;
+ }
+ case ChopDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ User has committed to start point of chopping line.
+ */
+ segment_info.x1=(short int) event.xbutton.x;
+ segment_info.x2=(short int) event.xbutton.x;
+ segment_info.y1=(short int) event.xbutton.y;
+ segment_info.y2=(short int) event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Chop",ImageChopHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ /*
+ Draw line as pointer moves until the mouse button is released.
+ */
+ chop_info.width=0;
+ chop_info.height=0;
+ chop_info.x=0;
+ chop_info.y=0;
+ distance=0;
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ state=DefaultState;
+ do
+ {
+ if (distance > 9)
+ {
+ /*
+ Display info and draw chopping line.
+ */
+ if (windows->info.mapped == MagickFalse)
+ (void) XMapWindow(display,windows->info.id);
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ chop_info.width,chop_info.height,chop_info.x,chop_info.y);
+ XInfoWidget(display,windows,text);
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&segment_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (distance > 9)
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&segment_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ segment_info.x2=(short int) event.xmotion.x;
+ segment_info.y2=(short int) event.xmotion.y;
+ break;
+ }
+ case ButtonRelease:
+ {
+ /*
+ User has committed to chopping line.
+ */
+ segment_info.x2=(short int) event.xbutton.x;
+ segment_info.y2=(short int) event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ segment_info.x2=(short int) event.xmotion.x;
+ segment_info.y2=(short int) event.xmotion.y;
+ }
+ default:
+ break;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if (segment_info.x2 < 0)
+ segment_info.x2=0;
+ else
+ if (segment_info.x2 > windows->image.ximage->width)
+ segment_info.x2=windows->image.ximage->width;
+ if (segment_info.y2 < 0)
+ segment_info.y2=0;
+ else
+ if (segment_info.y2 > windows->image.ximage->height)
+ segment_info.y2=windows->image.ximage->height;
+ distance=(unsigned int)
+ (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
+ ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
+ /*
+ Compute chopping geometry.
+ */
+ if (direction == HorizontalChopCommand)
+ {
+ chop_info.width=(unsigned long) (segment_info.x2-segment_info.x1+1);
+ chop_info.x=windows->image.x+segment_info.x1;
+ chop_info.height=0;
+ chop_info.y=0;
+ if (segment_info.x1 > (int) segment_info.x2)
+ {
+ chop_info.width=(unsigned long) (segment_info.x1-segment_info.x2+1);
+ chop_info.x=windows->image.x+segment_info.x2;
+ }
+ }
+ else
+ {
+ chop_info.width=0;
+ chop_info.height=(unsigned long) (segment_info.y2-segment_info.y1+1);
+ chop_info.x=0;
+ chop_info.y=windows->image.y+segment_info.y1;
+ if (segment_info.y1 > segment_info.y2)
+ {
+ chop_info.height=(unsigned long)
+ (segment_info.y1-segment_info.y2+1);
+ chop_info.y=windows->image.y+segment_info.y2;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (distance <= 9)
+ return(MagickTrue);
+ /*
+ Image chopping is relative to image configuration.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ windows->image.window_changes.width=windows->image.ximage->width-
+ (unsigned int) chop_info.width;
+ windows->image.window_changes.height=windows->image.ximage->height-
+ (unsigned int) chop_info.height;
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ chop_info.x+=x;
+ chop_info.x=(int) (scale_factor*chop_info.x+0.5);
+ chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ chop_info.y+=y;
+ chop_info.y=(int) (scale_factor*chop_info.y+0.5);
+ chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
+ /*
+ Chop image.
+ */
+ chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (chop_image == (Image *) NULL)
+ return(MagickFalse);
+ *image=DestroyImage(*image);
+ *image=chop_image;
+ /*
+ Update image configuration.
+ */
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C o l o r E d i t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XColorEditImage() allows the user to interactively change the color of one
+% pixel for a DirectColor image or one colormap entry for a PseudoClass image.
+%
+% The format of the XColorEditImage method is:
+%
+% MagickBooleanType XColorEditImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+
+
+static MagickBooleanType XColorEditImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+ static const char
+ *ColorEditMenu[] =
+ {
+ "Method",
+ "Pixel Color",
+ "Border Color",
+ "Fuzz",
+ "Undo",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static const ModeType
+ ColorEditCommands[] =
+ {
+ ColorEditMethodCommand,
+ ColorEditColorCommand,
+ ColorEditBorderCommand,
+ ColorEditFuzzCommand,
+ ColorEditUndoCommand,
+ ColorEditHelpCommand,
+ ColorEditDismissCommand
+ };
+
+ static PaintMethod
+ method = PointMethod;
+
+ static unsigned int
+ pen_id = 0;
+
+ static XColor
+ border_color = { 0, 0, 0, 0, 0, 0 };
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ entry,
+ id,
+ x,
+ x_offset,
+ y,
+ y_offset;
+
+ register PixelPacket
+ *q;
+
+ register long
+ i;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XColor
+ color;
+
+ XEvent
+ event;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Color Edit");
+ windows->command.data=4;
+ (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Make cursor.
+ */
+ cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
+ resource_info->background_color,resource_info->foreground_color);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ x+windows->image.x,y+windows->image.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,ColorEditMenu,&event);
+ if (id < 0)
+ {
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ continue;
+ }
+ switch (ColorEditCommands[id])
+ {
+ case ColorEditMethodCommand:
+ {
+ char
+ **methods;
+
+ /*
+ Select a method from the pop-up menu.
+ */
+ methods=(char **) GetMagickOptions(MagickMethodOptions);
+ if (methods == (char **) NULL)
+ break;
+ entry=XMenuWidget(display,windows,ColorEditMenu[id],
+ (const char **) methods,command);
+ if (entry >= 0)
+ method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
+ MagickFalse,methods[entry]);
+ methods=DestroyStringList(methods);
+ break;
+ }
+ case ColorEditColorCommand:
+ {
+ const char
+ *ColorMenu[MaxNumberPens];
+
+ int
+ pen_number;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="Browser...";
+ ColorMenu[MaxNumberPens-1]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ if (pen_number == (MaxNumberPens-2))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set pen color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&color);
+ XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
+ (unsigned int) MaxColors,&color);
+ windows->pixel_info->pen_colors[pen_number]=color;
+ pen_id=(unsigned int) pen_number;
+ break;
+ }
+ case ColorEditBorderCommand:
+ {
+ const char
+ *ColorMenu[MaxNumberPens];
+
+ int
+ pen_number;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="Browser...";
+ ColorMenu[MaxNumberPens-1]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ if (pen_number == (MaxNumberPens-2))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set border color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&border_color);
+ break;
+ }
+ case ColorEditFuzzCommand:
+ {
+ static char
+ fuzz[MaxTextExtent];
+
+ static const char
+ *FuzzMenu[] =
+ {
+ "0%",
+ "2%",
+ "5%",
+ "10%",
+ "15%",
+ "Dialog...",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (entry != 5)
+ {
+ (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
+ 1.0);
+ break;
+ }
+ (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
+ (void) XDialogWidget(display,windows,"Ok",
+ "Enter fuzz factor (0.0 - 99.9%):",fuzz);
+ if (*fuzz == '\0')
+ break;
+ (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
+ (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
+ break;
+ }
+ case ColorEditUndoCommand:
+ {
+ (void) XMagickCommand(display,resource_info,windows,UndoCommand,
+ image);
+ break;
+ }
+ case ColorEditHelpCommand:
+ default:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Annotation",ImageColorEditHelp);
+ break;
+ }
+ case ColorEditDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ }
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if ((event.xbutton.window != windows->image.id) &&
+ (event.xbutton.window != windows->magnify.id))
+ break;
+ /*
+ exit loop.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ (void) XMagickCommand(display,resource_info,windows,
+ SaveToUndoBufferCommand,image);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if ((event.xbutton.window != windows->image.id) &&
+ (event.xbutton.window != windows->magnify.id))
+ break;
+ /*
+ Update colormap information.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ XInfoWidget(display,windows,text);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ state&=(~UpdateConfigurationState);
+ break;
+ }
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window == windows->magnify.id)
+ {
+ Window
+ window;
+
+ window=windows->magnify.id;
+ while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
+ }
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Annotation",ImageColorEditHelp);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ break;
+ }
+ default:
+ break;
+ }
+ if (event.xany.window == windows->magnify.id)
+ {
+ x=windows->magnify.x-windows->image.x;
+ y=windows->magnify.y-windows->image.y;
+ }
+ x_offset=x;
+ y_offset=y;
+ if ((state & UpdateConfigurationState) != 0)
+ {
+ int
+ x,
+ y;
+
+ /*
+ Pixel edit is relative to image configuration.
+ */
+ (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
+ MagickTrue);
+ color=windows->pixel_info->pen_colors[pen_id];
+ XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ x_offset=(int)
+ (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
+ y_offset=(int)
+ (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
+ if ((x_offset < 0) || (y_offset < 0))
+ continue;
+ if ((x_offset >= (long) (*image)->columns) ||
+ (y_offset >= (long) (*image)->rows))
+ continue;
+ exception=(&(*image)->exception);
+ switch (method)
+ {
+ case PointMethod:
+ default:
+ {
+ /*
+ Update color information using point algorithm.
+ */
+ if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ q->red=ScaleShortToQuantum(color.red);
+ q->green=ScaleShortToQuantum(color.green);
+ q->blue=ScaleShortToQuantum(color.blue);
+ (void) SyncAuthenticPixels(*image,&(*image)->exception);
+ break;
+ }
+ case ReplaceMethod:
+ {
+ PixelPacket
+ target;
+
+ /*
+ Update color information using replace algorithm.
+ */
+ (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
+ &(*image)->exception);
+ if ((*image)->storage_class == DirectClass)
+ {
+ for (y=0; y < (long) (*image)->rows; y++)
+ {
+ q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) (*image)->columns; x++)
+ {
+ if (IsColorSimilar(*image,q,&target))
+ {
+ q->red=ScaleShortToQuantum(color.red);
+ q->green=ScaleShortToQuantum(color.green);
+ q->blue=ScaleShortToQuantum(color.blue);
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(*image,exception) == MagickFalse)
+ break;
+ }
+ }
+ else
+ {
+ for (i=0; i < (int) (*image)->colors; i++)
+ if (IsColorSimilar(*image,(*image)->colormap+i,&target))
+ {
+ (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
+ (*image)->colormap[i].green=ScaleShortToQuantum(
+ color.green);
+ (*image)->colormap[i].blue=ScaleShortToQuantum(
+ color.blue);
+ }
+ (void) SyncImage(*image);
+ }
+ break;
+ }
+ case FloodfillMethod:
+ case FillToBorderMethod:
+ {
+ DrawInfo
+ *draw_info;
+
+ MagickPixelPacket
+ target;
+
+ /*
+ Update color information using floodfill algorithm.
+ */
+ (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
+ exception);
+ if (method == FillToBorderMethod)
+ {
+ target.red=(MagickRealType)
+ ScaleShortToQuantum(border_color.red);
+ target.green=(MagickRealType)
+ ScaleShortToQuantum(border_color.green);
+ target.blue=(MagickRealType)
+ ScaleShortToQuantum(border_color.blue);
+ }
+ draw_info=CloneDrawInfo(resource_info->image_info,
+ (DrawInfo *) NULL);
+ (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
+ &draw_info->fill,exception);
+ (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
+ x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
+ MagickTrue);
+ draw_info=DestroyDrawInfo(draw_info);
+ break;
+ }
+ case ResetMethod:
+ {
+ /*
+ Update color information using reset algorithm.
+ */
+ if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ for (y=0; y < (long) (*image)->rows; y++)
+ {
+ q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) (*image)->columns; x++)
+ {
+ q->red=ScaleShortToQuantum(color.red);
+ q->green=ScaleShortToQuantum(color.green);
+ q->blue=ScaleShortToQuantum(color.blue);
+ q++;
+ }
+ if (SyncAuthenticPixels(*image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ }
+ state&=(~UpdateConfigurationState);
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XFreeCursor(display,cursor);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C o m p o s i t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XCompositeImage() requests an image name from the user, reads the image and
+% composites it with the X window image at a location the user chooses with
+% the pointer.
+%
+% The format of the XCompositeImage method is:
+%
+% MagickBooleanType XCompositeImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+static MagickBooleanType XCompositeImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ static char
+ displacement_geometry[MaxTextExtent] = "30x30",
+ filename[MaxTextExtent] = "\0";
+
+ static const char
+ *CompositeMenu[] =
+ {
+ "Operators",
+ "Dissolve",
+ "Displace",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static CompositeOperator
+ compose = CopyCompositeOp;
+
+ static const ModeType
+ CompositeCommands[] =
+ {
+ CompositeOperatorsCommand,
+ CompositeDissolveCommand,
+ CompositeDisplaceCommand,
+ CompositeHelpCommand,
+ CompositeDismissCommand
+ };
+
+ char
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ Image
+ *composite_image;
+
+ int
+ entry,
+ id,
+ x,
+ y;
+
+ MagickRealType
+ blend,
+ scale_factor;
+
+ RectangleInfo
+ highlight_info,
+ composite_info;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ /*
+ Request image file name from user.
+ */
+ XFileBrowserWidget(display,windows,"Composite",filename);
+ if (*filename == '\0')
+ return(MagickTrue);
+ /*
+ Read image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(resource_info->image_info->filename,filename,
+ MaxTextExtent);
+ composite_image=ReadImage(resource_info->image_info,&image->exception);
+ CatchException(&image->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (composite_image == (Image *) NULL)
+ return(MagickFalse);
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Composite");
+ windows->command.data=1;
+ (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ composite_info.x=windows->image.x+x;
+ composite_info.y=windows->image.y+y;
+ composite_info.width=0;
+ composite_info.height=0;
+ cursor=XCreateFontCursor(display,XC_ul_angle);
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ blend=0.0;
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
+ composite_info.x,composite_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ highlight_info=composite_info;
+ highlight_info.x=composite_info.x-windows->image.x;
+ highlight_info.y=composite_info.y-windows->image.y;
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,CompositeMenu,&event);
+ if (id < 0)
+ continue;
+ switch (CompositeCommands[id])
+ {
+ case CompositeOperatorsCommand:
+ {
+ char
+ command[MaxTextExtent],
+ **operators;
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ operators=GetMagickOptions(MagickComposeOptions);
+ if (operators == (char **) NULL)
+ break;
+ entry=XMenuWidget(display,windows,CompositeMenu[id],
+ (const char **) operators,command);
+ if (entry >= 0)
+ compose=(CompositeOperator) ParseMagickOption(
+ MagickComposeOptions,MagickFalse,operators[entry]);
+ operators=DestroyStringList(operators);
+ break;
+ }
+ case CompositeDissolveCommand:
+ {
+ static char
+ factor[MaxTextExtent] = "20.0";
+
+ /*
+ Dissolve the two images a given percent.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ (void) XDialogWidget(display,windows,"Dissolve",
+ "Enter the blend factor (0.0 - 99.9%):",factor);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ if (*factor == '\0')
+ break;
+ blend=atof(factor);
+ compose=DissolveCompositeOp;
+ break;
+ }
+ case CompositeDisplaceCommand:
+ {
+ /*
+ Get horizontal and vertical scale displacement geometry.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ (void) XDialogWidget(display,windows,"Displace",
+ "Enter the horizontal and vertical scale:",displacement_geometry);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ if (*displacement_geometry == '\0')
+ break;
+ compose=DisplaceCompositeOp;
+ break;
+ }
+ case CompositeHelpCommand:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Composite",ImageCompositeHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ case CompositeDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Change cursor.
+ */
+ composite_info.width=composite_image->columns;
+ composite_info.height=composite_image->rows;
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ composite_info.x=windows->image.x+event.xbutton.x;
+ composite_info.y=windows->image.y+event.xbutton.y;
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ if ((composite_info.width != 0) && (composite_info.height != 0))
+ {
+ /*
+ User has selected the location of the composite image.
+ */
+ composite_info.x=windows->image.x+event.xbutton.x;
+ composite_info.y=windows->image.y+event.xbutton.y;
+ state|=ExitState;
+ }
+ break;
+ }
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ int
+ length;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ composite_image=DestroyImage(composite_image);
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Composite",ImageCompositeHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ composite_info.x=windows->image.x+x;
+ composite_info.y=windows->image.y+y;
+ break;
+ }
+ default:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
+ event.type);
+ break;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XFreeCursor(display,cursor);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ /*
+ Image compositing is relative to image configuration.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ composite_info.x+=x;
+ composite_info.x=(int) (scale_factor*composite_info.x+0.5);
+ composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ composite_info.y+=y;
+ composite_info.y=(int) (scale_factor*composite_info.y+0.5);
+ composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
+ if ((composite_info.width != composite_image->columns) ||
+ (composite_info.height != composite_image->rows))
+ {
+ Image
+ *resize_image;
+
+ /*
+ Scale composite image.
+ */
+ resize_image=ZoomImage(composite_image,composite_info.width,
+ composite_info.height,&image->exception);
+ composite_image=DestroyImage(composite_image);
+ if (resize_image == (Image *) NULL)
+ {
+ XSetCursorState(display,windows,MagickFalse);
+ return(MagickFalse);
+ }
+ composite_image=resize_image;
+ }
+ if (compose == DisplaceCompositeOp)
+ (void) SetImageArtifact(composite_image,"compose:args",
+ displacement_geometry);
+ if (blend != 0.0)
+ {
+ ExceptionInfo
+ *exception;
+
+ int
+ y;
+
+ Quantum
+ opacity;
+
+ register int
+ x;
+
+ register PixelPacket
+ *q;
+
+ /*
+ Create mattes for blending.
+ */
+ (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
+ opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
+ ((long) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ image->matte=MagickTrue;
+ exception=(&image->exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) image->columns; x++)
+ {
+ q->opacity=opacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ }
+ /*
+ Composite image with X Image window.
+ */
+ (void) CompositeImage(image,compose,composite_image,composite_info.x,
+ composite_info.y);
+ composite_image=DestroyImage(composite_image);
+ XSetCursorState(display,windows,MagickFalse);
+ /*
+ Update image configuration.
+ */
+ XConfigureImageColormap(display,resource_info,windows,image);
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C o n f i g u r e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XConfigureImage() creates a new X image. It also notifies the window
+% manager of the new image size and configures the transient widows.
+%
+% The format of the XConfigureImage method is:
+%
+% MagickBooleanType XConfigureImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+%
+*/
+static MagickBooleanType XConfigureImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ char
+ geometry[MaxTextExtent];
+
+ long
+ x,
+ y;
+
+ MagickStatusType
+ status;
+
+ unsigned long
+ mask,
+ height,
+ width;
+
+ XSizeHints
+ *size_hints;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Dismiss if window dimensions are zero.
+ */
+ width=(unsigned int) windows->image.window_changes.width;
+ height=(unsigned int) windows->image.window_changes.height;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Configure Image: %dx%d=>%lux%lu",windows->image.ximage->width,
+ windows->image.ximage->height,width,height);
+ if ((width*height) == 0)
+ return(MagickTrue);
+ x=0;
+ y=0;
+ /*
+ Resize image to fit Image window dimensions.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ (void) XFlush(display);
+ if (((int) width != windows->image.ximage->width) ||
+ ((int) height != windows->image.ximage->height))
+ image->taint=MagickTrue;
+ windows->magnify.x=(int)
+ width*windows->magnify.x/windows->image.ximage->width;
+ windows->magnify.y=(int)
+ height*windows->magnify.y/windows->image.ximage->height;
+ windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
+ windows->image.y=(int)
+ (height*windows->image.y/windows->image.ximage->height);
+ status=XMakeImage(display,resource_info,&windows->image,image,
+ (unsigned int) width,(unsigned int) height);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to configure X image:",
+ windows->image.name);
+ /*
+ Notify window manager of the new configuration.
+ */
+ if (resource_info->image_geometry != (char *) NULL)
+ (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
+ resource_info->image_geometry);
+ else
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
+ XDisplayWidth(display,windows->image.screen),
+ XDisplayHeight(display,windows->image.screen));
+ (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
+ window_changes.width=(int) width;
+ if (window_changes.width > XDisplayWidth(display,windows->image.screen))
+ window_changes.width=XDisplayWidth(display,windows->image.screen);
+ window_changes.height=(int) height;
+ if (window_changes.height > XDisplayHeight(display,windows->image.screen))
+ window_changes.height=XDisplayHeight(display,windows->image.screen);
+ mask=(unsigned long) (CWWidth | CWHeight);
+ if (resource_info->backdrop)
+ {
+ mask|=CWX | CWY;
+ window_changes.x=(int)
+ ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
+ window_changes.y=(int)
+ ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
+ }
+ (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
+ (unsigned int) mask,&window_changes);
+ (void) XClearWindow(display,windows->image.id);
+ XRefreshWindow(display,&windows->image,(XEvent *) NULL);
+ /*
+ Update Magnify window configuration.
+ */
+ if (windows->magnify.mapped != MagickFalse)
+ XMakeMagnifyImage(display,windows);
+ windows->pan.crop_geometry=windows->image.crop_geometry;
+ XBestIconSize(display,&windows->pan,image);
+ while (((windows->pan.width << 1) < MaxIconSize) &&
+ ((windows->pan.height << 1) < MaxIconSize))
+ {
+ windows->pan.width<<=1;
+ windows->pan.height<<=1;
+ }
+ if (windows->pan.geometry != (char *) NULL)
+ (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
+ &windows->pan.width,&windows->pan.height);
+ window_changes.width=(int) windows->pan.width;
+ window_changes.height=(int) windows->pan.height;
+ size_hints=XAllocSizeHints();
+ if (size_hints != (XSizeHints *) NULL)
+ {
+ /*
+ Set new size hints.
+ */
+ size_hints->flags=PSize | PMinSize | PMaxSize;
+ size_hints->width=window_changes.width;
+ size_hints->height=window_changes.height;
+ size_hints->min_width=size_hints->width;
+ size_hints->min_height=size_hints->height;
+ size_hints->max_width=size_hints->width;
+ size_hints->max_height=size_hints->height;
+ (void) XSetNormalHints(display,windows->pan.id,size_hints);
+ (void) XFree((void *) size_hints);
+ }
+ (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
+ (unsigned int) (CWWidth | CWHeight),&window_changes);
+ /*
+ Update icon window configuration.
+ */
+ windows->icon.crop_geometry=windows->image.crop_geometry;
+ XBestIconSize(display,&windows->icon,image);
+ window_changes.width=(int) windows->icon.width;
+ window_changes.height=(int) windows->icon.height;
+ (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
+ (unsigned int) (CWWidth | CWHeight),&window_changes);
+ XSetCursorState(display,windows,MagickFalse);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C r o p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XCropImage() allows the user to select a region of the image and crop, copy,
+% or cut it. For copy or cut, the image can subsequently be composited onto
+% the image with XPasteImage.
+%
+% The format of the XCropImage method is:
+%
+% MagickBooleanType XCropImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image,
+% const ClipboardMode mode)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+% o mode: This unsigned value specified whether the image should be
+% cropped, copied, or cut.
+%
+*/
+static MagickBooleanType XCropImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image,
+ const ClipboardMode mode)
+{
+ static const char
+ *CropModeMenu[] =
+ {
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ },
+ *RectifyModeMenu[] =
+ {
+ "Crop",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static const ModeType
+ CropCommands[] =
+ {
+ CropHelpCommand,
+ CropDismissCommand
+ },
+ RectifyCommands[] =
+ {
+ RectifyCopyCommand,
+ RectifyHelpCommand,
+ RectifyDismissCommand
+ };
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ id,
+ x,
+ y;
+
+ KeySym
+ key_symbol;
+
+ Image
+ *crop_image;
+
+ MagickRealType
+ scale_factor;
+
+ RectangleInfo
+ crop_info,
+ highlight_info;
+
+ register PixelPacket
+ *q;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ /*
+ Map Command widget.
+ */
+ switch (mode)
+ {
+ case CopyMode:
+ {
+ (void) CloneString(&windows->command.name,"Copy");
+ break;
+ }
+ case CropMode:
+ {
+ (void) CloneString(&windows->command.name,"Crop");
+ break;
+ }
+ case CutMode:
+ {
+ (void) CloneString(&windows->command.name,"Cut");
+ break;
+ }
+ }
+ RectifyModeMenu[0]=windows->command.name;
+ windows->command.data=0;
+ (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ crop_info.x=windows->image.x+x;
+ crop_info.y=windows->image.y+y;
+ crop_info.width=0;
+ crop_info.height=0;
+ cursor=XCreateFontCursor(display,XC_fleur);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
+ crop_info.x,crop_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,CropModeMenu,&event);
+ if (id < 0)
+ continue;
+ switch (CropCommands[id])
+ {
+ case CropHelpCommand:
+ {
+ switch (mode)
+ {
+ case CopyMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Copy",ImageCopyHelp);
+ break;
+ }
+ case CropMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Crop",ImageCropHelp);
+ break;
+ }
+ case CutMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Cut",ImageCutHelp);
+ break;
+ }
+ }
+ break;
+ }
+ case CropDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Note first corner of cropping rectangle-- exit loop.
+ */
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ crop_info.x=windows->image.x+event.xbutton.x;
+ crop_info.y=windows->image.y+event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ switch (mode)
+ {
+ case CopyMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Copy",ImageCopyHelp);
+ break;
+ }
+ case CropMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Crop",ImageCropHelp);
+ break;
+ }
+ case CutMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Cut",ImageCutHelp);
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ if (event.xmotion.window != windows->image.id)
+ break;
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ crop_info.x=windows->image.x+x;
+ crop_info.y=windows->image.y+y;
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ if ((state & EscapeState) != 0)
+ {
+ /*
+ User want to exit without cropping.
+ */
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ (void) XFreeCursor(display,cursor);
+ return(MagickTrue);
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ do
+ {
+ /*
+ Size rectangle as pointer moves until the mouse button is released.
+ */
+ x=(int) crop_info.x;
+ y=(int) crop_info.y;
+ crop_info.width=0;
+ crop_info.height=0;
+ state=DefaultState;
+ do
+ {
+ highlight_info=crop_info;
+ highlight_info.x=crop_info.x-windows->image.x;
+ highlight_info.y=crop_info.y-windows->image.y;
+ if ((highlight_info.width > 3) && (highlight_info.height > 3))
+ {
+ /*
+ Display info and draw cropping rectangle.
+ */
+ if (windows->info.mapped == MagickFalse)
+ (void) XMapWindow(display,windows->info.id);
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ crop_info.width,crop_info.height,crop_info.x,crop_info.y);
+ XInfoWidget(display,windows,text);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if ((highlight_info.width > 3) && (highlight_info.height > 3))
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ crop_info.x=windows->image.x+event.xbutton.x;
+ crop_info.y=windows->image.y+event.xbutton.y;
+ break;
+ }
+ case ButtonRelease:
+ {
+ /*
+ User has committed to cropping rectangle.
+ */
+ crop_info.x=windows->image.x+event.xbutton.x;
+ crop_info.y=windows->image.y+event.xbutton.y;
+ XSetCursorState(display,windows,MagickFalse);
+ state|=ExitState;
+ windows->command.data=0;
+ (void) XCommandWidget(display,windows,RectifyModeMenu,
+ (XEvent *) NULL);
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ crop_info.x=windows->image.x+event.xmotion.x;
+ crop_info.y=windows->image.y+event.xmotion.y;
+ }
+ default:
+ break;
+ }
+ if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
+ ((state & ExitState) != 0))
+ {
+ /*
+ Check boundary conditions.
+ */
+ if (crop_info.x < 0)
+ crop_info.x=0;
+ else
+ if (crop_info.x > (int) windows->image.ximage->width)
+ crop_info.x=windows->image.ximage->width;
+ if ((int) crop_info.x < x)
+ crop_info.width=(unsigned int) (x-crop_info.x);
+ else
+ {
+ crop_info.width=(unsigned int) (crop_info.x-x);
+ crop_info.x=x;
+ }
+ if (crop_info.y < 0)
+ crop_info.y=0;
+ else
+ if (crop_info.y > (int) windows->image.ximage->height)
+ crop_info.y=windows->image.ximage->height;
+ if ((int) crop_info.y < y)
+ crop_info.height=(unsigned int) (y-crop_info.y);
+ else
+ {
+ crop_info.height=(unsigned int) (crop_info.y-y);
+ crop_info.y=y;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ /*
+ Wait for user to grab a corner of the rectangle or press return.
+ */
+ state=DefaultState;
+ (void) XMapWindow(display,windows->info.id);
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ crop_info.width,crop_info.height,crop_info.x,crop_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ highlight_info=crop_info;
+ highlight_info.x=crop_info.x-windows->image.x;
+ highlight_info.y=crop_info.y-windows->image.y;
+ if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
+ {
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ id=XCommandWidget(display,windows,RectifyModeMenu,&event);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ if (id >= 0)
+ switch (RectifyCommands[id])
+ {
+ case RectifyCopyCommand:
+ {
+ state|=ExitState;
+ break;
+ }
+ case RectifyHelpCommand:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ switch (mode)
+ {
+ case CopyMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Copy",ImageCopyHelp);
+ break;
+ }
+ case CropMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Crop",ImageCropHelp);
+ break;
+ }
+ case CutMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Cut",ImageCutHelp);
+ break;
+ }
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ case RectifyDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ x=windows->image.x+event.xbutton.x;
+ y=windows->image.y+event.xbutton.y;
+ if ((x < (int) (crop_info.x+RoiDelta)) &&
+ (x > (int) (crop_info.x-RoiDelta)) &&
+ (y < (int) (crop_info.y+RoiDelta)) &&
+ (y > (int) (crop_info.y-RoiDelta)))
+ {
+ crop_info.x=(long) (crop_info.x+crop_info.width);
+ crop_info.y=(long) (crop_info.y+crop_info.height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (crop_info.x+RoiDelta)) &&
+ (x > (int) (crop_info.x-RoiDelta)) &&
+ (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
+ (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
+ {
+ crop_info.x=(long) (crop_info.x+crop_info.width);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
+ (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
+ (y < (int) (crop_info.y+RoiDelta)) &&
+ (y > (int) (crop_info.y-RoiDelta)))
+ {
+ crop_info.y=(long) (crop_info.y+crop_info.height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
+ (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
+ (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
+ (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
+ {
+ state|=UpdateConfigurationState;
+ break;
+ }
+ }
+ case ButtonRelease:
+ {
+ if (event.xbutton.window == windows->pan.id)
+ if ((highlight_info.x != crop_info.x-windows->image.x) ||
+ (highlight_info.y != crop_info.y-windows->image.y))
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
+ event.xbutton.time);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window == windows->image.id)
+ if (event.xexpose.count == 0)
+ {
+ event.xexpose.x=(int) highlight_info.x;
+ event.xexpose.y=(int) highlight_info.y;
+ event.xexpose.width=(int) highlight_info.width;
+ event.xexpose.height=(int) highlight_info.height;
+ XRefreshWindow(display,&windows->image,&event);
+ }
+ if (event.xexpose.window == windows->info.id)
+ if (event.xexpose.count == 0)
+ XInfoWidget(display,windows,text);
+ break;
+ }
+ case KeyPress:
+ {
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ state|=EscapeState;
+ case XK_Return:
+ {
+ state|=ExitState;
+ break;
+ }
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ crop_info.x=(long) (windows->image.width/2L-crop_info.width/2L);
+ crop_info.y=(long) (windows->image.height/2L-crop_info.height/2L);
+ break;
+ }
+ case XK_Left:
+ case XK_KP_Left:
+ {
+ crop_info.x--;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ case XK_Next:
+ {
+ crop_info.y--;
+ break;
+ }
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ crop_info.x++;
+ break;
+ }
+ case XK_Prior:
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ crop_info.y++;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ switch (mode)
+ {
+ case CopyMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Copy",ImageCopyHelp);
+ break;
+ }
+ case CropMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Cropg",ImageCropHelp);
+ break;
+ }
+ case CutMode:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Cutg",ImageCutHelp);
+ break;
+ }
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
+ event.xkey.time);
+ break;
+ }
+ case KeyRelease:
+ break;
+ case MotionNotify:
+ {
+ if (event.xmotion.window != windows->image.id)
+ break;
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ crop_info.x=windows->image.x+event.xmotion.x;
+ crop_info.y=windows->image.y+event.xmotion.y;
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ /*
+ Set primary selection.
+ */
+ (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
+ crop_info.width,crop_info.height,crop_info.x,crop_info.y);
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) text,(int) strlen(text));
+ notify.type=SelectionNotify;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,0,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ if ((state & UpdateConfigurationState) != 0)
+ {
+ (void) XPutBackEvent(display,&event);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ XSetCursorState(display,windows,MagickFalse);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ if (mode == CropMode)
+ if (((int) crop_info.width != windows->image.ximage->width) ||
+ ((int) crop_info.height != windows->image.ximage->height))
+ {
+ /*
+ Reconfigure Image window as defined by cropping rectangle.
+ */
+ XSetCropGeometry(display,windows,&crop_info,image);
+ windows->image.window_changes.width=(int) crop_info.width;
+ windows->image.window_changes.height=(int) crop_info.height;
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+ }
+ /*
+ Copy image before applying image transforms.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ crop_info.x+=x;
+ crop_info.x=(int) (scale_factor*crop_info.x+0.5);
+ crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ crop_info.y+=y;
+ crop_info.y=(int) (scale_factor*crop_info.y+0.5);
+ crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
+ crop_image=CropImage(image,&crop_info,&image->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (crop_image == (Image *) NULL)
+ return(MagickFalse);
+ if (resource_info->copy_image != (Image *) NULL)
+ resource_info->copy_image=DestroyImage(resource_info->copy_image);
+ resource_info->copy_image=crop_image;
+ if (mode == CopyMode)
+ {
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+ }
+ /*
+ Cut image.
+ */
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ image->matte=MagickTrue;
+ exception=(&image->exception);
+ for (y=0; y < (long) crop_info.height; y++)
+ {
+ q=GetAuthenticPixels(image,crop_info.x,y+crop_info.y,crop_info.width,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) crop_info.width; x++)
+ {
+ q->opacity=(Quantum) TransparentOpacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ /*
+ Update image configuration.
+ */
+ XConfigureImageColormap(display,resource_info,windows,image);
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
+% the image.
+%
+% The format of the XDrawEditImage method is:
+%
+% MagickBooleanType XDrawEditImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XDrawEditImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+ static const char
+ *DrawMenu[] =
+ {
+ "Element",
+ "Color",
+ "Stipple",
+ "Width",
+ "Undo",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static ElementType
+ element = PointElement;
+
+ static const ModeType
+ DrawCommands[] =
+ {
+ DrawElementCommand,
+ DrawColorCommand,
+ DrawStippleCommand,
+ DrawWidthCommand,
+ DrawUndoCommand,
+ DrawHelpCommand,
+ DrawDismissCommand
+ };
+
+ static Pixmap
+ stipple = (Pixmap) NULL;
+
+ static unsigned int
+ pen_id = 0,
+ line_width = 1;
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ int
+ entry,
+ id,
+ number_coordinates,
+ x,
+ y;
+
+ MagickRealType
+ degrees;
+
+ MagickStatusType
+ status;
+
+ RectangleInfo
+ rectangle_info;
+
+ register int
+ i;
+
+ unsigned int
+ distance,
+ height,
+ max_coordinates,
+ width;
+
+ unsigned long
+ state;
+
+ Window
+ root_window;
+
+ XDrawInfo
+ draw_info;
+
+ XEvent
+ event;
+
+ XPoint
+ *coordinate_info;
+
+ XSegment
+ line_info;
+
+ /*
+ Allocate polygon info.
+ */
+ max_coordinates=2048;
+ coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
+ sizeof(*coordinate_info));
+ if (coordinate_info == (XPoint *) NULL)
+ {
+ (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
+ return(MagickFalse);
+ }
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Draw");
+ windows->command.data=4;
+ (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Wait for first button press.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ draw_info.stencil=OpaqueStencil;
+ status=MagickTrue;
+ cursor=XCreateFontCursor(display,XC_tcross);
+ for ( ; ; )
+ {
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ x+windows->image.x,y+windows->image.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,DrawMenu,&event);
+ if (id < 0)
+ continue;
+ switch (DrawCommands[id])
+ {
+ case DrawElementCommand:
+ {
+ static const char
+ *Elements[] =
+ {
+ "point",
+ "line",
+ "rectangle",
+ "fill rectangle",
+ "circle",
+ "fill circle",
+ "ellipse",
+ "fill ellipse",
+ "polygon",
+ "fill polygon",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ element=(ElementType) (XMenuWidget(display,windows,
+ DrawMenu[id],Elements,command)+1);
+ break;
+ }
+ case DrawColorCommand:
+ {
+ const char
+ *ColorMenu[MaxNumberPens+1];
+
+ int
+ pen_number;
+
+ MagickBooleanType
+ transparent;
+
+ XColor
+ color;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="transparent";
+ ColorMenu[MaxNumberPens-1]="Browser...";
+ ColorMenu[MaxNumberPens]=(char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,DrawMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
+ MagickFalse;
+ if (transparent != MagickFalse)
+ {
+ draw_info.stencil=TransparentStencil;
+ break;
+ }
+ if (pen_number == (MaxNumberPens-1))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set pen color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&color);
+ XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
+ (unsigned int) MaxColors,&color);
+ windows->pixel_info->pen_colors[pen_number]=color;
+ pen_id=(unsigned int) pen_number;
+ draw_info.stencil=OpaqueStencil;
+ break;
+ }
+ case DrawStippleCommand:
+ {
+ Image
+ *stipple_image;
+
+ ImageInfo
+ *image_info;
+
+ int
+ status;
+
+ static char
+ filename[MaxTextExtent] = "\0";
+
+ static const char
+ *StipplesMenu[] =
+ {
+ "Brick",
+ "Diagonal",
+ "Scales",
+ "Vertical",
+ "Wavy",
+ "Translucent",
+ "Opaque",
+ (char *) NULL,
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ StipplesMenu[7]="Open...";
+ entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (stipple != (Pixmap) NULL)
+ (void) XFreePixmap(display,stipple);
+ stipple=(Pixmap) NULL;
+ if (entry == 6)
+ break;
+ if (entry != 7)
+ {
+ switch (entry)
+ {
+ case 0:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) BricksBitmap,BricksWidth,BricksHeight);
+ break;
+ }
+ case 1:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
+ break;
+ }
+ case 2:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
+ break;
+ }
+ case 3:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
+ break;
+ }
+ case 4:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) WavyBitmap,WavyWidth,WavyHeight);
+ break;
+ }
+ case 5:
+ default:
+ {
+ stipple=XCreateBitmapFromData(display,root_window,
+ (char *) HighlightBitmap,HighlightWidth,
+ HighlightHeight);
+ break;
+ }
+ }
+ break;
+ }
+ XFileBrowserWidget(display,windows,"Stipple",filename);
+ if (*filename == '\0')
+ break;
+ /*
+ Read image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ image_info=AcquireImageInfo();
+ (void) CopyMagickString(image_info->filename,filename,
+ MaxTextExtent);
+ stipple_image=ReadImage(image_info,&(*image)->exception);
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (stipple_image == (Image *) NULL)
+ break;
+ (void) AcquireUniqueFileResource(filename);
+ (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
+ "xbm:%s",filename);
+ (void) WriteImage(image_info,stipple_image);
+ stipple_image=DestroyImage(stipple_image);
+ image_info=DestroyImageInfo(image_info);
+ status=XReadBitmapFile(display,root_window,filename,&width,
+ &height,&stipple,&x,&y);
+ (void) RelinquishUniqueFileResource(filename);
+ if ((status != BitmapSuccess) != 0)
+ XNoticeWidget(display,windows,"Unable to read X bitmap image:",
+ filename);
+ break;
+ }
+ case DrawWidthCommand:
+ {
+ static char
+ width[MaxTextExtent] = "0";
+
+ static const char
+ *WidthsMenu[] =
+ {
+ "1",
+ "2",
+ "4",
+ "8",
+ "16",
+ "Dialog...",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (entry != 5)
+ {
+ line_width=(unsigned int) atoi(WidthsMenu[entry]);
+ break;
+ }
+ (void) XDialogWidget(display,windows,"Ok","Enter line width:",
+ width);
+ if (*width == '\0')
+ break;
+ line_width=(unsigned int) atoi(width);
+ break;
+ }
+ case DrawUndoCommand:
+ {
+ (void) XMagickCommand(display,resource_info,windows,UndoCommand,
+ image);
+ break;
+ }
+ case DrawHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Rotation",ImageDrawHelp);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ break;
+ }
+ case DrawDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ exit loop.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Rotation",ImageDrawHelp);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ break;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if ((state & EscapeState) != 0)
+ break;
+ /*
+ Draw element as pointer moves until the button is released.
+ */
+ distance=0;
+ degrees=0.0;
+ line_info.x1=x;
+ line_info.y1=y;
+ line_info.x2=x;
+ line_info.y2=y;
+ rectangle_info.x=x;
+ rectangle_info.y=y;
+ rectangle_info.width=0;
+ rectangle_info.height=0;
+ number_coordinates=1;
+ coordinate_info->x=x;
+ coordinate_info->y=y;
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ state=DefaultState;
+ do
+ {
+ switch (element)
+ {
+ case PointElement:
+ default:
+ {
+ if (number_coordinates > 1)
+ {
+ (void) XDrawLines(display,windows->image.id,
+ windows->image.highlight_context,coordinate_info,
+ number_coordinates,CoordModeOrigin);
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
+ coordinate_info[number_coordinates-1].x,
+ coordinate_info[number_coordinates-1].y);
+ XInfoWidget(display,windows,text);
+ }
+ break;
+ }
+ case LineElement:
+ {
+ if (distance > 9)
+ {
+ /*
+ Display angle of the line.
+ */
+ degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
+ line_info.y1),(double) (line_info.x2-line_info.x1)));
+ (void) FormatMagickString(text,MaxTextExtent," %g",
+ (double) degrees);
+ XInfoWidget(display,windows,text);
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&line_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ case RectangleElement:
+ case FillRectangleElement:
+ {
+ if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
+ {
+ /*
+ Display info and draw drawing rectangle.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ rectangle_info.width,rectangle_info.height,rectangle_info.x,
+ rectangle_info.y);
+ XInfoWidget(display,windows,text);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&rectangle_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ case CircleElement:
+ case FillCircleElement:
+ case EllipseElement:
+ case FillEllipseElement:
+ {
+ if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
+ {
+ /*
+ Display info and draw drawing rectangle.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ rectangle_info.width,rectangle_info.height,rectangle_info.x,
+ rectangle_info.y);
+ XInfoWidget(display,windows,text);
+ XHighlightEllipse(display,windows->image.id,
+ windows->image.highlight_context,&rectangle_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ case PolygonElement:
+ case FillPolygonElement:
+ {
+ if (number_coordinates > 1)
+ (void) XDrawLines(display,windows->image.id,
+ windows->image.highlight_context,coordinate_info,
+ number_coordinates,CoordModeOrigin);
+ if (distance > 9)
+ {
+ /*
+ Display angle of the line.
+ */
+ degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
+ line_info.y1),(double) (line_info.x2-line_info.x1)));
+ (void) FormatMagickString(text,MaxTextExtent," %g",
+ (double) degrees);
+ XInfoWidget(display,windows,text);
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&line_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ switch (element)
+ {
+ case PointElement:
+ default:
+ {
+ if (number_coordinates > 1)
+ (void) XDrawLines(display,windows->image.id,
+ windows->image.highlight_context,coordinate_info,
+ number_coordinates,CoordModeOrigin);
+ break;
+ }
+ case LineElement:
+ {
+ if (distance > 9)
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&line_info);
+ break;
+ }
+ case RectangleElement:
+ case FillRectangleElement:
+ {
+ if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&rectangle_info);
+ break;
+ }
+ case CircleElement:
+ case FillCircleElement:
+ case EllipseElement:
+ case FillEllipseElement:
+ {
+ if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
+ XHighlightEllipse(display,windows->image.id,
+ windows->image.highlight_context,&rectangle_info);
+ break;
+ }
+ case PolygonElement:
+ case FillPolygonElement:
+ {
+ if (number_coordinates > 1)
+ (void) XDrawLines(display,windows->image.id,
+ windows->image.highlight_context,coordinate_info,
+ number_coordinates,CoordModeOrigin);
+ if (distance > 9)
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&line_info);
+ break;
+ }
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ break;
+ case ButtonRelease:
+ {
+ /*
+ User has committed to element.
+ */
+ line_info.x2=event.xbutton.x;
+ line_info.y2=event.xbutton.y;
+ rectangle_info.x=event.xbutton.x;
+ rectangle_info.y=event.xbutton.y;
+ coordinate_info[number_coordinates].x=event.xbutton.x;
+ coordinate_info[number_coordinates].y=event.xbutton.y;
+ if (((element != PolygonElement) &&
+ (element != FillPolygonElement)) || (distance <= 9))
+ {
+ state|=ExitState;
+ break;
+ }
+ number_coordinates++;
+ if (number_coordinates < (int) max_coordinates)
+ {
+ line_info.x1=event.xbutton.x;
+ line_info.y1=event.xbutton.y;
+ break;
+ }
+ max_coordinates<<=1;
+ coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
+ max_coordinates,sizeof(*coordinate_info));
+ if (coordinate_info == (XPoint *) NULL)
+ (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ if (event.xmotion.window != windows->image.id)
+ break;
+ if (element != PointElement)
+ {
+ line_info.x2=event.xmotion.x;
+ line_info.y2=event.xmotion.y;
+ rectangle_info.x=event.xmotion.x;
+ rectangle_info.y=event.xmotion.y;
+ break;
+ }
+ coordinate_info[number_coordinates].x=event.xbutton.x;
+ coordinate_info[number_coordinates].y=event.xbutton.y;
+ number_coordinates++;
+ if (number_coordinates < (int) max_coordinates)
+ break;
+ max_coordinates<<=1;
+ coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
+ max_coordinates,sizeof(*coordinate_info));
+ if (coordinate_info == (XPoint *) NULL)
+ (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
+ break;
+ }
+ default:
+ break;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if (line_info.x2 < 0)
+ line_info.x2=0;
+ else
+ if (line_info.x2 > (int) windows->image.width)
+ line_info.x2=(short) windows->image.width;
+ if (line_info.y2 < 0)
+ line_info.y2=0;
+ else
+ if (line_info.y2 > (int) windows->image.height)
+ line_info.y2=(short) windows->image.height;
+ distance=(unsigned int)
+ (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
+ ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
+ if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
+ ((state & ExitState) != 0))
+ {
+ if (rectangle_info.x < 0)
+ rectangle_info.x=0;
+ else
+ if (rectangle_info.x > (int) windows->image.width)
+ rectangle_info.x=(long) windows->image.width;
+ if ((int) rectangle_info.x < x)
+ rectangle_info.width=(unsigned int) (x-rectangle_info.x);
+ else
+ {
+ rectangle_info.width=(unsigned int) (rectangle_info.x-x);
+ rectangle_info.x=x;
+ }
+ if (rectangle_info.y < 0)
+ rectangle_info.y=0;
+ else
+ if (rectangle_info.y > (int) windows->image.height)
+ rectangle_info.y=(long) windows->image.height;
+ if ((int) rectangle_info.y < y)
+ rectangle_info.height=(unsigned int) (y-rectangle_info.y);
+ else
+ {
+ rectangle_info.height=(unsigned int) (rectangle_info.y-y);
+ rectangle_info.y=y;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ if ((element == PointElement) || (element == PolygonElement) ||
+ (element == FillPolygonElement))
+ {
+ /*
+ Determine polygon bounding box.
+ */
+ rectangle_info.x=coordinate_info->x;
+ rectangle_info.y=coordinate_info->y;
+ x=coordinate_info->x;
+ y=coordinate_info->y;
+ for (i=1; i < number_coordinates; i++)
+ {
+ if (coordinate_info[i].x > x)
+ x=coordinate_info[i].x;
+ if (coordinate_info[i].y > y)
+ y=coordinate_info[i].y;
+ if (coordinate_info[i].x < rectangle_info.x)
+ rectangle_info.x=MagickMax(coordinate_info[i].x,0);
+ if (coordinate_info[i].y < rectangle_info.y)
+ rectangle_info.y=MagickMax(coordinate_info[i].y,0);
+ }
+ rectangle_info.width=(unsigned long) (x-rectangle_info.x);
+ rectangle_info.height=(unsigned long) (y-rectangle_info.y);
+ for (i=0; i < number_coordinates; i++)
+ {
+ coordinate_info[i].x-=rectangle_info.x;
+ coordinate_info[i].y-=rectangle_info.y;
+ }
+ }
+ else
+ if (distance <= 9)
+ continue;
+ else
+ if ((element == RectangleElement) ||
+ (element == CircleElement) || (element == EllipseElement))
+ {
+ rectangle_info.width--;
+ rectangle_info.height--;
+ }
+ /*
+ Drawing is relative to image configuration.
+ */
+ draw_info.x=(int) rectangle_info.x;
+ draw_info.y=(int) rectangle_info.y;
+ (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
+ image);
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ draw_info.x+=windows->image.x-(line_width/2);
+ if (draw_info.x < 0)
+ draw_info.x=0;
+ draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
+ draw_info.y+=windows->image.y-(line_width/2);
+ if (draw_info.y < 0)
+ draw_info.y=0;
+ draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
+ draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
+ if (draw_info.width > (unsigned int) (*image)->columns)
+ draw_info.width=(unsigned int) (*image)->columns;
+ draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
+ if (draw_info.height > (unsigned int) (*image)->rows)
+ draw_info.height=(unsigned int) (*image)->rows;
+ (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
+ width*draw_info.width/windows->image.ximage->width,
+ height*draw_info.height/windows->image.ximage->height,
+ draw_info.x+x,draw_info.y+y);
+ /*
+ Initialize drawing attributes.
+ */
+ draw_info.degrees=0.0;
+ draw_info.element=element;
+ draw_info.stipple=stipple;
+ draw_info.line_width=line_width;
+ draw_info.line_info=line_info;
+ if (line_info.x1 > (int) (line_width/2))
+ draw_info.line_info.x1=(short) line_width/2;
+ if (line_info.y1 > (int) (line_width/2))
+ draw_info.line_info.y1=(short) line_width/2;
+ draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
+ draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
+ if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
+ {
+ draw_info.line_info.x2=(-draw_info.line_info.x2);
+ draw_info.line_info.y2=(-draw_info.line_info.y2);
+ }
+ if (draw_info.line_info.x2 < 0)
+ {
+ draw_info.line_info.x2=(-draw_info.line_info.x2);
+ Swap(draw_info.line_info.x1,draw_info.line_info.x2);
+ }
+ if (draw_info.line_info.y2 < 0)
+ {
+ draw_info.line_info.y2=(-draw_info.line_info.y2);
+ Swap(draw_info.line_info.y1,draw_info.line_info.y2);
+ }
+ draw_info.rectangle_info=rectangle_info;
+ if (draw_info.rectangle_info.x > (int) (line_width/2))
+ draw_info.rectangle_info.x=(long) line_width/2;
+ if (draw_info.rectangle_info.y > (int) (line_width/2))
+ draw_info.rectangle_info.y=(long) line_width/2;
+ draw_info.number_coordinates=(unsigned int) number_coordinates;
+ draw_info.coordinate_info=coordinate_info;
+ windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
+ /*
+ Draw element on image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
+ XSetCursorState(display,windows,MagickFalse);
+ /*
+ Update image colormap and return to image drawing.
+ */
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ }
+ XSetCursorState(display,windows,MagickFalse);
+ coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w P a n R e c t a n g l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawPanRectangle() draws a rectangle in the pan window. The pan window
+% displays a zoom image and the rectangle shows which portion of the image is
+% displayed in the Image window.
+%
+% The format of the XDrawPanRectangle method is:
+%
+% XDrawPanRectangle(Display *display,XWindows *windows)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+*/
+static void XDrawPanRectangle(Display *display,XWindows *windows)
+{
+ MagickRealType
+ scale_factor;
+
+ RectangleInfo
+ highlight_info;
+
+ /*
+ Determine dimensions of the panning rectangle.
+ */
+ scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
+ highlight_info.x=(int) (scale_factor*windows->image.x+0.5);
+ highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
+ scale_factor=(MagickRealType)
+ windows->pan.height/windows->image.ximage->height;
+ highlight_info.y=(int) (scale_factor*windows->image.y+0.5);
+ highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
+ /*
+ Display the panning rectangle.
+ */
+ (void) XClearWindow(display,windows->pan.id);
+ XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
+ &highlight_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X I m a g e C a c h e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XImageCache() handles the creation, manipulation, and destruction of the
+% image cache (undo and redo buffers).
+%
+% The format of the XImageCache method is:
+%
+% void XImageCache(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,const CommandType command,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o command: Specifies a command to perform.
+%
+% o image: the image; XImageCache
+% may transform the image and return a new image pointer.
+%
+*/
+static void XImageCache(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,const CommandType command,Image **image)
+{
+ Image
+ *cache_image;
+
+ static Image
+ *redo_image = (Image *) NULL,
+ *undo_image = (Image *) NULL;
+
+ switch (command)
+ {
+ case FreeBuffersCommand:
+ {
+ /*
+ Free memory from the undo and redo cache.
+ */
+ while (undo_image != (Image *) NULL)
+ {
+ cache_image=undo_image;
+ undo_image=GetPreviousImageInList(undo_image);
+ cache_image->list=DestroyImage(cache_image->list);
+ cache_image=DestroyImage(cache_image);
+ }
+ undo_image=NewImageList();
+ if (redo_image != (Image *) NULL)
+ redo_image=DestroyImage(redo_image);
+ redo_image=NewImageList();
+ return;
+ }
+ case UndoCommand:
+ {
+ /*
+ Undo the last image transformation.
+ */
+ if (undo_image == (Image *) NULL)
+ {
+ (void) XBell(display,0);
+ return;
+ }
+ cache_image=undo_image;
+ undo_image=GetPreviousImageInList(undo_image);
+ windows->image.window_changes.width=(int) cache_image->columns;
+ windows->image.window_changes.height=(int) cache_image->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ windows->image.crop_geometry=(char *)
+ RelinquishMagickMemory(windows->image.crop_geometry);
+ windows->image.crop_geometry=cache_image->geometry;
+ if (redo_image != (Image *) NULL)
+ redo_image=DestroyImage(redo_image);
+ redo_image=(*image);
+ *image=cache_image->list;
+ cache_image=DestroyImage(cache_image);
+ if (windows->image.orphan != MagickFalse)
+ return;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ return;
+ }
+ case CutCommand:
+ case PasteCommand:
+ case ApplyCommand:
+ case HalfSizeCommand:
+ case OriginalSizeCommand:
+ case DoubleSizeCommand:
+ case ResizeCommand:
+ case TrimCommand:
+ case CropCommand:
+ case ChopCommand:
+ case FlipCommand:
+ case FlopCommand:
+ case RotateRightCommand:
+ case RotateLeftCommand:
+ case RotateCommand:
+ case ShearCommand:
+ case RollCommand:
+ case NegateCommand:
+ case ContrastStretchCommand:
+ case SigmoidalContrastCommand:
+ case NormalizeCommand:
+ case EqualizeCommand:
+ case HueCommand:
+ case SaturationCommand:
+ case BrightnessCommand:
+ case GammaCommand:
+ case SpiffCommand:
+ case DullCommand:
+ case GrayscaleCommand:
+ case MapCommand:
+ case QuantizeCommand:
+ case DespeckleCommand:
+ case EmbossCommand:
+ case ReduceNoiseCommand:
+ case AddNoiseCommand:
+ case SharpenCommand:
+ case BlurCommand:
+ case ThresholdCommand:
+ case EdgeDetectCommand:
+ case SpreadCommand:
+ case ShadeCommand:
+ case RaiseCommand:
+ case SegmentCommand:
+ case SolarizeCommand:
+ case SepiaToneCommand:
+ case SwirlCommand:
+ case ImplodeCommand:
+ case VignetteCommand:
+ case WaveCommand:
+ case OilPaintCommand:
+ case CharcoalDrawCommand:
+ case AnnotateCommand:
+ case AddBorderCommand:
+ case AddFrameCommand:
+ case CompositeCommand:
+ case CommentCommand:
+ case LaunchCommand:
+ case RegionofInterestCommand:
+ case SaveToUndoBufferCommand:
+ case RedoCommand:
+ {
+ Image
+ *previous_image;
+
+ long
+ bytes;
+
+ bytes=(long) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
+ if (undo_image != (Image *) NULL)
+ {
+ /*
+ Ensure the undo stash.has enough memory available.
+ */
+ previous_image=undo_image;
+ while (previous_image != (Image *) NULL)
+ {
+ bytes+=previous_image->list->columns*previous_image->list->rows*
+ sizeof(PixelPacket);
+ if (bytes <= (long) (resource_info->undo_cache << 20))
+ {
+ previous_image=GetPreviousImageInList(previous_image);
+ continue;
+ }
+ bytes-=previous_image->list->columns*previous_image->list->rows*
+ sizeof(PixelPacket);
+ if (previous_image == undo_image)
+ undo_image=NewImageList();
+ else
+ previous_image->next->previous=NewImageList();
+ break;
+ }
+ while (previous_image != (Image *) NULL)
+ {
+ /*
+ Delete any excess memory from undo cache.
+ */
+ cache_image=previous_image;
+ previous_image=GetPreviousImageInList(previous_image);
+ cache_image->list=DestroyImage(cache_image->list);
+ cache_image=DestroyImage(cache_image);
+ }
+ }
+ if (bytes > (long) (resource_info->undo_cache << 20))
+ break;
+ /*
+ Save image before transformations are applied.
+ */
+ cache_image=AcquireImage((ImageInfo *) NULL);
+ if (cache_image == (Image *) NULL)
+ break;
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (cache_image->list == (Image *) NULL)
+ {
+ cache_image=DestroyImage(cache_image);
+ break;
+ }
+ cache_image->columns=(unsigned long) windows->image.ximage->width;
+ cache_image->rows=(unsigned long) windows->image.ximage->height;
+ cache_image->geometry=windows->image.crop_geometry;
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ cache_image->geometry=AcquireString((char *) NULL);
+ (void) CopyMagickString(cache_image->geometry,
+ windows->image.crop_geometry,MaxTextExtent);
+ }
+ if (undo_image == (Image *) NULL)
+ {
+ undo_image=cache_image;
+ break;
+ }
+ undo_image->next=cache_image;
+ undo_image->next->previous=undo_image;
+ undo_image=undo_image->next;
+ break;
+ }
+ default:
+ break;
+ }
+ if (command == RedoCommand)
+ {
+ /*
+ Redo the last image transformation.
+ */
+ if (redo_image == (Image *) NULL)
+ {
+ (void) XBell(display,0);
+ return;
+ }
+ windows->image.window_changes.width=(int) redo_image->columns;
+ windows->image.window_changes.height=(int) redo_image->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ windows->image.crop_geometry=(char *)
+ RelinquishMagickMemory(windows->image.crop_geometry);
+ windows->image.crop_geometry=redo_image->geometry;
+ *image=DestroyImage(*image);
+ *image=redo_image;
+ redo_image=NewImageList();
+ if (windows->image.orphan != MagickFalse)
+ return;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ return;
+ }
+ if (command != InfoCommand)
+ return;
+ /*
+ Display image info.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
+ XSetCursorState(display,windows,MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X I m a g e W i n d o w C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XImageWindowCommand() makes a transform to the image or Image window as
+% specified by a user menu button or keyboard command.
+%
+% The format of the XMagickCommand method is:
+%
+% CommandType XImageWindowCommand(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,
+% const MagickStatusType state,KeySym key_symbol,Image **image)
+%
+% A description of each parameter follows:
+%
+% o nexus: Method XImageWindowCommand returns an image when the
+% user chooses 'Open Image' from the command menu. Otherwise a null
+% image is returned.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o state: key mask.
+%
+% o key_symbol: Specifies a command to perform.
+%
+% o image: the image; XImageWIndowCommand
+% may transform the image and return a new image pointer.
+%
+*/
+static CommandType XImageWindowCommand(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
+ KeySym key_symbol,Image **image)
+{
+ static char
+ delta[MaxTextExtent] = "";
+
+ static const char
+ Digits[] = "01234567890";
+
+ static KeySym
+ last_symbol = XK_0;
+
+ if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
+ {
+ if (((last_symbol < XK_0) || (last_symbol > XK_9)))
+ {
+ *delta='\0';
+ resource_info->quantum=1;
+ }
+ last_symbol=key_symbol;
+ delta[strlen(delta)+1]='\0';
+ delta[strlen(delta)]=Digits[key_symbol-XK_0];
+ resource_info->quantum=atoi(delta);
+ return(NullCommand);
+ }
+ last_symbol=key_symbol;
+ if (resource_info->immutable)
+ {
+ /*
+ Virtual image window has a restricted command set.
+ */
+ switch (key_symbol)
+ {
+ case XK_question:
+ return(InfoCommand);
+ case XK_p:
+ case XK_Print:
+ return(PrintCommand);
+ case XK_space:
+ return(NextCommand);
+ case XK_q:
+ case XK_Escape:
+ return(QuitCommand);
+ default:
+ break;
+ }
+ return(NullCommand);
+ }
+ switch ((int) key_symbol)
+ {
+ case XK_o:
+ {
+ if ((state & ControlMask) == 0)
+ break;
+ return(OpenCommand);
+ }
+ case XK_space:
+ return(NextCommand);
+ case XK_BackSpace:
+ return(FormerCommand);
+ case XK_s:
+ {
+ if ((state & Mod1Mask) != 0)
+ return(SwirlCommand);
+ if ((state & ControlMask) == 0)
+ return(ShearCommand);
+ return(SaveCommand);
+ }
+ case XK_p:
+ case XK_Print:
+ {
+ if ((state & Mod1Mask) != 0)
+ return(OilPaintCommand);
+ if ((state & Mod4Mask) != 0)
+ return(ColorCommand);
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(PrintCommand);
+ }
+ case XK_d:
+ {
+ if ((state & Mod4Mask) != 0)
+ return(DrawCommand);
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(DeleteCommand);
+ }
+ case XK_Select:
+ {
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(SelectCommand);
+ }
+ case XK_n:
+ {
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(NewCommand);
+ }
+ case XK_q:
+ case XK_Escape:
+ return(QuitCommand);
+ case XK_z:
+ case XK_Undo:
+ {
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(UndoCommand);
+ }
+ case XK_r:
+ case XK_Redo:
+ {
+ if ((state & ControlMask) == 0)
+ return(RollCommand);
+ return(RedoCommand);
+ }
+ case XK_x:
+ {
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(CutCommand);
+ }
+ case XK_c:
+ {
+ if ((state & Mod1Mask) != 0)
+ return(CharcoalDrawCommand);
+ if ((state & ControlMask) == 0)
+ return(CropCommand);
+ return(CopyCommand);
+ }
+ case XK_v:
+ case XK_Insert:
+ {
+ if ((state & Mod4Mask) != 0)
+ return(CompositeCommand);
+ if ((state & ControlMask) == 0)
+ return(FlipCommand);
+ return(PasteCommand);
+ }
+ case XK_less:
+ return(HalfSizeCommand);
+ case XK_minus:
+ return(OriginalSizeCommand);
+ case XK_greater:
+ return(DoubleSizeCommand);
+ case XK_percent:
+ return(ResizeCommand);
+ case XK_at:
+ return(RefreshCommand);
+ case XK_bracketleft:
+ return(ChopCommand);
+ case XK_h:
+ return(FlopCommand);
+ case XK_slash:
+ return(RotateRightCommand);
+ case XK_backslash:
+ return(RotateLeftCommand);
+ case XK_asterisk:
+ return(RotateCommand);
+ case XK_t:
+ return(TrimCommand);
+ case XK_H:
+ return(HueCommand);
+ case XK_S:
+ return(SaturationCommand);
+ case XK_L:
+ return(BrightnessCommand);
+ case XK_G:
+ return(GammaCommand);
+ case XK_C:
+ return(SpiffCommand);
+ case XK_Z:
+ return(DullCommand);
+ case XK_N:
+ return(NormalizeCommand);
+ case XK_equal:
+ return(EqualizeCommand);
+ case XK_asciitilde:
+ return(NegateCommand);
+ case XK_period:
+ return(GrayscaleCommand);
+ case XK_numbersign:
+ return(QuantizeCommand);
+ case XK_F2:
+ return(DespeckleCommand);
+ case XK_F3:
+ return(EmbossCommand);
+ case XK_F4:
+ return(ReduceNoiseCommand);
+ case XK_F5:
+ return(AddNoiseCommand);
+ case XK_F6:
+ return(SharpenCommand);
+ case XK_F7:
+ return(BlurCommand);
+ case XK_F8:
+ return(ThresholdCommand);
+ case XK_F9:
+ return(EdgeDetectCommand);
+ case XK_F10:
+ return(SpreadCommand);
+ case XK_F11:
+ return(ShadeCommand);
+ case XK_F12:
+ return(RaiseCommand);
+ case XK_F13:
+ return(SegmentCommand);
+ case XK_i:
+ {
+ if ((state & Mod1Mask) == 0)
+ return(NullCommand);
+ return(ImplodeCommand);
+ }
+ case XK_w:
+ {
+ if ((state & Mod1Mask) == 0)
+ return(NullCommand);
+ return(WaveCommand);
+ }
+ case XK_m:
+ {
+ if ((state & Mod4Mask) == 0)
+ return(NullCommand);
+ return(MatteCommand);
+ }
+ case XK_b:
+ {
+ if ((state & Mod4Mask) == 0)
+ return(NullCommand);
+ return(AddBorderCommand);
+ }
+ case XK_f:
+ {
+ if ((state & Mod4Mask) == 0)
+ return(NullCommand);
+ return(AddFrameCommand);
+ }
+ case XK_exclam:
+ {
+ if ((state & Mod4Mask) == 0)
+ return(NullCommand);
+ return(CommentCommand);
+ }
+ case XK_a:
+ {
+ if ((state & Mod1Mask) != 0)
+ return(ApplyCommand);
+ if ((state & Mod4Mask) != 0)
+ return(AnnotateCommand);
+ if ((state & ControlMask) == 0)
+ return(NullCommand);
+ return(RegionofInterestCommand);
+ }
+ case XK_question:
+ return(InfoCommand);
+ case XK_plus:
+ return(ZoomCommand);
+ case XK_P:
+ {
+ if ((state & ShiftMask) == 0)
+ return(NullCommand);
+ return(ShowPreviewCommand);
+ }
+ case XK_Execute:
+ return(LaunchCommand);
+ case XK_F1:
+ return(HelpCommand);
+ case XK_Find:
+ return(BrowseDocumentationCommand);
+ case XK_Menu:
+ {
+ (void) XMapRaised(display,windows->command.id);
+ return(NullCommand);
+ }
+ case XK_Next:
+ case XK_Prior:
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ XTranslateImage(display,windows,*image,key_symbol);
+ return(NullCommand);
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ case XK_Down:
+ case XK_KP_Down:
+ case XK_Left:
+ case XK_KP_Left:
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ if ((state & Mod1Mask) != 0)
+ {
+ RectangleInfo
+ crop_info;
+
+ /*
+ Trim one pixel from edge of image.
+ */
+ crop_info.x=0;
+ crop_info.y=0;
+ crop_info.width=(unsigned long) windows->image.ximage->width;
+ crop_info.height=(unsigned long) windows->image.ximage->height;
+ if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
+ {
+ if (resource_info->quantum >= (int) crop_info.height)
+ resource_info->quantum=(int) crop_info.height-1;
+ crop_info.height-=resource_info->quantum;
+ }
+ if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
+ {
+ if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
+ resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
+ crop_info.y+=resource_info->quantum;
+ crop_info.height-=resource_info->quantum;
+ }
+ if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
+ {
+ if (resource_info->quantum >= (int) crop_info.width)
+ resource_info->quantum=(int) crop_info.width-1;
+ crop_info.width-=resource_info->quantum;
+ }
+ if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
+ {
+ if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
+ resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
+ crop_info.x+=resource_info->quantum;
+ crop_info.width-=resource_info->quantum;
+ }
+ if ((int) (windows->image.x+windows->image.width) >
+ (int) crop_info.width)
+ windows->image.x=(int) (crop_info.width-windows->image.width);
+ if ((int) (windows->image.y+windows->image.height) >
+ (int) crop_info.height)
+ windows->image.y=(int) (crop_info.height-windows->image.height);
+ XSetCropGeometry(display,windows,&crop_info,*image);
+ windows->image.window_changes.width=(int) crop_info.width;
+ windows->image.window_changes.height=(int) crop_info.height;
+ (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ return(NullCommand);
+ }
+ XTranslateImage(display,windows,*image,key_symbol);
+ return(NullCommand);
+ }
+ default:
+ return(NullCommand);
+ }
+ return(NullCommand);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a g i c k C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMagickCommand() makes a transform to the image or Image window as
+% specified by a user menu button or keyboard command.
+%
+% The format of the XMagickCommand method is:
+%
+% Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,const CommandType command,Image **image)
+%
+% A description of each parameter follows:
+%
+% o nexus: Method XMagickCommand returns an image when the
+% user chooses 'Load Image' from the command menu. Otherwise a null
+% image is returned.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o command: Specifies a command to perform.
+%
+% o image: the image; XMagickCommand
+% may transform the image and return a new image pointer.
+%
+*/
+static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,const CommandType command,Image **image)
+{
+ char
+ filename[MaxTextExtent],
+ geometry[MaxTextExtent],
+ modulate_factors[MaxTextExtent];
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *nexus;
+
+ ImageInfo
+ *image_info;
+
+ int
+ x,
+ y;
+
+ MagickStatusType
+ flags,
+ status;
+
+ QuantizeInfo
+ quantize_info;
+
+ RectangleInfo
+ page_geometry;
+
+ register int
+ i;
+
+ static char
+ color[MaxTextExtent] = "gray";
+
+ unsigned int
+ height,
+ width;
+
+ /*
+ Process user command.
+ */
+ XCheckRefreshWindows(display,windows);
+ XImageCache(display,resource_info,windows,command,image);
+ nexus=NewImageList();
+ windows->image.window_changes.width=windows->image.ximage->width;
+ windows->image.window_changes.height=windows->image.ximage->height;
+ image_info=CloneImageInfo(resource_info->image_info);
+ SetGeometryInfo(&geometry_info);
+ GetQuantizeInfo(&quantize_info);
+ switch (command)
+ {
+ case OpenCommand:
+ {
+ /*
+ Load image.
+ */
+ nexus=XOpenImage(display,resource_info,windows,MagickFalse);
+ break;
+ }
+ case NextCommand:
+ {
+ /*
+ Display next image.
+ */
+ for (i=0; i < resource_info->quantum; i++)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case FormerCommand:
+ {
+ /*
+ Display former image.
+ */
+ for (i=0; i < resource_info->quantum; i++)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_former_image,CurrentTime);
+ break;
+ }
+ case SelectCommand:
+ {
+ int
+ status;
+
+ /*
+ Select image.
+ */
+ status=chdir(resource_info->home_directory);
+ if (status == -1)
+ (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
+ nexus=XOpenImage(display,resource_info,windows,MagickTrue);
+ break;
+ }
+ case SaveCommand:
+ {
+ /*
+ Save image.
+ */
+ status=XSaveImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to write X image:",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case PrintCommand:
+ {
+ /*
+ Print image.
+ */
+ status=XPrintImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to print X image:",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case DeleteCommand:
+ {
+ static char
+ filename[MaxTextExtent] = "\0";
+
+ /*
+ Delete image file.
+ */
+ XFileBrowserWidget(display,windows,"Delete",filename);
+ if (*filename == '\0')
+ break;
+ status=remove(filename) != 0 ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ XNoticeWidget(display,windows,"Unable to delete image file:",filename);
+ break;
+ }
+ case NewCommand:
+ {
+ int
+ status;
+
+ static char
+ color[MaxTextExtent] = "gray",
+ geometry[MaxTextExtent] = "640x480";
+
+ static const char
+ *format = "gradient";
+
+ /*
+ Query user for canvas geometry.
+ */
+ status=XDialogWidget(display,windows,"New","Enter image geometry:",
+ geometry);
+ if (*geometry == '\0')
+ break;
+ if (status == 0)
+ format="xc";
+ XColorBrowserWidget(display,windows,"Select",color);
+ if (*color == '\0')
+ break;
+ /*
+ Create canvas.
+ */
+ (void) FormatMagickString(image_info->filename,MaxTextExtent,
+ "%s:%s",format,color);
+ (void) CloneString(&image_info->size,geometry);
+ nexus=ReadImage(image_info,&(*image)->exception);
+ CatchException(&(*image)->exception);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case VisualDirectoryCommand:
+ {
+ /*
+ Visual Image directory.
+ */
+ nexus=XVisualDirectoryImage(display,resource_info,windows);
+ break;
+ }
+ case QuitCommand:
+ {
+ /*
+ exit program.
+ */
+ if (resource_info->confirm_exit == MagickFalse)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ else
+ {
+ int
+ status;
+
+ /*
+ Confirm program exit.
+ */
+ status=XConfirmWidget(display,windows,"Do you really want to exit",
+ resource_info->client_name);
+ if (status > 0)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ }
+ break;
+ }
+ case CutCommand:
+ {
+ /*
+ Cut image.
+ */
+ (void) XCropImage(display,resource_info,windows,*image,CutMode);
+ break;
+ }
+ case CopyCommand:
+ {
+ /*
+ Copy image.
+ */
+ (void) XCropImage(display,resource_info,windows,*image,CopyMode);
+ break;
+ }
+ case PasteCommand:
+ {
+ /*
+ Paste image.
+ */
+ status=XPasteImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to paste X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case HalfSizeCommand:
+ {
+ /*
+ Half image size.
+ */
+ windows->image.window_changes.width=windows->image.ximage->width/2;
+ windows->image.window_changes.height=windows->image.ximage->height/2;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case OriginalSizeCommand:
+ {
+ /*
+ Original image size.
+ */
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case DoubleSizeCommand:
+ {
+ /*
+ Double the image size.
+ */
+ windows->image.window_changes.width=windows->image.ximage->width << 1;
+ windows->image.window_changes.height=windows->image.ximage->height << 1;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ResizeCommand:
+ {
+ int
+ status;
+
+ long
+ x,
+ y;
+
+ unsigned long
+ height,
+ width;
+
+ /*
+ Resize image.
+ */
+ width=(unsigned long) windows->image.ximage->width;
+ height=(unsigned long) windows->image.ximage->height;
+ x=0;
+ y=0;
+ (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu+0+0",
+ width,height);
+ status=XDialogWidget(display,windows,"Resize",
+ "Enter resize geometry (e.g. 640x480, 200%):",geometry);
+ if (*geometry == '\0')
+ break;
+ if (status == 0)
+ (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
+ (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
+ windows->image.window_changes.width=(int) width;
+ windows->image.window_changes.height=(int) height;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ApplyCommand:
+ {
+ char
+ image_geometry[MaxTextExtent];
+
+ if ((windows->image.crop_geometry == (char *) NULL) &&
+ ((int) (*image)->columns == windows->image.ximage->width) &&
+ ((int) (*image)->rows == windows->image.ximage->height))
+ break;
+ /*
+ Apply size transforms to image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ /*
+ Crop and/or scale displayed image.
+ */
+ (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
+ windows->image.ximage->width,windows->image.ximage->height);
+ (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
+ if (windows->image.crop_geometry != (char *) NULL)
+ windows->image.crop_geometry=(char *)
+ RelinquishMagickMemory(windows->image.crop_geometry);
+ windows->image.x=0;
+ windows->image.y=0;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case RefreshCommand:
+ {
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case RestoreCommand:
+ {
+ /*
+ Restore Image window to its original size.
+ */
+ if ((windows->image.width == (unsigned int) (*image)->columns) &&
+ (windows->image.height == (unsigned int) (*image)->rows) &&
+ (windows->image.crop_geometry == (char *) NULL))
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ windows->image.crop_geometry=(char *)
+ RelinquishMagickMemory(windows->image.crop_geometry);
+ windows->image.crop_geometry=(char *) NULL;
+ windows->image.x=0;
+ windows->image.y=0;
+ }
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case CropCommand:
+ {
+ /*
+ Crop image.
+ */
+ (void) XCropImage(display,resource_info,windows,*image,CropMode);
+ break;
+ }
+ case ChopCommand:
+ {
+ /*
+ Chop image.
+ */
+ status=XChopImage(display,resource_info,windows,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to cut X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case FlopCommand:
+ {
+ Image
+ *flop_image;
+
+ /*
+ Flop image scanlines.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flop_image=FlopImage(*image,&(*image)->exception);
+ if (flop_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=flop_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ /*
+ Flop crop geometry.
+ */
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
+ }
+ if (windows->image.orphan != MagickFalse)
+ break;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case FlipCommand:
+ {
+ Image
+ *flip_image;
+
+ /*
+ Flip image scanlines.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flip_image=FlipImage(*image,&(*image)->exception);
+ if (flip_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=flip_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ /*
+ Flip crop geometry.
+ */
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
+ }
+ if (windows->image.orphan != MagickFalse)
+ break;
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case RotateRightCommand:
+ {
+ /*
+ Rotate image 90 degrees clockwise.
+ */
+ status=XRotateImage(display,resource_info,windows,90.0,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to rotate X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case RotateLeftCommand:
+ {
+ /*
+ Rotate image 90 degrees counter-clockwise.
+ */
+ status=XRotateImage(display,resource_info,windows,-90.0,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to rotate X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case RotateCommand:
+ {
+ /*
+ Rotate image.
+ */
+ status=XRotateImage(display,resource_info,windows,0.0,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to rotate X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case ShearCommand:
+ {
+ Image
+ *shear_image;
+
+ static char
+ geometry[MaxTextExtent] = "45.0x45.0";
+
+ /*
+ Query user for shear color and geometry.
+ */
+ XColorBrowserWidget(display,windows,"Select",color);
+ if (*color == '\0')
+ break;
+ (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
+ geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Shear image.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) QueryColorDatabase(color,&(*image)->background_color,
+ &(*image)->exception);
+ flags=ParseGeometry(geometry,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=geometry_info.rho;
+ shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (shear_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=shear_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case RollCommand:
+ {
+ Image
+ *roll_image;
+
+ static char
+ geometry[MaxTextExtent] = "+2+2";
+
+ /*
+ Query user for the roll geometry.
+ */
+ (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
+ geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Roll image.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) ParsePageGeometry(*image,geometry,&page_geometry,
+ &(*image)->exception);
+ roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
+ &(*image)->exception);
+ if (roll_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=roll_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case TrimCommand:
+ {
+ static char
+ fuzz[MaxTextExtent];
+
+ /*
+ Query user for the fuzz factor.
+ */
+ (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*(*image)->fuzz/
+ (QuantumRange+1.0));
+ (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
+ if (*fuzz == '\0')
+ break;
+ (*image)->fuzz=StringToDouble(fuzz,(double) QuantumRange+1.0);
+ /*
+ Trim image.
+ */
+ status=XTrimImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to trim X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case HueCommand:
+ {
+ static char
+ hue_percent[MaxTextExtent] = "110";
+
+ /*
+ Query user for percent hue change.
+ */
+ (void) XDialogWidget(display,windows,"Apply",
+ "Enter percent change in image hue (0-200):",hue_percent);
+ if (*hue_percent == '\0')
+ break;
+ /*
+ Vary the image hue.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
+ (void) ConcatenateMagickString(modulate_factors,hue_percent,
+ MaxTextExtent);
+ (void) ModulateImage(*image,modulate_factors);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SaturationCommand:
+ {
+ static char
+ saturation_percent[MaxTextExtent] = "110";
+
+ /*
+ Query user for percent saturation change.
+ */
+ (void) XDialogWidget(display,windows,"Apply",
+ "Enter percent change in color saturation (0-200):",saturation_percent);
+ if (*saturation_percent == '\0')
+ break;
+ /*
+ Vary color saturation.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
+ (void) ConcatenateMagickString(modulate_factors,saturation_percent,
+ MaxTextExtent);
+ (void) ModulateImage(*image,modulate_factors);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case BrightnessCommand:
+ {
+ static char
+ brightness_percent[MaxTextExtent] = "110";
+
+ /*
+ Query user for percent brightness change.
+ */
+ (void) XDialogWidget(display,windows,"Apply",
+ "Enter percent change in color brightness (0-200):",brightness_percent);
+ if (*brightness_percent == '\0')
+ break;
+ /*
+ Vary the color brightness.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(modulate_factors,brightness_percent,
+ MaxTextExtent);
+ (void) ModulateImage(*image,modulate_factors);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case GammaCommand:
+ {
+ static char
+ factor[MaxTextExtent] = "1.6";
+
+ /*
+ Query user for gamma value.
+ */
+ (void) XDialogWidget(display,windows,"Gamma",
+ "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
+ if (*factor == '\0')
+ break;
+ /*
+ Gamma correct image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) GammaImage(*image,factor);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SpiffCommand:
+ {
+ /*
+ Sharpen the image contrast.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) ContrastImage(*image,MagickTrue);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case DullCommand:
+ {
+ /*
+ Dull the image contrast.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) ContrastImage(*image,MagickFalse);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ContrastStretchCommand:
+ {
+ double
+ black_point,
+ white_point;
+
+ static char
+ levels[MaxTextExtent] = "1%";
+
+ /*
+ Query user for gamma value.
+ */
+ (void) XDialogWidget(display,windows,"Contrast Stretch",
+ "Enter black and white points:",levels);
+ if (*levels == '\0')
+ break;
+ /*
+ Contrast stretch image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(levels,&geometry_info);
+ black_point=geometry_info.rho;
+ white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
+ if ((flags & PercentValue) != 0)
+ {
+ black_point*=(double) (*image)->columns*(*image)->rows/100.0;
+ white_point*=(double) (*image)->columns*(*image)->rows/100.0;
+ }
+ white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
+ (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
+ white_point);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SigmoidalContrastCommand:
+ {
+ static char
+ levels[MaxTextExtent] = "3x50%";
+
+ /*
+ Query user for gamma value.
+ */
+ (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
+ "Enter contrast and midpoint:",levels);
+ if (*levels == '\0')
+ break;
+ /*
+ Contrast stretch image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) SigmoidalContrastImage(*image,MagickTrue,levels);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case NormalizeCommand:
+ {
+ /*
+ Perform histogram normalization on the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) NormalizeImage(*image);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case EqualizeCommand:
+ {
+ /*
+ Perform histogram equalization on the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) EqualizeImage(*image);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case NegateCommand:
+ {
+ /*
+ Negate colors in image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) NegateImage(*image,MagickFalse);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case GrayscaleCommand:
+ {
+ /*
+ Convert image to grayscale.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) SetImageType(*image,(*image)->matte == MagickFalse ?
+ GrayscaleType : GrayscaleMatteType);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case MapCommand:
+ {
+ Image
+ *affinity_image;
+
+ static char
+ filename[MaxTextExtent] = "\0";
+
+ /*
+ Request image file name from user.
+ */
+ XFileBrowserWidget(display,windows,"Map",filename);
+ if (*filename == '\0')
+ break;
+ /*
+ Map image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ affinity_image=ReadImage(image_info,&(*image)->exception);
+ if (affinity_image != (Image *) NULL)
+ {
+ (void) RemapImage(&quantize_info,*image,affinity_image);
+ affinity_image=DestroyImage(affinity_image);
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case QuantizeCommand:
+ {
+ int
+ status;
+
+ static char
+ colors[MaxTextExtent] = "256";
+
+ /*
+ Query user for maximum number of colors.
+ */
+ status=XDialogWidget(display,windows,"Quantize",
+ "Maximum number of colors:",colors);
+ if (*colors == '\0')
+ break;
+ /*
+ Color reduce the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ quantize_info.number_colors=(unsigned long) atol(colors);
+ quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
+ (void) QuantizeImage(&quantize_info,*image);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case DespeckleCommand:
+ {
+ Image
+ *despeckle_image;
+
+ /*
+ Despeckle image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ despeckle_image=DespeckleImage(*image,&(*image)->exception);
+ if (despeckle_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=despeckle_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case EmbossCommand:
+ {
+ Image
+ *emboss_image;
+
+ static char
+ radius[MaxTextExtent] = "0.0x1.0";
+
+ /*
+ Query user for emboss radius.
+ */
+ (void) XDialogWidget(display,windows,"Emboss",
+ "Enter the emboss radius and standard deviation:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Reduce noise in the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0;
+ emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (emboss_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=emboss_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ReduceNoiseCommand:
+ {
+ Image
+ *noise_image;
+
+ static char
+ radius[MaxTextExtent] = "0";
+
+ /*
+ Query user for noise radius.
+ */
+ (void) XDialogWidget(display,windows,"Reduce Noise",
+ "Enter the noise radius:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Reduce noise in the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ noise_image=ReduceNoiseImage(*image,geometry_info.rho,
+ &(*image)->exception);
+ if (noise_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=noise_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case AddNoiseCommand:
+ {
+ char
+ **noises;
+
+ Image
+ *noise_image;
+
+ static char
+ noise_type[MaxTextExtent] = "Gaussian";
+
+ /*
+ Add noise to the image.
+ */
+ noises=GetMagickOptions(MagickNoiseOptions);
+ if (noises == (char **) NULL)
+ break;
+ XListBrowserWidget(display,windows,&windows->widget,
+ (const char **) noises,"Add Noise",
+ "Select a type of noise to add to your image:",noise_type);
+ noises=DestroyStringList(noises);
+ if (*noise_type == '\0')
+ break;
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
+ MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
+ if (noise_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=noise_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SharpenCommand:
+ {
+ Image
+ *sharp_image;
+
+ static char
+ radius[MaxTextExtent] = "0.0x1.0";
+
+ /*
+ Query user for sharpen radius.
+ */
+ (void) XDialogWidget(display,windows,"Sharpen",
+ "Enter the sharpen radius and standard deviation:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Sharpen image scanlines.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (sharp_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=sharp_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case BlurCommand:
+ {
+ Image
+ *blur_image;
+
+ static char
+ radius[MaxTextExtent] = "0.0x1.0";
+
+ /*
+ Query user for blur radius.
+ */
+ (void) XDialogWidget(display,windows,"Blur",
+ "Enter the blur radius and standard deviation:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Blur an image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (blur_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=blur_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ThresholdCommand:
+ {
+ double
+ threshold;
+
+ static char
+ factor[MaxTextExtent] = "128";
+
+ /*
+ Query user for threshold value.
+ */
+ (void) XDialogWidget(display,windows,"Threshold",
+ "Enter threshold value:",factor);
+ if (*factor == '\0')
+ break;
+ /*
+ Gamma correct image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ threshold=StringToDouble(factor,QuantumRange);
+ (void) BilevelImage(*image,threshold);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case EdgeDetectCommand:
+ {
+ Image
+ *edge_image;
+
+ static char
+ radius[MaxTextExtent] = "0";
+
+ /*
+ Query user for edge factor.
+ */
+ (void) XDialogWidget(display,windows,"Detect Edges",
+ "Enter the edge detect radius:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Detect edge in image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
+ if (edge_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=edge_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SpreadCommand:
+ {
+ Image
+ *spread_image;
+
+ static char
+ amount[MaxTextExtent] = "2";
+
+ /*
+ Query user for spread amount.
+ */
+ (void) XDialogWidget(display,windows,"Spread",
+ "Enter the displacement amount:",amount);
+ if (*amount == '\0')
+ break;
+ /*
+ Displace image pixels by a random amount.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(amount,&geometry_info);
+ spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
+ if (spread_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=spread_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ShadeCommand:
+ {
+ Image
+ *shade_image;
+
+ int
+ status;
+
+ static char
+ geometry[MaxTextExtent] = "30x30";
+
+ /*
+ Query user for the shade geometry.
+ */
+ status=XDialogWidget(display,windows,"Shade",
+ "Enter the azimuth and elevation of the light source:",geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Shade image pixels.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(geometry,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0;
+ shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
+ geometry_info.rho,geometry_info.sigma,&(*image)->exception);
+ if (shade_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=shade_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case RaiseCommand:
+ {
+ static char
+ bevel_width[MaxTextExtent] = "10";
+
+ /*
+ Query user for bevel width.
+ */
+ (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
+ if (*bevel_width == '\0')
+ break;
+ /*
+ Raise an image.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
+ &(*image)->exception);
+ (void) RaiseImage(*image,&page_geometry,MagickTrue);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SegmentCommand:
+ {
+ static char
+ threshold[MaxTextExtent] = "1.0x1.5";
+
+ /*
+ Query user for smoothing threshold.
+ */
+ (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
+ threshold);
+ if (*threshold == '\0')
+ break;
+ /*
+ Segment an image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(threshold,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0;
+ (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
+ geometry_info.sigma);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SepiaToneCommand:
+ {
+ double
+ threshold;
+
+ Image
+ *sepia_image;
+
+ static char
+ factor[MaxTextExtent] = "80%";
+
+ /*
+ Query user for sepia-tone factor.
+ */
+ (void) XDialogWidget(display,windows,"Sepia Tone",
+ "Enter the sepia tone factor (0 - 99.9%):",factor);
+ if (*factor == '\0')
+ break;
+ /*
+ Sepia tone image pixels.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ threshold=StringToDouble(factor,QuantumRange);
+ sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
+ if (sepia_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=sepia_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SolarizeCommand:
+ {
+ double
+ threshold;
+
+ static char
+ factor[MaxTextExtent] = "60%";
+
+ /*
+ Query user for solarize factor.
+ */
+ (void) XDialogWidget(display,windows,"Solarize",
+ "Enter the solarize factor (0 - 99.9%):",factor);
+ if (*factor == '\0')
+ break;
+ /*
+ Solarize image pixels.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ threshold=StringToDouble(factor,QuantumRange);
+ (void) SolarizeImage(*image,threshold);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case SwirlCommand:
+ {
+ Image
+ *swirl_image;
+
+ static char
+ degrees[MaxTextExtent] = "60";
+
+ /*
+ Query user for swirl angle.
+ */
+ (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
+ degrees);
+ if (*degrees == '\0')
+ break;
+ /*
+ Swirl image pixels about the center.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(degrees,&geometry_info);
+ swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
+ if (swirl_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=swirl_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case ImplodeCommand:
+ {
+ Image
+ *implode_image;
+
+ static char
+ factor[MaxTextExtent] = "0.3";
+
+ /*
+ Query user for implode factor.
+ */
+ (void) XDialogWidget(display,windows,"Implode",
+ "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
+ if (*factor == '\0')
+ break;
+ /*
+ Implode image pixels about the center.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(factor,&geometry_info);
+ implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
+ if (implode_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=implode_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case VignetteCommand:
+ {
+ Image
+ *vignette_image;
+
+ static char
+ geometry[MaxTextExtent] = "0x20";
+
+ /*
+ Query user for the vignette geometry.
+ */
+ (void) XDialogWidget(display,windows,"Vignette",
+ "Enter the radius, sigma, and x and y offsets:",geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Soften the edges of the image in vignette style
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(geometry,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0;
+ if ((flags & XiValue) == 0)
+ geometry_info.xi=0.1*(*image)->columns;
+ if ((flags & PsiValue) == 0)
+ geometry_info.psi=0.1*(*image)->rows;
+ vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
+ (long) (geometry_info.xi+0.5),(long) (geometry_info.psi+0.5),
+ &(*image)->exception);
+ if (vignette_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=vignette_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case WaveCommand:
+ {
+ Image
+ *wave_image;
+
+ static char
+ geometry[MaxTextExtent] = "25x150";
+
+ /*
+ Query user for the wave geometry.
+ */
+ (void) XDialogWidget(display,windows,"Wave",
+ "Enter the amplitude and length of the wave:",geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Alter an image along a sine wave.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(geometry,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0;
+ wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (wave_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=wave_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case OilPaintCommand:
+ {
+ Image
+ *paint_image;
+
+ static char
+ radius[MaxTextExtent] = "0";
+
+ /*
+ Query user for circular neighborhood radius.
+ */
+ (void) XDialogWidget(display,windows,"Oil Paint",
+ "Enter the mask radius:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ OilPaint image scanlines.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
+ if (paint_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=paint_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case CharcoalDrawCommand:
+ {
+ Image
+ *charcoal_image;
+
+ static char
+ radius[MaxTextExtent] = "0x1";
+
+ /*
+ Query user for charcoal radius.
+ */
+ (void) XDialogWidget(display,windows,"Charcoal Draw",
+ "Enter the charcoal radius and sigma:",radius);
+ if (*radius == '\0')
+ break;
+ /*
+ Charcoal the image.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ flags=ParseGeometry(radius,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=geometry_info.rho;
+ charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
+ &(*image)->exception);
+ if (charcoal_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=charcoal_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case AnnotateCommand:
+ {
+ /*
+ Annotate the image with text.
+ */
+ status=XAnnotateEditImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to annotate X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case DrawCommand:
+ {
+ /*
+ Draw image.
+ */
+ status=XDrawEditImage(display,resource_info,windows,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to draw on the X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case ColorCommand:
+ {
+ /*
+ Color edit.
+ */
+ status=XColorEditImage(display,resource_info,windows,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to pixel edit X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case MatteCommand:
+ {
+ /*
+ Matte edit.
+ */
+ status=XMatteEditImage(display,resource_info,windows,image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to matte edit X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case CompositeCommand:
+ {
+ /*
+ Composite image.
+ */
+ status=XCompositeImage(display,resource_info,windows,*image);
+ if (status == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to composite X image",
+ (*image)->filename);
+ break;
+ }
+ break;
+ }
+ case AddBorderCommand:
+ {
+ Image
+ *border_image;
+
+ static char
+ geometry[MaxTextExtent] = "6x6";
+
+ /*
+ Query user for border color and geometry.
+ */
+ XColorBrowserWidget(display,windows,"Select",color);
+ if (*color == '\0')
+ break;
+ (void) XDialogWidget(display,windows,"Add Border",
+ "Enter border geometry:",geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Add a border to the image.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) QueryColorDatabase(color,&(*image)->border_color,
+ &(*image)->exception);
+ (void) ParsePageGeometry(*image,geometry,&page_geometry,
+ &(*image)->exception);
+ border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
+ if (border_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=border_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case AddFrameCommand:
+ {
+ FrameInfo
+ frame_info;
+
+ Image
+ *frame_image;
+
+ static char
+ geometry[MaxTextExtent] = "6x6";
+
+ /*
+ Query user for frame color and geometry.
+ */
+ XColorBrowserWidget(display,windows,"Select",color);
+ if (*color == '\0')
+ break;
+ (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
+ geometry);
+ if (*geometry == '\0')
+ break;
+ /*
+ Surround image with an ornamental border.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) QueryColorDatabase(color,&(*image)->matte_color,
+ &(*image)->exception);
+ (void) ParsePageGeometry(*image,geometry,&page_geometry,
+ &(*image)->exception);
+ frame_info.width=page_geometry.width;
+ frame_info.height=page_geometry.height;
+ frame_info.outer_bevel=page_geometry.x;
+ frame_info.inner_bevel=page_geometry.y;
+ frame_info.x=(long) frame_info.width;
+ frame_info.y=(long) frame_info.height;
+ frame_info.width=(*image)->columns+2*frame_info.width;
+ frame_info.height=(*image)->rows+2*frame_info.height;
+ frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
+ if (frame_image != (Image *) NULL)
+ {
+ *image=DestroyImage(*image);
+ *image=frame_image;
+ }
+ CatchException(&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (windows->image.orphan != MagickFalse)
+ break;
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ break;
+ }
+ case CommentCommand:
+ {
+ const char
+ *value;
+
+ FILE
+ *file;
+
+ int
+ unique_file;
+
+ /*
+ Edit image comment.
+ */
+ unique_file=AcquireUniqueFileResource(image_info->filename);
+ if (unique_file == -1)
+ XNoticeWidget(display,windows,"Unable to edit image comment",
+ image_info->filename);
+ value=GetImageProperty(*image,"comment");
+ if (value == (char *) NULL)
+ unique_file=close(unique_file)-1;
+ else
+ {
+ register const char
+ *p;
+
+ file=fdopen(unique_file,"w");
+ if (file == (FILE *) NULL)
+ {
+ XNoticeWidget(display,windows,"Unable to edit image comment",
+ image_info->filename);
+ break;
+ }
+ for (p=value; *p != '\0'; p++)
+ (void) fputc((int) *p,file);
+ (void) fputc('\n',file);
+ (void) fclose(file);
+ }
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
+ &(*image)->exception);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to edit image comment",
+ (char *) NULL);
+ else
+ {
+ char
+ *comment;
+
+ comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
+ if (comment != (char *) NULL)
+ {
+ (void) SetImageProperty(*image,"comment",comment);
+ (*image)->taint=MagickTrue;
+ }
+ }
+ (void) RelinquishUniqueFileResource(image_info->filename);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case LaunchCommand:
+ {
+ /*
+ Launch program.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) AcquireUniqueFilename(filename);
+ (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
+ filename);
+ status=WriteImage(image_info,*image);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to launch image editor",
+ (char *) NULL);
+ else
+ {
+ nexus=ReadImage(resource_info->image_info,&(*image)->exception);
+ CatchException(&(*image)->exception);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ }
+ (void) RelinquishUniqueFileResource(filename);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case RegionofInterestCommand:
+ {
+ /*
+ Apply an image processing technique to a region of interest.
+ */
+ (void) XROIImage(display,resource_info,windows,image);
+ break;
+ }
+ case InfoCommand:
+ break;
+ case ZoomCommand:
+ {
+ /*
+ Zoom image.
+ */
+ if (windows->magnify.mapped != MagickFalse)
+ (void) XRaiseWindow(display,windows->magnify.id);
+ else
+ {
+ /*
+ Make magnify image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ (void) XMapRaised(display,windows->magnify.id);
+ XSetCursorState(display,windows,MagickFalse);
+ }
+ break;
+ }
+ case ShowPreviewCommand:
+ {
+ char
+ **previews;
+
+ Image
+ *preview_image;
+
+ static char
+ preview_type[MaxTextExtent] = "Gamma";
+
+ /*
+ Select preview type from menu.
+ */
+ previews=GetMagickOptions(MagickPreviewOptions);
+ if (previews == (char **) NULL)
+ break;
+ XListBrowserWidget(display,windows,&windows->widget,
+ (const char **) previews,"Preview",
+ "Select an enhancement, effect, or F/X:",preview_type);
+ previews=DestroyStringList(previews);
+ if (*preview_type == '\0')
+ break;
+ /*
+ Show image preview.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ image_info->preview_type=(PreviewType)
+ ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
+ image_info->group=(long) windows->image.id;
+ (void) DeleteImageProperty(*image,"label");
+ (void) SetImageProperty(*image,"label","Preview");
+ (void) AcquireUniqueFilename(filename);
+ (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
+ filename);
+ status=WriteImage(image_info,*image);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ preview_image=ReadImage(image_info,&(*image)->exception);
+ (void) RelinquishUniqueFileResource(filename);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
+ filename);
+ status=WriteImage(image_info,preview_image);
+ preview_image=DestroyImage(preview_image);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to show image preview",
+ (*image)->filename);
+ XDelay(display,1500);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case ShowHistogramCommand:
+ {
+ Image
+ *histogram_image;
+
+ /*
+ Show image histogram.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ image_info->group=(long) windows->image.id;
+ (void) DeleteImageProperty(*image,"label");
+ (void) SetImageProperty(*image,"label","Histogram");
+ (void) AcquireUniqueFilename(filename);
+ (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
+ filename);
+ status=WriteImage(image_info,*image);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ histogram_image=ReadImage(image_info,&(*image)->exception);
+ (void) RelinquishUniqueFileResource(filename);
+ if (histogram_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
+ "show:%s",filename);
+ status=WriteImage(image_info,histogram_image);
+ histogram_image=DestroyImage(histogram_image);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to show histogram",
+ (*image)->filename);
+ XDelay(display,1500);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case ShowMatteCommand:
+ {
+ Image
+ *matte_image;
+
+ if ((*image)->matte == MagickFalse)
+ {
+ XNoticeWidget(display,windows,
+ "Image does not have any matte information",(*image)->filename);
+ break;
+ }
+ /*
+ Show image matte.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ image_info->group=(long) windows->image.id;
+ (void) DeleteImageProperty(*image,"label");
+ (void) SetImageProperty(*image,"label","Matte");
+ (void) AcquireUniqueFilename(filename);
+ (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
+ filename);
+ status=WriteImage(image_info,*image);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ matte_image=ReadImage(image_info,&(*image)->exception);
+ (void) RelinquishUniqueFileResource(filename);
+ if (matte_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
+ filename);
+ status=WriteImage(image_info,matte_image);
+ matte_image=DestroyImage(matte_image);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to show matte",
+ (*image)->filename);
+ XDelay(display,1500);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case BackgroundCommand:
+ {
+ /*
+ Background image.
+ */
+ status=XBackgroundImage(display,resource_info,windows,image);
+ if (status == MagickFalse)
+ break;
+ nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
+ if (nexus != (Image *) NULL)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case SlideShowCommand:
+ {
+ static char
+ delay[MaxTextExtent] = "5";
+
+ /*
+ Display next image after pausing.
+ */
+ (void) XDialogWidget(display,windows,"Slide Show",
+ "Pause how many 1/100ths of a second between images:",delay);
+ if (*delay == '\0')
+ break;
+ resource_info->delay=(unsigned long) atol(delay);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case PreferencesCommand:
+ {
+ /*
+ Set user preferences.
+ */
+ status=XPreferencesWidget(display,resource_info,windows);
+ if (status == MagickFalse)
+ break;
+ nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
+ if (nexus != (Image *) NULL)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case HelpCommand:
+ {
+ /*
+ User requested help.
+ */
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Display",DisplayHelp);
+ break;
+ }
+ case BrowseDocumentationCommand:
+ {
+ Atom
+ mozilla_atom;
+
+ Window
+ mozilla_window,
+ root_window;
+
+ /*
+ Browse the ImageMagick documentation.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
+ mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
+ if (mozilla_window != (Window) NULL)
+ {
+ char
+ command[MaxTextExtent],
+ *url;
+
+ /*
+ Display documentation using Netscape remote control.
+ */
+ url=GetMagickHomeURL();
+ (void) FormatMagickString(command,MaxTextExtent,
+ "openurl(%s,new-tab)",url);
+ url=DestroyString(url);
+ mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
+ (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
+ 8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
+ &(*image)->exception);
+ if (status == MagickFalse)
+ XNoticeWidget(display,windows,"Unable to browse documentation",
+ (char *) NULL);
+ XDelay(display,1500);
+ XSetCursorState(display,windows,MagickFalse);
+ break;
+ }
+ case VersionCommand:
+ {
+ XNoticeWidget(display,windows,GetMagickVersion((unsigned long *) NULL),
+ GetMagickCopyright());
+ break;
+ }
+ case SaveToUndoBufferCommand:
+ break;
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ image_info=DestroyImageInfo(image_info);
+ return(nexus);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a g n i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMagnifyImage() magnifies portions of the image as indicated by the pointer.
+% The magnified portion is displayed in a separate window.
+%
+% The format of the XMagnifyImage method is:
+%
+% void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o event: Specifies a pointer to a XEvent structure. If it is NULL,
+% the entire image is refreshed.
+%
+*/
+static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
+{
+ char
+ text[MaxTextExtent];
+
+ register int
+ x,
+ y;
+
+ unsigned long
+ state;
+
+ /*
+ Update magnified image until the mouse button is released.
+ */
+ (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
+ state=DefaultState;
+ x=event->xbutton.x;
+ y=event->xbutton.y;
+ windows->magnify.x=windows->image.x+x;
+ windows->magnify.y=windows->image.y+y;
+ do
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ windows->magnify.x,windows->magnify.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,event);
+ switch (event->type)
+ {
+ case ButtonPress:
+ break;
+ case ButtonRelease:
+ {
+ /*
+ User has finished magnifying image.
+ */
+ x=event->xbutton.x;
+ y=event->xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ x=event->xmotion.x;
+ y=event->xmotion.y;
+ break;
+ }
+ default:
+ break;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if (x < 0)
+ x=0;
+ else
+ if (x >= (int) windows->image.width)
+ x=(int) windows->image.width-1;
+ if (y < 0)
+ y=0;
+ else
+ if (y >= (int) windows->image.height)
+ y=(int) windows->image.height-1;
+ } while ((state & ExitState) == 0);
+ /*
+ Display magnified image.
+ */
+ XSetCursorState(display,windows,MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a g n i f y W i n d o w C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMagnifyWindowCommand() moves the image within an Magnify window by one
+% pixel as specified by the key symbol.
+%
+% The format of the XMagnifyWindowCommand method is:
+%
+% void XMagnifyWindowCommand(Display *display,XWindows *windows,
+% const MagickStatusType state,const KeySym key_symbol)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o state: key mask.
+%
+% o key_symbol: Specifies a KeySym which indicates which side of the image
+% to trim.
+%
+*/
+static void XMagnifyWindowCommand(Display *display,XWindows *windows,
+ const MagickStatusType state,const KeySym key_symbol)
+{
+ unsigned int
+ quantum;
+
+ /*
+ User specified a magnify factor or position.
+ */
+ quantum=1;
+ if ((state & Mod1Mask) != 0)
+ quantum=10;
+ switch ((int) key_symbol)
+ {
+ case QuitCommand:
+ {
+ (void) XWithdrawWindow(display,windows->magnify.id,
+ windows->magnify.screen);
+ break;
+ }
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ windows->magnify.x=(int) windows->image.width/2;
+ windows->magnify.y=(int) windows->image.height/2;
+ break;
+ }
+ case XK_Left:
+ case XK_KP_Left:
+ {
+ if (windows->magnify.x > 0)
+ windows->magnify.x-=quantum;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ if (windows->magnify.y > 0)
+ windows->magnify.y-=quantum;
+ break;
+ }
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ if (windows->magnify.x < (int) (windows->image.ximage->width-1))
+ windows->magnify.x+=quantum;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ if (windows->magnify.y < (int) (windows->image.ximage->height-1))
+ windows->magnify.y+=quantum;
+ break;
+ }
+ case XK_0:
+ case XK_1:
+ case XK_2:
+ case XK_3:
+ case XK_4:
+ case XK_5:
+ case XK_6:
+ case XK_7:
+ case XK_8:
+ case XK_9:
+ {
+ windows->magnify.data=(key_symbol-XK_0);
+ break;
+ }
+ case XK_KP_0:
+ case XK_KP_1:
+ case XK_KP_2:
+ case XK_KP_3:
+ case XK_KP_4:
+ case XK_KP_5:
+ case XK_KP_6:
+ case XK_KP_7:
+ case XK_KP_8:
+ case XK_KP_9:
+ {
+ windows->magnify.data=(key_symbol-XK_KP_0);
+ break;
+ }
+ default:
+ break;
+ }
+ XMakeMagnifyImage(display,windows);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a k e P a n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakePanImage() creates a thumbnail of the image and displays it in the Pan
+% icon window.
+%
+% The format of the XMakePanImage method is:
+%
+% void XMakePanImage(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static void XMakePanImage(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,Image *image)
+{
+ MagickStatusType
+ status;
+
+ /*
+ Create and display image for panning icon.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ windows->pan.x=windows->image.x;
+ windows->pan.y=windows->image.y;
+ status=XMakeImage(display,resource_info,&windows->pan,image,
+ windows->pan.width,windows->pan.height);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerError,image->exception.reason,
+ image->exception.description);
+ (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
+ windows->pan.pixmap);
+ (void) XClearWindow(display,windows->pan.id);
+ XDrawPanRectangle(display,windows);
+ XSetCursorState(display,windows,MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a t t a E d i t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMatteEditImage() allows the user to interactively change the Matte channel
+% of an image. If the image is PseudoClass it is promoted to DirectClass
+% before the matte information is stored.
+%
+% The format of the XMatteEditImage method is:
+%
+% MagickBooleanType XMatteEditImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+static MagickBooleanType XMatteEditImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+ static char
+ matte[MaxTextExtent] = "0";
+
+ static const char
+ *MatteEditMenu[] =
+ {
+ "Method",
+ "Border Color",
+ "Fuzz",
+ "Matte Value",
+ "Undo",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static const ModeType
+ MatteEditCommands[] =
+ {
+ MatteEditMethod,
+ MatteEditBorderCommand,
+ MatteEditFuzzCommand,
+ MatteEditValueCommand,
+ MatteEditUndoCommand,
+ MatteEditHelpCommand,
+ MatteEditDismissCommand
+ };
+
+ static PaintMethod
+ method = PointMethod;
+
+ static XColor
+ border_color = { 0, 0, 0, 0, 0, 0 };
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ int
+ entry,
+ id,
+ x,
+ x_offset,
+ y,
+ y_offset;
+
+ register int
+ i;
+
+ register PixelPacket
+ *q;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Matte Edit");
+ windows->command.data=4;
+ (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Make cursor.
+ */
+ cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
+ resource_info->background_color,resource_info->foreground_color);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
+ x+windows->image.x,y+windows->image.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,MatteEditMenu,&event);
+ if (id < 0)
+ {
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ continue;
+ }
+ switch (MatteEditCommands[id])
+ {
+ case MatteEditMethod:
+ {
+ char
+ **methods;
+
+ /*
+ Select a method from the pop-up menu.
+ */
+ methods=GetMagickOptions(MagickMethodOptions);
+ if (methods == (char **) NULL)
+ break;
+ entry=XMenuWidget(display,windows,MatteEditMenu[id],
+ (const char **) methods,command);
+ if (entry >= 0)
+ method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
+ MagickFalse,methods[entry]);
+ methods=DestroyStringList(methods);
+ break;
+ }
+ case MatteEditBorderCommand:
+ {
+ const char
+ *ColorMenu[MaxNumberPens];
+
+ int
+ pen_number;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="Browser...";
+ ColorMenu[MaxNumberPens-1]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ if (pen_number == (MaxNumberPens-2))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set border color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&border_color);
+ break;
+ }
+ case MatteEditFuzzCommand:
+ {
+ static char
+ fuzz[MaxTextExtent];
+
+ static const char
+ *FuzzMenu[] =
+ {
+ "0%",
+ "2%",
+ "5%",
+ "10%",
+ "15%",
+ "Dialog...",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (entry != 5)
+ {
+ (*image)->fuzz=StringToDouble(FuzzMenu[entry],1.0*QuantumRange+
+ 1.0);
+ break;
+ }
+ (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
+ (void) XDialogWidget(display,windows,"Ok",
+ "Enter fuzz factor (0.0 - 99.9%):",fuzz);
+ if (*fuzz == '\0')
+ break;
+ (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
+ (*image)->fuzz=StringToDouble(fuzz,1.0*QuantumRange+1.0);
+ break;
+ }
+ case MatteEditValueCommand:
+ {
+ static char
+ message[MaxTextExtent];
+
+ static const char
+ *MatteMenu[] =
+ {
+ "Opaque",
+ "Transparent",
+ "Dialog...",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
+ command);
+ if (entry < 0)
+ break;
+ if (entry != 2)
+ {
+ (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
+ OpaqueOpacity);
+ if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
+ (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
+ (Quantum) TransparentOpacity);
+ break;
+ }
+ (void) FormatMagickString(message,MaxTextExtent,
+ "Enter matte value (0 - " QuantumFormat "):",(Quantum)
+ QuantumRange);
+ (void) XDialogWidget(display,windows,"Matte",message,matte);
+ if (*matte == '\0')
+ break;
+ break;
+ }
+ case MatteEditUndoCommand:
+ {
+ (void) XMagickCommand(display,resource_info,windows,UndoCommand,
+ image);
+ break;
+ }
+ case MatteEditHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Matte Edit",ImageMatteEditHelp);
+ break;
+ }
+ case MatteEditDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if ((event.xbutton.window != windows->image.id) &&
+ (event.xbutton.window != windows->magnify.id))
+ break;
+ /*
+ Update matte data.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ (void) XMagickCommand(display,resource_info,windows,
+ SaveToUndoBufferCommand,image);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if ((event.xbutton.window != windows->image.id) &&
+ (event.xbutton.window != windows->magnify.id))
+ break;
+ /*
+ Update colormap information.
+ */
+ x=event.xbutton.x;
+ y=event.xbutton.y;
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ XInfoWidget(display,windows,text);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ state&=(~UpdateConfigurationState);
+ break;
+ }
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window == windows->magnify.id)
+ {
+ Window
+ window;
+
+ window=windows->magnify.id;
+ while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
+ }
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Matte Edit",ImageMatteEditHelp);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ break;
+ }
+ default:
+ break;
+ }
+ if (event.xany.window == windows->magnify.id)
+ {
+ x=windows->magnify.x-windows->image.x;
+ y=windows->magnify.y-windows->image.y;
+ }
+ x_offset=x;
+ y_offset=y;
+ if ((state & UpdateConfigurationState) != 0)
+ {
+ ExceptionInfo
+ *exception;
+
+ int
+ x,
+ y;
+
+ /*
+ Matte edit is relative to image configuration.
+ */
+ (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
+ MagickTrue);
+ XPutPixel(windows->image.ximage,x_offset,y_offset,
+ windows->pixel_info->background_color.pixel);
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ x_offset=(int)
+ (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
+ y_offset=(int)
+ (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
+ if ((x_offset < 0) || (y_offset < 0))
+ continue;
+ if ((x_offset >= (int) (*image)->columns) ||
+ (y_offset >= (int) (*image)->rows))
+ continue;
+ if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ (*image)->matte=MagickTrue;
+ exception=(&(*image)->exception);
+ switch (method)
+ {
+ case PointMethod:
+ default:
+ {
+ /*
+ Update matte information using point algorithm.
+ */
+ q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ q->opacity=(Quantum) atol(matte);
+ (void) SyncAuthenticPixels(*image,exception);
+ break;
+ }
+ case ReplaceMethod:
+ {
+ PixelPacket
+ target;
+
+ /*
+ Update matte information using replace algorithm.
+ */
+ (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
+ exception);
+ for (y=0; y < (long) (*image)->rows; y++)
+ {
+ q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
+ &(*image)->exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) (*image)->columns; x++)
+ {
+ if (IsColorSimilar(*image,q,&target))
+ q->opacity=(Quantum) atol(matte);
+ q++;
+ }
+ if (SyncAuthenticPixels(*image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case FloodfillMethod:
+ case FillToBorderMethod:
+ {
+ DrawInfo
+ *draw_info;
+
+ MagickPixelPacket
+ target;
+
+ /*
+ Update matte information using floodfill algorithm.
+ */
+ (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
+ exception);
+ if (method == FillToBorderMethod)
+ {
+ target.red=(MagickRealType)
+ ScaleShortToQuantum(border_color.red);
+ target.green=(MagickRealType)
+ ScaleShortToQuantum(border_color.green);
+ target.blue=(MagickRealType)
+ ScaleShortToQuantum(border_color.blue);
+ }
+ draw_info=CloneDrawInfo(resource_info->image_info,
+ (DrawInfo *) NULL);
+ draw_info->fill.opacity=RoundToQuantum(atof(matte));
+ (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
+ x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
+ MagickTrue);
+ draw_info=DestroyDrawInfo(draw_info);
+ break;
+ }
+ case ResetMethod:
+ {
+ /*
+ Update matte information using reset algorithm.
+ */
+ if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ for (y=0; y < (long) (*image)->rows; y++)
+ {
+ q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) (*image)->columns; x++)
+ {
+ q->opacity=(Quantum) atol(matte);
+ q++;
+ }
+ if (SyncAuthenticPixels(*image,exception) == MagickFalse)
+ break;
+ }
+ if (atol(matte) == OpaqueOpacity)
+ (*image)->matte=MagickFalse;
+ break;
+ }
+ }
+ state&=(~UpdateConfigurationState);
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XFreeCursor(display,cursor);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X O p e n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XOpenImage() loads an image from a file.
+%
+% The format of the XOpenImage method is:
+%
+% Image *XOpenImage(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,const unsigned int command)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o command: A value other than zero indicates that the file is selected
+% from the command line argument list.
+%
+*/
+static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,const MagickBooleanType command)
+{
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *nexus;
+
+ ImageInfo
+ *image_info;
+
+ static char
+ filename[MaxTextExtent] = "\0";
+
+ /*
+ Request file name from user.
+ */
+ if (command == MagickFalse)
+ XFileBrowserWidget(display,windows,"Open",filename);
+ else
+ {
+ char
+ **filelist,
+ **files;
+
+ int
+ count,
+ status;
+
+ register int
+ i,
+ j;
+
+ /*
+ Select next image from the command line.
+ */
+ status=XGetCommand(display,windows->image.id,&files,&count);
+ if (status == 0)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
+ return((Image *) NULL);
+ }
+ filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
+ if (filelist == (char **) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed","...");
+ (void) XFreeStringList(files);
+ return((Image *) NULL);
+ }
+ j=0;
+ for (i=1; i < count; i++)
+ if (*files[i] != '-')
+ filelist[j++]=files[i];
+ filelist[j]=(char *) NULL;
+ XListBrowserWidget(display,windows,&windows->widget,
+ (const char **) filelist,"Load","Select Image to Load:",filename);
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ (void) XFreeStringList(files);
+ }
+ if (*filename == '\0')
+ return((Image *) NULL);
+ image_info=CloneImageInfo(resource_info->image_info);
+ (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
+ (void *) NULL);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ exception=AcquireExceptionInfo();
+ (void) SetImageInfo(image_info,MagickFalse,exception);
+ if (LocaleCompare(image_info->magick,"X") == 0)
+ {
+ char
+ seconds[MaxTextExtent];
+
+ /*
+ User may want to delay the X server screen grab.
+ */
+ (void) CopyMagickString(seconds,"0",MaxTextExtent);
+ (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
+ seconds);
+ if (*seconds == '\0')
+ return((Image *) NULL);
+ XDelay(display,(unsigned long) (1000*atol(seconds)));
+ }
+ magick_info=GetMagickInfo(image_info->magick,exception);
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (magick_info->raw != MagickFalse))
+ {
+ char
+ geometry[MaxTextExtent];
+
+ /*
+ Request image size from the user.
+ */
+ (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
+ if (image_info->size != (char *) NULL)
+ (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
+ (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
+ geometry);
+ (void) CloneString(&image_info->size,geometry);
+ }
+ /*
+ Load the image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ nexus=ReadImage(image_info,exception);
+ CatchException(exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (nexus != (Image *) NULL)
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ else
+ {
+ char
+ *text,
+ **textlist;
+
+ /*
+ Unknown image format.
+ */
+ text=FileToString(filename,~0,exception);
+ if (text == (char *) NULL)
+ return((Image *) NULL);
+ textlist=StringToList(text);
+ if (textlist != (char **) NULL)
+ {
+ char
+ title[MaxTextExtent];
+
+ register int
+ i;
+
+ (void) FormatMagickString(title,MaxTextExtent,
+ "Unknown format: %s",filename);
+ XTextViewWidget(display,resource_info,windows,MagickTrue,title,
+ (const char **) textlist);
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ textlist[i]=DestroyString(textlist[i]);
+ textlist=(char **) RelinquishMagickMemory(textlist);
+ }
+ text=DestroyString(text);
+ }
+ exception=DestroyExceptionInfo(exception);
+ image_info=DestroyImageInfo(image_info);
+ return(nexus);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X P a n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XPanImage() pans the image until the mouse button is released.
+%
+% The format of the XPanImage method is:
+%
+% void XPanImage(Display *display,XWindows *windows,XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o event: Specifies a pointer to a XEvent structure. If it is NULL,
+% the entire image is refreshed.
+%
+*/
+static void XPanImage(Display *display,XWindows *windows,XEvent *event)
+{
+ char
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ MagickRealType
+ x_factor,
+ y_factor;
+
+ RectangleInfo
+ pan_info;
+
+ unsigned long
+ state;
+
+ /*
+ Define cursor.
+ */
+ if ((windows->image.ximage->width > (int) windows->image.width) &&
+ (windows->image.ximage->height > (int) windows->image.height))
+ cursor=XCreateFontCursor(display,XC_fleur);
+ else
+ if (windows->image.ximage->width > (int) windows->image.width)
+ cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
+ else
+ if (windows->image.ximage->height > (int) windows->image.height)
+ cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
+ else
+ cursor=XCreateFontCursor(display,XC_arrow);
+ (void) XCheckDefineCursor(display,windows->pan.id,cursor);
+ /*
+ Pan image as pointer moves until the mouse button is released.
+ */
+ x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
+ y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
+ pan_info.width=windows->pan.width*windows->image.width/
+ windows->image.ximage->width;
+ pan_info.height=windows->pan.height*windows->image.height/
+ windows->image.ximage->height;
+ pan_info.x=0;
+ pan_info.y=0;
+ state=UpdateConfigurationState;
+ do
+ {
+ switch (event->type)
+ {
+ case ButtonPress:
+ {
+ /*
+ User choose an initial pan location.
+ */
+ pan_info.x=event->xbutton.x;
+ pan_info.y=event->xbutton.y;
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case ButtonRelease:
+ {
+ /*
+ User has finished panning the image.
+ */
+ pan_info.x=event->xbutton.x;
+ pan_info.y=event->xbutton.y;
+ state|=UpdateConfigurationState | ExitState;
+ break;
+ }
+ case MotionNotify:
+ {
+ pan_info.x=event->xmotion.x;
+ pan_info.y=event->xmotion.y;
+ state|=UpdateConfigurationState;
+ }
+ default:
+ break;
+ }
+ if ((state & UpdateConfigurationState) != 0)
+ {
+ /*
+ Check boundary conditions.
+ */
+ if (pan_info.x < (int) (pan_info.width/2))
+ pan_info.x=0;
+ else
+ pan_info.x=(int) (x_factor*(pan_info.x-(pan_info.width/2)));
+ if (pan_info.x < 0)
+ pan_info.x=0;
+ else
+ if ((int) (pan_info.x+windows->image.width) >
+ windows->image.ximage->width)
+ pan_info.x=(long)
+ (windows->image.ximage->width-windows->image.width);
+ if (pan_info.y < (long) (pan_info.height/2))
+ pan_info.y=0;
+ else
+ pan_info.y=(long) (y_factor*(pan_info.y-(pan_info.height/2)));
+ if (pan_info.y < 0)
+ pan_info.y=0;
+ else
+ if ((int) (pan_info.y+windows->image.height) >
+ windows->image.ximage->height)
+ pan_info.y=(long)
+ (windows->image.ximage->height-windows->image.height);
+ if ((windows->image.x != (int) pan_info.x) ||
+ (windows->image.y != (int) pan_info.y))
+ {
+ /*
+ Display image pan offset.
+ */
+ windows->image.x=(int) pan_info.x;
+ windows->image.y=(int) pan_info.y;
+ (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
+ windows->image.width,windows->image.height,windows->image.x,
+ windows->image.y);
+ XInfoWidget(display,windows,text);
+ /*
+ Refresh Image window.
+ */
+ XDrawPanRectangle(display,windows);
+ XRefreshWindow(display,&windows->image,(XEvent *) NULL);
+ }
+ state&=(~UpdateConfigurationState);
+ }
+ /*
+ Wait for next event.
+ */
+ if ((state & ExitState) == 0)
+ XScreenEvent(display,windows,event);
+ } while ((state & ExitState) == 0);
+ /*
+ Restore cursor.
+ */
+ (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
+ (void) XFreeCursor(display,cursor);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X P a s t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XPasteImage() pastes an image previously saved with XCropImage in the X
+% window image at a location the user chooses with the pointer.
+%
+% The format of the XPasteImage method is:
+%
+% MagickBooleanType XPasteImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+static MagickBooleanType XPasteImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ static const char
+ *PasteMenu[] =
+ {
+ "Operator",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static const ModeType
+ PasteCommands[] =
+ {
+ PasteOperatorsCommand,
+ PasteHelpCommand,
+ PasteDismissCommand
+ };
+
+ static CompositeOperator
+ compose = CopyCompositeOp;
+
+ char
+ text[MaxTextExtent];
+
+ Cursor
+ cursor;
+
+ Image
+ *paste_image;
+
+ int
+ entry,
+ id,
+ x,
+ y;
+
+ MagickRealType
+ scale_factor;
+
+ RectangleInfo
+ highlight_info,
+ paste_info;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ /*
+ Copy image.
+ */
+ if (resource_info->copy_image == (Image *) NULL)
+ return(MagickFalse);
+ paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
+ &image->exception);
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Paste");
+ windows->command.data=1;
+ (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XSetCursorState(display,windows,MagickFalse);
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ paste_info.x=windows->image.x+x;
+ paste_info.y=windows->image.y+y;
+ paste_info.width=0;
+ paste_info.height=0;
+ cursor=XCreateFontCursor(display,XC_ul_angle);
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
+ paste_info.x,paste_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ highlight_info=paste_info;
+ highlight_info.x=paste_info.x-windows->image.x;
+ highlight_info.y=paste_info.y-windows->image.y;
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,PasteMenu,&event);
+ if (id < 0)
+ continue;
+ switch (PasteCommands[id])
+ {
+ case PasteOperatorsCommand:
+ {
+ char
+ command[MaxTextExtent],
+ **operators;
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ operators=GetMagickOptions(MagickComposeOptions);
+ if (operators == (char **) NULL)
+ break;
+ entry=XMenuWidget(display,windows,PasteMenu[id],
+ (const char **) operators,command);
+ if (entry >= 0)
+ compose=(CompositeOperator) ParseMagickOption(
+ MagickComposeOptions,MagickFalse,operators[entry]);
+ operators=DestroyStringList(operators);
+ break;
+ }
+ case PasteHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Composite",ImagePasteHelp);
+ break;
+ }
+ case PasteDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Paste rectangle is relative to image configuration.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ scale_factor=(MagickRealType) windows->image.ximage->width/width;
+ paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
+ scale_factor=(MagickRealType) windows->image.ximage->height/height;
+ paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ paste_info.x=windows->image.x+event.xbutton.x;
+ paste_info.y=windows->image.y+event.xbutton.y;
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ if ((paste_info.width != 0) && (paste_info.height != 0))
+ {
+ /*
+ User has selected the location of the paste image.
+ */
+ paste_info.x=windows->image.x+event.xbutton.x;
+ paste_info.y=windows->image.y+event.xbutton.y;
+ state|=ExitState;
+ }
+ break;
+ }
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ int
+ length;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key press: 0x%lx (%s)",(long) key_symbol,command);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ paste_image=DestroyImage(paste_image);
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Composite",ImagePasteHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ paste_info.x=windows->image.x+x;
+ paste_info.y=windows->image.y+y;
+ break;
+ }
+ default:
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
+ event.type);
+ break;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XFreeCursor(display,cursor);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ /*
+ Image pasting is relative to image configuration.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ paste_info.x+=x;
+ paste_info.x=(int) (scale_factor*paste_info.x+0.5);
+ paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ paste_info.y+=y;
+ paste_info.y=(int) (scale_factor*paste_info.y*scale_factor+0.5);
+ paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
+ /*
+ Paste image with X Image window.
+ */
+ (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
+ paste_image=DestroyImage(paste_image);
+ XSetCursorState(display,windows,MagickFalse);
+ /*
+ Update image colormap.
+ */
+ XConfigureImageColormap(display,resource_info,windows,image);
+ (void) XConfigureImage(display,resource_info,windows,image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X P r i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XPrintImage() prints an image to a Postscript printer.
+%
+% The format of the XPrintImage method is:
+%
+% MagickBooleanType XPrintImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XPrintImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ char
+ filename[MaxTextExtent],
+ geometry[MaxTextExtent];
+
+ Image
+ *print_image;
+
+ ImageInfo
+ *image_info;
+
+ MagickStatusType
+ status;
+
+ /*
+ Request Postscript page geometry from user.
+ */
+ image_info=CloneImageInfo(resource_info->image_info);
+ (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
+ if (image_info->page != (char *) NULL)
+ (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
+ XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
+ "Select Postscript Page Geometry:",geometry);
+ if (*geometry == '\0')
+ return(MagickTrue);
+ image_info->page=GetPageGeometry(geometry);
+ /*
+ Apply image transforms.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (print_image == (Image *) NULL)
+ return(MagickFalse);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
+ windows->image.ximage->width,windows->image.ximage->height);
+ (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
+ /*
+ Print image.
+ */
+ (void) AcquireUniqueFilename(filename);
+ (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
+ filename);
+ status=WriteImage(image_info,print_image);
+ (void) RelinquishUniqueFileResource(filename);
+ print_image=DestroyImage(print_image);
+ image_info=DestroyImageInfo(image_info);
+ XSetCursorState(display,windows,MagickFalse);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X R O I I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XROIImage() applies an image processing technique to a region of interest.
+%
+% The format of the XROIImage method is:
+%
+% MagickBooleanType XROIImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+*/
+static MagickBooleanType XROIImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image **image)
+{
+#define ApplyMenus 7
+
+ static const char
+ *ROIMenu[] =
+ {
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ },
+ *ApplyMenu[] =
+ {
+ "File",
+ "Edit",
+ "Transform",
+ "Enhance",
+ "Effects",
+ "F/X",
+ "Miscellany",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ },
+ *FileMenu[] =
+ {
+ "Save...",
+ "Print...",
+ (char *) NULL
+ },
+ *EditMenu[] =
+ {
+ "Undo",
+ "Redo",
+ (char *) NULL
+ },
+ *TransformMenu[] =
+ {
+ "Flop",
+ "Flip",
+ "Rotate Right",
+ "Rotate Left",
+ (char *) NULL
+ },
+ *EnhanceMenu[] =
+ {
+ "Hue...",
+ "Saturation...",
+ "Brightness...",
+ "Gamma...",
+ "Spiff",
+ "Dull",
+ "Contrast Stretch...",
+ "Sigmoidal Contrast...",
+ "Normalize",
+ "Equalize",
+ "Negate",
+ "Grayscale",
+ "Map...",
+ "Quantize...",
+ (char *) NULL
+ },
+ *EffectsMenu[] =
+ {
+ "Despeckle",
+ "Emboss",
+ "Reduce Noise",
+ "Add Noise",
+ "Sharpen...",
+ "Blur...",
+ "Threshold...",
+ "Edge Detect...",
+ "Spread...",
+ "Shade...",
+ "Raise...",
+ "Segment...",
+ (char *) NULL
+ },
+ *FXMenu[] =
+ {
+ "Solarize...",
+ "Sepia Tone...",
+ "Swirl...",
+ "Implode...",
+ "Vignette...",
+ "Wave...",
+ "Oil Paint...",
+ "Charcoal Draw...",
+ (char *) NULL
+ },
+ *MiscellanyMenu[] =
+ {
+ "Image Info",
+ "Zoom Image",
+ "Show Preview...",
+ "Show Histogram",
+ "Show Matte",
+ (char *) NULL
+ };
+
+ static const char
+ **Menus[ApplyMenus] =
+ {
+ FileMenu,
+ EditMenu,
+ TransformMenu,
+ EnhanceMenu,
+ EffectsMenu,
+ FXMenu,
+ MiscellanyMenu
+ };
+
+ static const CommandType
+ ApplyCommands[] =
+ {
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ HelpCommand,
+ QuitCommand
+ },
+ FileCommands[] =
+ {
+ SaveCommand,
+ PrintCommand
+ },
+ EditCommands[] =
+ {
+ UndoCommand,
+ RedoCommand
+ },
+ TransformCommands[] =
+ {
+ FlopCommand,
+ FlipCommand,
+ RotateRightCommand,
+ RotateLeftCommand
+ },
+ EnhanceCommands[] =
+ {
+ HueCommand,
+ SaturationCommand,
+ BrightnessCommand,
+ GammaCommand,
+ SpiffCommand,
+ DullCommand,
+ ContrastStretchCommand,
+ SigmoidalContrastCommand,
+ NormalizeCommand,
+ EqualizeCommand,
+ NegateCommand,
+ GrayscaleCommand,
+ MapCommand,
+ QuantizeCommand
+ },
+ EffectsCommands[] =
+ {
+ DespeckleCommand,
+ EmbossCommand,
+ ReduceNoiseCommand,
+ AddNoiseCommand,
+ SharpenCommand,
+ BlurCommand,
+ EdgeDetectCommand,
+ SpreadCommand,
+ ShadeCommand,
+ RaiseCommand,
+ SegmentCommand
+ },
+ FXCommands[] =
+ {
+ SolarizeCommand,
+ SepiaToneCommand,
+ SwirlCommand,
+ ImplodeCommand,
+ VignetteCommand,
+ WaveCommand,
+ OilPaintCommand,
+ CharcoalDrawCommand
+ },
+ MiscellanyCommands[] =
+ {
+ InfoCommand,
+ ZoomCommand,
+ ShowPreviewCommand,
+ ShowHistogramCommand,
+ ShowMatteCommand
+ },
+ ROICommands[] =
+ {
+ ROIHelpCommand,
+ ROIDismissCommand
+ };
+
+ static const CommandType
+ *Commands[ApplyMenus] =
+ {
+ FileCommands,
+ EditCommands,
+ TransformCommands,
+ EnhanceCommands,
+ EffectsCommands,
+ FXCommands,
+ MiscellanyCommands
+ };
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ CommandType
+ command_type;
+
+ Cursor
+ cursor;
+
+ Image
+ *roi_image;
+
+ int
+ entry,
+ id,
+ x,
+ y;
+
+ MagickRealType
+ scale_factor;
+
+ MagickProgressMonitor
+ progress_monitor;
+
+ RectangleInfo
+ crop_info,
+ highlight_info,
+ roi_info;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"ROI");
+ windows->command.data=0;
+ (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Track pointer until button 1 is pressed.
+ */
+ XQueryPosition(display,windows->image.id,&x,&y);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask | PointerMotionMask);
+ roi_info.x=windows->image.x+x;
+ roi_info.y=windows->image.y+y;
+ roi_info.width=0;
+ roi_info.height=0;
+ cursor=XCreateFontCursor(display,XC_fleur);
+ state=DefaultState;
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
+ roi_info.x,roi_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,ROIMenu,&event);
+ if (id < 0)
+ continue;
+ switch (ROICommands[id])
+ {
+ case ROIHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Region of Interest",ImageROIHelp);
+ break;
+ }
+ case ROIDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Note first corner of region of interest rectangle-- exit loop.
+ */
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ roi_info.x=windows->image.x+event.xbutton.x;
+ roi_info.y=windows->image.y+event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Region of Interest",ImageROIHelp);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ roi_info.x=windows->image.x+x;
+ roi_info.y=windows->image.y+y;
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ (void) XSelectInput(display,windows->image.id,
+ windows->image.attributes.event_mask);
+ if ((state & EscapeState) != 0)
+ {
+ /*
+ User want to exit without region of interest.
+ */
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ (void) XFreeCursor(display,cursor);
+ return(MagickTrue);
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ do
+ {
+ /*
+ Size rectangle as pointer moves until the mouse button is released.
+ */
+ x=(int) roi_info.x;
+ y=(int) roi_info.y;
+ roi_info.width=0;
+ roi_info.height=0;
+ state=DefaultState;
+ do
+ {
+ highlight_info=roi_info;
+ highlight_info.x=roi_info.x-windows->image.x;
+ highlight_info.y=roi_info.y-windows->image.y;
+ if ((highlight_info.width > 3) && (highlight_info.height > 3))
+ {
+ /*
+ Display info and draw region of interest rectangle.
+ */
+ if (windows->info.mapped == MagickFalse)
+ (void) XMapWindow(display,windows->info.id);
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ roi_info.width,roi_info.height,roi_info.x,roi_info.y);
+ XInfoWidget(display,windows,text);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if ((highlight_info.width > 3) && (highlight_info.height > 3))
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ roi_info.x=windows->image.x+event.xbutton.x;
+ roi_info.y=windows->image.y+event.xbutton.y;
+ break;
+ }
+ case ButtonRelease:
+ {
+ /*
+ User has committed to region of interest rectangle.
+ */
+ roi_info.x=windows->image.x+event.xbutton.x;
+ roi_info.y=windows->image.y+event.xbutton.y;
+ XSetCursorState(display,windows,MagickFalse);
+ state|=ExitState;
+ if (LocaleCompare(windows->command.name,"Apply") == 0)
+ break;
+ (void) CloneString(&windows->command.name,"Apply");
+ windows->command.data=ApplyMenus;
+ (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ roi_info.x=windows->image.x+event.xmotion.x;
+ roi_info.y=windows->image.y+event.xmotion.y;
+ }
+ default:
+ break;
+ }
+ if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
+ ((state & ExitState) != 0))
+ {
+ /*
+ Check boundary conditions.
+ */
+ if (roi_info.x < 0)
+ roi_info.x=0;
+ else
+ if (roi_info.x > (int) windows->image.ximage->width)
+ roi_info.x=windows->image.ximage->width;
+ if ((int) roi_info.x < x)
+ roi_info.width=(unsigned int) (x-roi_info.x);
+ else
+ {
+ roi_info.width=(unsigned int) (roi_info.x-x);
+ roi_info.x=x;
+ }
+ if (roi_info.y < 0)
+ roi_info.y=0;
+ else
+ if (roi_info.y > (int) windows->image.ximage->height)
+ roi_info.y=windows->image.ximage->height;
+ if ((int) roi_info.y < y)
+ roi_info.height=(unsigned int) (y-roi_info.y);
+ else
+ {
+ roi_info.height=(unsigned int) (roi_info.y-y);
+ roi_info.y=y;
+ }
+ }
+ } while ((state & ExitState) == 0);
+ /*
+ Wait for user to grab a corner of the rectangle or press return.
+ */
+ state=DefaultState;
+ command_type=NullCommand;
+ (void) XMapWindow(display,windows->info.id);
+ do
+ {
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display pointer position.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ roi_info.width,roi_info.height,roi_info.x,roi_info.y);
+ XInfoWidget(display,windows,text);
+ }
+ highlight_info=roi_info;
+ highlight_info.x=roi_info.x-windows->image.x;
+ highlight_info.y=roi_info.y-windows->image.y;
+ if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
+ {
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ if ((state & UpdateRegionState) != 0)
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ switch (command_type)
+ {
+ case UndoCommand:
+ case RedoCommand:
+ {
+ (void) XMagickCommand(display,resource_info,windows,command_type,
+ image);
+ break;
+ }
+ default:
+ {
+ /*
+ Region of interest is relative to image configuration.
+ */
+ progress_monitor=SetImageProgressMonitor(*image,
+ (MagickProgressMonitor) NULL,(*image)->client_data);
+ crop_info=roi_info;
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ x=0;
+ y=0;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ crop_info.x+=x;
+ crop_info.x=(int) (scale_factor*crop_info.x+0.5);
+ crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
+ scale_factor=(MagickRealType)
+ height/windows->image.ximage->height;
+ crop_info.y+=y;
+ crop_info.y=(int) (scale_factor*crop_info.y+0.5);
+ crop_info.height=(unsigned int)
+ (scale_factor*crop_info.height+0.5);
+ roi_image=CropImage(*image,&crop_info,&(*image)->exception);
+ (void) SetImageProgressMonitor(*image,progress_monitor,
+ (*image)->client_data);
+ if (roi_image == (Image *) NULL)
+ continue;
+ /*
+ Apply image processing technique to the region of interest.
+ */
+ windows->image.orphan=MagickTrue;
+ (void) XMagickCommand(display,resource_info,windows,command_type,
+ &roi_image);
+ progress_monitor=SetImageProgressMonitor(*image,
+ (MagickProgressMonitor) NULL,(*image)->client_data);
+ (void) XMagickCommand(display,resource_info,windows,
+ SaveToUndoBufferCommand,image);
+ windows->image.orphan=MagickFalse;
+ (void) CompositeImage(*image,CopyCompositeOp,roi_image,
+ crop_info.x,crop_info.y);
+ roi_image=DestroyImage(roi_image);
+ (void) SetImageProgressMonitor(*image,progress_monitor,
+ (*image)->client_data);
+ break;
+ }
+ }
+ if (command_type != InfoCommand)
+ {
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ }
+ XCheckRefreshWindows(display,windows);
+ XInfoWidget(display,windows,text);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ state&=(~UpdateRegionState);
+ }
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ XScreenEvent(display,windows,&event);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ command_type=NullCommand;
+ id=XCommandWidget(display,windows,ApplyMenu,&event);
+ if (id >= 0)
+ {
+ (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
+ command_type=ApplyCommands[id];
+ if (id < ApplyMenus)
+ {
+ /*
+ Select a command from a pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,ApplyMenu[id],
+ (const char **) Menus[id],command);
+ if (entry >= 0)
+ {
+ (void) CopyMagickString(command,Menus[id][entry],
+ MaxTextExtent);
+ command_type=Commands[id][entry];
+ }
+ }
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ if (command_type == HelpCommand)
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Region of Interest",ImageROIHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ continue;
+ }
+ if (command_type == QuitCommand)
+ {
+ /*
+ exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ continue;
+ }
+ if (command_type != NullCommand)
+ state|=UpdateRegionState;
+ continue;
+ }
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ x=windows->image.x;
+ y=windows->image.y;
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ x=windows->image.x+event.xbutton.x;
+ y=windows->image.y+event.xbutton.y;
+ if ((x < (int) (roi_info.x+RoiDelta)) &&
+ (x > (int) (roi_info.x-RoiDelta)) &&
+ (y < (int) (roi_info.y+RoiDelta)) &&
+ (y > (int) (roi_info.y-RoiDelta)))
+ {
+ roi_info.x=(long) (roi_info.x+roi_info.width);
+ roi_info.y=(long) (roi_info.y+roi_info.height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (roi_info.x+RoiDelta)) &&
+ (x > (int) (roi_info.x-RoiDelta)) &&
+ (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
+ (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
+ {
+ roi_info.x=(long) (roi_info.x+roi_info.width);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
+ (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
+ (y < (int) (roi_info.y+RoiDelta)) &&
+ (y > (int) (roi_info.y-RoiDelta)))
+ {
+ roi_info.y=(long) (roi_info.y+roi_info.height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
+ (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
+ (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
+ (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
+ {
+ state|=UpdateConfigurationState;
+ break;
+ }
+ }
+ case ButtonRelease:
+ {
+ if (event.xbutton.window == windows->pan.id)
+ if ((highlight_info.x != crop_info.x-windows->image.x) ||
+ (highlight_info.y != crop_info.y-windows->image.y))
+ XHighlightRectangle(display,windows->image.id,
+ windows->image.highlight_context,&highlight_info);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
+ event.xbutton.time);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window == windows->image.id)
+ if (event.xexpose.count == 0)
+ {
+ event.xexpose.x=(int) highlight_info.x;
+ event.xexpose.y=(int) highlight_info.y;
+ event.xexpose.width=(int) highlight_info.width;
+ event.xexpose.height=(int) highlight_info.height;
+ XRefreshWindow(display,&windows->image,&event);
+ }
+ if (event.xexpose.window == windows->info.id)
+ if (event.xexpose.count == 0)
+ XInfoWidget(display,windows,text);
+ break;
+ }
+ case KeyPress:
+ {
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Shift_L:
+ case XK_Shift_R:
+ break;
+ case XK_Escape:
+ case XK_F20:
+ state|=EscapeState;
+ case XK_Return:
+ {
+ state|=ExitState;
+ break;
+ }
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ roi_info.x=(long) (windows->image.width/2L-roi_info.width/2L);
+ roi_info.y=(long) (windows->image.height/2L-roi_info.height/2L);
+ break;
+ }
+ case XK_Left:
+ case XK_KP_Left:
+ {
+ roi_info.x--;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ case XK_Next:
+ {
+ roi_info.y--;
+ break;
+ }
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ roi_info.x++;
+ break;
+ }
+ case XK_Prior:
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ roi_info.y++;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Region of Interest",ImageROIHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ command_type=XImageWindowCommand(display,resource_info,windows,
+ event.xkey.state,key_symbol,image);
+ if (command_type != NullCommand)
+ state|=UpdateRegionState;
+ break;
+ }
+ }
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
+ event.xkey.time);
+ break;
+ }
+ case KeyRelease:
+ break;
+ case MotionNotify:
+ {
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ Map and unmap Info widget as text cursor crosses its boundaries.
+ */
+ x=event.xmotion.x;
+ y=event.xmotion.y;
+ if (windows->info.mapped != MagickFalse)
+ {
+ if ((x < (int) (windows->info.x+windows->info.width)) &&
+ (y < (int) (windows->info.y+windows->info.height)))
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ }
+ else
+ if ((x > (int) (windows->info.x+windows->info.width)) ||
+ (y > (int) (windows->info.y+windows->info.height)))
+ (void) XMapWindow(display,windows->info.id);
+ roi_info.x=windows->image.x+event.xmotion.x;
+ roi_info.y=windows->image.y+event.xmotion.y;
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ /*
+ Set primary selection.
+ */
+ (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
+ roi_info.width,roi_info.height,roi_info.x,roi_info.y);
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) text,(int) strlen(text));
+ notify.type=SelectionNotify;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,0,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ if ((state & UpdateConfigurationState) != 0)
+ {
+ (void) XPutBackEvent(display,&event);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ XSetCursorState(display,windows,MagickFalse);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X R o t a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XRotateImage() rotates the X image. If the degrees parameter if zero, the
+% rotation angle is computed from the slope of a line drawn by the user.
+%
+% The format of the XRotateImage method is:
+%
+% MagickBooleanType XRotateImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,double degrees,
+% Image **image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o degrees: Specifies the number of degrees to rotate the image.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XRotateImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
+{
+ static const char
+ *RotateMenu[] =
+ {
+ "Pixel Color",
+ "Direction",
+ "Help",
+ "Dismiss",
+ (char *) NULL
+ };
+
+ static ModeType
+ direction = HorizontalRotateCommand;
+
+ static const ModeType
+ DirectionCommands[] =
+ {
+ HorizontalRotateCommand,
+ VerticalRotateCommand
+ },
+ RotateCommands[] =
+ {
+ RotateColorCommand,
+ RotateDirectionCommand,
+ RotateHelpCommand,
+ RotateDismissCommand
+ };
+
+ static unsigned int
+ pen_id = 0;
+
+ char
+ command[MaxTextExtent],
+ text[MaxTextExtent];
+
+ Image
+ *rotate_image;
+
+ int
+ id,
+ x,
+ y;
+
+ MagickRealType
+ normalized_degrees;
+
+ register int
+ i;
+
+ unsigned int
+ height,
+ rotations,
+ width;
+
+ if (degrees == 0.0)
+ {
+ unsigned int
+ distance;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XSegment
+ rotate_info;
+
+ /*
+ Map Command widget.
+ */
+ (void) CloneString(&windows->command.name,"Rotate");
+ windows->command.data=2;
+ (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_widget,CurrentTime);
+ /*
+ Wait for first button press.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ XQueryPosition(display,windows->image.id,&x,&y);
+ rotate_info.x1=x;
+ rotate_info.y1=y;
+ rotate_info.x2=x;
+ rotate_info.y2=y;
+ state=DefaultState;
+ do
+ {
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&rotate_info);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&rotate_info);
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,RotateMenu,&event);
+ if (id < 0)
+ continue;
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ switch (RotateCommands[id])
+ {
+ case RotateColorCommand:
+ {
+ const char
+ *ColorMenu[MaxNumberPens];
+
+ int
+ pen_number;
+
+ XColor
+ color;
+
+ /*
+ Initialize menu selections.
+ */
+ for (i=0; i < (int) (MaxNumberPens-2); i++)
+ ColorMenu[i]=resource_info->pen_colors[i];
+ ColorMenu[MaxNumberPens-2]="Browser...";
+ ColorMenu[MaxNumberPens-1]=(const char *) NULL;
+ /*
+ Select a pen color from the pop-up menu.
+ */
+ pen_number=XMenuWidget(display,windows,RotateMenu[id],
+ (const char **) ColorMenu,command);
+ if (pen_number < 0)
+ break;
+ if (pen_number == (MaxNumberPens-2))
+ {
+ static char
+ color_name[MaxTextExtent] = "gray";
+
+ /*
+ Select a pen color from a dialog.
+ */
+ resource_info->pen_colors[pen_number]=color_name;
+ XColorBrowserWidget(display,windows,"Select",color_name);
+ if (*color_name == '\0')
+ break;
+ }
+ /*
+ Set pen color.
+ */
+ (void) XParseColor(display,windows->map_info->colormap,
+ resource_info->pen_colors[pen_number],&color);
+ XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
+ (unsigned int) MaxColors,&color);
+ windows->pixel_info->pen_colors[pen_number]=color;
+ pen_id=(unsigned int) pen_number;
+ break;
+ }
+ case RotateDirectionCommand:
+ {
+ static const char
+ *Directions[] =
+ {
+ "horizontal",
+ "vertical",
+ (char *) NULL,
+ };
+
+ /*
+ Select a command from the pop-up menu.
+ */
+ id=XMenuWidget(display,windows,RotateMenu[id],
+ Directions,command);
+ if (id >= 0)
+ direction=DirectionCommands[id];
+ break;
+ }
+ case RotateHelpCommand:
+ {
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Rotation",ImageRotateHelp);
+ break;
+ }
+ case RotateDismissCommand:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ default:
+ break;
+ }
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.button != Button1)
+ break;
+ if (event.xbutton.window != windows->image.id)
+ break;
+ /*
+ exit loop.
+ */
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ rotate_info.x1=event.xbutton.x;
+ rotate_info.y1=event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case ButtonRelease:
+ break;
+ case Expose:
+ break;
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ if (event.xkey.window != windows->image.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ switch ((int) key_symbol)
+ {
+ case XK_Escape:
+ case XK_F20:
+ {
+ /*
+ Prematurely exit.
+ */
+ state|=EscapeState;
+ state|=ExitState;
+ break;
+ }
+ case XK_F1:
+ case XK_Help:
+ {
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXcopy);
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Rotation",ImageRotateHelp);
+ (void) XSetFunction(display,windows->image.highlight_context,
+ GXinvert);
+ break;
+ }
+ default:
+ {
+ (void) XBell(display,0);
+ break;
+ }
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ rotate_info.x1=event.xmotion.x;
+ rotate_info.y1=event.xmotion.y;
+ }
+ }
+ rotate_info.x2=rotate_info.x1;
+ rotate_info.y2=rotate_info.y1;
+ if (direction == HorizontalRotateCommand)
+ rotate_info.x2+=32;
+ else
+ rotate_info.y2-=32;
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if ((state & EscapeState) != 0)
+ return(MagickTrue);
+ /*
+ Draw line as pointer moves until the mouse button is released.
+ */
+ distance=0;
+ (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
+ state=DefaultState;
+ do
+ {
+ if (distance > 9)
+ {
+ /*
+ Display info and draw rotation line.
+ */
+ if (windows->info.mapped == MagickFalse)
+ (void) XMapWindow(display,windows->info.id);
+ (void) FormatMagickString(text,MaxTextExtent," %g",
+ direction == VerticalRotateCommand ? degrees-90.0 : degrees);
+ XInfoWidget(display,windows,text);
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&rotate_info);
+ }
+ else
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ /*
+ Wait for next event.
+ */
+ XScreenEvent(display,windows,&event);
+ if (distance > 9)
+ XHighlightLine(display,windows->image.id,
+ windows->image.highlight_context,&rotate_info);
+ switch (event.type)
+ {
+ case ButtonPress:
+ break;
+ case ButtonRelease:
+ {
+ /*
+ User has committed to rotation line.
+ */
+ rotate_info.x2=event.xbutton.x;
+ rotate_info.y2=event.xbutton.y;
+ state|=ExitState;
+ break;
+ }
+ case Expose:
+ break;
+ case MotionNotify:
+ {
+ rotate_info.x2=event.xmotion.x;
+ rotate_info.y2=event.xmotion.y;
+ }
+ default:
+ break;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if (rotate_info.x2 < 0)
+ rotate_info.x2=0;
+ else
+ if (rotate_info.x2 > (int) windows->image.width)
+ rotate_info.x2=(short) windows->image.width;
+ if (rotate_info.y2 < 0)
+ rotate_info.y2=0;
+ else
+ if (rotate_info.y2 > (int) windows->image.height)
+ rotate_info.y2=(short) windows->image.height;
+ /*
+ Compute rotation angle from the slope of the line.
+ */
+ degrees=0.0;
+ distance=(unsigned int)
+ ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
+ ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
+ if (distance > 9)
+ degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
+ rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
+ } while ((state & ExitState) == 0);
+ (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (distance <= 9)
+ return(MagickTrue);
+ }
+ if (direction == VerticalRotateCommand)
+ degrees-=90.0;
+ if (degrees == 0.0)
+ return(MagickTrue);
+ /*
+ Rotate image.
+ */
+ normalized_degrees=degrees;
+ while (normalized_degrees < -45.0)
+ normalized_degrees+=360.0;
+ for (rotations=0; normalized_degrees > 45.0; rotations++)
+ normalized_degrees-=90.0;
+ if (normalized_degrees != 0.0)
+ (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (*image)->background_color.red=ScaleShortToQuantum(
+ windows->pixel_info->pen_colors[pen_id].red);
+ (*image)->background_color.green=ScaleShortToQuantum(
+ windows->pixel_info->pen_colors[pen_id].green);
+ (*image)->background_color.blue=ScaleShortToQuantum(
+ windows->pixel_info->pen_colors[pen_id].blue);
+ rotate_image=RotateImage(*image,degrees,&(*image)->exception);
+ XSetCursorState(display,windows,MagickFalse);
+ if (rotate_image == (Image *) NULL)
+ return(MagickFalse);
+ *image=DestroyImage(*image);
+ *image=rotate_image;
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ /*
+ Rotate crop geometry.
+ */
+ width=(unsigned int) (*image)->columns;
+ height=(unsigned int) (*image)->rows;
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ switch (rotations % 4)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ {
+ /*
+ Rotate 90 degrees.
+ */
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",height,width,(int) (*image)->columns-
+ (int) height-y,x);
+ break;
+ }
+ case 2:
+ {
+ /*
+ Rotate 180 degrees.
+ */
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
+ break;
+ }
+ case 3:
+ {
+ /*
+ Rotate 270 degrees.
+ */
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
+ break;
+ }
+ }
+ }
+ if (windows->image.orphan != MagickFalse)
+ return(MagickTrue);
+ if (normalized_degrees != 0.0)
+ {
+ /*
+ Update image colormap.
+ */
+ windows->image.window_changes.width=(int) (*image)->columns;
+ windows->image.window_changes.height=(int) (*image)->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ {
+ /*
+ Obtain dimensions of image from crop geometry.
+ */
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
+ &width,&height);
+ windows->image.window_changes.width=(int) width;
+ windows->image.window_changes.height=(int) height;
+ }
+ XConfigureImageColormap(display,resource_info,windows,*image);
+ }
+ else
+ if (((rotations % 4) == 1) || ((rotations % 4) == 3))
+ {
+ windows->image.window_changes.width=windows->image.ximage->height;
+ windows->image.window_changes.height=windows->image.ximage->width;
+ }
+ /*
+ Update image configuration.
+ */
+ (void) XConfigureImage(display,resource_info,windows,*image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S a v e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSaveImage() saves an image to a file.
+%
+% The format of the XSaveImage method is:
+%
+% MagickBooleanType XSaveImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XSaveImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ char
+ filename[MaxTextExtent],
+ geometry[MaxTextExtent];
+
+ Image
+ *save_image;
+
+ ImageInfo
+ *image_info;
+
+ MagickStatusType
+ status;
+
+ /*
+ Request file name from user.
+ */
+ if (resource_info->write_filename != (char *) NULL)
+ (void) CopyMagickString(filename,resource_info->write_filename,
+ MaxTextExtent);
+ else
+ {
+ char
+ path[MaxTextExtent];
+
+ int
+ status;
+
+ GetPathComponent(image->filename,HeadPath,path);
+ GetPathComponent(image->filename,TailPath,filename);
+ status=chdir(path);
+ if (status == -1)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",path);
+ }
+ XFileBrowserWidget(display,windows,"Save",filename);
+ if (*filename == '\0')
+ return(MagickTrue);
+ if (IsPathAccessible(filename) != MagickFalse)
+ {
+ int
+ status;
+
+ /*
+ File exists-- seek user's permission before overwriting.
+ */
+ status=XConfirmWidget(display,windows,"Overwrite",filename);
+ if (status <= 0)
+ return(MagickTrue);
+ }
+ image_info=CloneImageInfo(resource_info->image_info);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ (void) SetImageInfo(image_info,MagickFalse,&image->exception);
+ if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
+ (LocaleCompare(image_info->magick,"JPG") == 0))
+ {
+ char
+ quality[MaxTextExtent];
+
+ int
+ status;
+
+ /*
+ Request JPEG quality from user.
+ */
+ (void) FormatMagickString(quality,MaxTextExtent,"%lu",image->quality);
+ status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
+ quality);
+ if (*quality == '\0')
+ return(MagickTrue);
+ image->quality=(unsigned long) atol(quality);
+ image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
+ }
+ if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
+ (LocaleCompare(image_info->magick,"PDF") == 0) ||
+ (LocaleCompare(image_info->magick,"PS") == 0) ||
+ (LocaleCompare(image_info->magick,"PS2") == 0))
+ {
+ char
+ geometry[MaxTextExtent];
+
+ /*
+ Request page geometry from user.
+ */
+ (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
+ if (LocaleCompare(image_info->magick,"PDF") == 0)
+ (void) FormatMagickString(geometry,MaxTextExtent,PSPageGeometry);
+ if (image_info->page != (char *) NULL)
+ (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
+ XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
+ "Select page geometry:",geometry);
+ if (*geometry != '\0')
+ image_info->page=GetPageGeometry(geometry);
+ }
+ /*
+ Apply image transforms.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (save_image == (Image *) NULL)
+ return(MagickFalse);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
+ windows->image.ximage->width,windows->image.ximage->height);
+ (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
+ /*
+ Write image.
+ */
+ (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
+ status=WriteImage(image_info,save_image);
+ if (status != MagickFalse)
+ image->taint=MagickFalse;
+ save_image=DestroyImage(save_image);
+ image_info=DestroyImageInfo(image_info);
+ XSetCursorState(display,windows,MagickFalse);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S c r e e n E v e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XScreenEvent() handles global events associated with the Pan and Magnify
+% windows.
+%
+% The format of the XScreenEvent function is:
+%
+% void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o event: Specifies a pointer to a X11 XEvent structure.
+%
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
+{
+ register XWindows
+ *windows;
+
+ windows=(XWindows *) data;
+ if ((event->type == ClientMessage) &&
+ (event->xclient.window == windows->image.id))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
+{
+ register int
+ x,
+ y;
+
+ (void) XIfEvent(display,event,XPredicate,(char *) windows);
+ if (event->xany.window == windows->command.id)
+ return;
+ switch (event->type)
+ {
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ if ((event->xbutton.button == Button3) &&
+ (event->xbutton.state & Mod1Mask))
+ {
+ /*
+ Convert Alt-Button3 to Button2.
+ */
+ event->xbutton.button=Button2;
+ event->xbutton.state&=(~Mod1Mask);
+ }
+ if (event->xbutton.window == windows->backdrop.id)
+ {
+ (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
+ event->xbutton.time);
+ break;
+ }
+ if (event->xbutton.window == windows->pan.id)
+ {
+ XPanImage(display,windows,event);
+ break;
+ }
+ if (event->xbutton.window == windows->image.id)
+ if (event->xbutton.button == Button2)
+ {
+ /*
+ Update magnified image.
+ */
+ x=event->xbutton.x;
+ y=event->xbutton.y;
+ if (x < 0)
+ x=0;
+ else
+ if (x >= (int) windows->image.width)
+ x=(int) (windows->image.width-1);
+ windows->magnify.x=windows->image.x+x;
+ if (y < 0)
+ y=0;
+ else
+ if (y >= (int) windows->image.height)
+ y=(int) (windows->image.height-1);
+ windows->magnify.y=windows->image.y+y;
+ if (windows->magnify.mapped == MagickFalse)
+ (void) XMapRaised(display,windows->magnify.id);
+ XMakeMagnifyImage(display,windows);
+ if (event->type == ButtonRelease)
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event->xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event->xclient.data.l != (long) windows->wm_delete_window)
+ break;
+ if (event->xclient.window == windows->magnify.id)
+ {
+ (void) XWithdrawWindow(display,windows->magnify.id,
+ windows->magnify.screen);
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ if (event->xconfigure.window == windows->magnify.id)
+ {
+ unsigned int
+ magnify;
+
+ /*
+ Magnify window has a new configuration.
+ */
+ windows->magnify.width=(unsigned int) event->xconfigure.width;
+ windows->magnify.height=(unsigned int) event->xconfigure.height;
+ if (windows->magnify.mapped == MagickFalse)
+ break;
+ magnify=1;
+ while ((int) magnify <= event->xconfigure.width)
+ magnify<<=1;
+ while ((int) magnify <= event->xconfigure.height)
+ magnify<<=1;
+ magnify>>=1;
+ if (((int) magnify != event->xconfigure.width) ||
+ ((int) magnify != event->xconfigure.height))
+ {
+ XWindowChanges
+ window_changes;
+
+ window_changes.width=(int) magnify;
+ window_changes.height=(int) magnify;
+ (void) XReconfigureWMWindow(display,windows->magnify.id,
+ windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
+ &window_changes);
+ break;
+ }
+ XMakeMagnifyImage(display,windows);
+ break;
+ }
+ break;
+ }
+ case Expose:
+ {
+ if (event->xexpose.window == windows->image.id)
+ {
+ XRefreshWindow(display,&windows->image,event);
+ break;
+ }
+ if (event->xexpose.window == windows->pan.id)
+ if (event->xexpose.count == 0)
+ {
+ XDrawPanRectangle(display,windows);
+ break;
+ }
+ if (event->xexpose.window == windows->magnify.id)
+ if (event->xexpose.count == 0)
+ {
+ XMakeMagnifyImage(display,windows);
+ break;
+ }
+ break;
+ }
+ case KeyPress:
+ {
+ char
+ command[MaxTextExtent];
+
+ KeySym
+ key_symbol;
+
+ if (event->xkey.window != windows->magnify.id)
+ break;
+ /*
+ Respond to a user key press.
+ */
+ (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
+ break;
+ }
+ case MapNotify:
+ {
+ if (event->xmap.window == windows->magnify.id)
+ {
+ windows->magnify.mapped=MagickTrue;
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ break;
+ }
+ if (event->xmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickTrue;
+ break;
+ }
+ break;
+ }
+ case MotionNotify:
+ {
+ while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
+ if (event->xmotion.window == windows->image.id)
+ if (windows->magnify.mapped != MagickFalse)
+ {
+ /*
+ Update magnified image.
+ */
+ x=event->xmotion.x;
+ y=event->xmotion.y;
+ if (x < 0)
+ x=0;
+ else
+ if (x >= (int) windows->image.width)
+ x=(int) (windows->image.width-1);
+ windows->magnify.x=windows->image.x+x;
+ if (y < 0)
+ y=0;
+ else
+ if (y >= (int) windows->image.height)
+ y=(int) (windows->image.height-1);
+ windows->magnify.y=windows->image.y+y;
+ XMakeMagnifyImage(display,windows);
+ }
+ break;
+ }
+ case UnmapNotify:
+ {
+ if (event->xunmap.window == windows->magnify.id)
+ {
+ windows->magnify.mapped=MagickFalse;
+ break;
+ }
+ if (event->xunmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickFalse;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S e t C r o p G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetCropGeometry() accepts a cropping geometry relative to the Image window
+% and translates it to a cropping geometry relative to the image.
+%
+% The format of the XSetCropGeometry method is:
+%
+% void XSetCropGeometry(Display *display,XWindows *windows,
+% RectangleInfo *crop_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o crop_info: A pointer to a RectangleInfo that defines a region of the
+% Image window to crop.
+%
+% o image: the image.
+%
+*/
+static void XSetCropGeometry(Display *display,XWindows *windows,
+ RectangleInfo *crop_info,Image *image)
+{
+ char
+ text[MaxTextExtent];
+
+ int
+ x,
+ y;
+
+ MagickRealType
+ scale_factor;
+
+ unsigned int
+ height,
+ width;
+
+ if (windows->info.mapped != MagickFalse)
+ {
+ /*
+ Display info on cropping rectangle.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
+ crop_info->width,crop_info->height,crop_info->x,crop_info->y);
+ XInfoWidget(display,windows,text);
+ }
+ /*
+ Cropping geometry is relative to any previous crop geometry.
+ */
+ x=0;
+ y=0;
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ else
+ windows->image.crop_geometry=AcquireString((char *) NULL);
+ /*
+ Define the crop geometry string from the cropping rectangle.
+ */
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ if (crop_info->x > 0)
+ x+=(int) (scale_factor*crop_info->x+0.5);
+ width=(unsigned int) (scale_factor*crop_info->width+0.5);
+ if (width == 0)
+ width=1;
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ if (crop_info->y > 0)
+ y+=(int) (scale_factor*crop_info->y+0.5);
+ height=(unsigned int) (scale_factor*crop_info->height+0.5);
+ if (height == 0)
+ height=1;
+ (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
+ "%ux%u%+d%+d",width,height,x,y);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X T i l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XTileImage() loads or deletes a selected tile from a visual image directory.
+% The load or delete command is chosen from a menu.
+%
+% The format of the XTileImage method is:
+%
+% Image *XTileImage(Display *display,XResourceInfo *resource_info,
+% XWindows *windows,Image *image,XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o tile_image: XTileImage reads or deletes the tile image
+% and returns it. A null image is returned if an error occurs.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image; returned from ReadImage.
+%
+% o event: Specifies a pointer to a XEvent structure. If it is NULL,
+% the entire image is refreshed.
+%
+*/
+static Image *XTileImage(Display *display,XResourceInfo *resource_info,
+ XWindows *windows,Image *image,XEvent *event)
+{
+ static const char
+ *VerbMenu[] =
+ {
+ "Load",
+ "Next",
+ "Former",
+ "Delete",
+ "Update",
+ (char *) NULL,
+ };
+
+ static const ModeType
+ TileCommands[] =
+ {
+ TileLoadCommand,
+ TileNextCommand,
+ TileFormerCommand,
+ TileDeleteCommand,
+ TileUpdateCommand
+ };
+
+ char
+ command[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ Image
+ *tile_image;
+
+ int
+ id,
+ status,
+ tile,
+ x,
+ y;
+
+ MagickRealType
+ scale_factor;
+
+ register char
+ *p,
+ *q;
+
+ register int
+ i;
+
+ unsigned int
+ height,
+ width;
+
+ /*
+ Tile image is relative to montage image configuration.
+ */
+ x=0;
+ y=0;
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
+ scale_factor=(MagickRealType) width/windows->image.ximage->width;
+ event->xbutton.x+=windows->image.x;
+ event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
+ scale_factor=(MagickRealType) height/windows->image.ximage->height;
+ event->xbutton.y+=windows->image.y;
+ event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
+ /*
+ Determine size and location of each tile in the visual image directory.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ (void) XParseGeometry(image->montage,&x,&y,&width,&height);
+ tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
+ (event->xbutton.x-x)/width;
+ if (tile < 0)
+ {
+ /*
+ Button press is outside any tile.
+ */
+ (void) XBell(display,0);
+ return((Image *) NULL);
+ }
+ /*
+ Determine file name from the tile directory.
+ */
+ p=image->directory;
+ for (i=tile; (i != 0) && (*p != '\0'); )
+ {
+ if (*p == '\n')
+ i--;
+ p++;
+ }
+ if (*p == '\0')
+ {
+ /*
+ Button press is outside any tile.
+ */
+ (void) XBell(display,0);
+ return((Image *) NULL);
+ }
+ /*
+ Select a command from the pop-up menu.
+ */
+ id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
+ if (id < 0)
+ return((Image *) NULL);
+ q=p;
+ while ((*q != '\n') && (*q != '\0'))
+ q++;
+ (void) CopyMagickString(filename,p,(size_t) (q-p+1));
+ /*
+ Perform command for the selected tile.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ tile_image=NewImageList();
+ switch (TileCommands[id])
+ {
+ case TileLoadCommand:
+ {
+ /*
+ Load tile image.
+ */
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
+ MaxTextExtent);
+ (void) CopyMagickString(resource_info->image_info->filename,filename,
+ MaxTextExtent);
+ tile_image=ReadImage(resource_info->image_info,&image->exception);
+ CatchException(&image->exception);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ break;
+ }
+ case TileNextCommand:
+ {
+ /*
+ Display next image.
+ */
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ break;
+ }
+ case TileFormerCommand:
+ {
+ /*
+ Display former image.
+ */
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_former_image,CurrentTime);
+ break;
+ }
+ case TileDeleteCommand:
+ {
+ /*
+ Delete tile image.
+ */
+ if (IsPathAccessible(filename) == MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Image file does not exist:",filename);
+ break;
+ }
+ status=XConfirmWidget(display,windows,"Really delete tile",filename);
+ if (status <= 0)
+ break;
+ status=remove(filename) != 0 ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ XNoticeWidget(display,windows,"Unable to delete image file:",
+ filename);
+ break;
+ }
+ }
+ case TileUpdateCommand:
+ {
+ ExceptionInfo
+ *exception;
+
+ int
+ x_offset,
+ y_offset;
+
+ PixelPacket
+ pixel;
+
+ register int
+ j;
+
+ register PixelPacket
+ *s;
+
+ /*
+ Ensure all the images exist.
+ */
+ tile=0;
+ for (p=image->directory; *p != '\0'; p++)
+ {
+ q=p;
+ while ((*q != '\n') && (*q != '\0'))
+ q++;
+ (void) CopyMagickString(filename,p,(size_t) (q-p+1));
+ p=q;
+ if (IsPathAccessible(filename) != MagickFalse)
+ {
+ tile++;
+ continue;
+ }
+ /*
+ Overwrite tile with background color.
+ */
+ x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
+ y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
+ exception=(&image->exception);
+ (void) GetOneVirtualPixel(image,0,0,&pixel,exception);
+ for (i=0; i < (int) height; i++)
+ {
+ s=GetAuthenticPixels(image,x_offset,y_offset+i,width,1,exception);
+ if (s == (PixelPacket *) NULL)
+ break;
+ for (j=0; j < (int) width; j++)
+ *s++=pixel;
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ tile++;
+ }
+ windows->image.window_changes.width=(int) image->columns;
+ windows->image.window_changes.height=(int) image->rows;
+ XConfigureImageColormap(display,resource_info,windows,image);
+ (void) XConfigureImage(display,resource_info,windows,image);
+ break;
+ }
+ default:
+ break;
+ }
+ XSetCursorState(display,windows,MagickFalse);
+ return(tile_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X T r a n s l a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XTranslateImage() translates the image within an Image window by one pixel
+% as specified by the key symbol. If the image has a `montage string the
+% translation is respect to the width and height contained within the string.
+%
+% The format of the XTranslateImage method is:
+%
+% void XTranslateImage(Display *display,XWindows *windows,
+% Image *image,const KeySym key_symbol)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+% o key_symbol: Specifies a KeySym which indicates which side of the image
+% to trim.
+%
+*/
+static void XTranslateImage(Display *display,XWindows *windows,
+ Image *image,const KeySym key_symbol)
+{
+ char
+ text[MaxTextExtent];
+
+ int
+ x,
+ y;
+
+ unsigned int
+ x_offset,
+ y_offset;
+
+ /*
+ User specified a pan position offset.
+ */
+ x_offset=windows->image.width;
+ y_offset=windows->image.height;
+ if (image->montage != (char *) NULL)
+ (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ windows->image.x=(int) windows->image.width/2;
+ windows->image.y=(int) windows->image.height/2;
+ break;
+ }
+ case XK_Left:
+ case XK_KP_Left:
+ {
+ windows->image.x-=x_offset;
+ break;
+ }
+ case XK_Next:
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ windows->image.y-=y_offset;
+ break;
+ }
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ windows->image.x+=x_offset;
+ break;
+ }
+ case XK_Prior:
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ windows->image.y+=y_offset;
+ break;
+ }
+ default:
+ return;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if (windows->image.x < 0)
+ windows->image.x=0;
+ else
+ if ((int) (windows->image.x+windows->image.width) >
+ windows->image.ximage->width)
+ windows->image.x=windows->image.ximage->width-windows->image.width;
+ if (windows->image.y < 0)
+ windows->image.y=0;
+ else
+ if ((int) (windows->image.y+windows->image.height) >
+ windows->image.ximage->height)
+ windows->image.y=windows->image.ximage->height-windows->image.height;
+ /*
+ Refresh Image window.
+ */
+ (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
+ windows->image.width,windows->image.height,windows->image.x,
+ windows->image.y);
+ XInfoWidget(display,windows,text);
+ XCheckRefreshWindows(display,windows);
+ XDrawPanRectangle(display,windows);
+ XRefreshWindow(display,&windows->image,(XEvent *) NULL);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X T r i m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XTrimImage() trims the edges from the Image window.
+%
+% The format of the XTrimImage method is:
+%
+% MagickBooleanType XTrimImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+static MagickBooleanType XTrimImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ RectangleInfo
+ trim_info;
+
+ register int
+ x,
+ y;
+
+ unsigned long
+ background,
+ pixel;
+
+ /*
+ Trim edges from image.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ /*
+ Crop the left edge.
+ */
+ background=XGetPixel(windows->image.ximage,0,0);
+ trim_info.width=(unsigned long) windows->image.ximage->width;
+ for (x=0; x < windows->image.ximage->width; x++)
+ {
+ for (y=0; y < windows->image.ximage->height; y++)
+ {
+ pixel=XGetPixel(windows->image.ximage,x,y);
+ if (pixel != background)
+ break;
+ }
+ if (y < windows->image.ximage->height)
+ break;
+ }
+ trim_info.x=x;
+ if (trim_info.x == (int) windows->image.ximage->width)
+ {
+ XSetCursorState(display,windows,MagickFalse);
+ return(MagickFalse);
+ }
+ /*
+ Crop the right edge.
+ */
+ background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
+ for (x=windows->image.ximage->width-1; x != 0; x--)
+ {
+ for (y=0; y < windows->image.ximage->height; y++)
+ {
+ pixel=XGetPixel(windows->image.ximage,x,y);
+ if (pixel != background)
+ break;
+ }
+ if (y < windows->image.ximage->height)
+ break;
+ }
+ trim_info.width=(unsigned long) (x-trim_info.x+1);
+ /*
+ Crop the top edge.
+ */
+ background=XGetPixel(windows->image.ximage,0,0);
+ trim_info.height=(unsigned long) windows->image.ximage->height;
+ for (y=0; y < windows->image.ximage->height; y++)
+ {
+ for (x=0; x < windows->image.ximage->width; x++)
+ {
+ pixel=XGetPixel(windows->image.ximage,x,y);
+ if (pixel != background)
+ break;
+ }
+ if (x < windows->image.ximage->width)
+ break;
+ }
+ trim_info.y=y;
+ /*
+ Crop the bottom edge.
+ */
+ background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
+ for (y=windows->image.ximage->height-1; y != 0; y--)
+ {
+ for (x=0; x < windows->image.ximage->width; x++)
+ {
+ pixel=XGetPixel(windows->image.ximage,x,y);
+ if (pixel != background)
+ break;
+ }
+ if (x < windows->image.ximage->width)
+ break;
+ }
+ trim_info.height=(unsigned long) y-trim_info.y+1;
+ if (((unsigned int) trim_info.width != windows->image.width) ||
+ ((unsigned int) trim_info.height != windows->image.height))
+ {
+ /*
+ Reconfigure Image window as defined by the trimming rectangle.
+ */
+ XSetCropGeometry(display,windows,&trim_info,image);
+ windows->image.window_changes.width=(int) trim_info.width;
+ windows->image.window_changes.height=(int) trim_info.height;
+ (void) XConfigureImage(display,resource_info,windows,image);
+ }
+ XSetCursorState(display,windows,MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X V i s u a l D i r e c t o r y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XVisualDirectoryImage() creates a Visual Image Directory.
+%
+% The format of the XVisualDirectoryImage method is:
+%
+% Image *XVisualDirectoryImage(Display *display,
+% XResourceInfo *resource_info,XWindows *windows)
+%
+% A description of each parameter follows:
+%
+% o nexus: Method XVisualDirectoryImage returns a visual image
+% directory if it can be created successfully. Otherwise a null image
+% is returned.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+*/
+static Image *XVisualDirectoryImage(Display *display,
+ XResourceInfo *resource_info,XWindows *windows)
+{
+#define TileImageTag "Scale/Image"
+#define XClientName "montage"
+
+ char
+ **filelist;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *images,
+ *montage_image,
+ *next_image,
+ *thumbnail_image;
+
+ ImageInfo
+ *read_info;
+
+ int
+ number_files;
+
+ MagickBooleanType
+ backdrop;
+
+ MagickStatusType
+ status;
+
+ MontageInfo
+ *montage_info;
+
+ RectangleInfo
+ geometry;
+
+ register int
+ i;
+
+ static char
+ filename[MaxTextExtent] = "\0",
+ filenames[MaxTextExtent] = "*";
+
+ XResourceInfo
+ background_resources;
+
+ /*
+ Request file name from user.
+ */
+ XFileBrowserWidget(display,windows,"Directory",filenames);
+ if (*filenames == '\0')
+ return((Image *) NULL);
+ /*
+ Expand the filenames.
+ */
+ filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
+ if (filelist == (char **) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ filenames);
+ return((Image *) NULL);
+ }
+ number_files=1;
+ filelist[0]=filenames;
+ status=ExpandFilenames(&number_files,&filelist);
+ if ((status == MagickFalse) || (number_files == 0))
+ {
+ if (number_files == 0)
+ ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
+ else
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ filenames);
+ return((Image *) NULL);
+ }
+ /*
+ Set image background resources.
+ */
+ background_resources=(*resource_info);
+ background_resources.window_id=AcquireString("");
+ (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
+ "0x%lx",windows->image.id);
+ background_resources.backdrop=MagickTrue;
+ /*
+ Read each image and convert them to a tile.
+ */
+ backdrop=(windows->visual_info->klass == TrueColor) ||
+ (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
+ read_info=CloneImageInfo(resource_info->image_info);
+ (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
+ (void *) NULL);
+ images=NewImageList();
+ exception=AcquireExceptionInfo();
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ for (i=0; i < (long) number_files; i++)
+ {
+ (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
+ filelist[i]=DestroyString(filelist[i]);
+ *read_info->magick='\0';
+ (void) CloneString(&read_info->size,DefaultTileGeometry);
+ next_image=ReadImage(read_info,exception);
+ CatchException(exception);
+ if (next_image != (Image *) NULL)
+ {
+ (void) DeleteImageProperty(next_image,"label");
+ (void) SetImageProperty(next_image,"label",DefaultTileLabel);
+ (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
+ exception);
+ thumbnail_image=ThumbnailImage(next_image,geometry.width,
+ geometry.height,exception);
+ if (thumbnail_image != (Image *) NULL)
+ {
+ next_image=DestroyImage(next_image);
+ next_image=thumbnail_image;
+ }
+ if (backdrop)
+ {
+ (void) XDisplayBackgroundImage(display,&background_resources,
+ next_image);
+ XSetCursorState(display,windows,MagickTrue);
+ }
+ AppendImageToList(&images,next_image);
+ if (images->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
+ (MagickSizeType) number_files);
+ if (proceed == MagickFalse)
+ break;
+ }
+ }
+ }
+ exception=DestroyExceptionInfo(exception);
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ read_info=DestroyImageInfo(read_info);
+ if (images == (Image *) NULL)
+ {
+ XSetCursorState(display,windows,MagickFalse);
+ ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
+ return((Image *) NULL);
+ }
+ /*
+ Create the Visual Image Directory.
+ */
+ montage_info=CloneMontageInfo(resource_info->image_info,(MontageInfo *) NULL);
+ if (resource_info->font != (char *) NULL)
+ (void) CloneString(&montage_info->font,resource_info->font);
+ (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
+ montage_image=MontageImageList(resource_info->image_info,montage_info,
+ GetFirstImageInList(images),&images->exception);
+ montage_info=DestroyMontageInfo(montage_info);
+ images=DestroyImageList(images);
+ XSetCursorState(display,windows,MagickFalse);
+ if (montage_image == (Image *) NULL)
+ return(montage_image);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_next_image,CurrentTime);
+ return(montage_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D i s p l a y B a c k g r o u n d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDisplayBackgroundImage() displays an image in the background of a window.
+%
+% The format of the XDisplayBackgroundImage method is:
+%
+% MagickBooleanType XDisplayBackgroundImage(Display *display,
+% XResourceInfo *resource_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
+ XResourceInfo *resource_info,Image *image)
+{
+ char
+ geometry[MaxTextExtent],
+ visual_type[MaxTextExtent];
+
+ int
+ height,
+ status,
+ width;
+
+ RectangleInfo
+ geometry_info;
+
+ static XPixelInfo
+ pixel;
+
+ static XStandardColormap
+ *map_info;
+
+ static XVisualInfo
+ *visual_info = (XVisualInfo *) NULL;
+
+ static XWindowInfo
+ window_info;
+
+ unsigned long
+ delay;
+
+ Window
+ root_window;
+
+ XGCValues
+ context_values;
+
+ XResourceInfo
+ resources;
+
+ XWindowAttributes
+ window_attributes;
+
+ /*
+ Determine target window.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ resources=(*resource_info);
+ window_info.id=(Window) NULL;
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ if (LocaleCompare(resources.window_id,"root") == 0)
+ window_info.id=root_window;
+ else
+ {
+ if (isdigit((unsigned char) *resources.window_id) != 0)
+ window_info.id=XWindowByID(display,root_window,
+ (Window) strtol((char *) resources.window_id,(char **) NULL,0));
+ if (window_info.id == (Window) NULL)
+ window_info.id=XWindowByName(display,root_window,resources.window_id);
+ }
+ if (window_info.id == (Window) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
+ resources.window_id);
+ return(MagickFalse);
+ }
+ /*
+ Determine window visual id.
+ */
+ window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
+ window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
+ (void) CopyMagickString(visual_type,"default",MaxTextExtent);
+ status=XGetWindowAttributes(display,window_info.id,&window_attributes);
+ if (status != 0)
+ (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
+ XVisualIDFromVisual(window_attributes.visual));
+ if (visual_info == (XVisualInfo *) NULL)
+ {
+ /*
+ Allocate standard colormap.
+ */
+ map_info=XAllocStandardColormap();
+ if (map_info == (XStandardColormap *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
+ image->filename);
+ map_info->colormap=(Colormap) NULL;
+ pixel.pixels=(unsigned long *) NULL;
+ /*
+ Initialize visual info.
+ */
+ resources.map_type=(char *) NULL;
+ resources.visual_type=visual_type;
+ visual_info=XBestVisualInfo(display,map_info,&resources);
+ if (visual_info == (XVisualInfo *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
+ resources.visual_type);
+ /*
+ Initialize window info.
+ */
+ window_info.ximage=(XImage *) NULL;
+ window_info.matte_image=(XImage *) NULL;
+ window_info.pixmap=(Pixmap) NULL;
+ window_info.matte_pixmap=(Pixmap) NULL;
+ }
+ /*
+ Free previous root colors.
+ */
+ if (window_info.id == root_window)
+ (void) XDestroyWindowColors(display,root_window);
+ /*
+ Initialize Standard Colormap.
+ */
+ resources.colormap=SharedColormap;
+ XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
+ /*
+ Graphic context superclass.
+ */
+ context_values.background=pixel.background_color.pixel;
+ context_values.foreground=pixel.foreground_color.pixel;
+ pixel.annotate_context=XCreateGC(display,window_info.id,
+ (unsigned long) (GCBackground | GCForeground),&context_values);
+ if (pixel.annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ image->filename);
+ /*
+ Initialize Image window attributes.
+ */
+ window_info.name=AcquireString("\0");
+ window_info.icon_name=AcquireString("\0");
+ XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
+ &resources,&window_info);
+ /*
+ Create the X image.
+ */
+ window_info.width=(unsigned int) image->columns;
+ window_info.height=(unsigned int) image->rows;
+ if ((image->columns != window_info.width) ||
+ (image->rows != window_info.height))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ image->filename);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
+ window_attributes.width,window_attributes.height);
+ geometry_info.width=window_info.width;
+ geometry_info.height=window_info.height;
+ geometry_info.x=window_info.x;
+ geometry_info.y=window_info.y;
+ (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
+ &geometry_info.width,&geometry_info.height);
+ window_info.width=(unsigned int) geometry_info.width;
+ window_info.height=(unsigned int) geometry_info.height;
+ window_info.x=(int) geometry_info.x;
+ window_info.y=(int) geometry_info.y;
+ status=XMakeImage(display,&resources,&window_info,image,window_info.width,
+ window_info.height);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ image->filename);
+ window_info.x=0;
+ window_info.y=0;
+ if (image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: %s[%lu] %lux%lu ",image->filename,image->scene,
+ image->columns,image->rows);
+ if (image->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",image->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
+ }
+ /*
+ Adjust image dimensions as specified by backdrop or geometry options.
+ */
+ width=(int) window_info.width;
+ height=(int) window_info.height;
+ if (resources.backdrop != MagickFalse)
+ {
+ /*
+ Center image on window.
+ */
+ window_info.x=(window_attributes.width/2)-
+ (window_info.ximage->width/2);
+ window_info.y=(window_attributes.height/2)-
+ (window_info.ximage->height/2);
+ width=window_attributes.width;
+ height=window_attributes.height;
+ }
+ if ((resources.image_geometry != (char *) NULL) &&
+ (*resources.image_geometry != '\0'))
+ {
+ char
+ default_geometry[MaxTextExtent];
+
+ int
+ flags,
+ gravity;
+
+ XSizeHints
+ *size_hints;
+
+ /*
+ User specified geometry.
+ */
+ size_hints=XAllocSizeHints();
+ if (size_hints == (XSizeHints *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed",image->filename);
+ size_hints->flags=0L;
+ (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
+ width,height);
+ flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
+ default_geometry,window_info.border_width,size_hints,&window_info.x,
+ &window_info.y,&width,&height,&gravity);
+ if (flags & (XValue | YValue))
+ {
+ width=window_attributes.width;
+ height=window_attributes.height;
+ }
+ (void) XFree((void *) size_hints);
+ }
+ /*
+ Create the X pixmap.
+ */
+ window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
+ (unsigned int) height,window_info.depth);
+ if (window_info.pixmap == (Pixmap) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
+ image->filename);
+ /*
+ Display pixmap on the window.
+ */
+ if (((unsigned int) width > window_info.width) ||
+ ((unsigned int) height > window_info.height))
+ (void) XFillRectangle(display,window_info.pixmap,
+ window_info.annotate_context,0,0,(unsigned int) width,
+ (unsigned int) height);
+ (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
+ window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
+ window_info.width,(unsigned int) window_info.height);
+ (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
+ (void) XClearWindow(display,window_info.id);
+ delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
+ XDelay(display,delay == 0UL ? 10UL : delay);
+ (void) XSync(display,MagickFalse);
+ return(window_info.id == root_window ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D i s p l a y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDisplayImage() displays an image via X11. A new image is created and
+% returned if the user interactively transforms the displayed image.
+%
+% The format of the XDisplayImage method is:
+%
+% Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
+% char **argv,int argc,Image **image,unsigned long *state)
+%
+% A description of each parameter follows:
+%
+% o nexus: Method XDisplayImage returns an image when the
+% user chooses 'Open Image' from the command menu or picks a tile
+% from the image directory. Otherwise a null image is returned.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o argv: Specifies the application's argument list.
+%
+% o argc: Specifies the number of arguments.
+%
+% o image: Specifies an address to an address of an Image structure;
+%
+*/
+MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
+ char **argv,int argc,Image **image,unsigned long *state)
+{
+#define MagnifySize 256 /* must be a power of 2 */
+#define MagickMenus 10
+#define MagickTitle "Commands"
+
+ static const char
+ *CommandMenu[] =
+ {
+ "File",
+ "Edit",
+ "View",
+ "Transform",
+ "Enhance",
+ "Effects",
+ "F/X",
+ "Image Edit",
+ "Miscellany",
+ "Help",
+ (char *) NULL
+ },
+ *FileMenu[] =
+ {
+ "Open...",
+ "Next",
+ "Former",
+ "Select...",
+ "Save...",
+ "Print...",
+ "Delete...",
+ "New...",
+ "Visual Directory...",
+ "Quit",
+ (char *) NULL
+ },
+ *EditMenu[] =
+ {
+ "Undo",
+ "Redo",
+ "Cut",
+ "Copy",
+ "Paste",
+ (char *) NULL
+ },
+ *ViewMenu[] =
+ {
+ "Half Size",
+ "Original Size",
+ "Double Size",
+ "Resize...",
+ "Apply",
+ "Refresh",
+ "Restore",
+ (char *) NULL
+ },
+ *TransformMenu[] =
+ {
+ "Crop",
+ "Chop",
+ "Flop",
+ "Flip",
+ "Rotate Right",
+ "Rotate Left",
+ "Rotate...",
+ "Shear...",
+ "Roll...",
+ "Trim Edges",
+ (char *) NULL
+ },
+ *EnhanceMenu[] =
+ {
+ "Hue...",
+ "Saturation...",
+ "Brightness...",
+ "Gamma...",
+ "Spiff",
+ "Dull",
+ "Contrast Stretch...",
+ "Sigmoidal Contrast...",
+ "Normalize",
+ "Equalize",
+ "Negate",
+ "Grayscale",
+ "Map...",
+ "Quantize...",
+ (char *) NULL
+ },
+ *EffectsMenu[] =
+ {
+ "Despeckle",
+ "Emboss",
+ "Reduce Noise",
+ "Add Noise...",
+ "Sharpen...",
+ "Blur...",
+ "Threshold...",
+ "Edge Detect...",
+ "Spread...",
+ "Shade...",
+ "Raise...",
+ "Segment...",
+ (char *) NULL
+ },
+ *FXMenu[] =
+ {
+ "Solarize...",
+ "Sepia Tone...",
+ "Swirl...",
+ "Implode...",
+ "Vignette...",
+ "Wave...",
+ "Oil Paint...",
+ "Charcoal Draw...",
+ (char *) NULL
+ },
+ *ImageEditMenu[] =
+ {
+ "Annotate...",
+ "Draw...",
+ "Color...",
+ "Matte...",
+ "Composite...",
+ "Add Border...",
+ "Add Frame...",
+ "Comment...",
+ "Launch...",
+ "Region of Interest...",
+ (char *) NULL
+ },
+ *MiscellanyMenu[] =
+ {
+ "Image Info",
+ "Zoom Image",
+ "Show Preview...",
+ "Show Histogram",
+ "Show Matte",
+ "Background...",
+ "Slide Show...",
+ "Preferences...",
+ (char *) NULL
+ },
+ *HelpMenu[] =
+ {
+ "Overview",
+ "Browse Documentation",
+ "About Display",
+ (char *) NULL
+ },
+ *ShortCutsMenu[] =
+ {
+ "Next",
+ "Former",
+ "Open...",
+ "Save...",
+ "Print...",
+ "Undo",
+ "Restore",
+ "Image Info",
+ "Quit",
+ (char *) NULL
+ },
+ *VirtualMenu[] =
+ {
+ "Image Info",
+ "Print",
+ "Next",
+ "Quit",
+ (char *) NULL
+ };
+
+ static const char
+ **Menus[MagickMenus] =
+ {
+ FileMenu,
+ EditMenu,
+ ViewMenu,
+ TransformMenu,
+ EnhanceMenu,
+ EffectsMenu,
+ FXMenu,
+ ImageEditMenu,
+ MiscellanyMenu,
+ HelpMenu
+ };
+
+ static CommandType
+ CommandMenus[] =
+ {
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ NullCommand,
+ },
+ FileCommands[] =
+ {
+ OpenCommand,
+ NextCommand,
+ FormerCommand,
+ SelectCommand,
+ SaveCommand,
+ PrintCommand,
+ DeleteCommand,
+ NewCommand,
+ VisualDirectoryCommand,
+ QuitCommand
+ },
+ EditCommands[] =
+ {
+ UndoCommand,
+ RedoCommand,
+ CutCommand,
+ CopyCommand,
+ PasteCommand
+ },
+ ViewCommands[] =
+ {
+ HalfSizeCommand,
+ OriginalSizeCommand,
+ DoubleSizeCommand,
+ ResizeCommand,
+ ApplyCommand,
+ RefreshCommand,
+ RestoreCommand
+ },
+ TransformCommands[] =
+ {
+ CropCommand,
+ ChopCommand,
+ FlopCommand,
+ FlipCommand,
+ RotateRightCommand,
+ RotateLeftCommand,
+ RotateCommand,
+ ShearCommand,
+ RollCommand,
+ TrimCommand
+ },
+ EnhanceCommands[] =
+ {
+ HueCommand,
+ SaturationCommand,
+ BrightnessCommand,
+ GammaCommand,
+ SpiffCommand,
+ DullCommand,
+ ContrastStretchCommand,
+ SigmoidalContrastCommand,
+ NormalizeCommand,
+ EqualizeCommand,
+ NegateCommand,
+ GrayscaleCommand,
+ MapCommand,
+ QuantizeCommand
+ },
+ EffectsCommands[] =
+ {
+ DespeckleCommand,
+ EmbossCommand,
+ ReduceNoiseCommand,
+ AddNoiseCommand,
+ SharpenCommand,
+ BlurCommand,
+ ThresholdCommand,
+ EdgeDetectCommand,
+ SpreadCommand,
+ ShadeCommand,
+ RaiseCommand,
+ SegmentCommand
+ },
+ FXCommands[] =
+ {
+ SolarizeCommand,
+ SepiaToneCommand,
+ SwirlCommand,
+ ImplodeCommand,
+ VignetteCommand,
+ WaveCommand,
+ OilPaintCommand,
+ CharcoalDrawCommand
+ },
+ ImageEditCommands[] =
+ {
+ AnnotateCommand,
+ DrawCommand,
+ ColorCommand,
+ MatteCommand,
+ CompositeCommand,
+ AddBorderCommand,
+ AddFrameCommand,
+ CommentCommand,
+ LaunchCommand,
+ RegionofInterestCommand
+ },
+ MiscellanyCommands[] =
+ {
+ InfoCommand,
+ ZoomCommand,
+ ShowPreviewCommand,
+ ShowHistogramCommand,
+ ShowMatteCommand,
+ BackgroundCommand,
+ SlideShowCommand,
+ PreferencesCommand
+ },
+ HelpCommands[] =
+ {
+ HelpCommand,
+ BrowseDocumentationCommand,
+ VersionCommand
+ },
+ ShortCutsCommands[] =
+ {
+ NextCommand,
+ FormerCommand,
+ OpenCommand,
+ SaveCommand,
+ PrintCommand,
+ UndoCommand,
+ RestoreCommand,
+ InfoCommand,
+ QuitCommand
+ },
+ VirtualCommands[] =
+ {
+ InfoCommand,
+ PrintCommand,
+ NextCommand,
+ QuitCommand
+ };
+
+ static CommandType
+ *Commands[MagickMenus] =
+ {
+ FileCommands,
+ EditCommands,
+ ViewCommands,
+ TransformCommands,
+ EnhanceCommands,
+ EffectsCommands,
+ FXCommands,
+ ImageEditCommands,
+ MiscellanyCommands,
+ HelpCommands
+ };
+
+ char
+ command[MaxTextExtent],
+ *cwd,
+ geometry[MaxTextExtent],
+ resource_name[MaxTextExtent];
+
+ CommandType
+ command_type;
+
+ Image
+ *display_image,
+ *nexus;
+
+ int
+ entry,
+ id;
+
+ KeySym
+ key_symbol;
+
+ MagickStatusType
+ context_mask,
+ status;
+
+ RectangleInfo
+ geometry_info;
+
+ register int
+ i;
+
+ static char
+ working_directory[MaxTextExtent];
+
+ static XPoint
+ vid_info;
+
+ static XWindowInfo
+ *magick_windows[MaxXWindows];
+
+ static unsigned int
+ number_windows;
+
+ struct stat
+ attributes;
+
+ time_t
+ timer,
+ timestamp,
+ update_time;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ delay;
+
+ WarningHandler
+ warning_handler;
+
+ Window
+ root_window;
+
+ XClassHint
+ *class_hints;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XGCValues
+ context_values;
+
+ XPixelInfo
+ *icon_pixel,
+ *pixel;
+
+ XResourceInfo
+ *icon_resources;
+
+ XStandardColormap
+ *icon_map,
+ *map_info;
+
+ XVisualInfo
+ *icon_visual,
+ *visual_info;
+
+ XWindowChanges
+ window_changes;
+
+ XWindows
+ *windows;
+
+ XWMHints
+ *manager_hints;
+
+ assert(image != (Image **) NULL);
+ assert((*image)->signature == MagickSignature);
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
+ display_image=(*image);
+ warning_handler=(WarningHandler) NULL;
+ windows=XSetWindows((XWindows *) ~0);
+ if (windows != (XWindows *) NULL)
+ {
+ int
+ status;
+
+ status=chdir(working_directory);
+ if (status == -1)
+ (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",working_directory);
+ warning_handler=resource_info->display_warnings ?
+ SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
+ warning_handler=resource_info->display_warnings ?
+ SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
+ }
+ else
+ {
+ /*
+ Allocate windows structure.
+ */
+ resource_info->colors=display_image->colors;
+ windows=XSetWindows(XInitializeWindows(display,resource_info));
+ if (windows == (XWindows *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
+ (*image)->filename);
+ /*
+ Initialize window id's.
+ */
+ number_windows=0;
+ magick_windows[number_windows++]=(&windows->icon);
+ magick_windows[number_windows++]=(&windows->backdrop);
+ magick_windows[number_windows++]=(&windows->image);
+ magick_windows[number_windows++]=(&windows->info);
+ magick_windows[number_windows++]=(&windows->command);
+ magick_windows[number_windows++]=(&windows->widget);
+ magick_windows[number_windows++]=(&windows->popup);
+ magick_windows[number_windows++]=(&windows->magnify);
+ magick_windows[number_windows++]=(&windows->pan);
+ for (i=0; i < (int) number_windows; i++)
+ magick_windows[i]->id=(Window) NULL;
+ vid_info.x=0;
+ vid_info.y=0;
+ }
+ /*
+ Initialize font info.
+ */
+ if (windows->font_info != (XFontStruct *) NULL)
+ (void) XFreeFont(display,windows->font_info);
+ windows->font_info=XBestFont(display,resource_info,MagickFalse);
+ if (windows->font_info == (XFontStruct *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
+ resource_info->font);
+ /*
+ Initialize Standard Colormap.
+ */
+ map_info=windows->map_info;
+ icon_map=windows->icon_map;
+ visual_info=windows->visual_info;
+ icon_visual=windows->icon_visual;
+ pixel=windows->pixel_info;
+ icon_pixel=windows->icon_pixel;
+ font_info=windows->font_info;
+ icon_resources=windows->icon_resources;
+ class_hints=windows->class_hints;
+ manager_hints=windows->manager_hints;
+ root_window=XRootWindow(display,visual_info->screen);
+ nexus=NewImageList();
+ if (display_image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Image: %s[%lu] %lux%lu ",display_image->filename,
+ display_image->scene,display_image->columns,display_image->rows);
+ if (display_image->colors != 0)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
+ display_image->colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
+ display_image->magick);
+ }
+ XMakeStandardColormap(display,visual_info,resource_info,display_image,
+ map_info,pixel);
+ display_image->taint=MagickFalse;
+ /*
+ Initialize graphic context.
+ */
+ windows->context.id=(Window) NULL;
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->context);
+ (void) CloneString(&class_hints->res_name,resource_info->client_name);
+ (void) CloneString(&class_hints->res_class,resource_info->client_name);
+ class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
+ manager_hints->flags=InputHint | StateHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=WithdrawnState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->context);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (context)",windows->context.id);
+ context_values.background=pixel->background_color.pixel;
+ context_values.font=font_info->fid;
+ context_values.foreground=pixel->foreground_color.pixel;
+ context_values.graphics_exposures=MagickFalse;
+ context_mask=(MagickStatusType)
+ (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
+ if (pixel->annotate_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->annotate_context);
+ pixel->annotate_context=XCreateGC(display,windows->context.id,
+ context_mask,&context_values);
+ if (pixel->annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ display_image->filename);
+ context_values.background=pixel->depth_color.pixel;
+ if (pixel->widget_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->widget_context);
+ pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
+ &context_values);
+ if (pixel->widget_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ display_image->filename);
+ context_values.background=pixel->foreground_color.pixel;
+ context_values.foreground=pixel->background_color.pixel;
+ context_values.plane_mask=context_values.background ^
+ context_values.foreground;
+ if (pixel->highlight_context != (GC) NULL)
+ (void) XFreeGC(display,pixel->highlight_context);
+ pixel->highlight_context=XCreateGC(display,windows->context.id,
+ (unsigned long) (context_mask | GCPlaneMask),&context_values);
+ if (pixel->highlight_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ display_image->filename);
+ (void) XDestroyWindow(display,windows->context.id);
+ /*
+ Initialize icon window.
+ */
+ XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
+ icon_resources,&windows->icon);
+ windows->icon.geometry=resource_info->icon_geometry;
+ XBestIconSize(display,&windows->icon,display_image);
+ windows->icon.attributes.colormap=XDefaultColormap(display,
+ icon_visual->screen);
+ windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=IconicState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->icon);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
+ windows->icon.id);
+ /*
+ Initialize graphic context for icon window.
+ */
+ if (icon_pixel->annotate_context != (GC) NULL)
+ (void) XFreeGC(display,icon_pixel->annotate_context);
+ context_values.background=icon_pixel->background_color.pixel;
+ context_values.foreground=icon_pixel->foreground_color.pixel;
+ icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
+ (unsigned long) (GCBackground | GCForeground),&context_values);
+ if (icon_pixel->annotate_context == (GC) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
+ display_image->filename);
+ windows->icon.annotate_context=icon_pixel->annotate_context;
+ /*
+ Initialize Image window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
+ &windows->image);
+ windows->image.shape=MagickTrue; /* non-rectangular shape hint */
+ if (resource_info->use_shared_memory == MagickFalse)
+ windows->image.shared_memory=MagickFalse;
+ if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
+ {
+ char
+ *title;
+
+ title=InterpretImageProperties(resource_info->image_info,display_image,
+ resource_info->title);
+ (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
+ (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
+ title=DestroyString(title);
+ }
+ else
+ {
+ char
+ filename[MaxTextExtent];
+
+ /*
+ Window name is the base of the filename.
+ */
+ GetPathComponent(display_image->magick_filename,TailPath,filename);
+ if (GetImageListLength(display_image) == 1)
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s",filename);
+ else
+ (void) FormatMagickString(windows->image.name,MaxTextExtent,
+ "ImageMagick: %s[%lu of %lu]",filename,display_image->scene,
+ GetImageListLength(display_image));
+ (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
+ }
+ if (resource_info->immutable)
+ windows->image.immutable=MagickTrue;
+ windows->image.use_pixmap=resource_info->use_pixmap;
+ windows->image.geometry=resource_info->image_geometry;
+ (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
+ XDisplayWidth(display,visual_info->screen),
+ XDisplayHeight(display,visual_info->screen));
+ geometry_info.width=display_image->columns;
+ geometry_info.height=display_image->rows;
+ geometry_info.x=0;
+ geometry_info.y=0;
+ (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
+ &geometry_info.width,&geometry_info.height);
+ windows->image.width=(unsigned int) geometry_info.width;
+ windows->image.height=(unsigned int) geometry_info.height;
+ windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
+ PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->backdrop);
+ if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
+ {
+ /*
+ Initialize backdrop window.
+ */
+ windows->backdrop.x=0;
+ windows->backdrop.y=0;
+ (void) CloneString(&windows->backdrop.name,"ImageMagick Backdrop");
+ windows->backdrop.flags=(unsigned long) (USSize | USPosition);
+ windows->backdrop.width=(unsigned int)
+ XDisplayWidth(display,visual_info->screen);
+ windows->backdrop.height=(unsigned int)
+ XDisplayHeight(display,visual_info->screen);
+ windows->backdrop.border_width=0;
+ windows->backdrop.immutable=MagickTrue;
+ windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
+ ButtonReleaseMask;
+ windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
+ StructureNotifyMask;
+ manager_hints->flags=IconWindowHint | InputHint | StateHint;
+ manager_hints->icon_window=windows->icon.id;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=resource_info->iconic ? IconicState :
+ NormalState;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->backdrop);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (backdrop)",windows->backdrop.id);
+ (void) XMapWindow(display,windows->backdrop.id);
+ (void) XClearWindow(display,windows->backdrop.id);
+ if (windows->image.id != (Window) NULL)
+ {
+ (void) XDestroyWindow(display,windows->image.id);
+ windows->image.id=(Window) NULL;
+ }
+ /*
+ Position image in the center the backdrop.
+ */
+ windows->image.flags|=USPosition;
+ windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
+ (windows->image.width/2);
+ windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
+ (windows->image.height/2);
+ }
+ manager_hints->flags=IconWindowHint | InputHint | StateHint;
+ manager_hints->icon_window=windows->icon.id;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=resource_info->iconic ? IconicState :
+ NormalState;
+ if (windows->group_leader.id != (Window) NULL)
+ {
+ /*
+ Follow the leader.
+ */
+ manager_hints->flags|=WindowGroupHint;
+ manager_hints->window_group=windows->group_leader.id;
+ (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (group leader)",windows->group_leader.id);
+ }
+ XMakeWindow(display,
+ (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
+ argv,argc,class_hints,manager_hints,&windows->image);
+ (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
+ XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
+ if (windows->group_leader.id != (Window) NULL)
+ (void) XSetTransientForHint(display,windows->image.id,
+ windows->group_leader.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
+ windows->image.id);
+ /*
+ Initialize Info widget.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
+ &windows->info);
+ (void) CloneString(&windows->info.name,"Info");
+ (void) CloneString(&windows->info.icon_name,"Info");
+ windows->info.border_width=1;
+ windows->info.x=2;
+ windows->info.y=2;
+ windows->info.flags|=PPosition;
+ windows->info.attributes.win_gravity=UnmapGravity;
+ windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
+ &windows->info);
+ windows->info.highlight_stipple=XCreateBitmapFromData(display,
+ windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->info.shadow_stipple=XCreateBitmapFromData(display,
+ windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
+ if (windows->image.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
+ windows->info.id);
+ /*
+ Initialize Command widget.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->command);
+ windows->command.data=MagickMenus;
+ (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
+ resource_info->client_name);
+ windows->command.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ (void) CloneString(&windows->command.name,MagickTitle);
+ windows->command.border_width=0;
+ windows->command.flags|=PPosition;
+ windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
+ OwnerGrabButtonMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->command);
+ windows->command.highlight_stipple=XCreateBitmapFromData(display,
+ windows->command.id,(char *) HighlightBitmap,HighlightWidth,
+ HighlightHeight);
+ windows->command.shadow_stipple=XCreateBitmapFromData(display,
+ windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
+ if (windows->command.mapped != MagickFalse)
+ (void) XMapRaised(display,windows->command.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (command)",windows->command.id);
+ /*
+ Initialize Widget window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->widget);
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
+ resource_info->client_name);
+ windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ windows->widget.border_width=0;
+ windows->widget.flags|=PPosition;
+ windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->widget);
+ windows->widget.highlight_stipple=XCreateBitmapFromData(display,
+ windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->widget.shadow_stipple=XCreateBitmapFromData(display,
+ windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (widget)",windows->widget.id);
+ /*
+ Initialize popup window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->popup);
+ windows->popup.border_width=0;
+ windows->popup.flags|=PPosition;
+ windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
+ KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->popup);
+ windows->popup.highlight_stipple=XCreateBitmapFromData(display,
+ windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
+ windows->popup.shadow_stipple=XCreateBitmapFromData(display,
+ windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
+ (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (pop up)",windows->popup.id);
+ /*
+ Initialize Magnify window and cursor.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->magnify);
+ if (resource_info->use_shared_memory == MagickFalse)
+ windows->magnify.shared_memory=MagickFalse;
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
+ resource_info->client_name);
+ windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
+ resource_info->magnify);
+ if (windows->magnify.cursor != (Cursor) NULL)
+ (void) XFreeCursor(display,windows->magnify.cursor);
+ windows->magnify.cursor=XMakeCursor(display,windows->image.id,
+ map_info->colormap,resource_info->background_color,
+ resource_info->foreground_color);
+ if (windows->magnify.cursor == (Cursor) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
+ display_image->filename);
+ windows->magnify.width=MagnifySize;
+ windows->magnify.height=MagnifySize;
+ windows->magnify.flags|=PPosition;
+ windows->magnify.min_width=MagnifySize;
+ windows->magnify.min_height=MagnifySize;
+ windows->magnify.width_inc=MagnifySize;
+ windows->magnify.height_inc=MagnifySize;
+ windows->magnify.data=resource_info->magnify;
+ windows->magnify.attributes.cursor=windows->magnify.cursor;
+ windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
+ ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickTrue;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->magnify);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Window id: 0x%lx (magnify)",windows->magnify.id);
+ (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
+ /*
+ Initialize panning window.
+ */
+ XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
+ resource_info,&windows->pan);
+ (void) CloneString(&windows->pan.name,"Pan Icon");
+ windows->pan.width=windows->icon.width;
+ windows->pan.height=windows->icon.height;
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
+ resource_info->client_name);
+ windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
+ resource_name,"geometry",(char *) NULL);
+ (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
+ &windows->pan.width,&windows->pan.height);
+ windows->pan.flags|=PPosition;
+ windows->pan.immutable=MagickTrue;
+ windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
+ ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
+ StructureNotifyMask;
+ manager_hints->flags=InputHint | StateHint | WindowGroupHint;
+ manager_hints->input=MagickFalse;
+ manager_hints->initial_state=NormalState;
+ manager_hints->window_group=windows->image.id;
+ XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
+ &windows->pan);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
+ windows->pan.id);
+ (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if ((windows->image.mapped == MagickFalse) ||
+ (windows->backdrop.id != (Window) NULL))
+ (void) XMapWindow(display,windows->image.id);
+ /*
+ Set our progress monitor and warning handlers.
+ */
+ if (warning_handler == (WarningHandler) NULL)
+ {
+ warning_handler=resource_info->display_warnings ?
+ SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
+ warning_handler=resource_info->display_warnings ?
+ SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
+ }
+ /*
+ Initialize Image and Magnify X images.
+ */
+ windows->image.x=0;
+ windows->image.y=0;
+ windows->magnify.shape=MagickFalse;
+ width=(unsigned int) display_image->columns;
+ height=(unsigned int) display_image->rows;
+ if ((display_image->columns != width) || (display_image->rows != height))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ display_image->filename);
+ status=XMakeImage(display,resource_info,&windows->image,display_image,
+ width,height);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ display_image->filename);
+ status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
+ windows->magnify.width,windows->magnify.height);
+ if (status == MagickFalse)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
+ display_image->filename);
+ if (windows->magnify.mapped != MagickFalse)
+ (void) XMapRaised(display,windows->magnify.id);
+ if (windows->pan.mapped != MagickFalse)
+ (void) XMapRaised(display,windows->pan.id);
+ windows->image.window_changes.width=(int) display_image->columns;
+ windows->image.window_changes.height=(int) display_image->rows;
+ (void) XConfigureImage(display,resource_info,windows,display_image);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ (void) XSync(display,MagickFalse);
+ /*
+ Respond to events.
+ */
+ delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
+ timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
+ update_time=0;
+ if (resource_info->update != MagickFalse)
+ {
+ MagickBooleanType
+ status;
+
+ /*
+ Determine when file data was last modified.
+ */
+ status=GetPathAttributes(display_image->filename,&attributes);
+ if (status != MagickFalse)
+ update_time=attributes.st_mtime;
+ }
+ *state&=(~FormerImageState);
+ *state&=(~MontageImageState);
+ *state&=(~NextImageState);
+ do
+ {
+ /*
+ Handle a window event.
+ */
+ if (windows->image.mapped != MagickFalse)
+ if ((display_image->delay != 0) || (resource_info->update != 0))
+ {
+ if (timer < time((time_t *) NULL))
+ {
+ if (resource_info->update == MagickFalse)
+ *state|=NextImageState | ExitState;
+ else
+ {
+ MagickBooleanType
+ status;
+
+ /*
+ Determine if image file was modified.
+ */
+ status=GetPathAttributes(display_image->filename,&attributes);
+ if (status != MagickFalse)
+ if (update_time != attributes.st_mtime)
+ {
+ /*
+ Redisplay image.
+ */
+ (void) FormatMagickString(
+ resource_info->image_info->filename,MaxTextExtent,
+ "%s:%s",display_image->magick,
+ display_image->filename);
+ nexus=ReadImage(resource_info->image_info,
+ &display_image->exception);
+ if (nexus != (Image *) NULL)
+ {
+ nexus=DestroyImage(nexus);
+ *state|=NextImageState | ExitState;
+ }
+ }
+ delay=display_image->delay/MagickMax(
+ display_image->ticks_per_second,1L);
+ timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
+ }
+ }
+ if (XEventsQueued(display,QueuedAfterFlush) == 0)
+ {
+ /*
+ Do not block if delay > 0.
+ */
+ XDelay(display,SuspendTime << 2);
+ continue;
+ }
+ }
+ timestamp=time((time_t *) NULL);
+ (void) XNextEvent(display,&event);
+ if (windows->image.stasis == MagickFalse)
+ windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
+ MagickTrue : MagickFalse;
+ if (windows->magnify.stasis == MagickFalse)
+ windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
+ MagickTrue : MagickFalse;
+ if (event.xany.window == windows->command.id)
+ {
+ /*
+ Select a command from the Command widget.
+ */
+ id=XCommandWidget(display,windows,CommandMenu,&event);
+ if (id < 0)
+ continue;
+ (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
+ command_type=CommandMenus[id];
+ if (id < MagickMenus)
+ {
+ /*
+ Select a command from a pop-up menu.
+ */
+ entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
+ command);
+ if (entry < 0)
+ continue;
+ (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
+ command_type=Commands[id][entry];
+ }
+ if (command_type != NullCommand)
+ nexus=XMagickCommand(display,resource_info,windows,command_type,
+ &display_image);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ if ((event.xbutton.button == Button3) &&
+ (event.xbutton.state & Mod1Mask))
+ {
+ /*
+ Convert Alt-Button3 to Button2.
+ */
+ event.xbutton.button=Button2;
+ event.xbutton.state&=(~Mod1Mask);
+ }
+ if (event.xbutton.window == windows->backdrop.id)
+ {
+ (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
+ event.xbutton.time);
+ break;
+ }
+ if (event.xbutton.window == windows->image.id)
+ {
+ switch (event.xbutton.button)
+ {
+ case Button1:
+ {
+ if (resource_info->immutable)
+ {
+ /*
+ Select a command from the Virtual menu.
+ */
+ entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
+ command);
+ if (entry >= 0)
+ nexus=XMagickCommand(display,resource_info,windows,
+ VirtualCommands[entry],&display_image);
+ break;
+ }
+ /*
+ Map/unmap Command widget.
+ */
+ if (windows->command.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->command.id,
+ windows->command.screen);
+ else
+ {
+ (void) XCommandWidget(display,windows,CommandMenu,
+ (XEvent *) NULL);
+ (void) XMapRaised(display,windows->command.id);
+ }
+ break;
+ }
+ case Button2:
+ {
+ /*
+ User pressed the image magnify button.
+ */
+ (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
+ &display_image);
+ XMagnifyImage(display,windows,&event);
+ break;
+ }
+ case Button3:
+ {
+ if (resource_info->immutable)
+ {
+ /*
+ Select a command from the Virtual menu.
+ */
+ entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
+ command);
+ if (entry >= 0)
+ nexus=XMagickCommand(display,resource_info,windows,
+ VirtualCommands[entry],&display_image);
+ break;
+ }
+ if (display_image->montage != (char *) NULL)
+ {
+ /*
+ Open or delete a tile from a visual image directory.
+ */
+ nexus=XTileImage(display,resource_info,windows,
+ display_image,&event);
+ if (nexus != (Image *) NULL)
+ *state|=MontageImageState | NextImageState | ExitState;
+ vid_info.x=windows->image.x;
+ vid_info.y=windows->image.y;
+ break;
+ }
+ /*
+ Select a command from the Short Cuts menu.
+ */
+ entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
+ command);
+ if (entry >= 0)
+ nexus=XMagickCommand(display,resource_info,windows,
+ ShortCutsCommands[entry],&display_image);
+ break;
+ }
+ case Button4:
+ {
+ /*
+ Wheel up.
+ */
+ XTranslateImage(display,windows,*image,XK_Up);
+ break;
+ }
+ case Button5:
+ {
+ /*
+ Wheel down.
+ */
+ XTranslateImage(display,windows,*image,XK_Down);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ if (event.xbutton.window == windows->magnify.id)
+ {
+ int
+ factor;
+
+ static const char
+ *MagnifyMenu[] =
+ {
+ "2",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "3",
+ (char *) NULL,
+ };
+
+ static KeySym
+ MagnifyCommands[] =
+ {
+ XK_2,
+ XK_4,
+ XK_5,
+ XK_6,
+ XK_7,
+ XK_8,
+ XK_9,
+ XK_3
+ };
+
+ /*
+ Select a magnify factor from the pop-up menu.
+ */
+ factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
+ if (factor >= 0)
+ XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
+ break;
+ }
+ if (event.xbutton.window == windows->pan.id)
+ {
+ switch (event.xbutton.button)
+ {
+ case Button4:
+ {
+ /*
+ Wheel up.
+ */
+ XTranslateImage(display,windows,*image,XK_Up);
+ break;
+ }
+ case Button5:
+ {
+ /*
+ Wheel down.
+ */
+ XTranslateImage(display,windows,*image,XK_Down);
+ break;
+ }
+ default:
+ {
+ XPanImage(display,windows,&event);
+ break;
+ }
+ }
+ break;
+ }
+ delay=display_image->delay/MagickMax(display_image->ticks_per_second,
+ 1L);
+ timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
+ event.xbutton.button,event.xbutton.x,event.xbutton.y);
+ break;
+ }
+ case ClientMessage:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
+ event.xclient.message_type,event.xclient.format,(unsigned long)
+ event.xclient.data.l[0]);
+ if (event.xclient.message_type == windows->im_protocols)
+ {
+ if (*event.xclient.data.l == (long) windows->im_update_widget)
+ {
+ (void) CloneString(&windows->command.name,MagickTitle);
+ windows->command.data=MagickMenus;
+ (void) XCommandWidget(display,windows,CommandMenu,
+ (XEvent *) NULL);
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_update_colormap)
+ {
+ /*
+ Update graphic context and window colormap.
+ */
+ for (i=0; i < (int) number_windows; i++)
+ {
+ if (magick_windows[i]->id == windows->icon.id)
+ continue;
+ context_values.background=pixel->background_color.pixel;
+ context_values.foreground=pixel->foreground_color.pixel;
+ (void) XChangeGC(display,magick_windows[i]->annotate_context,
+ context_mask,&context_values);
+ (void) XChangeGC(display,magick_windows[i]->widget_context,
+ context_mask,&context_values);
+ context_values.background=pixel->foreground_color.pixel;
+ context_values.foreground=pixel->background_color.pixel;
+ context_values.plane_mask=context_values.background ^
+ context_values.foreground;
+ (void) XChangeGC(display,magick_windows[i]->highlight_context,
+ (unsigned long) (context_mask | GCPlaneMask),
+ &context_values);
+ magick_windows[i]->attributes.background_pixel=
+ pixel->background_color.pixel;
+ magick_windows[i]->attributes.border_pixel=
+ pixel->border_color.pixel;
+ magick_windows[i]->attributes.colormap=map_info->colormap;
+ (void) XChangeWindowAttributes(display,magick_windows[i]->id,
+ magick_windows[i]->mask,&magick_windows[i]->attributes);
+ }
+ if (windows->pan.mapped != MagickFalse)
+ {
+ (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
+ windows->pan.pixmap);
+ (void) XClearWindow(display,windows->pan.id);
+ XDrawPanRectangle(display,windows);
+ }
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XInstallColormap(display,map_info->colormap);
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_former_image)
+ {
+ *state|=FormerImageState | ExitState;
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_next_image)
+ {
+ *state|=NextImageState | ExitState;
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_retain_colors)
+ {
+ *state|=RetainColorsState;
+ break;
+ }
+ if (*event.xclient.data.l == (long) windows->im_exit)
+ {
+ *state|=ExitState;
+ break;
+ }
+ break;
+ }
+ if (event.xclient.message_type == windows->dnd_protocols)
+ {
+ Atom
+ selection,
+ type;
+
+ int
+ format,
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Display image named by the Drag-and-Drop selection.
+ */
+ if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
+ break;
+ selection=XInternAtom(display,"DndSelection",MagickFalse);
+ status=XGetWindowProperty(display,root_window,selection,0L,(long)
+ MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
+ &length,&after,&data);
+ if ((status != Success) || (length == 0))
+ break;
+ if (*event.xclient.data.l == 2)
+ {
+ /*
+ Offix DND.
+ */
+ (void) CopyMagickString(resource_info->image_info->filename,
+ (char *) data,MaxTextExtent);
+ }
+ else
+ {
+ /*
+ XDND.
+ */
+ if (strncmp((char *) data, "file:", 5) != 0)
+ {
+ (void) XFree((void *) data);
+ break;
+ }
+ (void) CopyMagickString(resource_info->image_info->filename,
+ ((char *) data)+5,MaxTextExtent);
+ }
+ nexus=ReadImage(resource_info->image_info,
+ &display_image->exception);
+ CatchException(&display_image->exception);
+ if (nexus != (Image *) NULL)
+ *state|=NextImageState | ExitState;
+ (void) XFree((void *) data);
+ break;
+ }
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l != (long) windows->wm_delete_window)
+ break;
+ (void) XWithdrawWindow(display,event.xclient.window,
+ visual_info->screen);
+ if (event.xclient.window == windows->image.id)
+ {
+ *state|=ExitState;
+ break;
+ }
+ if (event.xclient.window == windows->pan.id)
+ {
+ /*
+ Restore original image size when pan window is deleted.
+ */
+ windows->image.window_changes.width=windows->image.ximage->width;
+ windows->image.window_changes.height=windows->image.ximage->height;
+ (void) XConfigureImage(display,resource_info,windows,
+ display_image);
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
+ event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
+ event.xconfigure.y,event.xconfigure.send_event);
+ if (event.xconfigure.window == windows->image.id)
+ {
+ /*
+ Image window has a new configuration.
+ */
+ if (event.xconfigure.send_event != 0)
+ {
+ XWindowChanges
+ window_changes;
+
+ /*
+ Position the transient windows relative of the Image window.
+ */
+ if (windows->command.geometry == (char *) NULL)
+ if (windows->command.mapped == MagickFalse)
+ {
+ windows->command.x=event.xconfigure.x-
+ windows->command.width-25;
+ windows->command.y=event.xconfigure.y;
+ XConstrainWindowPosition(display,&windows->command);
+ window_changes.x=windows->command.x;
+ window_changes.y=windows->command.y;
+ (void) XReconfigureWMWindow(display,windows->command.id,
+ windows->command.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ if (windows->widget.geometry == (char *) NULL)
+ if (windows->widget.mapped == MagickFalse)
+ {
+ windows->widget.x=event.xconfigure.x+
+ event.xconfigure.width/10;
+ windows->widget.y=event.xconfigure.y+
+ event.xconfigure.height/10;
+ XConstrainWindowPosition(display,&windows->widget);
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,
+ windows->widget.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ if (windows->magnify.geometry == (char *) NULL)
+ if (windows->magnify.mapped == MagickFalse)
+ {
+ windows->magnify.x=event.xconfigure.x+
+ event.xconfigure.width+25;
+ windows->magnify.y=event.xconfigure.y;
+ XConstrainWindowPosition(display,&windows->magnify);
+ window_changes.x=windows->magnify.x;
+ window_changes.y=windows->magnify.y;
+ (void) XReconfigureWMWindow(display,windows->magnify.id,
+ windows->magnify.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ if (windows->pan.geometry == (char *) NULL)
+ if (windows->pan.mapped == MagickFalse)
+ {
+ windows->pan.x=event.xconfigure.x+
+ event.xconfigure.width+25;
+ windows->pan.y=event.xconfigure.y+
+ windows->magnify.height+50;
+ XConstrainWindowPosition(display,&windows->pan);
+ window_changes.x=windows->pan.x;
+ window_changes.y=windows->pan.y;
+ (void) XReconfigureWMWindow(display,windows->pan.id,
+ windows->pan.screen,(unsigned int) (CWX | CWY),
+ &window_changes);
+ }
+ }
+ if ((event.xconfigure.width == (long) windows->image.width) &&
+ (event.xconfigure.height == (long) windows->image.height))
+ break;
+ windows->image.width=(unsigned int) event.xconfigure.width;
+ windows->image.height=(unsigned int) event.xconfigure.height;
+ windows->image.x=0;
+ windows->image.y=0;
+ if (display_image->montage != (char *) NULL)
+ {
+ windows->image.x=vid_info.x;
+ windows->image.y=vid_info.y;
+ }
+ /*
+ Update pan window configuration.
+ */
+ if ((event.xconfigure.width < windows->image.ximage->width) ||
+ (event.xconfigure.height < windows->image.ximage->height))
+ {
+ (void) XMapRaised(display,windows->pan.id);
+ XDrawPanRectangle(display,windows);
+ }
+ else
+ if (windows->pan.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->pan.id,
+ windows->pan.screen);
+ break;
+ }
+ if (event.xconfigure.window == windows->magnify.id)
+ {
+ unsigned int
+ magnify;
+
+ /*
+ Magnify window has a new configuration.
+ */
+ windows->magnify.width=(unsigned int) event.xconfigure.width;
+ windows->magnify.height=(unsigned int) event.xconfigure.height;
+ if (windows->magnify.mapped == MagickFalse)
+ break;
+ magnify=1;
+ while ((int) magnify <= event.xconfigure.width)
+ magnify<<=1;
+ while ((int) magnify <= event.xconfigure.height)
+ magnify<<=1;
+ magnify>>=1;
+ if (((int) magnify != event.xconfigure.width) ||
+ ((int) magnify != event.xconfigure.height))
+ {
+ window_changes.width=(int) magnify;
+ window_changes.height=(int) magnify;
+ (void) XReconfigureWMWindow(display,windows->magnify.id,
+ windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
+ &window_changes);
+ break;
+ }
+ if ((windows->magnify.mapped != MagickFalse) &&
+ (windows->magnify.stasis != MagickFalse))
+ {
+ status=XMakeImage(display,resource_info,&windows->magnify,
+ display_image,windows->magnify.width,windows->magnify.height);
+ XMakeMagnifyImage(display,windows);
+ }
+ break;
+ }
+ if ((windows->magnify.mapped != MagickFalse) &&
+ (event.xconfigure.window == windows->pan.id))
+ {
+ /*
+ Pan icon window has a new configuration.
+ */
+ if (event.xconfigure.send_event != 0)
+ {
+ windows->pan.x=event.xconfigure.x;
+ windows->pan.y=event.xconfigure.y;
+ }
+ windows->pan.width=(unsigned int) event.xconfigure.width;
+ windows->pan.height=(unsigned int) event.xconfigure.height;
+ break;
+ }
+ if (event.xconfigure.window == windows->icon.id)
+ {
+ /*
+ Icon window has a new configuration.
+ */
+ windows->icon.width=(unsigned int) event.xconfigure.width;
+ windows->icon.height=(unsigned int) event.xconfigure.height;
+ break;
+ }
+ break;
+ }
+ case DestroyNotify:
+ {
+ /*
+ Group leader has exited.
+ */
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Destroy Notify: 0x%lx",event.xdestroywindow.window);
+ if (event.xdestroywindow.window == windows->group_leader.id)
+ {
+ *state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case EnterNotify:
+ {
+ /*
+ Selectively install colormap.
+ */
+ if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
+ if (event.xcrossing.mode != NotifyUngrab)
+ XInstallColormap(display,map_info->colormap);
+ break;
+ }
+ case Expose:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
+ event.xexpose.width,event.xexpose.height,event.xexpose.x,
+ event.xexpose.y);
+ /*
+ Refresh windows that are now exposed.
+ */
+ if (event.xexpose.window == windows->image.id)
+ if (windows->image.mapped != MagickFalse)
+ {
+ XRefreshWindow(display,&windows->image,&event);
+ delay=display_image->delay/MagickMax(
+ display_image->ticks_per_second,1L);
+ timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
+ break;
+ }
+ if (event.xexpose.window == windows->magnify.id)
+ if (event.xexpose.count == 0)
+ if (windows->magnify.mapped != MagickFalse)
+ {
+ XMakeMagnifyImage(display,windows);
+ break;
+ }
+ if (event.xexpose.window == windows->pan.id)
+ if (event.xexpose.count == 0)
+ {
+ XDrawPanRectangle(display,windows);
+ break;
+ }
+ if (event.xexpose.window == windows->icon.id)
+ if (event.xexpose.count == 0)
+ {
+ XRefreshWindow(display,&windows->icon,&event);
+ break;
+ }
+ break;
+ }
+ case KeyPress:
+ {
+ int
+ length;
+
+ /*
+ Respond to a user key press.
+ */
+ length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
+ key_symbol,command);
+ if (event.xkey.window == windows->image.id)
+ {
+ command_type=XImageWindowCommand(display,resource_info,windows,
+ event.xkey.state,key_symbol,&display_image);
+ if (command_type != NullCommand)
+ nexus=XMagickCommand(display,resource_info,windows,command_type,
+ &display_image);
+ }
+ if (event.xkey.window == windows->magnify.id)
+ XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
+ if (event.xkey.window == windows->pan.id)
+ {
+ if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
+ (void) XWithdrawWindow(display,windows->pan.id,
+ windows->pan.screen);
+ else
+ if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
+ XTextViewWidget(display,resource_info,windows,MagickFalse,
+ "Help Viewer - Image Pan",ImagePanHelp);
+ else
+ XTranslateImage(display,windows,*image,key_symbol);
+ }
+ delay=display_image->delay/MagickMax(
+ display_image->ticks_per_second,1L);
+ timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
+ break;
+ }
+ case KeyRelease:
+ {
+ /*
+ Respond to a user key release.
+ */
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
+ sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
+ break;
+ }
+ case LeaveNotify:
+ {
+ /*
+ Selectively uninstall colormap.
+ */
+ if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
+ if (event.xcrossing.mode != NotifyUngrab)
+ XUninstallColormap(display,map_info->colormap);
+ break;
+ }
+ case MapNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
+ event.xmap.window);
+ if (event.xmap.window == windows->backdrop.id)
+ {
+ (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
+ CurrentTime);
+ windows->backdrop.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->image.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XInstallColormap(display,map_info->colormap);
+ if (LocaleCompare(display_image->magick,"LOGO") == 0)
+ {
+ if (LocaleCompare(display_image->filename,"LOGO") == 0)
+ nexus=XOpenImage(display,resource_info,windows,MagickFalse);
+ }
+ if (((int) windows->image.width < windows->image.ximage->width) ||
+ ((int) windows->image.height < windows->image.ximage->height))
+ (void) XMapRaised(display,windows->pan.id);
+ windows->image.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->magnify.id)
+ {
+ XMakeMagnifyImage(display,windows);
+ windows->magnify.mapped=MagickTrue;
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ break;
+ }
+ if (event.xmap.window == windows->pan.id)
+ {
+ XMakePanImage(display,resource_info,windows,display_image);
+ windows->pan.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->icon.id)
+ {
+ MagickBooleanType
+ taint;
+
+ /*
+ Create an icon image.
+ */
+ taint=display_image->taint;
+ XMakeStandardColormap(display,icon_visual,icon_resources,
+ display_image,icon_map,icon_pixel);
+ (void) XMakeImage(display,icon_resources,&windows->icon,
+ display_image,windows->icon.width,windows->icon.height);
+ display_image->taint=taint;
+ (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
+ windows->icon.pixmap);
+ (void) XClearWindow(display,windows->icon.id);
+ (void) XWithdrawWindow(display,windows->info.id,
+ windows->info.screen);
+ windows->icon.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->command.id)
+ {
+ windows->command.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->popup.id)
+ {
+ windows->popup.mapped=MagickTrue;
+ break;
+ }
+ if (event.xmap.window == windows->widget.id)
+ {
+ windows->widget.mapped=MagickTrue;
+ break;
+ }
+ break;
+ }
+ case MappingNotify:
+ {
+ (void) XRefreshKeyboardMapping(&event.xmapping);
+ break;
+ }
+ case NoExpose:
+ break;
+ case PropertyNotify:
+ {
+ Atom
+ type;
+
+ int
+ format,
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
+ event.xproperty.atom,event.xproperty.state);
+ if (event.xproperty.atom != windows->im_remote_command)
+ break;
+ /*
+ Display image named by the remote command protocol.
+ */
+ status=XGetWindowProperty(display,event.xproperty.window,
+ event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
+ AnyPropertyType,&type,&format,&length,&after,&data);
+ if ((status != Success) || (length == 0))
+ break;
+ if (LocaleCompare((char *) data,"-quit") == 0)
+ {
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_exit,CurrentTime);
+ (void) XFree((void *) data);
+ break;
+ }
+ (void) CopyMagickString(resource_info->image_info->filename,
+ (char *) data,MaxTextExtent);
+ (void) XFree((void *) data);
+ nexus=ReadImage(resource_info->image_info,&display_image->exception);
+ CatchException(&display_image->exception);
+ if (nexus != (Image *) NULL)
+ *state|=NextImageState | ExitState;
+ break;
+ }
+ case ReparentNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
+ event.xreparent.window);
+ break;
+ }
+ case UnmapNotify:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Unmap Notify: 0x%lx",event.xunmap.window);
+ if (event.xunmap.window == windows->backdrop.id)
+ {
+ windows->backdrop.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->image.id)
+ {
+ windows->image.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->magnify.id)
+ {
+ windows->magnify.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->pan.id)
+ {
+ windows->pan.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->info.id)
+ {
+ windows->info.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->icon.id)
+ {
+ if (map_info->colormap == icon_map->colormap)
+ XConfigureImageColormap(display,resource_info,windows,
+ display_image);
+ (void) XFreeStandardColormap(display,icon_visual,icon_map,
+ icon_pixel);
+ windows->icon.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->command.id)
+ {
+ windows->command.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->popup.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XSetInputFocus(display,windows->image.id,RevertToParent,
+ CurrentTime);
+ windows->popup.mapped=MagickFalse;
+ break;
+ }
+ if (event.xunmap.window == windows->widget.id)
+ {
+ if (windows->backdrop.id != (Window) NULL)
+ (void) XSetInputFocus(display,windows->image.id,RevertToParent,
+ CurrentTime);
+ windows->widget.mapped=MagickFalse;
+ break;
+ }
+ break;
+ }
+ default:
+ {
+ if (display_image->debug != MagickFalse)
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
+ event.type);
+ break;
+ }
+ }
+ } while (!(*state & ExitState));
+ if ((*state & ExitState) == 0)
+ (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
+ &display_image);
+ else
+ if (resource_info->confirm_edit != MagickFalse)
+ {
+ /*
+ Query user if image has changed.
+ */
+ if ((resource_info->immutable == MagickFalse) &&
+ (display_image->taint != MagickFalse))
+ {
+ int
+ status;
+
+ status=XConfirmWidget(display,windows,"Your image changed.",
+ "Do you want to save it");
+ if (status == 0)
+ *state&=(~ExitState);
+ else
+ if (status > 0)
+ (void) XMagickCommand(display,resource_info,windows,SaveCommand,
+ &display_image);
+ }
+ }
+ if ((windows->visual_info->klass == GrayScale) ||
+ (windows->visual_info->klass == PseudoColor) ||
+ (windows->visual_info->klass == DirectColor))
+ {
+ /*
+ Withdraw pan and Magnify window.
+ */
+ if (windows->info.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ if (windows->magnify.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->magnify.id,
+ windows->magnify.screen);
+ if (windows->command.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->command.id,
+ windows->command.screen);
+ }
+ if (windows->pan.mapped != MagickFalse)
+ (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
+ if (resource_info->backdrop == MagickFalse)
+ if (windows->backdrop.mapped)
+ {
+ (void) XWithdrawWindow(display,windows->backdrop.id,
+ windows->backdrop.screen);
+ (void) XDestroyWindow(display,windows->backdrop.id);
+ windows->backdrop.id=(Window) NULL;
+ (void) XWithdrawWindow(display,windows->image.id,
+ windows->image.screen);
+ (void) XDestroyWindow(display,windows->image.id);
+ windows->image.id=(Window) NULL;
+ }
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
+ *state&=(~ExitState);
+ if (*state & ExitState)
+ {
+ /*
+ Free Standard Colormap.
+ */
+ (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
+ if (resource_info->map_type == (char *) NULL)
+ (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
+ /*
+ Free X resources.
+ */
+ if (resource_info->copy_image != (Image *) NULL)
+ {
+ resource_info->copy_image=DestroyImage(resource_info->copy_image);
+ resource_info->copy_image=NewImageList();
+ }
+ DestroyXResources();
+ }
+ (void) XSync(display,MagickFalse);
+ /*
+ Restore our progress monitor and warning handlers.
+ */
+ (void) SetErrorHandler(warning_handler);
+ (void) SetWarningHandler(warning_handler);
+ /*
+ Change to home directory.
+ */
+ cwd=getcwd(working_directory,MaxTextExtent);
+ {
+ int
+ status;
+
+ status=chdir(resource_info->home_directory);
+ if (status == -1)
+ (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
+ FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
+ }
+ *image=display_image;
+ return(nexus);
+}
+#else
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D i s p l a y I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DisplayImages() displays an image sequence to any X window screen. It
+% returns a value other than 0 if successful. Check the exception member
+% of image to determine the reason for any failure.
+%
+% The format of the DisplayImages method is:
+%
+% MagickBooleanType DisplayImages(const ImageInfo *image_info,
+% Image *images)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
+ Image *image)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
+ image->filename);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e m o t e D i s p l a y C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoteDisplayCommand() encourages a remote display program to display the
+% specified image filename.
+%
+% The format of the RemoteDisplayCommand method is:
+%
+% MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
+% const char *window,const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o window: Specifies the name or id of an X window.
+%
+% o filename: the name of the image filename to display.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
+ const char *window,const char *filename,ExceptionInfo *exception)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ assert(filename != (char *) NULL);
+ (void) window;
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
+ "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
+ return(MagickFalse);
+}
+#endif
diff --git a/magick/display.h b/magick/display.h
new file mode 100644
index 0000000..66c6ef3
--- /dev/null
+++ b/magick/display.h
@@ -0,0 +1,34 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore methods to interactively display and edit an image.
+*/
+#ifndef _MAGICKCORE_DISPLAY_H
+#define _MAGICKCORE_DISPLAY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ DisplayImages(const ImageInfo *,Image *),
+ RemoteDisplayCommand(const ImageInfo *,const char *,const char *,
+ ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/distort.c b/magick/distort.c
new file mode 100644
index 0000000..c89d799
--- /dev/null
+++ b/magick/distort.c
@@ -0,0 +1,2600 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% DDDD IIIII SSSSS TTTTT OOO RRRR TTTTT %
+% D D I SS T O O R R T %
+% D D I SSS T O O RRRR T %
+% D D I SS T O O R R T %
+% DDDD IIIII SSSSS T OOO R R T %
+% %
+% %
+% MagickCore Image Distortion Methods %
+% %
+% Software Design %
+% John Cristy %
+% Anthony Thyssen %
+% June 2007 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite-private.h"
+#include "magick/distort.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/hashmap.h"
+#include "magick/image.h"
+#include "magick/list.h"
+#include "magick/matrix.h"
+#include "magick/memory_.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/resample.h"
+#include "magick/resample-private.h"
+#include "magick/registry.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+
+/*
+ Numerous internal routines for image distortions.
+*/
+static inline double MagickMin(const double x,const double y)
+{
+ return( x < y ? x : y);
+}
+static inline double MagickMax(const double x,const double y)
+{
+ return( x > y ? x : y);
+}
+
+static inline void AffineArgsToCoefficients(double *affine)
+{
+ /* map external sx,ry,rx,sy,tx,ty to internal c0,c2,c4,c1,c3,c5 */
+ double tmp[4]; /* note indexes 0 and 5 remain unchanged */
+ tmp[0]=affine[1]; tmp[1]=affine[2]; tmp[2]=affine[3]; tmp[3]=affine[4];
+ affine[3]=tmp[0]; affine[1]=tmp[1]; affine[4]=tmp[2]; affine[2]=tmp[3];
+}
+static inline void CoefficientsToAffineArgs(double *coeff)
+{
+ /* map internal c0,c1,c2,c3,c4,c5 to external sx,ry,rx,sy,tx,ty */
+ double tmp[4]; /* note indexes 0 and 5 remain unchanged */
+ tmp[0]=coeff[3]; tmp[1]=coeff[1]; tmp[2]=coeff[4]; tmp[3]=coeff[2];
+ coeff[1]=tmp[0]; coeff[2]=tmp[1]; coeff[3]=tmp[2]; coeff[4]=tmp[3];
+}
+static void InvertAffineCoefficients(const double *coeff,double *inverse)
+{
+ /* From "Digital Image Warping" by George Wolberg, page 50 */
+ double determinant;
+
+ determinant=1.0/(coeff[0]*coeff[4]-coeff[1]*coeff[3]);
+ inverse[0]=determinant*coeff[4];
+ inverse[1]=determinant*(-coeff[1]);
+ inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[2]*coeff[4]);
+ inverse[3]=determinant*(-coeff[3]);
+ inverse[4]=determinant*coeff[0];
+ inverse[5]=determinant*(coeff[2]*coeff[3]-coeff[0]*coeff[5]);
+}
+
+static void InvertPerspectiveCoefficients(const double *coeff,
+ double *inverse)
+{
+ /* From "Digital Image Warping" by George Wolberg, page 53 */
+ double determinant;
+
+ determinant=1.0/(coeff[0]*coeff[4]-coeff[3]*coeff[1]);
+ inverse[0]=determinant*(coeff[4]-coeff[7]*coeff[5]);
+ inverse[1]=determinant*(coeff[7]*coeff[2]-coeff[1]);
+ inverse[2]=determinant*(coeff[1]*coeff[5]-coeff[4]*coeff[2]);
+ inverse[3]=determinant*(coeff[6]*coeff[5]-coeff[3]);
+ inverse[4]=determinant*(coeff[0]-coeff[6]*coeff[2]);
+ inverse[5]=determinant*(coeff[3]*coeff[2]-coeff[0]*coeff[5]);
+ inverse[6]=determinant*(coeff[3]*coeff[7]-coeff[6]*coeff[4]);
+ inverse[7]=determinant*(coeff[6]*coeff[1]-coeff[0]*coeff[7]);
+}
+
+static inline double MagickRound(double x)
+{
+ /* round the fraction to nearest integer */
+ if (x >= 0.0)
+ return((double) ((long) (x+0.5)));
+ return((double) ((long) (x-0.5)));
+}
+
+static unsigned long poly_number_terms(double order)
+{
+ /* Return the number of terms for a 2d polynomial
+ Order must either be an integer, or 1.5 to produce
+ the 2 number_valuesal polyminal function...
+ affine 1 (3) u = c0 + c1*x + c2*y
+ bilinear 1.5 (4) u = '' + c3*x*y
+ quadratic 2 (6) u = '' + c4*x*x + c5*y*y
+ cubic 3 (10) u = '' + c6*x^3 + c7*x*x*y + c8*x*y*y + c9*y^3
+ quartic 4 (15) u = '' + c10*x^4 + ... + c14*y^4
+ quintic 5 (21) u = '' + c15*x^5 + ... + c20*y^5
+ number in parenthesis minimum number of points needed.
+ Anything beyond quintic, has not been implemented until
+ a more automated way of determined terms is found.
+ */
+ if ( order < 1 || order > 5 ||
+ ( order != floor(order) && (order-1.5) > MagickEpsilon) )
+ return 0; /* invalid polynomial order */
+ return((unsigned long) floor((order+1)*(order+2)/2));
+}
+
+static double poly_basis_fn(long n, double x, double y)
+{
+ /* return the result for this polynomial term */
+ switch(n) {
+ case 0: return( 1.0 ); /* constant */
+ case 1: return( x );
+ case 2: return( y ); /* affine order = 1 terms = 3 */
+ case 3: return( x*y ); /* bilinear order = 1.5 terms = 4 */
+ case 4: return( x*x );
+ case 5: return( y*y ); /* quadratic order = 2 terms = 6 */
+ case 6: return( x*x*x );
+ case 7: return( x*x*y );
+ case 8: return( x*y*y );
+ case 9: return( y*y*y ); /* cubic order = 3 terms = 10 */
+ case 10: return( x*x*x*x );
+ case 11: return( x*x*x*y );
+ case 12: return( x*x*y*y );
+ case 13: return( x*y*y*y );
+ case 14: return( y*y*y*y ); /* quartic order = 4 terms = 15 */
+ case 15: return( x*x*x*x*x );
+ case 16: return( x*x*x*x*y );
+ case 17: return( x*x*x*y*y );
+ case 18: return( x*x*y*y*y );
+ case 19: return( x*y*y*y*y );
+ case 20: return( y*y*y*y*y ); /* quintic order = 5 terms = 21 */
+ }
+ return( 0 ); /* should never happen */
+}
+static const char *poly_basis_str(long n)
+{
+ /* return the result for this polynomial term */
+ switch(n) {
+ case 0: return(""); /* constant */
+ case 1: return("*ii");
+ case 2: return("*jj"); /* affiine order = 1 terms = 3 */
+ case 3: return("*ii*jj"); /* biiliinear order = 1.5 terms = 4 */
+ case 4: return("*ii*ii");
+ case 5: return("*jj*jj"); /* quadratiic order = 2 terms = 6 */
+ case 6: return("*ii*ii*ii");
+ case 7: return("*ii*ii*jj");
+ case 8: return("*ii*jj*jj");
+ case 9: return("*jj*jj*jj"); /* cubiic order = 3 terms = 10 */
+ case 10: return("*ii*ii*ii*ii");
+ case 11: return("*ii*ii*ii*jj");
+ case 12: return("*ii*ii*jj*jj");
+ case 13: return("*ii*jj*jj*jj");
+ case 14: return("*jj*jj*jj*jj"); /* quartiic order = 4 terms = 15 */
+ case 15: return("*ii*ii*ii*ii*ii");
+ case 16: return("*ii*ii*ii*ii*jj");
+ case 17: return("*ii*ii*ii*jj*jj");
+ case 18: return("*ii*ii*jj*jj*jj");
+ case 19: return("*ii*jj*jj*jj*jj");
+ case 20: return("*jj*jj*jj*jj*jj"); /* quiintiic order = 5 terms = 21 */
+ }
+ return( "UNKNOWN" ); /* should never happen */
+}
+static double poly_basis_dx(long n, double x, double y)
+{
+ /* polynomial term for x derivative */
+ switch(n) {
+ case 0: return( 0.0 ); /* constant */
+ case 1: return( 1.0 );
+ case 2: return( 0.0 ); /* affine order = 1 terms = 3 */
+ case 3: return( y ); /* bilinear order = 1.5 terms = 4 */
+ case 4: return( x );
+ case 5: return( 0.0 ); /* quadratic order = 2 terms = 6 */
+ case 6: return( x*x );
+ case 7: return( x*y );
+ case 8: return( y*y );
+ case 9: return( 0.0 ); /* cubic order = 3 terms = 10 */
+ case 10: return( x*x*x );
+ case 11: return( x*x*y );
+ case 12: return( x*y*y );
+ case 13: return( y*y*y );
+ case 14: return( 0.0 ); /* quartic order = 4 terms = 15 */
+ case 15: return( x*x*x*x );
+ case 16: return( x*x*x*y );
+ case 17: return( x*x*y*y );
+ case 18: return( x*y*y*y );
+ case 19: return( y*y*y*y );
+ case 20: return( 0.0 ); /* quintic order = 5 terms = 21 */
+ }
+ return( 0.0 ); /* should never happen */
+}
+static double poly_basis_dy(long n, double x, double y)
+{
+ /* polynomial term for y derivative */
+ switch(n) {
+ case 0: return( 0.0 ); /* constant */
+ case 1: return( 0.0 );
+ case 2: return( 1.0 ); /* affine order = 1 terms = 3 */
+ case 3: return( x ); /* bilinear order = 1.5 terms = 4 */
+ case 4: return( 0.0 );
+ case 5: return( y ); /* quadratic order = 2 terms = 6 */
+ default: return( poly_basis_dx(n-1,x,y) ); /* weird but true */
+ }
+ /* NOTE: the only reason that last is not true for 'quadtratic'
+ is due to the re-arrangement of terms to allow for 'bilinear'
+ */
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e n e r a t e C o e f f i c i e n t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GenerateCoefficients() takes user provided input arguments and generates
+% the coefficients, needed to apply the specific distortion for either
+% distorting images (generally using control points) or generating a color
+% gradient from sparsely separated color points.
+%
+% The format of the GenerateCoefficients() method is:
+%
+% Image *GenerateCoefficients(const Image *image,DistortImageMethod method,
+% const unsigned long number_arguments,const double *arguments,
+% unsigned long number_values, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image to be distorted.
+%
+% o method: the method of image distortion/ sparse gradient
+%
+% o number_arguments: the number of arguments given.
+%
+% o arguments: the arguments for this distortion method.
+%
+% o number_values: the style and format of given control points, (caller type)
+% 0: 2 dimensional mapping of control points (Distort)
+% Format: u,v,x,y where u,v is the 'source' of the
+% the color to be plotted, for DistortImage()
+% N: Interpolation of control points with N values (usally r,g,b)
+% Format: x,y,r,g,b mapping x,y to color values r,g,b
+% IN future, varible number of values may be given (1 to N)
+%
+% o exception: return any errors or warnings in this structure
+%
+% Note that the returned array of double values must be freed by the
+% calling method using RelinquishMagickMemory(). This however may change in
+% the future to require a more 'method' specific method.
+%
+% Because of this this method should not be classed as stable or used
+% outside other MagickCore library methods.
+*/
+
+static double *GenerateCoefficients(const Image *image,
+ DistortImageMethod *method,const unsigned long number_arguments,
+ const double *arguments,unsigned long number_values,ExceptionInfo *exception)
+{
+ double
+ *coeff;
+
+ register unsigned long
+ i;
+
+ unsigned long
+ number_coeff, /* number of coefficients to return (array size) */
+ cp_size, /* number floating point numbers per control point */
+ cp_x,cp_y, /* the x,y indexes for control point */
+ cp_values; /* index of values for this control point */
+ /* number_values Number of values given per control point */
+
+ if ( number_values == 0 ) {
+ /* Image distortion using control points (or other distortion)
+ That is generate a mapping so that x,y->u,v given u,v,x,y
+ */
+ number_values = 2; /* special case: two values of u,v */
+ cp_values = 0; /* the values i,j are BEFORE the destination CP x,y */
+ cp_x = 2; /* location of x,y in input control values */
+ cp_y = 3;
+ /* NOTE: cp_values, also used for later 'reverse map distort' tests */
+ }
+ else {
+ cp_x = 0; /* location of x,y in input control values */
+ cp_y = 1;
+ cp_values = 2; /* and the other values are after x,y */
+ /* Typically in this case the values are R,G,B color values */
+ }
+ cp_size = number_values+2; /* each CP defintion involves this many numbers */
+
+ /* If not enough control point pairs are found for specific distortions
+ fall back to Affine distortion (allowing 0 to 3 point pairs)
+ */
+ if ( number_arguments < 4*cp_size &&
+ ( *method == BilinearForwardDistortion
+ || *method == BilinearReverseDistortion
+ || *method == PerspectiveDistortion
+ ) )
+ *method = AffineDistortion;
+
+ number_coeff=0;
+ switch (*method) {
+ case AffineDistortion:
+ /* also BarycentricColorInterpolate: */
+ number_coeff=3*number_values;
+ break;
+ case PolynomialDistortion:
+ /* number of coefficents depend on the given polynomal 'order' */
+ if ( number_arguments <= 1 && (number_arguments-1)%cp_size != 0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","Polynomial",
+ "Invalid number of args: order [CPs]...");
+ return((double *) NULL);
+ }
+ i = poly_number_terms(arguments[0]);
+ number_coeff = 2 + i*number_values;
+ if ( i == 0 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","Polynomial",
+ "Invalid order, should be 1 to 5, or 1.5");
+ return((double *) NULL);
+ }
+ if ( number_arguments < 1+i*cp_size ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : 'require at least %ld CPs'",
+ "Polynomial", i);
+ return((double *) NULL);
+ }
+ break;
+ case BilinearReverseDistortion:
+ number_coeff=4*number_values;
+ break;
+ /*
+ The rest are constants as they are only used for image distorts
+ */
+ case BilinearForwardDistortion:
+ number_coeff=11; /* 2*4 coeff plus 3 constants */
+ cp_x = 0; /* Reverse src/destination for forward mapping */
+ cp_y = 1;
+ cp_values = 2;
+ break;
+ case ShepardsDistortion:
+ case VoronoiColorInterpolate:
+ number_coeff=1; /* may not be used, but provide some type of return */
+ break;
+ case ArcDistortion:
+ number_coeff=5;
+ break;
+ case ScaleRotateTranslateDistortion:
+ case AffineProjectionDistortion:
+ number_coeff=6;
+ break;
+ case PolarDistortion:
+ case DePolarDistortion:
+ number_coeff=8;
+ number_coeff=8;
+ break;
+ case PerspectiveDistortion:
+ case PerspectiveProjectionDistortion:
+ number_coeff=9;
+ break;
+ case BarrelDistortion:
+ case BarrelInverseDistortion:
+ number_coeff=10;
+ break;
+ case UndefinedDistortion:
+ default:
+ assert(! "Unknown Method Given"); /* just fail assertion */
+ }
+
+ /* allocate the array of coefficients needed */
+ coeff = (double *) AcquireQuantumMemory(number_coeff,sizeof(*coeff));
+ if (coeff == (double *) NULL) {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "GenerateCoefficients");
+ return((double *) NULL);
+ }
+
+ /* zero out coeffiecents array */
+ for (i=0; i < number_coeff; i++)
+ coeff[i] = 0.0;
+
+ switch (*method)
+ {
+ case AffineDistortion:
+ {
+ /* Affine Distortion
+ v = c0*x + c1*y + c2
+ for each 'value' given
+
+ Input Arguments are sets of control points...
+ For Distort Images u,v, x,y ...
+ For Sparse Gradients x,y, r,g,b ...
+ */
+ if ( number_arguments%cp_size != 0 ||
+ number_arguments < cp_size ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : 'require at least %ld CPs'",
+ "Affine", 1L);
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ return((double *) NULL);
+ }
+ /* handle special cases of not enough arguments */
+ if ( number_arguments == cp_size ) {
+ /* Only 1 CP Set Given */
+ if ( cp_values == 0 ) {
+ /* image distortion - translate the image */
+ coeff[0] = 1.0;
+ coeff[2] = arguments[0] - arguments[2];
+ coeff[4] = 1.0;
+ coeff[5] = arguments[1] - arguments[3];
+ }
+ else {
+ /* sparse gradient - use the values directly */
+ for (i=0; i<number_values; i++)
+ coeff[i*3+2] = arguments[cp_values+i];
+ }
+ }
+ else {
+ /* 2 or more points (usally 3) given.
+ Solve a least squares simultanious equation for coefficients.
+ */
+ double
+ **matrix,
+ **vectors,
+ terms[3];
+
+ MagickBooleanType
+ status;
+
+ /* create matrix, and a fake vectors matrix */
+ matrix = AcquireMagickMatrix(3UL,3UL);
+ vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
+ if (matrix == (double **) NULL || vectors == (double **) NULL)
+ {
+ matrix = RelinquishMagickMatrix(matrix, 3UL);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortCoefficients");
+ return((double *) NULL);
+ }
+ /* fake a number_values x3 vectors matrix from coefficients array */
+ for (i=0; i < number_values; i++)
+ vectors[i] = &(coeff[i*3]);
+ /* Add given control point pairs for least squares solving */
+ for (i=0; i < number_arguments; i+=cp_size) {
+ terms[0] = arguments[i+cp_x]; /* x */
+ terms[1] = arguments[i+cp_y]; /* y */
+ terms[2] = 1; /* 1 */
+ LeastSquaresAddTerms(matrix,vectors,terms,
+ &(arguments[i+cp_values]),3UL,number_values);
+ }
+ if ( number_arguments == 2*cp_size ) {
+ /* Only two pairs were given, but we need 3 to solve the affine.
+ Fake extra coordinates by rotating p1 around p0 by 90 degrees.
+ x2 = x0 - (y1-y0) y2 = y0 + (x1-x0)
+ */
+ terms[0] = arguments[cp_x]
+ - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
+ terms[1] = arguments[cp_y] +
+ + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
+ terms[2] = 1; /* 1 */
+ if ( cp_values == 0 ) {
+ /* Image Distortion - rotate the u,v coordients too */
+ double
+ uv2[2];
+ uv2[0] = arguments[0] - arguments[5] + arguments[1]; /* u2 */
+ uv2[1] = arguments[1] + arguments[4] - arguments[0]; /* v2 */
+ LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
+ }
+ else {
+ /* Sparse Gradient - use values of p0 for linear gradient */
+ LeastSquaresAddTerms(matrix,vectors,terms,
+ &(arguments[cp_values]),3UL,number_values);
+ }
+ }
+ /* Solve for LeastSquares Coefficients */
+ status=GaussJordanElimination(matrix,vectors,3UL,number_values);
+ matrix = RelinquishMagickMatrix(matrix, 3UL);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ if ( status == MagickFalse ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","Affine",
+ "Unsolvable Matrix");
+ return((double *) NULL);
+ }
+ }
+ return(coeff);
+ }
+ case AffineProjectionDistortion:
+ {
+ /*
+ Arguments: Affine Matrix (forward mapping)
+ Arguments sx, rx, ry, sy, tx, ty
+ Where u = sx*x + ry*y + tx
+ v = rx*x + sy*y + ty
+
+ Returns coefficients (in there inverse form) ordered as...
+ sx ry tx rx sy ty
+
+ AffineProjection Distortion Notes...
+ + Will only work with a 2 number_values for Image Distortion
+ + Can not be used for generating a sparse gradient (interpolation)
+ */
+ double inverse[8];
+ if (number_arguments != 6) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","AffineProjection",
+ "Needs 6 coeff values");
+ return((double *) NULL);
+ }
+ /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinate = 0, no inverse) */
+ for(i=0; i<6UL; i++ )
+ inverse[i] = arguments[i];
+ AffineArgsToCoefficients(inverse); /* map into coefficents */
+ InvertAffineCoefficients(inverse, coeff); /* invert */
+ *method = AffineDistortion;
+
+ return(coeff);
+ }
+ case ScaleRotateTranslateDistortion:
+ {
+ /* Scale, Rotate and Translate Distortion
+ An alturnative Affine Distortion
+ Argument options, by number of arguments given:
+ 7: x,y, sx,sy, a, nx,ny
+ 6: x,y, s, a, nx,ny
+ 5: x,y, sx,sy, a
+ 4: x,y, s, a
+ 3: x,y, a
+ 2: s, a
+ 1: a
+ Where actions are (in order of application)
+ x,y 'center' of transforms (default = image center)
+ sx,sy scale image by this amount (default = 1)
+ a angle of rotation (argument required)
+ nx,ny move 'center' here (default = no movement)
+ And convert to affine mapping coefficients
+
+ ScaleRotateTranslate Distortion Notes...
+ + Does not use a set of CPs in any normal way
+ + Will only work with a 2 number_valuesal Image Distortion
+ + Can not be used for generating a sparse gradient (interpolation)
+ */
+ double
+ cosine, sine,
+ x,y,sx,sy,a,nx,ny;
+
+ /* set default center, and default scale */
+ x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
+ y = ny = (double)(image->rows)/2.0 + (double)image->page.y;
+ sx = sy = 1.0;
+ switch ( number_arguments ) {
+ case 0:
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
+ "Needs at least 1 argument");
+ return((double *) NULL);
+ case 1:
+ a = arguments[0];
+ break;
+ case 2:
+ sx = sy = arguments[0];
+ a = arguments[1];
+ break;
+ default:
+ x = nx = arguments[0];
+ y = ny = arguments[1];
+ switch ( number_arguments ) {
+ case 3:
+ a = arguments[2];
+ break;
+ case 4:
+ sx = sy = arguments[2];
+ a = arguments[3];
+ break;
+ case 5:
+ sx = arguments[2];
+ sy = arguments[3];
+ a = arguments[4];
+ break;
+ case 6:
+ sx = sy = arguments[2];
+ a = arguments[3];
+ nx = arguments[4];
+ ny = arguments[5];
+ break;
+ case 7:
+ sx = arguments[2];
+ sy = arguments[3];
+ a = arguments[4];
+ nx = arguments[5];
+ ny = arguments[6];
+ break;
+ default:
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
+ "Too Many Arguments (7 or less)");
+ return((double *) NULL);
+ }
+ break;
+ }
+ /* Trap if sx or sy == 0 -- image is scaled out of existance! */
+ if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'", "ScaleTranslateRotate",
+ "Zero Scale Given");
+ return((double *) NULL);
+ }
+ /* Save the given arguments as an affine distortion */
+ a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);
+
+ *method = AffineDistortion;
+ coeff[0]=cosine/sx;
+ coeff[1]=sine/sx;
+ coeff[2]=x-nx*coeff[0]-ny*coeff[1];
+ coeff[3]=(-sine)/sy;
+ coeff[4]=cosine/sy;
+ coeff[5]=y-nx*coeff[3]-ny*coeff[4];
+ return(coeff);
+ }
+ case PerspectiveDistortion:
+ { /*
+ Perspective Distortion (a ratio of affine distortions)
+
+ p(x,y) c0*x + c1*y + c2
+ u = ------ = ------------------
+ r(x,y) c6*x + c7*y + 1
+
+ q(x,y) c3*x + c4*y + c5
+ v = ------ = ------------------
+ r(x,y) c6*x + c7*y + 1
+
+ c8 = Sign of 'r', or the denominator affine, for the actual image.
+ This determines what part of the distorted image is 'ground'
+ side of the horizon, the other part is 'sky' or invalid.
+ Valid values are +1.0 or -1.0 only.
+
+ Input Arguments are sets of control points...
+ For Distort Images u,v, x,y ...
+ For Sparse Gradients x,y, r,g,b ...
+
+ Perspective Distortion Notes...
+ + Can be thought of as ratio of 3 affine transformations
+ + Not separatable: r() or c6 and c7 are used by both equations
+ + All 8 coefficients must be determined simultaniously
+ + Will only work with a 2 number_valuesal Image Distortion
+ + Can not be used for generating a sparse gradient (interpolation)
+ + It is not linear, but is simple to generate an inverse
+ + All lines within an image remain lines.
+ + but distances between points may vary.
+ */
+ double
+ **matrix,
+ *vectors[1],
+ terms[8];
+
+ unsigned long
+ cp_u = cp_values,
+ cp_v = cp_values+1;
+
+ MagickBooleanType
+ status;
+
+ /* fake 1x8 vectors matrix directly using the coefficients array */
+ vectors[0] = &(coeff[0]);
+ /* 8x8 least-squares matrix (zeroed) */
+ matrix = AcquireMagickMatrix(8UL,8UL);
+ if (matrix == (double **) NULL) {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortCoefficients");
+ return((double *) NULL);
+ }
+ /* Add control points for least squares solving */
+ for (i=0; i < number_arguments; i+=4) {
+ terms[0]=arguments[i+cp_x]; /* c0*x */
+ terms[1]=arguments[i+cp_y]; /* c1*y */
+ terms[2]=1.0; /* c2*1 */
+ terms[3]=0.0;
+ terms[4]=0.0;
+ terms[5]=0.0;
+ terms[6]=-terms[0]*arguments[i+cp_u]; /* 1/(c6*x) */
+ terms[7]=-terms[1]*arguments[i+cp_u]; /* 1/(c7*y) */
+ LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
+ 8UL,1UL);
+
+ terms[0]=0.0;
+ terms[1]=0.0;
+ terms[2]=0.0;
+ terms[3]=arguments[i+cp_x]; /* c3*x */
+ terms[4]=arguments[i+cp_y]; /* c4*y */
+ terms[5]=1.0; /* c5*1 */
+ terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
+ terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
+ LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
+ 8UL,1UL);
+ }
+ /* Solve for LeastSquares Coefficients */
+ status=GaussJordanElimination(matrix,vectors,8UL,1UL);
+ matrix = RelinquishMagickMatrix(matrix, 8UL);
+ if ( status == MagickFalse ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","Perspective",
+ "Unsolvable Matrix");
+ return((double *) NULL);
+ }
+ /*
+ Calculate 9'th coefficient! The ground-sky determination.
+ What is sign of the 'ground' in r() denominator affine function?
+ Just use any valid image coordinate (first control point) in
+ destination for determination of what part of view is 'ground'.
+ */
+ coeff[8] = coeff[6]*arguments[cp_x]
+ + coeff[7]*arguments[cp_y] + 1.0;
+ coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
+
+ return(coeff);
+ }
+ case PerspectiveProjectionDistortion:
+ {
+ /*
+ Arguments: Perspective Coefficents (forward mapping)
+ */
+ if (number_arguments != 8) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","PerspectiveProjection",
+ "Needs 8 coefficient values");
+ return((double *) NULL);
+ }
+ /* FUTURE: trap test c0*c4-c3*c1 == 0 (determinate = 0, no inverse) */
+ InvertPerspectiveCoefficients(arguments, coeff);
+ /*
+ Calculate 9'th coefficient! The ground-sky determination.
+ What is sign of the 'ground' in r() denominator affine function?
+ Just use any valid image cocodinate in destination for determination.
+ For a forward mapped perspective the images 0,0 coord will map to
+ c2,c5 in the distorted image, so set the sign of denominator of that.
+ */
+ coeff[8] = coeff[6]*arguments[2]
+ + coeff[7]*arguments[5] + 1.0;
+ coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
+ *method = PerspectiveDistortion;
+
+ return(coeff);
+ }
+ case BilinearForwardDistortion:
+ case BilinearReverseDistortion:
+ {
+ /* Bilinear Distortion
+ v = c0*x + c1*y + c2*x*y + c3;
+ for each 'value' given
+
+ Input Arguments are sets of control points...
+ For Distort Images u,v, x,y ...
+ For Sparse Gradients x,y, r,g,b ...
+
+ */
+ double
+ **matrix,
+ **vectors,
+ terms[4];
+
+ MagickBooleanType
+ status;
+
+ /* create matrix, and a fake vectors matrix */
+ matrix = AcquireMagickMatrix(4UL,4UL);
+ vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
+ if (matrix == (double **) NULL || vectors == (double **) NULL)
+ {
+ matrix = RelinquishMagickMatrix(matrix, 4UL);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortCoefficients");
+ return((double *) NULL);
+ }
+ /* fake a number_values x4 vectors matrix from coefficients array */
+ for (i=0; i < number_values; i++)
+ vectors[i] = &(coeff[i*4]);
+ /* Add given control point pairs for least squares solving */
+ for (i=0; i < number_arguments; i+=cp_size) {
+ terms[0] = arguments[i+cp_x]; /* x */
+ terms[1] = arguments[i+cp_y]; /* y */
+ terms[2] = terms[0]*terms[1]; /* x*y */
+ terms[3] = 1; /* 1 */
+ LeastSquaresAddTerms(matrix,vectors,terms,
+ &(arguments[i+cp_values]),4UL,number_values);
+ }
+ /* Solve for LeastSquares Coefficients */
+ status=GaussJordanElimination(matrix,vectors,4UL,number_values);
+ matrix = RelinquishMagickMatrix(matrix, 4UL);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ if ( status == MagickFalse ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'",
+ *method == BilinearForwardDistortion ?
+ "BilinearForward" : "BilinearReverse",
+ "Unsolvable Matrix");
+ return((double *) NULL);
+ }
+ if ( *method == BilinearForwardDistortion ) {
+ /* Bilinear Forward Mapped Distortion
+
+ The above least-squares solved for coefficents but in the forward
+ direction, due to changes to indexing constants.
+
+ i = c0*x + c1*y + c2*x*y + c3;
+ j = c4*x + c5*y + c6*x*y + c7;
+
+ where u,v are in the destination image, NOT the source.
+
+ Reverse mapping however request the reverse of these functions.
+ This requires a full page of algbra to work out the reversed
+ mapping formula, but resolves down to the following...
+
+ a = c2*c5-c1*c6;
+ c8 = c0*c5-c1*c4;
+ c9 = 4*a;
+ c10 = 1/(2*a);
+
+ i = i - c3; j = j - c7;
+ b = c6*i - c2*j + c8;
+ r = b*b - 2*c9*(c4*ii - c0*jj);
+
+ y = ( -b + sqrt(r) ) * c10;
+ x = ( i - c1*y) / ( c1 - c2*y );
+
+ NB: if 'r' is negative there is no solution!
+ NB: the sign of the sqrt() should be negative if image becomes
+ flipped or flopped.
+
+ For details see Anthony Thyssen <[email protected]>
+
+ constants needed for forward mapped bilinear...
+ */
+ double a = coeff[2]*coeff[5] - coeff[1]*coeff[6];
+ coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
+ coeff[9] = 4*a;
+ coeff[10] = 1/(2*a);
+ }
+ return(coeff);
+ }
+ case PolynomialDistortion:
+ {
+ /* Polynomial Distortion
+
+ First two coefficents are used to hole global polynomal information
+ c0 = Order of the polynimial being created
+ c1 = number_of_terms in one polynomial equation
+
+ Rest of the coefficients map to the equations....
+ v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
+ for each 'value' (number_values of them) given.
+ As such total coefficients = 2 + number_terms * number_values
+
+ Input Arguments are sets of control points...
+ For Distort Images order [u,v, x,y] ...
+ For Sparse Gradients order [x,y, r,g,b] ...
+
+ Polynomial Distortion Notes...
+ + UNDER DEVELOPMENT -- Do not expect this to remain as is.
+ + Currently polynomial is a reversed mapped distortion.
+ + Should be used to generate a 'grid' of bilinear distortions
+ so that it will be 'forward' mapped.
+ + Order 1.5 is fudged to map into a bilinear distortion.
+ */
+ double
+ **matrix,
+ **vectors,
+ *terms;
+
+ unsigned long
+ nterms; /* number of polynomial terms per number_values */
+
+ register long
+ j;
+
+ MagickBooleanType
+ status;
+
+ /* first two coefficients hold polynomial order information */
+ coeff[0] = arguments[0];
+ coeff[1] = (double) poly_number_terms(arguments[0]);
+ nterms = (unsigned long) coeff[1];
+
+ /* create matrix, a fake vectors matrix, and least sqs terms */
+ matrix = AcquireMagickMatrix(nterms,nterms);
+ vectors = (double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
+ terms = (double *) AcquireQuantumMemory(nterms, sizeof(*terms));
+ if (matrix == (double **) NULL ||
+ vectors == (double **) NULL ||
+ terms == (double *) NULL )
+ {
+ matrix = RelinquishMagickMatrix(matrix, nterms);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ terms = (double *) RelinquishMagickMemory(terms);
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortCoefficients");
+ return((double *) NULL);
+ }
+ /* fake a number_values x3 vectors matrix from coefficients array */
+ for (i=0; i < number_values; i++)
+ vectors[i] = &(coeff[2+i*nterms]);
+ /* Add given control point pairs for least squares solving */
+ for (i=0; i < number_arguments; i+=cp_size) {
+ for (j=0; j < (long) nterms; j++)
+ terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
+ LeastSquaresAddTerms(matrix,vectors,terms,
+ &(arguments[i+cp_values]),nterms,number_values);
+ }
+ terms = (double *) RelinquishMagickMemory(terms);
+ /* Solve for LeastSquares Coefficients */
+ status=GaussJordanElimination(matrix,vectors,nterms,number_values);
+ matrix = RelinquishMagickMatrix(matrix, nterms);
+ vectors = (double **) RelinquishMagickMemory(vectors);
+ if ( status == MagickFalse ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","Polynomial",
+ "Unsolvable Matrix");
+ return((double *) NULL);
+ }
+ return(coeff);
+ }
+ case ArcDistortion:
+ {
+ /* Arc Distortion
+ Args: arc_width rotate top_edge_radius bottom_edge_radius
+ All but first argument are optional
+ arc_width The angle over which to arc the image side-to-side
+ rotate Angle to rotate image from vertical center
+ top_radius Set top edge of source image at this radius
+ bottom_radius Set bootom edge to this radius (radial scaling)
+
+ By default, if the radii arguments are nor provided the image radius
+ is calculated so the horizontal center-line is fits the given arc
+ without scaling.
+
+ The output image size is ALWAYS adjusted to contain the whole image,
+ and an offset is given to position image relative to the 0,0 point of
+ the origin, allowing users to use relative positioning onto larger
+ background (via -flatten).
+
+ The arguments are converted to these coefficients
+ c0: angle for center of source image
+ c1: angle scale for mapping to source image
+ c2: radius for top of source image
+ c3: radius scale for mapping source image
+ c4: centerline of arc within source image
+
+ Note the coefficients use a center angle, so asymptotic join is
+ furthest from both sides of the source image. This also means that
+ for arc angles greater than 360 the sides of the image will be
+ trimmed equally.
+
+ Arc Distortion Notes...
+ + Does not use a set of CPs
+ + Will only work with Image Distortion
+ + Can not be used for generating a sparse gradient (interpolation)
+ */
+ if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'", "Arc",
+ "Arc Angle Too Small");
+ return((double *) NULL);
+ }
+ if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'", "Arc",
+ "Outer Radius Too Small");
+ return((double *) NULL);
+ }
+ coeff[0] = -MagickPI2; /* -90, place at top! */
+ if ( number_arguments >= 1 )
+ coeff[1] = DegreesToRadians(arguments[0]);
+ else
+ coeff[1] = MagickPI2; /* zero arguments - center is at top */
+ if ( number_arguments >= 2 )
+ coeff[0] += DegreesToRadians(arguments[1]);
+ coeff[0] /= Magick2PI; /* normalize radians */
+ coeff[0] -= MagickRound(coeff[0]);
+ coeff[0] *= Magick2PI; /* de-normalize back to radians */
+ coeff[3] = (double)image->rows-1;
+ coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
+ if ( number_arguments >= 3 ) {
+ if ( number_arguments >= 4 )
+ coeff[3] = arguments[2] - arguments[3];
+ else
+ coeff[3] *= arguments[2]/coeff[2];
+ coeff[2] = arguments[2];
+ }
+ coeff[4] = ((double)image->columns-1.0)/2.0;
+
+ return(coeff);
+ }
+ case PolarDistortion:
+ case DePolarDistortion:
+ {
+ /* (De)Polar Distortion (same set of arguments)
+ Args: Rmax, Rmin, Xcenter,Ycenter, Afrom,Ato
+ DePolar can also have the extra arguments of Width, Height
+
+ Coefficients 0 to 5 is the sanatized version first 6 input args
+ Coefficient 6 is the angle to coord ratio and visa-versa
+ Coefficient 7 is the radius to coord ratio and visa-versa
+
+ WARNING: It is posible for Radius max<min and/or Angle from>to
+ */
+ if ( number_arguments == 3
+ || ( number_arguments > 6 && *method == PolarDistortion )
+ || number_arguments > 8 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : number of arguments",
+ *method == PolarDistortion ? "Polar" : "DePolar");
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ return((double *) NULL);
+ }
+ /* Rmax - if 0 calculate appropriate value */
+ if ( number_arguments >= 1 )
+ coeff[0] = arguments[0];
+ else
+ coeff[0] = 0.0;
+ /* Rmin - usally 0 */
+ coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
+ /* Center X,Y */
+ if ( number_arguments >= 4 ) {
+ coeff[2] = arguments[2];
+ coeff[3] = arguments[3];
+ }
+ else { /* center of actual image */
+ coeff[2] = (double)(image->columns)/2.0+image->page.x;
+ coeff[3] = (double)(image->rows)/2.0+image->page.y;
+ }
+ /* Angle from,to - about polar center 0 is downward */
+ coeff[4] = -MagickPI;
+ if ( number_arguments >= 5 )
+ coeff[4] = DegreesToRadians(arguments[4]);
+ coeff[5] = coeff[4];
+ if ( number_arguments >= 6 )
+ coeff[5] = DegreesToRadians(arguments[5]);
+ if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
+ coeff[5] += Magick2PI; /* same angle is a full circle */
+ /* if radius 0 or negative, its a special value... */
+ if ( coeff[0] < MagickEpsilon ) {
+ /* Use closest edge if radius == 0 */
+ if ( fabs(coeff[0]) < MagickEpsilon ) {
+ coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
+ fabs(coeff[3]-image->page.y));
+ coeff[0]=MagickMin(coeff[0],
+ fabs(coeff[2]-image->page.x-image->columns));
+ coeff[0]=MagickMin(coeff[0],
+ fabs(coeff[3]-image->page.y-image->rows));
+ }
+ /* furthest diagonal if radius == -1 */
+ if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
+ double rx,ry;
+ rx = coeff[2]-image->page.x;
+ ry = coeff[3]-image->page.y;
+ coeff[0] = rx*rx+ry*ry;
+ ry = coeff[3]-image->page.y-image->rows;
+ coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
+ rx = coeff[2]-image->page.x-image->columns;
+ coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
+ ry = coeff[3]-image->page.y;
+ coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
+ coeff[0] = sqrt(coeff[0]);
+ }
+ }
+ /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
+ if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
+ || (coeff[0]-coeff[1]) < MagickEpsilon ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : Invalid Radius",
+ *method == PolarDistortion ? "Polar" : "DePolar");
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ return((double *) NULL);
+ }
+ /* converstion ratios */
+ if ( *method == PolarDistortion ) {
+ coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
+ coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
+ }
+ else { /* *method == DePolarDistortion */
+ coeff[6]=(coeff[5]-coeff[4])/image->columns;
+ coeff[7]=(coeff[0]-coeff[1])/image->rows;
+ }
+ return(coeff);
+ }
+ case BarrelDistortion:
+ case BarrelInverseDistortion:
+ {
+ /* Barrel Distortion
+ Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
+ BarrelInv Distortion
+ Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)
+
+ Where Rd is the normalized radius from corner to middle of image
+ Input Arguments are one of the following forms...
+ A,B,C
+ A,B,C,D
+ A,B,C X,Y
+ A,B,C,D X,Y
+ Ax,Bx,Cx,Dx Ay,By,Cy,Dy
+ Ax,Bx,Cx,Dx Ay,By,Cy,Dy X,Y
+
+ Returns 10 coefficent values, which are de-normalized (pixel scale)
+ Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, Xc, Yc
+ */
+ /* Radius de-normalization scaling factor */
+ double
+ rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);
+
+ if ( number_arguments != 4 && number_arguments != 6 &&
+ number_arguments != 8 && number_arguments != 10 ) {
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : '%s'", "Barrel(Inv)",
+ "number of arguments" );
+ return((double *) NULL);
+ }
+ /* A,B,C,D coefficients */
+ coeff[0] = arguments[0];
+ coeff[1] = arguments[1];
+ coeff[2] = arguments[2];
+ if ( number_arguments == 3 || number_arguments == 5 )
+ coeff[3] = 1 - arguments[0] - arguments[1] - arguments[2];
+ else
+ coeff[3] = arguments[3];
+ /* de-normalize the X coefficients */
+ coeff[0] *= pow(rscale,3.0);
+ coeff[1] *= rscale*rscale;
+ coeff[2] *= rscale;
+ /* Y coefficients: as given OR as X coefficients */
+ if ( number_arguments >= 8 ) {
+ coeff[4] = arguments[4] * pow(rscale,3.0);
+ coeff[5] = arguments[5] * rscale*rscale;
+ coeff[6] = arguments[6] * rscale;
+ coeff[7] = arguments[7];
+ }
+ else {
+ coeff[4] = coeff[0];
+ coeff[5] = coeff[1];
+ coeff[6] = coeff[2];
+ coeff[7] = coeff[3];
+ }
+ /* X,Y Center of Distortion */
+ coeff[8] = ((double)image->columns-1)/2.0 + image->page.x;
+ coeff[9] = ((double)image->rows-1)/2.0 + image->page.y;
+ if ( number_arguments == 5 ) {
+ coeff[8] = arguments[3];
+ coeff[9] = arguments[4];
+ }
+ if ( number_arguments == 6 ) {
+ coeff[8] = arguments[4];
+ coeff[9] = arguments[5];
+ }
+ if ( number_arguments == 10 ) {
+ coeff[8] = arguments[8];
+ coeff[9] = arguments[9];
+ }
+ return(coeff);
+ }
+ case ShepardsDistortion:
+ case VoronoiColorInterpolate:
+ {
+ /* Shepards Distortion input arguments are the coefficents!
+ Just check the number of arguments is valid!
+ Args: u1,v1, x1,y1, ...
+ OR : u1,v1, r1,g1,c1, ...
+ */
+ if ( number_arguments%cp_size != 0 ||
+ number_arguments < cp_size ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument", "%s : 'require at least %ld CPs'",
+ "Shepards", 1UL);
+ coeff=(double *) RelinquishMagickMemory(coeff);
+ return((double *) NULL);
+ }
+ return(coeff);
+ }
+ default:
+ break;
+ }
+ /* you should never reach this point */
+ assert(! "No Method Handler"); /* just fail assertion */
+ return((double *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D i s t o r t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DistortImage() distorts an image using various distortion methods, by
+% mapping color lookups of the source image to a new destination image
+% usally of the same size as the source image, unless 'bestfit' is set to
+% true.
+%
+% If 'bestfit' is enabled, and distortion allows it, the destination image is
+% adjusted to ensure the whole source 'image' will just fit within the final
+% destination image, which will be sized and offset accordingly. Also in
+% many cases the virtual offset of the source image will be taken into
+% account in the mapping.
+%
+% If the '-verbose' control option has been set print to standard error the
+% equicelent '-fx' formula with coefficients for the function, if practical.
+%
+% The format of the DistortImage() method is:
+%
+% Image *DistortImage(const Image *image,const DistortImageMethod method,
+% const unsigned long number_arguments,const double *arguments,
+% MagickBooleanType bestfit, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image to be distorted.
+%
+% o method: the method of image distortion.
+%
+% ArcDistortion always ignores source image offset, and always
+% 'bestfit' the destination image with the top left corner offset
+% relative to the polar mapping center.
+%
+% Affine, Perspective, and Bilinear, do least squares fitting of the
+% distrotion when more than the minimum number of control point pairs
+% are provided.
+%
+% Perspective, and Bilinear, fall back to a Affine distortion when less
+% than 4 control point pairs are provided. While Affine distortions
+% let you use any number of control point pairs, that is Zero pairs is
+% a No-Op (viewport only) distortion, one pair is a translation and
+% two pairs of control points do a scale-rotate-translate, without any
+% shearing.
+%
+% o number_arguments: the number of arguments given.
+%
+% o arguments: an array of floating point arguments for this method.
+%
+% o bestfit: Attempt to 'bestfit' the size of the resulting image.
+% This also forces the resulting image to be a 'layered' virtual
+% canvas image. Can be overridden using 'distort:viewport' setting.
+%
+% o exception: return any errors or warnings in this structure
+%
+% Extra Controls from Image meta-data (artifacts)...
+%
+% o "verbose"
+% Output to stderr alternatives, internal coefficents, and FX
+% equivelents for the distortion operation (if feasible).
+% This forms an extra check of the distortion method, and allows users
+% access to the internal constants IM calculates for the distortion.
+%
+% o "distort:viewport"
+% Directly set the output image canvas area and offest to use for the
+% resulting image, rather than use the original images canvas, or a
+% calculated 'bestfit' canvas.
+%
+% o "distort:scale"
+% Scale the size of the output canvas by this amount to provide a
+% method of Zooming, and for super-sampling the results.
+%
+% Other settings that can effect results include
+%
+% o 'interpolate' For source image lookups (scale enlargements)
+%
+% o 'filter' Set filter to use for area-resampling (scale shrinking).
+% Set to 'point' to turn off and use 'interpolate' lookup
+% instead
+%
+*/
+MagickExport Image *DistortImage(const Image *image,DistortImageMethod method,
+ const unsigned long number_arguments,const double *arguments,
+ MagickBooleanType bestfit,ExceptionInfo *exception)
+{
+#define DistortImageTag "Distort/Image"
+
+ double
+ *coeff,
+ output_scaling;
+
+ Image
+ *distort_image;
+
+ RectangleInfo
+ geometry; /* geometry of the distorted space viewport */
+
+ MagickBooleanType
+ viewport_given;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ /*
+ Convert input arguments (usally as control points for reverse mapping)
+ into mapping coefficients to apply the distortion.
+
+ Note that some distortions are mapped to other distortions,
+ and as such do not require specific code after this point.
+ */
+ coeff = GenerateCoefficients(image, &method, number_arguments,
+ arguments, 0, exception);
+ if ( coeff == (double *) NULL )
+ return((Image *) NULL);
+
+ /*
+ Determine the size and offset for a 'bestfit' destination.
+ Usally the four corners of the source image is enough.
+ */
+
+ /* default output image bounds, when no 'bestfit' is requested */
+ geometry.width=image->columns;
+ geometry.height=image->rows;
+ geometry.x=0;
+ geometry.y=0;
+
+ if ( method == ArcDistortion ) {
+ /* always use the 'best fit' viewport */
+ bestfit = MagickTrue;
+ }
+
+ /* Work out the 'best fit', (required for ArcDistortion) */
+ if ( bestfit ) {
+ PointInfo
+ s,d,min,max;
+
+ s.x=s.y=min.x=max.x=min.y=max.y=0.0; /* keep compiler happy */
+
+/* defines to figure out the bounds of the distorted image */
+#define InitalBounds(p) \
+{ \
+ /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
+ min.x = max.x = p.x; \
+ min.y = max.y = p.y; \
+}
+#define ExpandBounds(p) \
+{ \
+ /* printf("%lg,%lg -> %lg,%lg\n", s.x,s.y, d.x,d.y); */ \
+ min.x = MagickMin(min.x,p.x); \
+ max.x = MagickMax(max.x,p.x); \
+ min.y = MagickMin(min.y,p.y); \
+ max.y = MagickMax(max.y,p.y); \
+}
+
+ switch (method)
+ {
+ case AffineDistortion:
+ { double inverse[6];
+ InvertAffineCoefficients(coeff, inverse);
+ s.x = (double) image->page.x;
+ s.y = (double) image->page.y;
+ d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
+ d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
+ InitalBounds(d);
+ s.x = (double) image->page.x+image->columns;
+ s.y = (double) image->page.y;
+ d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
+ d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
+ ExpandBounds(d);
+ s.x = (double) image->page.x;
+ s.y = (double) image->page.y+image->rows;
+ d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
+ d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
+ ExpandBounds(d);
+ s.x = (double) image->page.x+image->columns;
+ s.y = (double) image->page.y+image->rows;
+ d.x = inverse[0]*s.x+inverse[1]*s.y+inverse[2];
+ d.y = inverse[3]*s.x+inverse[4]*s.y+inverse[5];
+ ExpandBounds(d);
+ break;
+ }
+ case PerspectiveDistortion:
+ { double inverse[8], scale;
+ InvertPerspectiveCoefficients(coeff, inverse);
+ s.x = (double) image->page.x;
+ s.y = (double) image->page.y;
+ scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
+ scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
+ d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
+ InitalBounds(d);
+ s.x = (double) image->page.x+image->columns;
+ s.y = (double) image->page.y;
+ scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
+ scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
+ d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
+ ExpandBounds(d);
+ s.x = (double) image->page.x;
+ s.y = (double) image->page.y+image->rows;
+ scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
+ scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
+ d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
+ ExpandBounds(d);
+ s.x = (double) image->page.x+image->columns;
+ s.y = (double) image->page.y+image->rows;
+ scale=inverse[6]*s.x+inverse[7]*s.y+1.0;
+ scale=1.0/( (fabs(scale) <= MagickEpsilon) ? 1.0 : scale );
+ d.x = scale*(inverse[0]*s.x+inverse[1]*s.y+inverse[2]);
+ d.y = scale*(inverse[3]*s.x+inverse[4]*s.y+inverse[5]);
+ ExpandBounds(d);
+ break;
+ }
+ case ArcDistortion:
+ { double a, ca, sa;
+ /* Forward Map Corners */
+ a = coeff[0]-coeff[1]/2; ca = cos(a); sa = sin(a);
+ d.x = coeff[2]*ca;
+ d.y = coeff[2]*sa;
+ InitalBounds(d);
+ d.x = (coeff[2]-coeff[3])*ca;
+ d.y = (coeff[2]-coeff[3])*sa;
+ ExpandBounds(d);
+ a = coeff[0]+coeff[1]/2; ca = cos(a); sa = sin(a);
+ d.x = coeff[2]*ca;
+ d.y = coeff[2]*sa;
+ ExpandBounds(d);
+ d.x = (coeff[2]-coeff[3])*ca;
+ d.y = (coeff[2]-coeff[3])*sa;
+ ExpandBounds(d);
+ /* Orthogonal points along top of arc */
+ for( a=ceil((coeff[0]-coeff[1]/2.0)/MagickPI2)*MagickPI2;
+ a<(coeff[0]+coeff[1]/2.0); a+=MagickPI2 ) {
+ ca = cos(a); sa = sin(a);
+ d.x = coeff[2]*ca;
+ d.y = coeff[2]*sa;
+ ExpandBounds(d);
+ }
+ /*
+ Convert the angle_to_width and radius_to_height
+ to appropriate scaling factors, to allow faster processing
+ in the mapping function.
+ */
+ coeff[1] = Magick2PI*image->columns/coeff[1];
+ coeff[3] = (double)image->rows/coeff[3];
+ break;
+ }
+ case PolarDistortion:
+ {
+ if (number_arguments < 2)
+ coeff[2] = coeff[3] = 0.0;
+ min.x = coeff[2]-coeff[0];
+ max.x = coeff[2]+coeff[0];
+ min.y = coeff[3]-coeff[0];
+ max.y = coeff[3]+coeff[0];
+ /* should be about 1.0 if Rmin = 0 */
+ coeff[7]=(double) geometry.height/(coeff[0]-coeff[1]);
+ break;
+ }
+ case DePolarDistortion:
+ {
+ /* direct calculation as it needs to tile correctly
+ * for reversibility in a DePolar-Polar cycle */
+ geometry.x = geometry.y = 0;
+ geometry.height = (unsigned long) ceil(coeff[0]-coeff[1]);
+ geometry.width = (unsigned long)
+ ceil((coeff[0]-coeff[1])*(coeff[5]-coeff[4])*0.5);
+ break;
+ }
+ case ShepardsDistortion:
+ case BilinearForwardDistortion:
+ case BilinearReverseDistortion:
+ case PolynomialDistortion:
+ case BarrelDistortion:
+ case BarrelInverseDistortion:
+ default:
+ /* no bestfit available for this distortion */
+ bestfit = MagickFalse;
+ break;
+ }
+ /* Set the output image geometry to calculated 'bestfit'
+ Do not do this for DePolar which needs to be exact for tiling
+ */
+ if ( bestfit && method != DePolarDistortion ) {
+ geometry.x = (long) floor(min.x-0.5);
+ geometry.y = (long) floor(min.y-0.5);
+ geometry.width=(unsigned long) ceil(max.x-geometry.x+0.5);
+ geometry.height=(unsigned long) ceil(max.y-geometry.y+0.5);
+ }
+ /* now that we have a new size lets fit distortion to it exactly */
+ if ( method == DePolarDistortion ) {
+ coeff[6]=(coeff[5]-coeff[4])/geometry.width; /* changed width */
+ coeff[7]=(coeff[0]-coeff[1])/geometry.height; /* should be about 1.0 */
+ }
+ }
+
+ /* The user provided a 'viewport' expert option which may
+ overrides some parts of the current output image geometry.
+ For ArcDistortion, this also overrides its default 'bestfit' setting.
+ */
+ { const char *artifact=GetImageArtifact(image,"distort:viewport");
+ viewport_given = MagickFalse;
+ if ( artifact != (const char *) NULL ) {
+ (void) ParseAbsoluteGeometry(artifact,&geometry);
+ viewport_given = MagickTrue;
+ }
+ }
+
+ /* Verbose output */
+ if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
+ register long
+ i;
+ char image_gen[MaxTextExtent];
+ const char *lookup;
+
+ /* Set destination image size and virtual offset */
+ if ( bestfit || viewport_given ) {
+ (void) FormatMagickString(image_gen, MaxTextExtent," -size %lux%lu "
+ "-page %+ld%+ld xc: +insert \\\n",geometry.width,geometry.height,
+ geometry.x,geometry.y);
+ lookup="v.p{ xx-v.page.x-.5, yy-v.page.x-.5 }";
+ }
+ else {
+ image_gen[0] = '\0'; /* no destination to generate */
+ lookup = "p{ xx-page.x-.5, yy-page.x-.5 }"; /* simplify lookup */
+ }
+
+ switch (method) {
+ case AffineDistortion:
+ {
+ double *inverse;
+
+ inverse = (double *) AcquireQuantumMemory(6,sizeof(*inverse));
+ if (inverse == (double *) NULL) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortImages");
+ return((Image *) NULL);
+ }
+ InvertAffineCoefficients(coeff, inverse);
+ CoefficientsToAffineArgs(inverse);
+ fprintf(stderr, "Affine Projection:\n");
+ fprintf(stderr, " -distort AffineProjection \\\n '");
+ for (i=0; i<5; i++)
+ fprintf(stderr, "%lg,", inverse[i]);
+ fprintf(stderr, "%lg'\n", inverse[5]);
+ inverse = (double *) RelinquishMagickMemory(inverse);
+
+ fprintf(stderr, "Affine Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
+ fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf;\n",
+ coeff[0], coeff[1], coeff[2]);
+ fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf;\n",
+ coeff[3], coeff[4], coeff[5]);
+ fprintf(stderr, " %s'\n", lookup);
+
+ break;
+ }
+
+ case PerspectiveDistortion:
+ {
+ double *inverse;
+
+ inverse = (double *) AcquireQuantumMemory(8,sizeof(*inverse));
+ if (inverse == (double *) NULL) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed",
+ "%s", "DistortCoefficients");
+ return((Image *) NULL);
+ }
+ InvertPerspectiveCoefficients(coeff, inverse);
+ fprintf(stderr, "Perspective Projection:\n");
+ fprintf(stderr, " -distort PerspectiveProjection \\\n '");
+ for (i=0; i<4; i++)
+ fprintf(stderr, "%lg,", inverse[i]);
+ fprintf(stderr, "\n ");
+ for (; i<7; i++)
+ fprintf(stderr, "%lg,", inverse[i]);
+ fprintf(stderr, "%lg'\n", inverse[7]);
+ inverse = (double *) RelinquishMagickMemory(inverse);
+
+ fprintf(stderr, "Perspective Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
+ fprintf(stderr, " rr=%+lf*ii %+lf*jj + 1;\n",
+ coeff[6], coeff[7]);
+ fprintf(stderr, " xx=(%+lf*ii %+lf*jj %+lf)/rr;\n",
+ coeff[0], coeff[1], coeff[2]);
+ fprintf(stderr, " yy=(%+lf*ii %+lf*jj %+lf)/rr;\n",
+ coeff[3], coeff[4], coeff[5]);
+ fprintf(stderr, " rr%s0 ? %s : blue'\n",
+ coeff[8] < 0 ? "<" : ">", lookup);
+ break;
+ }
+
+ case BilinearForwardDistortion:
+ fprintf(stderr, "Bilinear Forward Mapping Equations:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " i = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
+ coeff[0], coeff[1], coeff[2], coeff[3]);
+ fprintf(stderr, " j = %+lf*x %+lf*y %+lf*x*y %+lf;\n",
+ coeff[4], coeff[5], coeff[6], coeff[7]);
+ for ( i=8; i<11; i++ )
+ fprintf(stderr, " c%ld = %+lf", i, coeff[i]);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Bilinear Forward Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
+ 0.5-coeff[3], 0.5-coeff[7]);
+ fprintf(stderr, " bb=%lf*ii %+lf*jj %+lf;\n",
+ coeff[6], -coeff[2], coeff[8]);
+ fprintf(stderr, " rt=bb*bb %+lf*(%lf*ii%+lf*jj);\n",
+ -coeff[9], coeff[4], -coeff[0]);
+ fprintf(stderr, " yy=( -bb + sqrt(rt) ) * %+lf;\n",
+ coeff[10]);
+ fprintf(stderr, " xx=(ii %+lf*yy)/(%lf %+lf*yy);\n",
+ -coeff[1], coeff[0], coeff[2]);
+ fprintf(stderr, " (rt < 0 ) ? red : %s'\n", lookup);
+ break;
+
+ case BilinearReverseDistortion:
+ fprintf(stderr, "Bilinear Reverse Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
+ fprintf(stderr, " xx=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
+ coeff[0], coeff[1], coeff[2], coeff[3]);
+ fprintf(stderr, " yy=%+lf*ii %+lf*jj %+lf*ii*jj %+lf;\n",
+ coeff[4], coeff[5], coeff[6], coeff[7]);
+ fprintf(stderr, " %s'\n", lookup);
+ break;
+
+ case PolynomialDistortion:
+ {
+ unsigned long nterms = (unsigned long) coeff[1];
+ fprintf(stderr, "Polynomial (order %lg, terms %lu), FX Equivelent\n",
+ coeff[0], nterms);
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x+0.5; jj=j+page.y+0.5;\n");
+ fprintf(stderr, " xx =");
+ for (i=0; i<(long) nterms; i++) {
+ if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
+ fprintf(stderr, " %+lf%s", coeff[2+i],
+ poly_basis_str(i));
+ }
+ fprintf(stderr, ";\n yy =");
+ for (i=0; i<(long) nterms; i++) {
+ if ( i != 0 && i%4 == 0 ) fprintf(stderr, "\n ");
+ fprintf(stderr, " %+lf%s", coeff[2+i+nterms],
+ poly_basis_str(i));
+ }
+ fprintf(stderr, ";\n %s'\n", lookup);
+ break;
+ }
+ case ArcDistortion:
+ {
+ fprintf(stderr, "Arc Distort, Internal Coefficients:\n");
+ for ( i=0; i<5; i++ )
+ fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
+ fprintf(stderr, "Arc Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x; jj=j+page.y;\n");
+ fprintf(stderr, " xx=(atan2(jj,ii)%+lf)/(2*pi);\n",
+ -coeff[0]);
+ fprintf(stderr, " xx=xx-round(xx);\n");
+ fprintf(stderr, " xx=xx*%lf %+lf;\n",
+ coeff[1], coeff[4]);
+ fprintf(stderr, " yy=(%lf - hypot(ii,jj)) * %lf;\n",
+ coeff[2], coeff[3]);
+ fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
+ break;
+ }
+ case PolarDistortion:
+ {
+ fprintf(stderr, "Polar Distort, Internal Coefficents\n");
+ for ( i=0; i<8; i++ )
+ fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
+ fprintf(stderr, "Polar Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'ii=i+page.x%+lf; jj=j+page.y%+lf;\n",
+ -coeff[2], -coeff[3]);
+ fprintf(stderr, " xx=(atan2(ii,jj)%+lf)/(2*pi);\n",
+ -(coeff[4]+coeff[5])/2 );
+ fprintf(stderr, " xx=xx-round(xx);\n");
+ fprintf(stderr, " xx=xx*2*pi*%lf + v.w/2;\n",
+ coeff[6] );
+ fprintf(stderr, " yy=(hypot(ii,jj)%+lf)*%lf;\n",
+ -coeff[1], coeff[7] );
+ fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
+ break;
+ }
+ case DePolarDistortion:
+ {
+ fprintf(stderr, "DePolar Distort, Internal Coefficents\n");
+ for ( i=0; i<8; i++ )
+ fprintf(stderr, " c%ld = %+lf\n", i, coeff[i]);
+ fprintf(stderr, "DePolar Distort, FX Equivelent:\n");
+ fprintf(stderr, "%s", image_gen);
+ fprintf(stderr, " -fx 'aa=(i+.5)*%lf %+lf;\n", coeff[6], -coeff[4] );
+ fprintf(stderr, " rr=(j+.5)*%lf %+lf;\n", coeff[7], +coeff[1] );
+ fprintf(stderr, " xx=rr*sin(aa) %+lf;\n", coeff[2] );
+ fprintf(stderr, " yy=rr*cos(aa) %+lf;\n", coeff[3] );
+ fprintf(stderr, " v.p{xx-.5,yy-.5}'\n");
+ break;
+ }
+ case BarrelDistortion:
+ case BarrelInverseDistortion:
+ { double xc,yc;
+ xc = ((double)image->columns-1.0)/2.0 + image->page.x;
+ yc = ((double)image->rows-1.0)/2.0 + image->page.y;
+ fprintf(stderr, "Barrel%s Distort, FX Equivelent:\n",
+ method == BarrelDistortion ? "" : "Inv");
+ fprintf(stderr, "%s", image_gen);
+ if ( fabs(coeff[8]-xc) < 0.1 && fabs(coeff[9]-yc) < 0.1 )
+ fprintf(stderr, " -fx 'xc=(w-1)/2; yc=(h-1)/2;\n");
+ else
+ fprintf(stderr, " -fx 'xc=%lf; yc=%lf;\n",
+ coeff[8], coeff[9]);
+ fprintf(stderr,
+ " ii=i-xc; jj=j-yc; rr=hypot(ii,jj);\n");
+ fprintf(stderr, " ii=ii%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
+ method == BarrelDistortion ? "*" : "/",
+ coeff[0],coeff[1],coeff[2],coeff[3]);
+ fprintf(stderr, " jj=jj%s(%lf*rr*rr*rr %+lf*rr*rr %+lf*rr %+lf);\n",
+ method == BarrelDistortion ? "*" : "/",
+ coeff[4],coeff[5],coeff[6],coeff[7]);
+ fprintf(stderr, " v.p{fx*ii+xc,fy*jj+yc}'\n");
+ }
+ default:
+ break;
+ }
+ }
+
+ /* The user provided a 'scale' expert option will scale the
+ output image size, by the factor given allowing for super-sampling
+ of the distorted image space. Any scaling factors must naturally
+ be halved as a result.
+ */
+ { const char *artifact;
+ artifact=GetImageArtifact(image,"distort:scale");
+ output_scaling = 1.0;
+ if (artifact != (const char *) NULL) {
+ output_scaling = fabs(atof(artifact));
+ geometry.width *= output_scaling;
+ geometry.height *= output_scaling;
+ geometry.x *= output_scaling;
+ geometry.y *= output_scaling;
+ if ( output_scaling < 0.1 ) {
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s", "-set option:distort:scale" );
+ return((Image *) NULL);
+ }
+ output_scaling = 1/output_scaling;
+ }
+ }
+#define ScaleFilter(F,A,B,C,D) \
+ ScaleResampleFilter( (F), \
+ output_scaling*(A), output_scaling*(B), \
+ output_scaling*(C), output_scaling*(D) )
+
+ /*
+ Initialize the distort image attributes.
+ */
+ distort_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
+ exception);
+ if (distort_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(distort_image,DirectClass) == MagickFalse)
+ { /* if image is ColorMapped - change it to DirectClass */
+ InheritException(exception,&distort_image->exception);
+ distort_image=DestroyImage(distort_image);
+ return((Image *) NULL);
+ }
+ distort_image->page.x=geometry.x;
+ distort_image->page.y=geometry.y;
+ if (distort_image->background_color.opacity != OpaqueOpacity)
+ distort_image->matte=MagickTrue;
+
+ { /* ----- MAIN CODE -----
+ Sample the source image to each pixel in the distort image.
+ */
+ long
+ j,
+ progress,
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ ResampleFilter
+ **resample_filter;
+
+ CacheView
+ *distort_view;
+
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(distort_image,&zero);
+ resample_filter=AcquireResampleFilterThreadSet(image,MagickFalse,exception);
+ distort_view=AcquireCacheView(distort_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (j=0; j < (long) distort_image->rows; j++)
+ {
+ double
+ validity; /* how mathematically valid is this the mapping */
+
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel, /* pixel color to assign to distorted image */
+ invalid; /* the color to assign when distort result is invalid */
+
+ PointInfo
+ d,s; /* transform destination image x,y to source image x,y */
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ i,
+ id;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueCacheViewAuthenticPixels(distort_view,0,j,distort_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(distort_view);
+ pixel=zero;
+
+ /* Define constant scaling vectors for Affine Distortions
+ Other methods are either variable, or use interpolated lookup
+ */
+ id=GetOpenMPThreadId();
+ switch (method)
+ {
+ case AffineDistortion:
+ ScaleFilter( resample_filter[id],
+ coeff[0], coeff[1],
+ coeff[3], coeff[4] );
+ break;
+ default:
+ break;
+ }
+
+ /* Initialize default pixel validity
+ * negative: pixel is invalid output 'matte_color'
+ * 0.0 to 1.0: antialiased, mix with resample output
+ * 1.0 or greater: use resampled output.
+ */
+ validity = 1.0;
+
+ GetMagickPixelPacket(distort_image,&invalid);
+ SetMagickPixelPacket(distort_image,&distort_image->matte_color,
+ (IndexPacket *) NULL, &invalid);
+ if (distort_image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&invalid); /* what about other color spaces? */
+
+ for (i=0; i < (long) distort_image->columns; i++)
+ {
+ /* map pixel coordinate to distortion space coordinate */
+ d.x = (double) (geometry.x+i+0.5)*output_scaling;
+ d.y = (double) (geometry.y+j+0.5)*output_scaling;
+ s = d; /* default is a no-op mapping */
+ switch (method)
+ {
+ case AffineDistortion:
+ {
+ s.x=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
+ s.y=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
+ /* Affine partial derivitives are constant -- set above */
+ break;
+ }
+ case PerspectiveDistortion:
+ {
+ double
+ p,q,r,abs_r,abs_c6,abs_c7,scale;
+ /* perspective is a ratio of affines */
+ p=coeff[0]*d.x+coeff[1]*d.y+coeff[2];
+ q=coeff[3]*d.x+coeff[4]*d.y+coeff[5];
+ r=coeff[6]*d.x+coeff[7]*d.y+1.0;
+ /* Pixel Validity -- is it a 'sky' or 'ground' pixel */
+ validity = (r*coeff[8] < 0.0) ? 0.0 : 1.0;
+ /* Determine horizon anti-alias blending */
+ abs_r = fabs(r)*2;
+ abs_c6 = fabs(coeff[6]);
+ abs_c7 = fabs(coeff[7]);
+ if ( abs_c6 > abs_c7 ) {
+ if ( abs_r < abs_c6 )
+ validity = 0.5 - coeff[8]*r/coeff[6];
+ }
+ else if ( abs_r < abs_c7 )
+ validity = 0.5 - coeff[8]*r/coeff[7];
+ /* Perspective Sampling Point (if valid) */
+ if ( validity > 0.0 ) {
+ /* divide by r affine, for perspective scaling */
+ scale = 1.0/r;
+ s.x = p*scale;
+ s.y = q*scale;
+ /* Perspective Partial Derivatives or Scaling Vectors */
+ scale *= scale;
+ ScaleFilter( resample_filter[id],
+ (r*coeff[0] - p*coeff[6])*scale,
+ (r*coeff[1] - p*coeff[7])*scale,
+ (r*coeff[3] - q*coeff[6])*scale,
+ (r*coeff[4] - q*coeff[7])*scale );
+ }
+ break;
+ }
+ case BilinearReverseDistortion:
+ {
+ s.x=coeff[0]*d.x+coeff[1]*d.y
+ +coeff[2]*d.x*d.y+coeff[3];
+ s.y=coeff[4]*d.x+coeff[5]*d.y
+ +coeff[6]*d.x*d.y+coeff[7];
+ /* Bilinear partial derivitives of scaling vectors */
+ ScaleFilter( resample_filter[id],
+ coeff[0] + coeff[2]*d.y,
+ coeff[1] + coeff[2]*d.x,
+ coeff[4] + coeff[6]*d.y,
+ coeff[5] + coeff[6]*d.x );
+ break;
+ }
+ case BilinearForwardDistortion:
+ {
+ double b,r;
+#if 0
+fprintf(stderr, " d=%lf,%lf => ", d.x,d.y);
+#endif
+ d.x -= coeff[3]; d.y -= coeff[7];
+ b = coeff[6]*d.x - coeff[2]*d.y + coeff[8];
+ r = b*b - coeff[9]*( coeff[4]*d.x - coeff[0]*d.y );
+
+ validity = ( r < 0.0 ) ? 0.0 : 1.0;
+ if ( validity > 0.0 ) {
+ s.y = ( -b + sqrt(r) ) * coeff[10];
+ s.x = ( d.x - coeff[1]*s.y) / ( coeff[0] + coeff[2]*s.y );
+ }
+#if 0
+fprintf(stderr, "s=%lf,%lf b=%lf r=%lf\n", s.x,s.y,b,r );
+#endif
+ /* NOTE: the sign of the square root should be -ve for parts
+ where the source image becomes 'flipped' or 'mirrored'.
+ At the moment it will produce 'unknown' results.
+ */
+ /* FUTURE: Horizon handling */
+ /* FUTURE: Scaling factors or Deritives */
+ break;
+ }
+ case PolynomialDistortion:
+ {
+ register long
+ k;
+ long
+ nterms=(long)coeff[1];
+
+ PointInfo
+ du,dv; /* the du,dv vectors from unit dx,dy -- derivatives */
+
+ s.x=s.y=du.x=du.y=dv.x=dv.y=0.0;
+ for(k=0; k < nterms; k++) {
+ s.x += poly_basis_fn(k,d.x,d.y)*coeff[2+k];
+ du.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k];
+ du.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k];
+ s.y += poly_basis_fn(k,d.x,d.y)*coeff[2+k+nterms];
+ dv.x += poly_basis_dx(k,d.x,d.y)*coeff[2+k+nterms];
+ dv.y += poly_basis_dy(k,d.x,d.y)*coeff[2+k+nterms];
+ }
+ ScaleFilter( resample_filter[id], du.x,du.y,dv.x,dv.y );
+ break;
+ }
+ case ArcDistortion:
+ {
+ /* what is the angle and radius in the destination image */
+ s.x = (atan2(d.y,d.x) - coeff[0])/Magick2PI;
+ s.x -= MagickRound(s.x); /* angle */
+ s.y = hypot(d.x,d.y); /* radius */
+
+ /* Arc Distortion Partial Scaling Vectors
+ Are derived by mapping the perpendicular unit vectors
+ dR and dA*R*2PI rather than trying to map dx and dy
+ The results is a very simple orthogonal aligned ellipse.
+ */
+ if ( s.y > MagickEpsilon )
+ ScaleFilter( resample_filter[id],
+ coeff[1]/(Magick2PI*s.y), 0, 0, coeff[3] );
+ else
+ ScaleFilter( resample_filter[id],
+ distort_image->columns*2, 0, 0, coeff[3] );
+
+ /* now scale the angle and radius for source image lookup point */
+ s.x = s.x*coeff[1] + coeff[4] + image->page.x +0.5;
+ s.y = (coeff[2] - s.y) * coeff[3] + image->page.y;
+ break;
+ }
+ case PolarDistortion:
+ { /* Rect/Cartesain/Cylinder to Polar View */
+ d.x -= coeff[2];
+ d.y -= coeff[3];
+ s.x = atan2(d.x,d.y) - (coeff[4]+coeff[5])/2;
+ s.x /= Magick2PI;
+ s.x -= MagickRound(s.x);
+ s.x *= Magick2PI; /* angle - relative to centerline */
+ s.y = hypot(d.x,d.y); /* radius */
+
+ /* Polar Scaling vectors are based on mapping dR and dA vectors
+ This results in very simple orthogonal scaling vectors
+ */
+ if ( s.y > MagickEpsilon )
+ ScaleFilter( resample_filter[id],
+ coeff[6]/(Magick2PI*s.y), 0, 0, coeff[7] );
+ else
+ ScaleFilter( resample_filter[id],
+ distort_image->columns*2, 0, 0, coeff[7] );
+
+ /* now finish mapping radius/angle to source x,y coords */
+ s.x = s.x*coeff[6] + (double)image->columns/2.0 + image->page.x;
+ s.y = (s.y-coeff[1])*coeff[7] + image->page.y;
+ break;
+ }
+ case DePolarDistortion:
+ { /* Polar to Cylindical */
+ /* ignore all destination virtual offsets */
+ d.x = ((double)i+0.5)*output_scaling*coeff[6]-coeff[4];
+ d.y = ((double)j+0.5)*output_scaling*coeff[7]+coeff[1];
+ s.x = d.y*sin(d.x) + coeff[2];
+ s.y = d.y*cos(d.x) + coeff[3];
+ /* derivatives are usless - better to use SuperSampling */
+ break;
+ }
+ case BarrelDistortion:
+ case BarrelInverseDistortion:
+ {
+ double r,fx,fy,gx,gy;
+ /* Radial Polynomial Distortion (de-normalized) */
+ d.x -= coeff[8];
+ d.y -= coeff[9];
+ r = sqrt(d.x*d.x+d.y*d.y);
+ if ( r > MagickEpsilon ) {
+ fx = ((coeff[0]*r + coeff[1])*r + coeff[2])*r + coeff[3];
+ fy = ((coeff[4]*r + coeff[5])*r + coeff[6])*r + coeff[7];
+ gx = ((3*coeff[0]*r + 2*coeff[1])*r + coeff[2])/r;
+ gy = ((3*coeff[4]*r + 2*coeff[5])*r + coeff[6])/r;
+ /* adjust functions and scaling for 'inverse' form */
+ if ( method == BarrelInverseDistortion ) {
+ fx = 1/fx; fy = 1/fy;
+ gx *= -fx*fx; gy *= -fy*fy;
+ }
+ /* Set source pixel and EWA derivative vectors */
+ s.x = d.x*fx + coeff[8];
+ s.y = d.y*fy + coeff[9];
+ ScaleFilter( resample_filter[id],
+ gx*d.x*d.x + fx, gx*d.x*d.y,
+ gy*d.x*d.y, gy*d.y*d.y + fy );
+ }
+ else { /* Special handling to avoid divide by zero when r=0 */
+ s.x=s.y=0.0;
+ if ( method == BarrelDistortion )
+ ScaleFilter( resample_filter[id],
+ coeff[3], 0, 0, coeff[7] );
+ else /* method == BarrelInverseDistortion */
+ /* FUTURE, trap for D==0 causing division by zero */
+ ScaleFilter( resample_filter[id],
+ 1.0/coeff[3], 0, 0, 1.0/coeff[7] );
+ }
+ break;
+ }
+ case ShepardsDistortion:
+ { /* Shepards Method, or Inverse Weighted Distance for
+ displacement around the destination image control points
+ The input arguments are the coefficents to the function.
+ This is more of a 'displacement' function rather than an
+ absolute distortion function.
+ */
+ unsigned long
+ i;
+ double
+ denominator;
+
+ denominator = s.x = s.y = 0;
+ for(i=0; i<number_arguments; i+=4) {
+ double weight =
+ ((double)d.x-arguments[i+2])*((double)d.x-arguments[i+2])
+ + ((double)d.y-arguments[i+3])*((double)d.y-arguments[i+3]);
+ if ( weight != 0 )
+ weight = 1/weight;
+ else
+ weight = 1;
+
+ s.x += (arguments[ i ]-arguments[i+2])*weight;
+ s.y += (arguments[i+1]-arguments[i+3])*weight;
+ denominator += weight;
+ }
+ s.x /= denominator;
+ s.y /= denominator;
+ s.x += d.x;
+ s.y += d.y;
+
+ /* We can not determine derivatives using shepards method
+ only color interpolatation, not area-resampling */
+ break;
+ }
+ default:
+ break; /* use the default no-op given above */
+ }
+ /* map virtual canvas location back to real image coordinate */
+ if ( bestfit && method != ArcDistortion ) {
+ s.x -= image->page.x;
+ s.y -= image->page.y;
+ }
+ s.x -= 0.5;
+ s.y -= 0.5;
+
+ if ( validity <= 0.0 ) {
+ /* result of distortion is an invalid pixel - don't resample */
+ SetPixelPacket(distort_image,&invalid,q,indexes);
+ }
+ else {
+ /* resample the source image to find its correct color */
+ (void) ResamplePixelColor(resample_filter[id],s.x,s.y,&pixel);
+ /* if validity between 0.0 and 1.0 mix result with invalid pixel */
+ if ( validity < 1.0 ) {
+ /* Do a blend of sample color and invalid pixel */
+ /* should this be a 'Blend', or an 'Over' compose */
+ MagickPixelCompositeBlend(&pixel,validity,&invalid,(1.0-validity),
+ &pixel);
+ }
+ SetPixelPacket(distort_image,&pixel,q,indexes);
+ }
+ q++;
+ indexes++;
+ }
+ sync=SyncCacheViewAuthenticPixels(distort_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_DistortImage)
+#endif
+ proceed=SetImageProgress(image,DistortImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+#if 0
+fprintf(stderr, "\n");
+#endif
+ }
+ distort_view=DestroyCacheView(distort_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+
+ if (status == MagickFalse)
+ distort_image=DestroyImage(distort_image);
+ }
+
+ /* Arc does not return an offset unless 'bestfit' is in effect
+ And the user has not provided an overriding 'viewport'.
+ */
+ if ( method == ArcDistortion && !bestfit && !viewport_given ) {
+ distort_image->page.x = 0;
+ distort_image->page.y = 0;
+ }
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ return(distort_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p a r s e C o l o r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SparseColorImage(), given a set of coordinates, interpolates the colors
+% found at those coordinates, across the whole image, using various methods.
+%
+% The format of the SparseColorImage() method is:
+%
+% Image *SparseColorImage(const Image *image,const ChannelType channel,
+% SparseColorMethod method,const unsigned long number_arguments,
+% const double *arguments,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image to be filled in.
+%
+% o channel: Specify which color values (in RGBKA sequence) are being set.
+% This also determines the number of color_values in above.
+%
+% o method: the method to fill in the gradient between the control points.
+%
+% The methods used for SparseColor() are often simular to methods
+% used for DistortImage(), and even share the same code for determination
+% of the function coefficents, though with more dimensions (or resulting
+% values).
+%
+% o number_arguments: the number of arguments given.
+%
+% o arguments: array of floating point arguments for this method--
+% x,y,color_values-- with color_values given as normalized values.
+%
+% o exception: return any errors or warnings in this structure
+%
+*/
+MagickExport Image *SparseColorImage(const Image *image,
+ const ChannelType channel,SparseColorMethod method,
+ const unsigned long number_arguments,const double *arguments,
+ ExceptionInfo *exception)
+{
+#define SparseColorTag "Distort/SparseColor"
+
+ DistortImageMethod
+ *distort_method;
+
+ double
+ *coeff;
+
+ Image
+ *sparse_image;
+
+ MagickPixelPacket
+ zero;
+
+ unsigned long
+ number_colors;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ /* Determine number of color values needed per control point */
+ number_colors=0;
+ if ( channel & RedChannel ) number_colors++;
+ if ( channel & GreenChannel ) number_colors++;
+ if ( channel & BlueChannel ) number_colors++;
+ if ( channel & IndexChannel ) number_colors++;
+ if ( channel & OpacityChannel ) number_colors++;
+
+ /*
+ Convert input arguments into mapping coefficients to apply the distortion.
+ Note some Methods may fall back to other simpler methods.
+ */
+ distort_method=(DistortImageMethod *) &method;
+ coeff = GenerateCoefficients(image, distort_method, number_arguments,
+ arguments, number_colors, exception);
+ if ( coeff == (double *) NULL )
+ return((Image *) NULL);
+
+ /* Verbose output */
+ if ( GetImageArtifact(image,"verbose") != (const char *) NULL ) {
+
+ switch (method) {
+ case BarycentricColorInterpolate:
+ {
+ register long x=0;
+ fprintf(stderr, "Barycentric Sparse Color:\n");
+ if ( channel & RedChannel )
+ fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf' \\\n",
+ coeff[x], coeff[x+1], coeff[x+2]),x+=3;
+ if ( channel & GreenChannel )
+ fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf' \\\n",
+ coeff[x], coeff[x+1], coeff[x+2]),x+=3;
+ if ( channel & BlueChannel )
+ fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf' \\\n",
+ coeff[x], coeff[x+1], coeff[x+2]),x+=3;
+ if ( channel & IndexChannel )
+ fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf' \\\n",
+ coeff[x], coeff[x+1], coeff[x+2]),x+=3;
+ if ( channel & OpacityChannel )
+ fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf' \\\n",
+ coeff[x], coeff[x+1], coeff[x+2]),x+=3;
+ break;
+ }
+ case BilinearColorInterpolate:
+ {
+ register long x=0;
+ fprintf(stderr, "Bilinear Sparse Color\n");
+ if ( channel & RedChannel )
+ fprintf(stderr, " -channel R -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
+ coeff[ x ], coeff[x+1],
+ coeff[x+2], coeff[x+3]),x+=4;
+ if ( channel & GreenChannel )
+ fprintf(stderr, " -channel G -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
+ coeff[ x ], coeff[x+1],
+ coeff[x+2], coeff[x+3]),x+=4;
+ if ( channel & BlueChannel )
+ fprintf(stderr, " -channel B -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
+ coeff[ x ], coeff[x+1],
+ coeff[x+2], coeff[x+3]),x+=4;
+ if ( channel & IndexChannel )
+ fprintf(stderr, " -channel K -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
+ coeff[ x ], coeff[x+1],
+ coeff[x+2], coeff[x+3]),x+=4;
+ if ( channel & OpacityChannel )
+ fprintf(stderr, " -channel A -fx '%+lf*i %+lf*j %+lf*i*j %+lf;\n",
+ coeff[ x ], coeff[x+1],
+ coeff[x+2], coeff[x+3]),x+=4;
+ break;
+ }
+ default:
+ /* unknown, or which are too complex for FX alturnatives */
+ break;
+ }
+ }
+
+ /* Generate new image for generated interpolated gradient.
+ * ASIDE: Actually we could have just replaced the colors of the original
+ * image, but IM core policy, is if storage class could change then clone
+ * the image.
+ */
+
+ sparse_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (sparse_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(sparse_image,DirectClass) == MagickFalse)
+ { /* if image is ColorMapped - change it to DirectClass */
+ InheritException(exception,&image->exception);
+ sparse_image=DestroyImage(sparse_image);
+ return((Image *) NULL);
+ }
+ { /* ----- MAIN CODE ----- */
+ long
+ j,
+ progress;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *sparse_view;
+
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(sparse_image,&zero);
+ sparse_view=AcquireCacheView(sparse_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (j=0; j < (long) sparse_image->rows; j++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel; /* pixel to assign to distorted image */
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueCacheViewAuthenticPixels(sparse_view,0,j,sparse_image->columns,
+ 1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+/* FUTURE: get pixel from source image - so channel can replace parts */
+ indexes=GetCacheViewAuthenticIndexQueue(sparse_view);
+ pixel=zero;
+ for (i=0; i < (long) sparse_image->columns; i++)
+ {
+ switch (method)
+ {
+ case BarycentricColorInterpolate:
+ {
+ register long x=0;
+ if ( channel & RedChannel )
+ pixel.red = coeff[x]*i +coeff[x+1]*j
+ +coeff[x+2], x+=3;
+ if ( channel & GreenChannel )
+ pixel.green = coeff[x]*i +coeff[x+1]*j
+ +coeff[x+2], x+=3;
+ if ( channel & BlueChannel )
+ pixel.blue = coeff[x]*i +coeff[x+1]*j
+ +coeff[x+2], x+=3;
+ if ( channel & IndexChannel )
+ pixel.index = coeff[x]*i +coeff[x+1]*j
+ +coeff[x+2], x+=3;
+ if ( channel & OpacityChannel )
+ pixel.opacity = coeff[x]*i +coeff[x+1]*j
+ +coeff[x+2], x+=3;
+ break;
+ }
+ case BilinearColorInterpolate:
+ {
+ register long x=0;
+ if ( channel & RedChannel )
+ pixel.red = coeff[x]*i + coeff[x+1]*j +
+ coeff[x+2]*i*j + coeff[x+3], x+=4;
+ if ( channel & GreenChannel )
+ pixel.green = coeff[x]*i + coeff[x+1]*j +
+ coeff[x+2]*i*j + coeff[x+3], x+=4;
+ if ( channel & BlueChannel )
+ pixel.blue = coeff[x]*i + coeff[x+1]*j +
+ coeff[x+2]*i*j + coeff[x+3], x+=4;
+ if ( channel & IndexChannel )
+ pixel.index = coeff[x]*i + coeff[x+1]*j +
+ coeff[x+2]*i*j + coeff[x+3], x+=4;
+ if ( channel & OpacityChannel )
+ pixel.opacity = coeff[x]*i + coeff[x+1]*j +
+ coeff[x+2]*i*j + coeff[x+3], x+=4;
+ break;
+ }
+ case ShepardsColorInterpolate:
+ { /* Shepards Method,uses its own input arguments as coefficients.
+ */
+ unsigned long
+ k;
+ double
+ denominator;
+
+ if ( channel & RedChannel ) pixel.red = 0.0;
+ if ( channel & GreenChannel ) pixel.green = 0.0;
+ if ( channel & BlueChannel ) pixel.blue = 0.0;
+ if ( channel & IndexChannel ) pixel.index = 0.0;
+ if ( channel & OpacityChannel ) pixel.opacity = 0.0;
+ denominator = 0.0;
+ for(k=0; k<number_arguments; k+=2+number_colors) {
+ register long x=(long) k+2;
+ double weight =
+ ((double)i-arguments[ k ])*((double)i-arguments[ k ])
+ + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
+ if ( weight != 0 )
+ weight = 1/weight;
+ else
+ weight = 1;
+ if ( channel & RedChannel )
+ pixel.red += arguments[x++]*weight;
+ if ( channel & GreenChannel )
+ pixel.green += arguments[x++]*weight;
+ if ( channel & BlueChannel )
+ pixel.blue += arguments[x++]*weight;
+ if ( channel & IndexChannel )
+ pixel.index += arguments[x++]*weight;
+ if ( channel & OpacityChannel )
+ pixel.opacity += arguments[x++]*weight;
+ denominator += weight;
+ }
+ if ( channel & RedChannel ) pixel.red /= denominator;
+ if ( channel & GreenChannel ) pixel.green /= denominator;
+ if ( channel & BlueChannel ) pixel.blue /= denominator;
+ if ( channel & IndexChannel ) pixel.index /= denominator;
+ if ( channel & OpacityChannel ) pixel.opacity /= denominator;
+ break;
+ }
+ case VoronoiColorInterpolate:
+ default:
+ { /* Just use the closest control point you can find! */
+ unsigned long
+ k;
+ double
+ minimum = MagickHuge;
+
+ for(k=0; k<number_arguments; k+=2+number_colors) {
+ double distance =
+ ((double)i-arguments[ k ])*((double)i-arguments[ k ])
+ + ((double)j-arguments[k+1])*((double)j-arguments[k+1]);
+ if ( distance < minimum ) {
+ register long x=(long) k+2;
+ if ( channel & RedChannel ) pixel.red = arguments[x++];
+ if ( channel & GreenChannel ) pixel.green = arguments[x++];
+ if ( channel & BlueChannel ) pixel.blue = arguments[x++];
+ if ( channel & IndexChannel ) pixel.index = arguments[x++];
+ if ( channel & OpacityChannel ) pixel.opacity = arguments[x++];
+ minimum = distance;
+ }
+ }
+ break;
+ }
+ }
+ /* set the color directly back into the source image */
+ if ( channel & RedChannel ) pixel.red *= QuantumRange;
+ if ( channel & GreenChannel ) pixel.green *= QuantumRange;
+ if ( channel & BlueChannel ) pixel.blue *= QuantumRange;
+ if ( channel & IndexChannel ) pixel.index *= QuantumRange;
+ if ( channel & OpacityChannel ) pixel.opacity *= QuantumRange;
+ SetPixelPacket(sparse_image,&pixel,q,indexes);
+ q++;
+ indexes++;
+ }
+ sync=SyncCacheViewAuthenticPixels(sparse_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SparseColorImage)
+#endif
+ proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ sparse_view=DestroyCacheView(sparse_view);
+ if (status == MagickFalse)
+ sparse_image=DestroyImage(sparse_image);
+ }
+ coeff = (double *) RelinquishMagickMemory(coeff);
+ return(sparse_image);
+}
diff --git a/magick/distort.h b/magick/distort.h
new file mode 100644
index 0000000..ceb584f
--- /dev/null
+++ b/magick/distort.h
@@ -0,0 +1,79 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image distortion methods.
+*/
+#ifndef _MAGICKCORE_DISTORT_H
+#define _MAGICKCORE_DISTORT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/draw.h>
+
+/*
+ These two enum are linked, with common enumerated values. Both
+ DistortImages() and SparseColor() often share code to determine
+ functional coefficients for common methods.
+
+ Caution should be taken to ensure that only the common methods contain the
+ same enumerated value, while all others remain unique across both
+ enumerations.
+*/
+typedef enum
+{
+ UndefinedDistortion,
+ AffineDistortion,
+ AffineProjectionDistortion,
+ ScaleRotateTranslateDistortion,
+ PerspectiveDistortion,
+ PerspectiveProjectionDistortion,
+ BilinearForwardDistortion,
+ BilinearDistortion = BilinearForwardDistortion,
+ BilinearReverseDistortion,
+ PolynomialDistortion,
+ ArcDistortion,
+ PolarDistortion,
+ DePolarDistortion,
+ BarrelDistortion,
+ BarrelInverseDistortion,
+ ShepardsDistortion,
+ SentinelDistortion
+} DistortImageMethod;
+
+
+typedef enum
+{
+ UndefinedColorInterpolate = UndefinedDistortion,
+ BarycentricColorInterpolate = AffineDistortion,
+ BilinearColorInterpolate = BilinearReverseDistortion,
+ PolynomialColorInterpolate = PolynomialDistortion,
+ ShepardsColorInterpolate = ShepardsDistortion,
+ /* Methods unique to SparseColor(): */
+ VoronoiColorInterpolate = SentinelDistortion
+} SparseColorMethod;
+
+extern MagickExport Image
+ *DistortImage(const Image *,const DistortImageMethod,const unsigned long,
+ const double *,MagickBooleanType,ExceptionInfo *exception),
+ *SparseColorImage(const Image *,const ChannelType,const SparseColorMethod,
+ const unsigned long,const double *,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/draw-private.h b/magick/draw-private.h
new file mode 100644
index 0000000..ece53e9
--- /dev/null
+++ b/magick/draw-private.h
@@ -0,0 +1,85 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private image drawing methods.
+*/
+#ifndef _MAGICKCORE_DRAW_PRIVATE_H
+#define _MAGICKCORE_DRAW_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/cache.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+
+static inline MagickBooleanType GetFillColor(const DrawInfo *draw_info,
+ const long x,const long y,PixelPacket *pixel)
+{
+ Image
+ *pattern;
+
+ MagickBooleanType
+ status;
+
+ pattern=draw_info->fill_pattern;
+ if (pattern == (Image *) NULL)
+ {
+ *pixel=draw_info->fill;
+ return(MagickTrue);
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical
+#endif
+ status=GetOneVirtualMethodPixel(pattern,TileVirtualPixelMethod,
+ x+pattern->tile_offset.x,y+pattern->tile_offset.y,pixel,
+ &pattern->exception);
+ if (pattern->matte == MagickFalse)
+ pixel->opacity=OpaqueOpacity;
+ return(status);
+}
+
+static inline MagickBooleanType GetStrokeColor(const DrawInfo *draw_info,
+ const long x,const long y,PixelPacket *pixel)
+{
+ Image
+ *pattern;
+
+ MagickBooleanType
+ status;
+
+ pattern=draw_info->stroke_pattern;
+ if (pattern == (Image *) NULL)
+ {
+ *pixel=draw_info->stroke;
+ return(MagickTrue);
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical
+#endif
+ status=GetOneVirtualMethodPixel(pattern,TileVirtualPixelMethod,
+ x+pattern->tile_offset.x,y+pattern->tile_offset.y,pixel,
+ &pattern->exception);
+ if (pattern->matte == MagickFalse)
+ pixel->opacity=OpaqueOpacity;
+ return(status);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/draw.c b/magick/draw.c
new file mode 100644
index 0000000..95f1456
--- /dev/null
+++ b/magick/draw.c
@@ -0,0 +1,6147 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% DDDD RRRR AAA W W %
+% D D R R A A W W %
+% D D RRRR AAAAA W W W %
+% D D R RN A A WW WW %
+% DDDD R R A A W W %
+% %
+% %
+% MagickCore Image Drawing Methods %
+% %
+% %
+% Software Design %
+% John Cristy %
+% July 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Bill Radcliffe of Corbis (www.corbis.com) contributed the polygon
+% rendering code based on Paul Heckbert's "Concave Polygon Scan Conversion",
+% Graphics Gems, 1990. Leonard Rosenthal and David Harr of Appligent
+% (www.appligent.com) contributed the dash pattern, linecap stroking
+% algorithm, and minor rendering improvements.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/annotate.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/draw-private.h"
+#include "magick/enhance.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/list.h"
+#include "magick/log.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/paint.h"
+#include "magick/pixel-private.h"
+#include "magick/property.h"
+#include "magick/resample.h"
+#include "magick/resample-private.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+
+/*
+ Define declarations.
+*/
+#define BezierQuantum 200
+
+/*
+ Typedef declarations.
+*/
+typedef struct _EdgeInfo
+{
+ SegmentInfo
+ bounds;
+
+ MagickRealType
+ scanline;
+
+ PointInfo
+ *points;
+
+ unsigned long
+ number_points;
+
+ long
+ direction;
+
+ MagickBooleanType
+ ghostline;
+
+ unsigned long
+ highwater;
+} EdgeInfo;
+
+typedef struct _ElementInfo
+{
+ MagickRealType
+ cx,
+ cy,
+ major,
+ minor,
+ angle;
+} ElementInfo;
+
+typedef struct _PolygonInfo
+{
+ EdgeInfo
+ *edges;
+
+ unsigned long
+ number_edges;
+} PolygonInfo;
+
+typedef enum
+{
+ MoveToCode,
+ OpenCode,
+ GhostlineCode,
+ LineToCode,
+ EndCode
+} PathInfoCode;
+
+typedef struct _PathInfo
+{
+ PointInfo
+ point;
+
+ PathInfoCode
+ code;
+} PathInfo;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ DrawStrokePolygon(Image *,const DrawInfo *,const PrimitiveInfo *);
+
+static PrimitiveInfo
+ *TraceStrokePolygon(const DrawInfo *,const PrimitiveInfo *);
+
+static unsigned long
+ TracePath(PrimitiveInfo *,const char *);
+
+static void
+ TraceArc(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
+ TraceArcPath(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo,
+ const MagickRealType,const MagickBooleanType,const MagickBooleanType),
+ TraceBezier(PrimitiveInfo *,const unsigned long),
+ TraceCircle(PrimitiveInfo *,const PointInfo,const PointInfo),
+ TraceEllipse(PrimitiveInfo *,const PointInfo,const PointInfo,const PointInfo),
+ TraceLine(PrimitiveInfo *,const PointInfo,const PointInfo),
+ TraceRectangle(PrimitiveInfo *,const PointInfo,const PointInfo),
+ TraceRoundRectangle(PrimitiveInfo *,const PointInfo,const PointInfo,
+ PointInfo),
+ TraceSquareLinecap(PrimitiveInfo *,const unsigned long,const MagickRealType);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e D r a w I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireDrawInfo() returns a DrawInfo structure properly initialized.
+%
+% The format of the AcquireDrawInfo method is:
+%
+% DrawInfo *AcquireDrawInfo(void)
+%
+*/
+MagickExport DrawInfo *AcquireDrawInfo(void)
+{
+ DrawInfo
+ *draw_info;
+
+ draw_info=(DrawInfo *) AcquireMagickMemory(sizeof(*draw_info));
+ if (draw_info == (DrawInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetDrawInfo((ImageInfo *) NULL,draw_info);
+ return(draw_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e D r a w I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneDrawInfo() makes a copy of the given draw info structure. If NULL
+% is specified, a new image info structure is created initialized to
+% default values.
+%
+% The format of the CloneDrawInfo method is:
+%
+% DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
+% const DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o draw_info: the draw info.
+%
+*/
+MagickExport DrawInfo *CloneDrawInfo(const ImageInfo *image_info,
+ const DrawInfo *draw_info)
+{
+ DrawInfo
+ *clone_info;
+
+ clone_info=(DrawInfo *) AcquireMagickMemory(sizeof(*clone_info));
+ if (clone_info == (DrawInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetDrawInfo(image_info,clone_info);
+ if (draw_info == (DrawInfo *) NULL)
+ return(clone_info);
+ if (clone_info->primitive != (char *) NULL)
+ (void) CloneString(&clone_info->primitive,draw_info->primitive);
+ if (draw_info->geometry != (char *) NULL)
+ (void) CloneString(&clone_info->geometry,draw_info->geometry);
+ clone_info->viewbox=draw_info->viewbox;
+ clone_info->affine=draw_info->affine;
+ clone_info->gravity=draw_info->gravity;
+ clone_info->fill=draw_info->fill;
+ clone_info->stroke=draw_info->stroke;
+ clone_info->stroke_width=draw_info->stroke_width;
+ if (draw_info->fill_pattern != (Image *) NULL)
+ clone_info->fill_pattern=CloneImage(draw_info->fill_pattern,0,0,MagickTrue,
+ &draw_info->fill_pattern->exception);
+ else
+ if (draw_info->tile != (Image *) NULL)
+ clone_info->fill_pattern=CloneImage(draw_info->tile,0,0,MagickTrue,
+ &draw_info->tile->exception);
+ clone_info->tile=NewImageList(); /* tile is deprecated */
+ if (draw_info->stroke_pattern != (Image *) NULL)
+ clone_info->stroke_pattern=CloneImage(draw_info->stroke_pattern,0,0,
+ MagickTrue,&draw_info->stroke_pattern->exception);
+ clone_info->stroke_antialias=draw_info->stroke_antialias;
+ clone_info->text_antialias=draw_info->text_antialias;
+ clone_info->fill_rule=draw_info->fill_rule;
+ clone_info->linecap=draw_info->linecap;
+ clone_info->linejoin=draw_info->linejoin;
+ clone_info->miterlimit=draw_info->miterlimit;
+ clone_info->dash_offset=draw_info->dash_offset;
+ clone_info->decorate=draw_info->decorate;
+ clone_info->compose=draw_info->compose;
+ if (draw_info->text != (char *) NULL)
+ (void) CloneString(&clone_info->text,draw_info->text);
+ if (draw_info->font != (char *) NULL)
+ (void) CloneString(&clone_info->font,draw_info->font);
+ if (draw_info->metrics != (char *) NULL)
+ (void) CloneString(&clone_info->metrics,draw_info->metrics);
+ if (draw_info->family != (char *) NULL)
+ (void) CloneString(&clone_info->family,draw_info->family);
+ clone_info->style=draw_info->style;
+ clone_info->stretch=draw_info->stretch;
+ clone_info->weight=draw_info->weight;
+ if (draw_info->encoding != (char *) NULL)
+ (void) CloneString(&clone_info->encoding,draw_info->encoding);
+ clone_info->pointsize=draw_info->pointsize;
+ clone_info->kerning=draw_info->kerning;
+ clone_info->interword_spacing=draw_info->interword_spacing;
+ if (draw_info->density != (char *) NULL)
+ (void) CloneString(&clone_info->density,draw_info->density);
+ clone_info->align=draw_info->align;
+ clone_info->undercolor=draw_info->undercolor;
+ clone_info->border_color=draw_info->border_color;
+ if (draw_info->server_name != (char *) NULL)
+ (void) CloneString(&clone_info->server_name,draw_info->server_name);
+ if (draw_info->dash_pattern != (double *) NULL)
+ {
+ register long
+ x;
+
+ for (x=0; draw_info->dash_pattern[x] != 0.0; x++) ;
+ clone_info->dash_pattern=(double *) AcquireQuantumMemory((size_t) x+1UL,
+ sizeof(*clone_info->dash_pattern));
+ if (clone_info->dash_pattern == (double *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAllocateDashPattern");
+ (void) CopyMagickMemory(clone_info->dash_pattern,draw_info->dash_pattern,
+ (size_t) (x+1)*sizeof(*clone_info->dash_pattern));
+ }
+ clone_info->gradient=draw_info->gradient;
+ if (draw_info->gradient.stops != (StopInfo *) NULL)
+ {
+ unsigned long
+ number_stops;
+
+ number_stops=clone_info->gradient.number_stops;
+ clone_info->gradient.stops=(StopInfo *) AcquireQuantumMemory((size_t)
+ number_stops,sizeof(*clone_info->gradient.stops));
+ if (clone_info->gradient.stops == (StopInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAllocateDashPattern");
+ (void) CopyMagickMemory(clone_info->gradient.stops,
+ draw_info->gradient.stops,(size_t) number_stops*
+ sizeof(*clone_info->gradient.stops));
+ }
+ if (draw_info->clip_mask != (char *) NULL)
+ (void) CloneString(&clone_info->clip_mask,draw_info->clip_mask);
+ clone_info->bounds=draw_info->bounds;
+ clone_info->clip_units=draw_info->clip_units;
+ clone_info->render=draw_info->render;
+ clone_info->opacity=draw_info->opacity;
+ clone_info->element_reference=draw_info->element_reference;
+ clone_info->debug=IsEventLogging();
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o n v e r t P a t h T o P o l y g o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertPathToPolygon() converts a path to the more efficient sorted
+% rendering form.
+%
+% The format of the ConvertPathToPolygon method is:
+%
+% PolygonInfo *ConvertPathToPolygon(const DrawInfo *draw_info,
+% const PathInfo *path_info)
+%
+% A description of each parameter follows:
+%
+% o Method ConvertPathToPolygon returns the path in a more efficient sorted
+% rendering form of type PolygonInfo.
+%
+% o draw_info: Specifies a pointer to an DrawInfo structure.
+%
+% o path_info: Specifies a pointer to an PathInfo structure.
+%
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int CompareEdges(const void *x,const void *y)
+{
+ register const EdgeInfo
+ *p,
+ *q;
+
+ /*
+ Compare two edges.
+ */
+ p=(const EdgeInfo *) x;
+ q=(const EdgeInfo *) y;
+ if ((p->points[0].y-MagickEpsilon) > q->points[0].y)
+ return(1);
+ if ((p->points[0].y+MagickEpsilon) < q->points[0].y)
+ return(-1);
+ if ((p->points[0].x-MagickEpsilon) > q->points[0].x)
+ return(1);
+ if ((p->points[0].x+MagickEpsilon) < q->points[0].x)
+ return(-1);
+ if (((p->points[1].x-p->points[0].x)*(q->points[1].y-q->points[0].y)-
+ (p->points[1].y-p->points[0].y)*(q->points[1].x-q->points[0].x)) > 0.0)
+ return(1);
+ return(-1);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static void LogPolygonInfo(const PolygonInfo *polygon_info)
+{
+ register EdgeInfo
+ *p;
+
+ register long
+ i,
+ j;
+
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin active-edge");
+ p=polygon_info->edges;
+ for (i=0; i < (long) polygon_info->number_edges; i++)
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," edge %lu:",i);
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," direction: %s",
+ p->direction != MagickFalse ? "down" : "up");
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," ghostline: %s",
+ p->ghostline != MagickFalse ? "transparent" : "opaque");
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " bounds: %g,%g - %g,%g",p->bounds.x1,p->bounds.y1,p->bounds.x2,
+ p->bounds.y2);
+ for (j=0; j < (long) p->number_points; j++)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," %g,%g",
+ p->points[j].x,p->points[j].y);
+ p++;
+ }
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end active-edge");
+}
+
+static void ReversePoints(PointInfo *points,const unsigned long number_points)
+{
+ PointInfo
+ point;
+
+ register long
+ i;
+
+ for (i=0; i < (long) (number_points >> 1); i++)
+ {
+ point=points[i];
+ points[i]=points[number_points-(i+1)];
+ points[number_points-(i+1)]=point;
+ }
+}
+
+static PolygonInfo *ConvertPathToPolygon(
+ const DrawInfo *magick_unused(draw_info),const PathInfo *path_info)
+{
+ long
+ direction,
+ next_direction;
+
+ PointInfo
+ point,
+ *points;
+
+ PolygonInfo
+ *polygon_info;
+
+ SegmentInfo
+ bounds;
+
+ register long
+ i,
+ n;
+
+ MagickBooleanType
+ ghostline;
+
+ unsigned long
+ edge,
+ number_edges,
+ number_points;
+
+ /*
+ Convert a path to the more efficient sorted rendering form.
+ */
+ polygon_info=(PolygonInfo *) AcquireMagickMemory(sizeof(*polygon_info));
+ if (polygon_info == (PolygonInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ number_edges=16;
+ polygon_info->edges=(EdgeInfo *) AcquireQuantumMemory((size_t) number_edges,
+ sizeof(*polygon_info->edges));
+ if (polygon_info->edges == (EdgeInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ direction=0;
+ edge=0;
+ ghostline=MagickFalse;
+ n=0;
+ number_points=0;
+ points=(PointInfo *) NULL;
+ (void) ResetMagickMemory(&point,0,sizeof(point));
+ (void) ResetMagickMemory(&bounds,0,sizeof(bounds));
+ for (i=0; path_info[i].code != EndCode; i++)
+ {
+ if ((path_info[i].code == MoveToCode) || (path_info[i].code == OpenCode) ||
+ (path_info[i].code == GhostlineCode))
+ {
+ /*
+ Move to.
+ */
+ if ((points != (PointInfo *) NULL) && (n >= 2))
+ {
+ if (edge == number_edges)
+ {
+ number_edges<<=1;
+ polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
+ polygon_info->edges,(size_t) number_edges,
+ sizeof(*polygon_info->edges));
+ if (polygon_info->edges == (EdgeInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ }
+ polygon_info->edges[edge].number_points=(unsigned long) n;
+ polygon_info->edges[edge].scanline=(-1.0);
+ polygon_info->edges[edge].highwater=0;
+ polygon_info->edges[edge].ghostline=ghostline;
+ polygon_info->edges[edge].direction=(long) (direction > 0);
+ if (direction < 0)
+ ReversePoints(points,(unsigned long) n);
+ polygon_info->edges[edge].points=points;
+ polygon_info->edges[edge].bounds=bounds;
+ polygon_info->edges[edge].bounds.y1=points[0].y;
+ polygon_info->edges[edge].bounds.y2=points[n-1].y;
+ points=(PointInfo *) NULL;
+ ghostline=MagickFalse;
+ edge++;
+ }
+ if (points == (PointInfo *) NULL)
+ {
+ number_points=16;
+ points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
+ sizeof(*points));
+ if (points == (PointInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ }
+ ghostline=path_info[i].code == GhostlineCode ? MagickTrue : MagickFalse;
+ point=path_info[i].point;
+ points[0]=point;
+ bounds.x1=point.x;
+ bounds.x2=point.x;
+ direction=0;
+ n=1;
+ continue;
+ }
+ /*
+ Line to.
+ */
+ next_direction=((path_info[i].point.y > point.y) ||
+ ((path_info[i].point.y == point.y) &&
+ (path_info[i].point.x > point.x))) ? 1 : -1;
+ if ((direction != 0) && (direction != next_direction))
+ {
+ /*
+ New edge.
+ */
+ point=points[n-1];
+ if (edge == number_edges)
+ {
+ number_edges<<=1;
+ polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
+ polygon_info->edges,(size_t) number_edges,
+ sizeof(*polygon_info->edges));
+ if (polygon_info->edges == (EdgeInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ }
+ polygon_info->edges[edge].number_points=(unsigned long) n;
+ polygon_info->edges[edge].scanline=(-1.0);
+ polygon_info->edges[edge].highwater=0;
+ polygon_info->edges[edge].ghostline=ghostline;
+ polygon_info->edges[edge].direction=(long) (direction > 0);
+ if (direction < 0)
+ ReversePoints(points,(unsigned long) n);
+ polygon_info->edges[edge].points=points;
+ polygon_info->edges[edge].bounds=bounds;
+ polygon_info->edges[edge].bounds.y1=points[0].y;
+ polygon_info->edges[edge].bounds.y2=points[n-1].y;
+ number_points=16;
+ points=(PointInfo *) AcquireQuantumMemory((size_t) number_points,
+ sizeof(*points));
+ if (points == (PointInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ n=1;
+ ghostline=MagickFalse;
+ points[0]=point;
+ bounds.x1=point.x;
+ bounds.x2=point.x;
+ edge++;
+ }
+ direction=next_direction;
+ if (points == (PointInfo *) NULL)
+ continue;
+ if (n == (long) number_points)
+ {
+ number_points<<=1;
+ points=(PointInfo *) ResizeQuantumMemory(points,(size_t) number_points,
+ sizeof(*points));
+ if (points == (PointInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ }
+ point=path_info[i].point;
+ points[n]=point;
+ if (point.x < bounds.x1)
+ bounds.x1=point.x;
+ if (point.x > bounds.x2)
+ bounds.x2=point.x;
+ n++;
+ }
+ if (points != (PointInfo *) NULL)
+ {
+ if (n < 2)
+ points=(PointInfo *) RelinquishMagickMemory(points);
+ else
+ {
+ if (edge == number_edges)
+ {
+ number_edges<<=1;
+ polygon_info->edges=(EdgeInfo *) ResizeQuantumMemory(
+ polygon_info->edges,(size_t) number_edges,
+ sizeof(*polygon_info->edges));
+ if (polygon_info->edges == (EdgeInfo *) NULL)
+ return((PolygonInfo *) NULL);
+ }
+ polygon_info->edges[edge].number_points=(unsigned long) n;
+ polygon_info->edges[edge].scanline=(-1.0);
+ polygon_info->edges[edge].highwater=0;
+ polygon_info->edges[edge].ghostline=ghostline;
+ polygon_info->edges[edge].direction=(long) (direction > 0);
+ if (direction < 0)
+ ReversePoints(points,(unsigned long) n);
+ polygon_info->edges[edge].points=points;
+ polygon_info->edges[edge].bounds=bounds;
+ polygon_info->edges[edge].bounds.y1=points[0].y;
+ polygon_info->edges[edge].bounds.y2=points[n-1].y;
+ ghostline=MagickFalse;
+ edge++;
+ }
+ }
+ polygon_info->number_edges=edge;
+ qsort(polygon_info->edges,(size_t) polygon_info->number_edges,
+ sizeof(*polygon_info->edges),CompareEdges);
+ if (IsEventLogging() != MagickFalse)
+ LogPolygonInfo(polygon_info);
+ return(polygon_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o n v e r t P r i m i t i v e T o P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertPrimitiveToPath() converts a PrimitiveInfo structure into a vector
+% path structure.
+%
+% The format of the ConvertPrimitiveToPath method is:
+%
+% PathInfo *ConvertPrimitiveToPath(const DrawInfo *draw_info,
+% const PrimitiveInfo *primitive_info)
+%
+% A description of each parameter follows:
+%
+% o Method ConvertPrimitiveToPath returns a vector path structure of type
+% PathInfo.
+%
+% o draw_info: a structure of type DrawInfo.
+%
+% o primitive_info: Specifies a pointer to an PrimitiveInfo structure.
+%
+%
+*/
+
+static void LogPathInfo(const PathInfo *path_info)
+{
+ register const PathInfo
+ *p;
+
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin vector-path");
+ for (p=path_info; p->code != EndCode; p++)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " %g,%g %s",p->point.x,p->point.y,p->code == GhostlineCode ?
+ "moveto ghostline" : p->code == OpenCode ? "moveto open" :
+ p->code == MoveToCode ? "moveto" : p->code == LineToCode ? "lineto" :
+ "?");
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end vector-path");
+}
+
+static PathInfo *ConvertPrimitiveToPath(
+ const DrawInfo *magick_unused(draw_info),const PrimitiveInfo *primitive_info)
+{
+ long
+ coordinates,
+ start;
+
+ PathInfo
+ *path_info;
+
+ PathInfoCode
+ code;
+
+ PointInfo
+ p,
+ q;
+
+ register long
+ i,
+ n;
+
+ /*
+ Converts a PrimitiveInfo structure into a vector path structure.
+ */
+ switch (primitive_info->primitive)
+ {
+ case PointPrimitive:
+ case ColorPrimitive:
+ case MattePrimitive:
+ case TextPrimitive:
+ case ImagePrimitive:
+ return((PathInfo *) NULL);
+ default:
+ break;
+ }
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
+ path_info=(PathInfo *) AcquireQuantumMemory((size_t) (2UL*i+3UL),
+ sizeof(*path_info));
+ if (path_info == (PathInfo *) NULL)
+ return((PathInfo *) NULL);
+ coordinates=0;
+ n=0;
+ p.x=(-1.0);
+ p.y=(-1.0);
+ q.x=(-1.0);
+ q.y=(-1.0);
+ start=0;
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
+ {
+ code=LineToCode;
+ if (coordinates <= 0)
+ {
+ coordinates=(long) primitive_info[i].coordinates;
+ p=primitive_info[i].point;
+ start=n;
+ code=MoveToCode;
+ }
+ coordinates--;
+ /*
+ Eliminate duplicate points.
+ */
+ if ((i == 0) || (fabs(q.x-primitive_info[i].point.x) > MagickEpsilon) ||
+ (fabs(q.y-primitive_info[i].point.y) > MagickEpsilon))
+ {
+ path_info[n].code=code;
+ path_info[n].point=primitive_info[i].point;
+ q=primitive_info[i].point;
+ n++;
+ }
+ if (coordinates > 0)
+ continue;
+ if ((fabs(p.x-primitive_info[i].point.x) <= MagickEpsilon) &&
+ (fabs(p.y-primitive_info[i].point.y) <= MagickEpsilon))
+ continue;
+ /*
+ Mark the p point as open if it does not match the q.
+ */
+ path_info[start].code=OpenCode;
+ path_info[n].code=GhostlineCode;
+ path_info[n].point=primitive_info[i].point;
+ n++;
+ path_info[n].code=LineToCode;
+ path_info[n].point=p;
+ n++;
+ }
+ path_info[n].code=EndCode;
+ path_info[n].point.x=0.0;
+ path_info[n].point.y=0.0;
+ if (IsEventLogging() != MagickFalse)
+ LogPathInfo(path_info);
+ return(path_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y D r a w I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyDrawInfo() deallocates memory associated with an DrawInfo
+% structure.
+%
+% The format of the DestroyDrawInfo method is:
+%
+% DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o draw_info: the draw info.
+%
+*/
+MagickExport DrawInfo *DestroyDrawInfo(DrawInfo *draw_info)
+{
+ if (draw_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if (draw_info->primitive != (char *) NULL)
+ draw_info->primitive=DestroyString(draw_info->primitive);
+ if (draw_info->text != (char *) NULL)
+ draw_info->text=DestroyString(draw_info->text);
+ if (draw_info->geometry != (char *) NULL)
+ draw_info->geometry=DestroyString(draw_info->geometry);
+ if (draw_info->tile != (Image *) NULL)
+ draw_info->tile=DestroyImage(draw_info->tile);
+ if (draw_info->fill_pattern != (Image *) NULL)
+ draw_info->fill_pattern=DestroyImage(draw_info->fill_pattern);
+ if (draw_info->stroke_pattern != (Image *) NULL)
+ draw_info->stroke_pattern=DestroyImage(draw_info->stroke_pattern);
+ if (draw_info->font != (char *) NULL)
+ draw_info->font=DestroyString(draw_info->font);
+ if (draw_info->metrics != (char *) NULL)
+ draw_info->metrics=DestroyString(draw_info->metrics);
+ if (draw_info->family != (char *) NULL)
+ draw_info->family=DestroyString(draw_info->family);
+ if (draw_info->encoding != (char *) NULL)
+ draw_info->encoding=DestroyString(draw_info->encoding);
+ if (draw_info->density != (char *) NULL)
+ draw_info->density=DestroyString(draw_info->density);
+ if (draw_info->server_name != (char *) NULL)
+ draw_info->server_name=(char *)
+ RelinquishMagickMemory(draw_info->server_name);
+ if (draw_info->dash_pattern != (double *) NULL)
+ draw_info->dash_pattern=(double *) RelinquishMagickMemory(
+ draw_info->dash_pattern);
+ if (draw_info->gradient.stops != (StopInfo *) NULL)
+ draw_info->gradient.stops=(StopInfo *) RelinquishMagickMemory(
+ draw_info->gradient.stops);
+ if (draw_info->clip_mask != (char *) NULL)
+ draw_info->clip_mask=DestroyString(draw_info->clip_mask);
+ draw_info->signature=(~MagickSignature);
+ draw_info=(DrawInfo *) RelinquishMagickMemory(draw_info);
+ return(draw_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y E d g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyEdge() destroys the specified polygon edge.
+%
+% The format of the DestroyEdge method is:
+%
+% long DestroyEdge(PolygonInfo *polygon_info,const int edge)
+%
+% A description of each parameter follows:
+%
+% o polygon_info: Specifies a pointer to an PolygonInfo structure.
+%
+% o edge: the polygon edge number to destroy.
+%
+*/
+static unsigned long DestroyEdge(PolygonInfo *polygon_info,
+ const unsigned long edge)
+{
+ assert(edge < polygon_info->number_edges);
+ polygon_info->edges[edge].points=(PointInfo *) RelinquishMagickMemory(
+ polygon_info->edges[edge].points);
+ polygon_info->number_edges--;
+ if (edge < polygon_info->number_edges)
+ (void) CopyMagickMemory(polygon_info->edges+edge,polygon_info->edges+edge+1,
+ (size_t) (polygon_info->number_edges-edge)*sizeof(*polygon_info->edges));
+ return(polygon_info->number_edges);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P o l y g o n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPolygonInfo() destroys the PolygonInfo data structure.
+%
+% The format of the DestroyPolygonInfo method is:
+%
+% PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
+%
+% A description of each parameter follows:
+%
+% o polygon_info: Specifies a pointer to an PolygonInfo structure.
+%
+*/
+static PolygonInfo *DestroyPolygonInfo(PolygonInfo *polygon_info)
+{
+ register long
+ i;
+
+ for (i=0; i < (long) polygon_info->number_edges; i++)
+ polygon_info->edges[i].points=(PointInfo *)
+ RelinquishMagickMemory(polygon_info->edges[i].points);
+ polygon_info->edges=(EdgeInfo *) RelinquishMagickMemory(polygon_info->edges);
+ return((PolygonInfo *) RelinquishMagickMemory(polygon_info));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w A f f i n e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawAffineImage() composites the source over the destination image as
+% dictated by the affine transform.
+%
+% The format of the DrawAffineImage method is:
+%
+% MagickBooleanType DrawAffineImage(Image *image,const Image *source,
+% const AffineMatrix *affine)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o source: the source image.
+%
+% o affine: the affine transform.
+%
+*/
+static SegmentInfo AffineEdge(const Image *image,const AffineMatrix *affine,
+ const double y,const SegmentInfo *edge)
+{
+ double
+ intercept,
+ z;
+
+ register double
+ x;
+
+ SegmentInfo
+ inverse_edge;
+
+ /*
+ Determine left and right edges.
+ */
+ inverse_edge.x1=edge->x1;
+ inverse_edge.y1=edge->y1;
+ inverse_edge.x2=edge->x2;
+ inverse_edge.y2=edge->y2;
+ z=affine->ry*y+affine->tx;
+ if (affine->sx > MagickEpsilon)
+ {
+ intercept=(-z/affine->sx);
+ x=intercept+MagickEpsilon;
+ if (x > inverse_edge.x1)
+ inverse_edge.x1=x;
+ intercept=(-z+(double) image->columns)/affine->sx;
+ x=intercept-MagickEpsilon;
+ if (x < inverse_edge.x2)
+ inverse_edge.x2=x;
+ }
+ else
+ if (affine->sx < -MagickEpsilon)
+ {
+ intercept=(-z+(double) image->columns)/affine->sx;
+ x=intercept+MagickEpsilon;
+ if (x > inverse_edge.x1)
+ inverse_edge.x1=x;
+ intercept=(-z/affine->sx);
+ x=intercept-MagickEpsilon;
+ if (x < inverse_edge.x2)
+ inverse_edge.x2=x;
+ }
+ else
+ if ((z < 0.0) || ((unsigned long) (z+0.5) >= image->columns))
+ {
+ inverse_edge.x2=edge->x1;
+ return(inverse_edge);
+ }
+ /*
+ Determine top and bottom edges.
+ */
+ z=affine->sy*y+affine->ty;
+ if (affine->rx > MagickEpsilon)
+ {
+ intercept=(-z/affine->rx);
+ x=intercept+MagickEpsilon;
+ if (x > inverse_edge.x1)
+ inverse_edge.x1=x;
+ intercept=(-z+(double) image->rows)/affine->rx;
+ x=intercept-MagickEpsilon;
+ if (x < inverse_edge.x2)
+ inverse_edge.x2=x;
+ }
+ else
+ if (affine->rx < -MagickEpsilon)
+ {
+ intercept=(-z+(double) image->rows)/affine->rx;
+ x=intercept+MagickEpsilon;
+ if (x > inverse_edge.x1)
+ inverse_edge.x1=x;
+ intercept=(-z/affine->rx);
+ x=intercept-MagickEpsilon;
+ if (x < inverse_edge.x2)
+ inverse_edge.x2=x;
+ }
+ else
+ if ((z < 0.0) || ((unsigned long) (z+0.5) >= image->rows))
+ {
+ inverse_edge.x2=edge->x2;
+ return(inverse_edge);
+ }
+ return(inverse_edge);
+}
+
+static AffineMatrix InverseAffineMatrix(const AffineMatrix *affine)
+{
+ AffineMatrix
+ inverse_affine;
+
+ double
+ determinant;
+
+ determinant=1.0/(affine->sx*affine->sy-affine->rx*affine->ry);
+ inverse_affine.sx=determinant*affine->sy;
+ inverse_affine.rx=determinant*(-affine->rx);
+ inverse_affine.ry=determinant*(-affine->ry);
+ inverse_affine.sy=determinant*affine->sx;
+ inverse_affine.tx=(-affine->tx)*inverse_affine.sx-affine->ty*
+ inverse_affine.ry;
+ inverse_affine.ty=(-affine->tx)*inverse_affine.rx-affine->ty*
+ inverse_affine.sy;
+ return(inverse_affine);
+}
+
+static inline long MagickAbsoluteValue(const long x)
+{
+ if (x < 0)
+ return(-x);
+ return(x);
+}
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType DrawAffineImage(Image *image,
+ const Image *source,const AffineMatrix *affine)
+{
+ AffineMatrix
+ inverse_affine;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ PointInfo
+ extent[4],
+ min,
+ max,
+ point;
+
+ register long
+ i;
+
+ ResampleFilter
+ **resample_filter;
+
+ SegmentInfo
+ edge;
+
+ CacheView
+ *image_view,
+ *source_view;
+
+ /*
+ Determine bounding box.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(source != (const Image *) NULL);
+ assert(source->signature == MagickSignature);
+ assert(affine != (AffineMatrix *) NULL);
+ extent[0].x=0.0;
+ extent[0].y=0.0;
+ extent[1].x=(double) source->columns-1.0;
+ extent[1].y=0.0;
+ extent[2].x=(double) source->columns-1.0;
+ extent[2].y=(double) source->rows-1.0;
+ extent[3].x=0.0;
+ extent[3].y=(double) source->rows-1.0;
+ for (i=0; i < 4; i++)
+ {
+ point=extent[i];
+ extent[i].x=point.x*affine->sx+point.y*affine->ry+affine->tx;
+ extent[i].y=point.x*affine->rx+point.y*affine->sy+affine->ty;
+ }
+ min=extent[0];
+ max=extent[0];
+ for (i=1; i < 4; i++)
+ {
+ if (min.x > extent[i].x)
+ min.x=extent[i].x;
+ if (min.y > extent[i].y)
+ min.y=extent[i].y;
+ if (max.x < extent[i].x)
+ max.x=extent[i].x;
+ if (max.y < extent[i].y)
+ max.y=extent[i].y;
+ }
+ /*
+ Affine transform image.
+ */
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ status=MagickTrue;
+ edge.x1=MagickMax(min.x,0.0);
+ edge.y1=MagickMax(min.y,0.0);
+ edge.x2=MagickMin(max.x,(double) image->columns-1.0);
+ edge.y2=MagickMin(max.y,(double) image->rows-1.0);
+ inverse_affine=InverseAffineMatrix(affine);
+ GetMagickPixelPacket(image,&zero);
+ exception=(&image->exception);
+ resample_filter=AcquireResampleFilterThreadSet(source,MagickTrue,exception);
+ image_view=AcquireCacheView(image);
+ source_view=AcquireCacheView(source);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(status)
+#endif
+ for (y=(long) (edge.y1+0.5); y <= (long) (edge.y2+0.5); y++)
+ {
+ long
+ x_offset;
+
+ MagickPixelPacket
+ composite,
+ pixel;
+
+ PointInfo
+ point;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ SegmentInfo
+ inverse_edge;
+
+ inverse_edge=AffineEdge(source,&inverse_affine,(double) y,&edge);
+ if (inverse_edge.x2 < inverse_edge.x1)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,(long) (inverse_edge.x1+0.5),y,
+ (unsigned long) ((long) (inverse_edge.x2+0.5)-(long) (inverse_edge.x1+
+ 0.5)+1),1,exception);
+ if (q == (PixelPacket *) NULL)
+ continue;
+ id=GetOpenMPThreadId();
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ pixel=zero;
+ composite=zero;
+ x_offset=0;
+ for (x=(long) (inverse_edge.x1+0.5); x <= (long) (inverse_edge.x2+0.5); x++)
+ {
+ point.x=(double) x*inverse_affine.sx+y*inverse_affine.ry+
+ inverse_affine.tx;
+ point.y=(double) x*inverse_affine.rx+y*inverse_affine.sy+
+ inverse_affine.ty;
+ (void) ResamplePixelColor(resample_filter[id],point.x,point.y,&pixel);
+ SetMagickPixelPacket(image,q,indexes+x_offset,&composite);
+ MagickPixelCompositeOver(&pixel,pixel.opacity,&composite,
+ composite.opacity,&composite);
+ SetPixelPacket(image,&composite,q,indexes+x_offset);
+ x_offset++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ source_view=DestroyCacheView(source_view);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D r a w B o u n d i n g R e c t a n g l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawBoundingRectangles() draws the bounding rectangles on the image. This
+% is only useful for developers debugging the rendering algorithm.
+%
+% The format of the DrawBoundingRectangles method is:
+%
+% void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
+% PolygonInfo *polygon_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o polygon_info: Specifies a pointer to a PolygonInfo structure.
+%
+*/
+static void DrawBoundingRectangles(Image *image,const DrawInfo *draw_info,
+ const PolygonInfo *polygon_info)
+{
+ DrawInfo
+ *clone_info;
+
+ long
+ coordinates;
+
+ MagickRealType
+ mid;
+
+ PointInfo
+ end,
+ resolution,
+ start;
+
+ PrimitiveInfo
+ primitive_info[6];
+
+ register long
+ i;
+
+ SegmentInfo
+ bounds;
+
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ (void) QueryColorDatabase("#0000",&clone_info->fill,&image->exception);
+ resolution.x=DefaultResolution;
+ resolution.y=DefaultResolution;
+ if (clone_info->density != (char *) NULL)
+ {
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ flags=ParseGeometry(clone_info->density,&geometry_info);
+ resolution.x=geometry_info.rho;
+ resolution.y=geometry_info.sigma;
+ if ((flags & SigmaValue) == MagickFalse)
+ resolution.y=resolution.x;
+ }
+ mid=(resolution.x/72.0)*ExpandAffine(&clone_info->affine)*
+ clone_info->stroke_width/2.0;
+ bounds.x1=0.0;
+ bounds.y1=0.0;
+ bounds.x2=0.0;
+ bounds.y2=0.0;
+ if (polygon_info != (PolygonInfo *) NULL)
+ {
+ bounds=polygon_info->edges[0].bounds;
+ for (i=1; i < (long) polygon_info->number_edges; i++)
+ {
+ if (polygon_info->edges[i].bounds.x1 < (double) bounds.x1)
+ bounds.x1=polygon_info->edges[i].bounds.x1;
+ if (polygon_info->edges[i].bounds.y1 < (double) bounds.y1)
+ bounds.y1=polygon_info->edges[i].bounds.y1;
+ if (polygon_info->edges[i].bounds.x2 > (double) bounds.x2)
+ bounds.x2=polygon_info->edges[i].bounds.x2;
+ if (polygon_info->edges[i].bounds.y2 > (double) bounds.y2)
+ bounds.y2=polygon_info->edges[i].bounds.y2;
+ }
+ bounds.x1-=mid;
+ bounds.x1=bounds.x1 < 0.0 ? 0.0 : bounds.x1 >= (double)
+ image->columns ? (double) image->columns-1 : bounds.x1;
+ bounds.y1-=mid;
+ bounds.y1=bounds.y1 < 0.0 ? 0.0 : bounds.y1 >= (double)
+ image->rows ? (double) image->rows-1 : bounds.y1;
+ bounds.x2+=mid;
+ bounds.x2=bounds.x2 < 0.0 ? 0.0 : bounds.x2 >= (double)
+ image->columns ? (double) image->columns-1 : bounds.x2;
+ bounds.y2+=mid;
+ bounds.y2=bounds.y2 < 0.0 ? 0.0 : bounds.y2 >= (double)
+ image->rows ? (double) image->rows-1 : bounds.y2;
+ for (i=0; i < (long) polygon_info->number_edges; i++)
+ {
+ if (polygon_info->edges[i].direction != 0)
+ (void) QueryColorDatabase("red",&clone_info->stroke,
+ &image->exception);
+ else
+ (void) QueryColorDatabase("green",&clone_info->stroke,
+ &image->exception);
+ start.x=(double) (polygon_info->edges[i].bounds.x1-mid);
+ start.y=(double) (polygon_info->edges[i].bounds.y1-mid);
+ end.x=(double) (polygon_info->edges[i].bounds.x2+mid);
+ end.y=(double) (polygon_info->edges[i].bounds.y2+mid);
+ primitive_info[0].primitive=RectanglePrimitive;
+ TraceRectangle(primitive_info,start,end);
+ primitive_info[0].method=ReplaceMethod;
+ coordinates=(long) primitive_info[0].coordinates;
+ primitive_info[coordinates].primitive=UndefinedPrimitive;
+ (void) DrawPrimitive(image,clone_info,primitive_info);
+ }
+ }
+ (void) QueryColorDatabase("blue",&clone_info->stroke,&image->exception);
+ start.x=(double) (bounds.x1-mid);
+ start.y=(double) (bounds.y1-mid);
+ end.x=(double) (bounds.x2+mid);
+ end.y=(double) (bounds.y2+mid);
+ primitive_info[0].primitive=RectanglePrimitive;
+ TraceRectangle(primitive_info,start,end);
+ primitive_info[0].method=ReplaceMethod;
+ coordinates=(long) primitive_info[0].coordinates;
+ primitive_info[coordinates].primitive=UndefinedPrimitive;
+ (void) DrawPrimitive(image,clone_info,primitive_info);
+ clone_info=DestroyDrawInfo(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w C l i p P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawClipPath() draws the clip path on the image mask.
+%
+% The format of the DrawClipPath method is:
+%
+% MagickBooleanType DrawClipPath(Image *image,const DrawInfo *draw_info,
+% const char *name)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o name: the name of the clip path.
+%
+*/
+MagickExport MagickBooleanType DrawClipPath(Image *image,
+ const DrawInfo *draw_info,const char *name)
+{
+ char
+ clip_mask[MaxTextExtent];
+
+ const char
+ *value;
+
+ DrawInfo
+ *clone_info;
+
+ MagickStatusType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (const DrawInfo *) NULL);
+ (void) FormatMagickString(clip_mask,MaxTextExtent,"%s",name);
+ value=GetImageArtifact(image,clip_mask);
+ if (value == (const char *) NULL)
+ return(MagickFalse);
+ if (image->clip_mask == (Image *) NULL)
+ {
+ Image
+ *clip_mask;
+
+ clip_mask=CloneImage(image,image->columns,image->rows,MagickTrue,
+ &image->exception);
+ if (clip_mask == (Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageClipMask(image,clip_mask);
+ clip_mask=DestroyImage(clip_mask);
+ }
+ (void) QueryColorDatabase("#00000000",&image->clip_mask->background_color,
+ &image->exception);
+ image->clip_mask->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(image->clip_mask);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),"\nbegin clip-path %s",
+ draw_info->clip_mask);
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ (void) CloneString(&clone_info->primitive,value);
+ (void) QueryColorDatabase("#ffffff",&clone_info->fill,&image->exception);
+ clone_info->clip_mask=(char *) NULL;
+ status=DrawImage(image->clip_mask,clone_info);
+ status|=NegateImage(image->clip_mask,MagickFalse);
+ clone_info=DestroyDrawInfo(clone_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end clip-path");
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D r a w D a s h P o l y g o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawDashPolygon() draws a dashed polygon (line, rectangle, ellipse) on the
+% image while respecting the dash offset and dash pattern attributes.
+%
+% The format of the DrawDashPolygon method is:
+%
+% MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
+% const PrimitiveInfo *primitive_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o draw_info: the draw info.
+%
+% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
+%
+% o image: the image.
+%
+%
+*/
+static MagickBooleanType DrawDashPolygon(const DrawInfo *draw_info,
+ const PrimitiveInfo *primitive_info,Image *image)
+{
+ DrawInfo
+ *clone_info;
+
+ long
+ j,
+ n;
+
+ MagickRealType
+ length,
+ maximum_length,
+ offset,
+ scale,
+ total_length;
+
+ MagickStatusType
+ status;
+
+ PrimitiveInfo
+ *dash_polygon;
+
+ register long
+ i;
+
+ register MagickRealType
+ dx,
+ dy;
+
+ unsigned long
+ number_vertices;
+
+ assert(draw_info != (const DrawInfo *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-dash");
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ clone_info->miterlimit=0;
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
+ number_vertices=(unsigned long) i;
+ dash_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
+ (2UL*number_vertices+1UL),sizeof(*dash_polygon));
+ if (dash_polygon == (PrimitiveInfo *) NULL)
+ return(MagickFalse);
+ dash_polygon[0]=primitive_info[0];
+ scale=ExpandAffine(&draw_info->affine);
+ length=scale*(draw_info->dash_pattern[0]-0.5);
+ offset=draw_info->dash_offset != 0.0 ? scale*draw_info->dash_offset : 0.0;
+ j=1;
+ for (n=0; offset > 0.0; j=0)
+ {
+ if (draw_info->dash_pattern[n] <= 0.0)
+ break;
+ length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
+ if (offset > length)
+ {
+ offset-=length;
+ n++;
+ length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
+ continue;
+ }
+ if (offset < length)
+ {
+ length-=offset;
+ offset=0.0;
+ break;
+ }
+ offset=0.0;
+ n++;
+ }
+ status=MagickTrue;
+ maximum_length=0.0;
+ total_length=0.0;
+ for (i=1; i < (long) number_vertices; i++)
+ {
+ dx=primitive_info[i].point.x-primitive_info[i-1].point.x;
+ dy=primitive_info[i].point.y-primitive_info[i-1].point.y;
+ maximum_length=hypot((double) dx,dy);
+ if (length == 0.0)
+ {
+ n++;
+ if (draw_info->dash_pattern[n] == 0.0)
+ n=0;
+ length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
+ }
+ for (total_length=0.0; (total_length+length) < maximum_length; )
+ {
+ total_length+=length;
+ if ((n & 0x01) != 0)
+ {
+ dash_polygon[0]=primitive_info[0];
+ dash_polygon[0].point.x=(double) (primitive_info[i-1].point.x+dx*
+ total_length/maximum_length);
+ dash_polygon[0].point.y=(double) (primitive_info[i-1].point.y+dy*
+ total_length/maximum_length);
+ j=1;
+ }
+ else
+ {
+ if ((j+1) > (long) (2*number_vertices))
+ break;
+ dash_polygon[j]=primitive_info[i-1];
+ dash_polygon[j].point.x=(double) (primitive_info[i-1].point.x+dx*
+ total_length/maximum_length);
+ dash_polygon[j].point.y=(double) (primitive_info[i-1].point.y+dy*
+ total_length/maximum_length);
+ dash_polygon[j].coordinates=1;
+ j++;
+ dash_polygon[0].coordinates=(unsigned long) j;
+ dash_polygon[j].primitive=UndefinedPrimitive;
+ status|=DrawStrokePolygon(image,clone_info,dash_polygon);
+ }
+ n++;
+ if (draw_info->dash_pattern[n] == 0.0)
+ n=0;
+ length=scale*(draw_info->dash_pattern[n]+(n == 0 ? -0.5 : 0.5));
+ }
+ length-=(maximum_length-total_length);
+ if ((n & 0x01) != 0)
+ continue;
+ dash_polygon[j]=primitive_info[i];
+ dash_polygon[j].coordinates=1;
+ j++;
+ }
+ if ((total_length < maximum_length) && ((n & 0x01) == 0) && (j > 1))
+ {
+ dash_polygon[j]=primitive_info[i-1];
+ dash_polygon[j].point.x+=MagickEpsilon;
+ dash_polygon[j].point.y+=MagickEpsilon;
+ dash_polygon[j].coordinates=1;
+ j++;
+ dash_polygon[0].coordinates=(unsigned long) j;
+ dash_polygon[j].primitive=UndefinedPrimitive;
+ status|=DrawStrokePolygon(image,clone_info,dash_polygon);
+ }
+ dash_polygon=(PrimitiveInfo *) RelinquishMagickMemory(dash_polygon);
+ clone_info=DestroyDrawInfo(clone_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-dash");
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawImage() draws a graphic primitive on your image. The primitive
+% may be represented as a string or filename. Precede the filename with an
+% "at" sign (@) and the contents of the file are drawn on the image. You
+% can affect how text is drawn by setting one or more members of the draw
+% info structure.
+%
+% The format of the DrawImage method is:
+%
+% MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+*/
+
+static inline MagickBooleanType IsPoint(const char *point)
+{
+ char
+ *p;
+
+ double
+ value;
+
+ value=strtod(point,&p);
+ return((value == 0.0) && (p == point) ? MagickFalse : MagickTrue);
+}
+
+static inline void TracePoint(PrimitiveInfo *primitive_info,
+ const PointInfo point)
+{
+ primitive_info->coordinates=1;
+ primitive_info->point=point;
+}
+
+MagickExport MagickBooleanType DrawImage(Image *image,const DrawInfo *draw_info)
+{
+#define RenderImageTag "Render/Image"
+
+ AffineMatrix
+ affine,
+ current;
+
+ char
+ key[2*MaxTextExtent],
+ keyword[MaxTextExtent],
+ geometry[MaxTextExtent],
+ name[MaxTextExtent],
+ pattern[MaxTextExtent],
+ *primitive,
+ *token;
+
+ const char
+ *q;
+
+ DrawInfo
+ **graphic_context;
+
+ long
+ j,
+ k,
+ n;
+
+ MagickBooleanType
+ proceed,
+ status;
+
+ MagickRealType
+ angle,
+ factor,
+ primitive_extent;
+
+ PointInfo
+ point;
+
+ PixelPacket
+ start_color;
+
+ PrimitiveInfo
+ *primitive_info;
+
+ PrimitiveType
+ primitive_type;
+
+ register const char
+ *p;
+
+ register long
+ i,
+ x;
+
+ SegmentInfo
+ bounds;
+
+ size_t
+ length;
+
+ unsigned long
+ number_points;
+
+ /*
+ Ensure the annotation info is valid.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((draw_info->primitive == (char *) NULL) ||
+ (*draw_info->primitive == '\0'))
+ return(MagickFalse);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),"begin draw-image");
+ if (*draw_info->primitive != '@')
+ primitive=AcquireString(draw_info->primitive);
+ else
+ primitive=FileToString(draw_info->primitive+1,~0,&image->exception);
+ if (primitive == (char *) NULL)
+ return(MagickFalse);
+ primitive_extent=(MagickRealType) strlen(primitive);
+ (void) SetImageArtifact(image,"MVG",primitive);
+ n=0;
+ /*
+ Allocate primitive info memory.
+ */
+ graphic_context=(DrawInfo **) AcquireMagickMemory(sizeof(*graphic_context));
+ if (graphic_context == (DrawInfo **) NULL)
+ {
+ primitive=DestroyString(primitive);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ number_points=2047;
+ primitive_info=(PrimitiveInfo *) AcquireQuantumMemory((size_t) number_points,
+ sizeof(*primitive_info));
+ if (primitive_info == (PrimitiveInfo *) NULL)
+ {
+ primitive=DestroyString(primitive);
+ for ( ; n >= 0; n--)
+ graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
+ graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ graphic_context[n]->viewbox=image->page;
+ if ((image->page.width == 0) || (image->page.height == 0))
+ {
+ graphic_context[n]->viewbox.width=image->columns;
+ graphic_context[n]->viewbox.height=image->rows;
+ }
+ token=AcquireString(primitive);
+ (void) QueryColorDatabase("#000000",&start_color,&image->exception);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ status=MagickTrue;
+ for (q=primitive; *q != '\0'; )
+ {
+ /*
+ Interpret graphic primitive.
+ */
+ GetMagickToken(q,&q,keyword);
+ if (*keyword == '\0')
+ break;
+ if (*keyword == '#')
+ {
+ /*
+ Comment.
+ */
+ while ((*q != '\n') && (*q != '\0'))
+ q++;
+ continue;
+ }
+ p=q-strlen(keyword)-1;
+ primitive_type=UndefinedPrimitive;
+ current=graphic_context[n]->affine;
+ GetAffineMatrix(&affine);
+ switch (*keyword)
+ {
+ case ';':
+ break;
+ case 'a':
+ case 'A':
+ {
+ if (LocaleCompare("affine",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ affine.sx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.rx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.ry=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.sy=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.tx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.ty=atof(token);
+ break;
+ }
+ if (LocaleCompare("arc",keyword) == 0)
+ {
+ primitive_type=ArcPrimitive;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'b':
+ case 'B':
+ {
+ if (LocaleCompare("bezier",keyword) == 0)
+ {
+ primitive_type=BezierPrimitive;
+ break;
+ }
+ if (LocaleCompare("border-color",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) QueryColorDatabase(token,&graphic_context[n]->border_color,
+ &image->exception);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'c':
+ case 'C':
+ {
+ if (LocaleCompare("clip-path",keyword) == 0)
+ {
+ /*
+ Create clip mask.
+ */
+ GetMagickToken(q,&q,token);
+ (void) CloneString(&graphic_context[n]->clip_mask,token);
+ (void) DrawClipPath(image,graphic_context[n],
+ graphic_context[n]->clip_mask);
+ break;
+ }
+ if (LocaleCompare("clip-rule",keyword) == 0)
+ {
+ long
+ fill_rule;
+
+ GetMagickToken(q,&q,token);
+ fill_rule=ParseMagickOption(MagickFillRuleOptions,MagickFalse,
+ token);
+ if (fill_rule == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->fill_rule=(FillRule) fill_rule;
+ break;
+ }
+ if (LocaleCompare("clip-units",keyword) == 0)
+ {
+ long
+ clip_units;
+
+ GetMagickToken(q,&q,token);
+ clip_units=ParseMagickOption(MagickClipPathOptions,MagickFalse,
+ token);
+ if (clip_units == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->clip_units=(ClipPathUnits) clip_units;
+ if (clip_units == ObjectBoundingBox)
+ {
+ GetAffineMatrix(¤t);
+ affine.sx=draw_info->bounds.x2;
+ affine.sy=draw_info->bounds.y2;
+ affine.tx=draw_info->bounds.x1;
+ affine.ty=draw_info->bounds.y1;
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare("circle",keyword) == 0)
+ {
+ primitive_type=CirclePrimitive;
+ break;
+ }
+ if (LocaleCompare("color",keyword) == 0)
+ {
+ primitive_type=ColorPrimitive;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'd':
+ case 'D':
+ {
+ if (LocaleCompare("decorate",keyword) == 0)
+ {
+ long
+ decorate;
+
+ GetMagickToken(q,&q,token);
+ decorate=ParseMagickOption(MagickDecorateOptions,MagickFalse,
+ token);
+ if (decorate == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->decorate=(DecorationType) decorate;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'e':
+ case 'E':
+ {
+ if (LocaleCompare("ellipse",keyword) == 0)
+ {
+ primitive_type=EllipsePrimitive;
+ break;
+ }
+ if (LocaleCompare("encoding",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) CloneString(&graphic_context[n]->encoding,token);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'f':
+ case 'F':
+ {
+ if (LocaleCompare("fill",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) FormatMagickString(pattern,MaxTextExtent,"%s",token);
+ if (GetImageArtifact(image,pattern) != (const char *) NULL)
+ (void) DrawPatternPath(image,draw_info,token,
+ &graphic_context[n]->fill_pattern);
+ else
+ {
+ status=QueryColorDatabase(token,&graphic_context[n]->fill,
+ &image->exception);
+ if (status == MagickFalse)
+ {
+ ImageInfo
+ *pattern_info;
+
+ pattern_info=AcquireImageInfo();
+ (void) CopyMagickString(pattern_info->filename,token,
+ MaxTextExtent);
+ graphic_context[n]->fill_pattern=
+ ReadImage(pattern_info,&image->exception);
+ CatchException(&image->exception);
+ pattern_info=DestroyImageInfo(pattern_info);
+ }
+ }
+ break;
+ }
+ if (LocaleCompare("fill-opacity",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
+ graphic_context[n]->fill.opacity=RoundToQuantum((MagickRealType)
+ QuantumRange*(1.0-factor*atof(token)));
+ break;
+ }
+ if (LocaleCompare("fill-rule",keyword) == 0)
+ {
+ long
+ fill_rule;
+
+ GetMagickToken(q,&q,token);
+ fill_rule=ParseMagickOption(MagickFillRuleOptions,MagickFalse,
+ token);
+ if (fill_rule == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->fill_rule=(FillRule) fill_rule;
+ break;
+ }
+ if (LocaleCompare("font",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) CloneString(&graphic_context[n]->font,token);
+ if (LocaleCompare("none",token) == 0)
+ graphic_context[n]->font=(char *)
+ RelinquishMagickMemory(graphic_context[n]->font);
+ break;
+ }
+ if (LocaleCompare("font-family",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) CloneString(&graphic_context[n]->family,token);
+ break;
+ }
+ if (LocaleCompare("font-size",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->pointsize=atof(token);
+ break;
+ }
+ if (LocaleCompare("font-stretch",keyword) == 0)
+ {
+ long
+ stretch;
+
+ GetMagickToken(q,&q,token);
+ stretch=ParseMagickOption(MagickStretchOptions,MagickFalse,token);
+ if (stretch == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->stretch=(StretchType) stretch;
+ break;
+ }
+ if (LocaleCompare("font-style",keyword) == 0)
+ {
+ long
+ style;
+
+ GetMagickToken(q,&q,token);
+ style=ParseMagickOption(MagickStyleOptions,MagickFalse,token);
+ if (style == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->style=(StyleType) style;
+ break;
+ }
+ if (LocaleCompare("font-weight",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->weight=(unsigned long) atol(token);
+ if (LocaleCompare(token,"all") == 0)
+ graphic_context[n]->weight=0;
+ if (LocaleCompare(token,"bold") == 0)
+ graphic_context[n]->weight=700;
+ if (LocaleCompare(token,"bolder") == 0)
+ if (graphic_context[n]->weight <= 800)
+ graphic_context[n]->weight+=100;
+ if (LocaleCompare(token,"lighter") == 0)
+ if (graphic_context[n]->weight >= 100)
+ graphic_context[n]->weight-=100;
+ if (LocaleCompare(token,"normal") == 0)
+ graphic_context[n]->weight=400;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'g':
+ case 'G':
+ {
+ if (LocaleCompare("gradient-units",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("gravity",keyword) == 0)
+ {
+ long
+ gravity;
+
+ GetMagickToken(q,&q,token);
+ gravity=ParseMagickOption(MagickGravityOptions,MagickFalse,token);
+ if (gravity == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->gravity=(GravityType) gravity;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'i':
+ case 'I':
+ {
+ if (LocaleCompare("image",keyword) == 0)
+ {
+ long
+ compose;
+
+ primitive_type=ImagePrimitive;
+ GetMagickToken(q,&q,token);
+ compose=ParseMagickOption(MagickComposeOptions,MagickFalse,token);
+ if (compose == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->compose=(CompositeOperator) compose;
+ break;
+ }
+ if (LocaleCompare("interword-spacing",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->interword_spacing=atof(token);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'k':
+ case 'K':
+ {
+ if (LocaleCompare("kerning",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->kerning=atof(token);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'l':
+ case 'L':
+ {
+ if (LocaleCompare("line",keyword) == 0)
+ {
+ primitive_type=LinePrimitive;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'm':
+ case 'M':
+ {
+ if (LocaleCompare("matte",keyword) == 0)
+ {
+ primitive_type=MattePrimitive;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'o':
+ case 'O':
+ {
+ if (LocaleCompare("offset",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("opacity",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
+ graphic_context[n]->opacity=RoundToQuantum((MagickRealType)
+ QuantumRange*(1.0-((1.0-QuantumScale*graphic_context[n]->opacity)*
+ factor*atof(token))));
+ graphic_context[n]->fill.opacity=graphic_context[n]->opacity;
+ graphic_context[n]->stroke.opacity=graphic_context[n]->opacity;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'p':
+ case 'P':
+ {
+ if (LocaleCompare("path",keyword) == 0)
+ {
+ primitive_type=PathPrimitive;
+ break;
+ }
+ if (LocaleCompare("point",keyword) == 0)
+ {
+ primitive_type=PointPrimitive;
+ break;
+ }
+ if (LocaleCompare("polyline",keyword) == 0)
+ {
+ primitive_type=PolylinePrimitive;
+ break;
+ }
+ if (LocaleCompare("polygon",keyword) == 0)
+ {
+ primitive_type=PolygonPrimitive;
+ break;
+ }
+ if (LocaleCompare("pop",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare("clip-path",token) == 0)
+ break;
+ if (LocaleCompare("defs",token) == 0)
+ break;
+ if (LocaleCompare("gradient",token) == 0)
+ break;
+ if (LocaleCompare("graphic-context",token) == 0)
+ {
+ if (n <= 0)
+ {
+ (void) ThrowMagickException(&image->exception,
+ GetMagickModule(),DrawError,
+ "UnbalancedGraphicContextPushPop","`%s'",token);
+ n=0;
+ break;
+ }
+ if (graphic_context[n]->clip_mask != (char *) NULL)
+ if (LocaleCompare(graphic_context[n]->clip_mask,
+ graphic_context[n-1]->clip_mask) != 0)
+ (void) SetImageClipMask(image,(Image *) NULL);
+ graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
+ n--;
+ break;
+ }
+ if (LocaleCompare("pattern",token) == 0)
+ break;
+ status=MagickFalse;
+ break;
+ }
+ if (LocaleCompare("push",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare("clip-path",token) == 0)
+ {
+ char
+ name[MaxTextExtent];
+
+ GetMagickToken(q,&q,token);
+ (void) FormatMagickString(name,MaxTextExtent,"%s",token);
+ for (p=q; *q != '\0'; )
+ {
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(token,"pop") != 0)
+ continue;
+ GetMagickToken(q,(const char **) NULL,token);
+ if (LocaleCompare(token,"clip-path") != 0)
+ continue;
+ break;
+ }
+ (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
+ (void) SetImageArtifact(image,name,token);
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("gradient",token) == 0)
+ {
+ char
+ key[2*MaxTextExtent],
+ name[MaxTextExtent],
+ type[MaxTextExtent];
+
+ ElementInfo
+ element;
+
+ SegmentInfo
+ segment;
+
+ GetMagickToken(q,&q,token);
+ (void) CopyMagickString(name,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ (void) CopyMagickString(type,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ segment.x1=atof(token);
+ element.cx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ segment.y1=atof(token);
+ element.cy=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ segment.x2=atof(token);
+ element.major=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ segment.y2=atof(token);
+ element.minor=atof(token);
+ if (LocaleCompare(type,"radial") == 0)
+ {
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ element.angle=atof(token);
+ }
+ for (p=q; *q != '\0'; )
+ {
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(token,"pop") != 0)
+ continue;
+ GetMagickToken(q,(const char **) NULL,token);
+ if (LocaleCompare(token,"gradient") != 0)
+ continue;
+ break;
+ }
+ (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
+ bounds.x1=graphic_context[n]->affine.sx*segment.x1+
+ graphic_context[n]->affine.ry*segment.y1+
+ graphic_context[n]->affine.tx;
+ bounds.y1=graphic_context[n]->affine.rx*segment.x1+
+ graphic_context[n]->affine.sy*segment.y1+
+ graphic_context[n]->affine.ty;
+ bounds.x2=graphic_context[n]->affine.sx*segment.x2+
+ graphic_context[n]->affine.ry*segment.y2+
+ graphic_context[n]->affine.tx;
+ bounds.y2=graphic_context[n]->affine.rx*segment.x2+
+ graphic_context[n]->affine.sy*segment.y2+
+ graphic_context[n]->affine.ty;
+ (void) FormatMagickString(key,MaxTextExtent,"%s",name);
+ (void) SetImageArtifact(image,key,token);
+ (void) FormatMagickString(key,MaxTextExtent,"%s-geometry",name);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%gx%g%+f%+f",
+ MagickMax(fabs(bounds.x2-bounds.x1+1.0),1.0),
+ MagickMax(fabs(bounds.y2-bounds.y1+1.0),1.0),
+ bounds.x1,bounds.y1);
+ (void) SetImageArtifact(image,key,geometry);
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("pattern",token) == 0)
+ {
+ RectangleInfo
+ bounds;
+
+ GetMagickToken(q,&q,token);
+ (void) CopyMagickString(name,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ bounds.x=(long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ bounds.y=(long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ bounds.width=(unsigned long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ bounds.height=(unsigned long) (atof(token)+0.5);
+ for (p=q; *q != '\0'; )
+ {
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(token,"pop") != 0)
+ continue;
+ GetMagickToken(q,(const char **) NULL,token);
+ if (LocaleCompare(token,"pattern") != 0)
+ continue;
+ break;
+ }
+ (void) CopyMagickString(token,p,(size_t) (q-p-4+1));
+ (void) FormatMagickString(key,MaxTextExtent,"%s",name);
+ (void) SetImageArtifact(image,key,token);
+ (void) FormatMagickString(key,MaxTextExtent,"%s-geometry",name);
+ (void) FormatMagickString(geometry,MaxTextExtent,
+ "%lux%lu%+ld%+ld",bounds.width,bounds.height,bounds.x,
+ bounds.y);
+ (void) SetImageArtifact(image,key,geometry);
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("graphic-context",token) == 0)
+ {
+ n++;
+ graphic_context=(DrawInfo **) ResizeQuantumMemory(
+ graphic_context,(size_t) (n+1),sizeof(*graphic_context));
+ if (graphic_context == (DrawInfo **) NULL)
+ {
+ (void) ThrowMagickException(&image->exception,
+ GetMagickModule(),ResourceLimitError,
+ "MemoryAllocationFailed","`%s'",image->filename);
+ break;
+ }
+ graphic_context[n]=CloneDrawInfo((ImageInfo *) NULL,
+ graphic_context[n-1]);
+ break;
+ }
+ if (LocaleCompare("defs",token) == 0)
+ break;
+ status=MagickFalse;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'r':
+ case 'R':
+ {
+ if (LocaleCompare("rectangle",keyword) == 0)
+ {
+ primitive_type=RectanglePrimitive;
+ break;
+ }
+ if (LocaleCompare("rotate",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ angle=atof(token);
+ affine.sx=cos(DegreesToRadians(fmod((double) angle,360.0)));
+ affine.rx=sin(DegreesToRadians(fmod((double) angle,360.0)));
+ affine.ry=(-sin(DegreesToRadians(fmod((double) angle,360.0))));
+ affine.sy=cos(DegreesToRadians(fmod((double) angle,360.0)));
+ break;
+ }
+ if (LocaleCompare("roundRectangle",keyword) == 0)
+ {
+ primitive_type=RoundRectanglePrimitive;
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 's':
+ case 'S':
+ {
+ if (LocaleCompare("scale",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ affine.sx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.sy=atof(token);
+ break;
+ }
+ if (LocaleCompare("skewX",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ angle=atof(token);
+ affine.ry=sin(DegreesToRadians(angle));
+ break;
+ }
+ if (LocaleCompare("skewY",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ angle=atof(token);
+ affine.rx=(-tan(DegreesToRadians(angle)/2.0));
+ break;
+ }
+ if (LocaleCompare("stop-color",keyword) == 0)
+ {
+ PixelPacket
+ stop_color;
+
+ GetMagickToken(q,&q,token);
+ (void) QueryColorDatabase(token,&stop_color,&image->exception);
+ (void) GradientImage(image,LinearGradient,ReflectSpread,
+ &start_color,&stop_color);
+ start_color=stop_color;
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("stroke",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) FormatMagickString(pattern,MaxTextExtent,"%s",token);
+ if (GetImageArtifact(image,pattern) != (const char *) NULL)
+ (void) DrawPatternPath(image,draw_info,token,
+ &graphic_context[n]->stroke_pattern);
+ else
+ {
+ status=QueryColorDatabase(token,&graphic_context[n]->stroke,
+ &image->exception);
+ if (status == MagickFalse)
+ {
+ ImageInfo
+ *pattern_info;
+
+ pattern_info=AcquireImageInfo();
+ (void) CopyMagickString(pattern_info->filename,token,
+ MaxTextExtent);
+ graphic_context[n]->stroke_pattern=
+ ReadImage(pattern_info,&image->exception);
+ CatchException(&image->exception);
+ pattern_info=DestroyImageInfo(pattern_info);
+ }
+ }
+ break;
+ }
+ if (LocaleCompare("stroke-antialias",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->stroke_antialias=
+ atoi(token) != 0 ? MagickTrue : MagickFalse;
+ break;
+ }
+ if (LocaleCompare("stroke-dasharray",keyword) == 0)
+ {
+ if (graphic_context[n]->dash_pattern != (double *) NULL)
+ graphic_context[n]->dash_pattern=(double *)
+ RelinquishMagickMemory(graphic_context[n]->dash_pattern);
+ if (IsPoint(q) != MagickFalse)
+ {
+ const char
+ *p;
+
+ p=q;
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ for (x=0; IsPoint(token) != MagickFalse; x++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ }
+ graphic_context[n]->dash_pattern=(double *)
+ AcquireQuantumMemory((size_t) (2UL*x+1UL),
+ sizeof(*graphic_context[n]->dash_pattern));
+ if (graphic_context[n]->dash_pattern == (double *) NULL)
+ {
+ (void) ThrowMagickException(&image->exception,
+ GetMagickModule(),ResourceLimitError,
+ "MemoryAllocationFailed","`%s'",image->filename);
+ break;
+ }
+ for (j=0; j < x; j++)
+ {
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->dash_pattern[j]=atof(token);
+ }
+ if ((x & 0x01) != 0)
+ for ( ; j < (2*x); j++)
+ graphic_context[n]->dash_pattern[j]=
+ graphic_context[n]->dash_pattern[j-x];
+ graphic_context[n]->dash_pattern[j]=0.0;
+ break;
+ }
+ GetMagickToken(q,&q,token);
+ break;
+ }
+ if (LocaleCompare("stroke-dashoffset",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->dash_offset=atof(token);
+ break;
+ }
+ if (LocaleCompare("stroke-linecap",keyword) == 0)
+ {
+ long
+ linecap;
+
+ GetMagickToken(q,&q,token);
+ linecap=ParseMagickOption(MagickLineCapOptions,MagickFalse,token);
+ if (linecap == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->linecap=(LineCap) linecap;
+ break;
+ }
+ if (LocaleCompare("stroke-linejoin",keyword) == 0)
+ {
+ long
+ linejoin;
+
+ GetMagickToken(q,&q,token);
+ linejoin=ParseMagickOption(MagickLineJoinOptions,MagickFalse,token);
+ if (linejoin == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->linejoin=(LineJoin) linejoin;
+ break;
+ }
+ if (LocaleCompare("stroke-miterlimit",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->miterlimit=(unsigned long) atol(token);
+ break;
+ }
+ if (LocaleCompare("stroke-opacity",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ factor=strchr(token,'%') != (char *) NULL ? 0.01 : 1.0;
+ graphic_context[n]->stroke.opacity=RoundToQuantum((MagickRealType)
+ QuantumRange*(1.0-factor*atof(token)));
+ break;
+ }
+ if (LocaleCompare("stroke-width",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->stroke_width=atof(token);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 't':
+ case 'T':
+ {
+ if (LocaleCompare("text",keyword) == 0)
+ {
+ primitive_type=TextPrimitive;
+ break;
+ }
+ if (LocaleCompare("text-align",keyword) == 0)
+ {
+ long
+ align;
+
+ GetMagickToken(q,&q,token);
+ align=ParseMagickOption(MagickAlignOptions,MagickFalse,token);
+ if (align == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->align=(AlignType) align;
+ break;
+ }
+ if (LocaleCompare("text-anchor",keyword) == 0)
+ {
+ long
+ align;
+
+ GetMagickToken(q,&q,token);
+ align=ParseMagickOption(MagickAlignOptions,MagickFalse,token);
+ if (align == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ graphic_context[n]->align=(AlignType) align;
+ break;
+ }
+ if (LocaleCompare("text-antialias",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->text_antialias=
+ atoi(token) != 0 ? MagickTrue : MagickFalse;
+ break;
+ }
+ if (LocaleCompare("text-undercolor",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ (void) QueryColorDatabase(token,&graphic_context[n]->undercolor,
+ &image->exception);
+ break;
+ }
+ if (LocaleCompare("translate",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ affine.tx=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ affine.ty=atof(token);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ case 'v':
+ case 'V':
+ {
+ if (LocaleCompare("viewbox",keyword) == 0)
+ {
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->viewbox.x=(long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->viewbox.y=(long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->viewbox.width=(unsigned long) (atof(token)+0.5);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ graphic_context[n]->viewbox.height=(unsigned long) (atof(token)+
+ 0.5);
+ break;
+ }
+ status=MagickFalse;
+ break;
+ }
+ default:
+ {
+ status=MagickFalse;
+ break;
+ }
+ }
+ if (status == MagickFalse)
+ break;
+ if ((affine.sx != 1.0) || (affine.rx != 0.0) || (affine.ry != 0.0) ||
+ (affine.sy != 1.0) || (affine.tx != 0.0) || (affine.ty != 0.0))
+ {
+ graphic_context[n]->affine.sx=current.sx*affine.sx+current.ry*affine.rx;
+ graphic_context[n]->affine.rx=current.rx*affine.sx+current.sy*affine.rx;
+ graphic_context[n]->affine.ry=current.sx*affine.ry+current.ry*affine.sy;
+ graphic_context[n]->affine.sy=current.rx*affine.ry+current.sy*affine.sy;
+ graphic_context[n]->affine.tx=
+ current.sx*affine.tx+current.ry*affine.ty+current.tx;
+ graphic_context[n]->affine.ty=
+ current.rx*affine.tx+current.sy*affine.ty+current.ty;
+ }
+ if (primitive_type == UndefinedPrimitive)
+ {
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",
+ (int) (q-p),p);
+ continue;
+ }
+ /*
+ Parse the primitive attributes.
+ */
+ i=0;
+ j=0;
+ primitive_info[0].point.x=0.0;
+ primitive_info[0].point.y=0.0;
+ for (x=0; *q != '\0'; x++)
+ {
+ /*
+ Define points.
+ */
+ if (IsPoint(q) == MagickFalse)
+ break;
+ GetMagickToken(q,&q,token);
+ point.x=atof(token);
+ GetMagickToken(q,&q,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ point.y=atof(token);
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token == ',')
+ GetMagickToken(q,&q,token);
+ primitive_info[i].primitive=primitive_type;
+ primitive_info[i].point=point;
+ primitive_info[i].coordinates=0;
+ primitive_info[i].method=FloodfillMethod;
+ i++;
+ if (i < (long) number_points)
+ continue;
+ number_points<<=1;
+ primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
+ (size_t) number_points,sizeof(*primitive_info));
+ if (primitive_info == (PrimitiveInfo *) NULL)
+ {
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ break;
+ }
+ }
+ primitive_info[j].primitive=primitive_type;
+ primitive_info[j].coordinates=(unsigned long) x;
+ primitive_info[j].method=FloodfillMethod;
+ primitive_info[j].text=(char *) NULL;
+ /*
+ Circumscribe primitive within a circle.
+ */
+ bounds.x1=primitive_info[j].point.x;
+ bounds.y1=primitive_info[j].point.y;
+ bounds.x2=primitive_info[j].point.x;
+ bounds.y2=primitive_info[j].point.y;
+ for (k=1; k < (long) primitive_info[j].coordinates; k++)
+ {
+ point=primitive_info[j+k].point;
+ if (point.x < bounds.x1)
+ bounds.x1=point.x;
+ if (point.y < bounds.y1)
+ bounds.y1=point.y;
+ if (point.x > bounds.x2)
+ bounds.x2=point.x;
+ if (point.y > bounds.y2)
+ bounds.y2=point.y;
+ }
+ /*
+ Speculate how many points our primitive might consume.
+ */
+ length=primitive_info[j].coordinates;
+ switch (primitive_type)
+ {
+ case RectanglePrimitive:
+ {
+ length*=5;
+ break;
+ }
+ case RoundRectanglePrimitive:
+ {
+ length*=5+4*BezierQuantum;
+ break;
+ }
+ case BezierPrimitive:
+ {
+ if (primitive_info[j].coordinates > 107)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ DrawError,"TooManyBezierCoordinates","`%s'",token);
+ length=BezierQuantum*primitive_info[j].coordinates;
+ break;
+ }
+ case PathPrimitive:
+ {
+ char
+ *s,
+ *t;
+
+ GetMagickToken(q,&q,token);
+ k=1;
+ t=token;
+ for (s=token; *s != '\0'; s=t)
+ {
+ double
+ value;
+
+ value=strtod(s,&t);
+ if (s == t)
+ {
+ t++;
+ continue;
+ }
+ k++;
+ }
+ length+=k*BezierQuantum;
+ break;
+ }
+ case CirclePrimitive:
+ case ArcPrimitive:
+ case EllipsePrimitive:
+ {
+ MagickRealType
+ alpha,
+ beta,
+ radius;
+
+ alpha=bounds.x2-bounds.x1;
+ beta=bounds.y2-bounds.y1;
+ radius=hypot((double) alpha,(double) beta);
+ length=2*((size_t) (MagickPI*radius))+6*BezierQuantum+360+1;
+ break;
+ }
+ default:
+ break;
+ }
+ if ((unsigned long) (i+length) >= number_points)
+ {
+ /*
+ Resize based on speculative points required by primitive.
+ */
+ while ((unsigned long) (i+length) >= number_points)
+ number_points<<=1;
+ primitive_info=(PrimitiveInfo *) ResizeQuantumMemory(primitive_info,
+ (size_t) number_points,sizeof(*primitive_info));
+ if (primitive_info == (PrimitiveInfo *) NULL)
+ {
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ break;
+ }
+ }
+ switch (primitive_type)
+ {
+ case PointPrimitive:
+ default:
+ {
+ if (primitive_info[j].coordinates != 1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TracePoint(primitive_info+j,primitive_info[j].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case LinePrimitive:
+ {
+ if (primitive_info[j].coordinates != 2)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceLine(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case RectanglePrimitive:
+ {
+ if (primitive_info[j].coordinates != 2)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceRectangle(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case RoundRectanglePrimitive:
+ {
+ if (primitive_info[j].coordinates != 3)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceRoundRectangle(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point,primitive_info[j+2].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case ArcPrimitive:
+ {
+ if (primitive_info[j].coordinates != 3)
+ {
+ primitive_type=UndefinedPrimitive;
+ break;
+ }
+ TraceArc(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point,primitive_info[j+2].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case EllipsePrimitive:
+ {
+ if (primitive_info[j].coordinates != 3)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceEllipse(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point,primitive_info[j+2].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case CirclePrimitive:
+ {
+ if (primitive_info[j].coordinates != 2)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceCircle(primitive_info+j,primitive_info[j].point,
+ primitive_info[j+1].point);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case PolylinePrimitive:
+ break;
+ case PolygonPrimitive:
+ {
+ primitive_info[i]=primitive_info[j];
+ primitive_info[i].coordinates=0;
+ primitive_info[j].coordinates++;
+ i++;
+ break;
+ }
+ case BezierPrimitive:
+ {
+ if (primitive_info[j].coordinates < 3)
+ {
+ status=MagickFalse;
+ break;
+ }
+ TraceBezier(primitive_info+j,primitive_info[j].coordinates);
+ i=(long) (j+primitive_info[j].coordinates);
+ break;
+ }
+ case PathPrimitive:
+ {
+ i=(long) (j+TracePath(primitive_info+j,token));
+ break;
+ }
+ case ColorPrimitive:
+ case MattePrimitive:
+ {
+ long
+ method;
+
+ if (primitive_info[j].coordinates != 1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ GetMagickToken(q,&q,token);
+ method=ParseMagickOption(MagickMethodOptions,MagickFalse,token);
+ if (method == -1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ primitive_info[j].method=(PaintMethod) method;
+ break;
+ }
+ case TextPrimitive:
+ {
+ if (primitive_info[j].coordinates != 1)
+ {
+ status=MagickFalse;
+ break;
+ }
+ if (*token != ',')
+ GetMagickToken(q,&q,token);
+ primitive_info[j].text=AcquireString(token);
+ break;
+ }
+ case ImagePrimitive:
+ {
+ if (primitive_info[j].coordinates != 2)
+ {
+ status=MagickFalse;
+ break;
+ }
+ GetMagickToken(q,&q,token);
+ primitive_info[j].text=AcquireString(token);
+ break;
+ }
+ }
+ if (primitive_info == (PrimitiveInfo *) NULL)
+ break;
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," %.*s",(int) (q-p),p);
+ if (status == MagickFalse)
+ break;
+ primitive_info[i].primitive=UndefinedPrimitive;
+ if (i == 0)
+ continue;
+ /*
+ Transform points.
+ */
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
+ {
+ point=primitive_info[i].point;
+ primitive_info[i].point.x=graphic_context[n]->affine.sx*point.x+
+ graphic_context[n]->affine.ry*point.y+graphic_context[n]->affine.tx;
+ primitive_info[i].point.y=graphic_context[n]->affine.rx*point.x+
+ graphic_context[n]->affine.sy*point.y+graphic_context[n]->affine.ty;
+ point=primitive_info[i].point;
+ if (point.x < graphic_context[n]->bounds.x1)
+ graphic_context[n]->bounds.x1=point.x;
+ if (point.y < graphic_context[n]->bounds.y1)
+ graphic_context[n]->bounds.y1=point.y;
+ if (point.x > graphic_context[n]->bounds.x2)
+ graphic_context[n]->bounds.x2=point.x;
+ if (point.y > graphic_context[n]->bounds.y2)
+ graphic_context[n]->bounds.y2=point.y;
+ if (primitive_info[i].primitive == ImagePrimitive)
+ break;
+ }
+ if (i >= (long) number_points)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ if (graphic_context[n]->render != MagickFalse)
+ {
+ if ((n != 0) && (graphic_context[n]->clip_mask != (char *) NULL) &&
+ (LocaleCompare(graphic_context[n]->clip_mask,
+ graphic_context[n-1]->clip_mask) != 0))
+ (void) DrawClipPath(image,graphic_context[n],
+ graphic_context[n]->clip_mask);
+ (void) DrawPrimitive(image,graphic_context[n],primitive_info);
+ }
+ if (primitive_info->text != (char *) NULL)
+ primitive_info->text=(char *) RelinquishMagickMemory(
+ primitive_info->text);
+ proceed=SetImageProgress(image,RenderImageTag,q-primitive,(MagickSizeType)
+ primitive_extent);
+ if (proceed == MagickFalse)
+ break;
+ }
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end draw-image");
+ /*
+ Relinquish resources.
+ */
+ token=DestroyString(token);
+ if (primitive_info != (PrimitiveInfo *) NULL)
+ primitive_info=(PrimitiveInfo *) RelinquishMagickMemory(primitive_info);
+ primitive=DestroyString(primitive);
+ for ( ; n >= 0; n--)
+ graphic_context[n]=DestroyDrawInfo(graphic_context[n]);
+ graphic_context=(DrawInfo **) RelinquishMagickMemory(graphic_context);
+ if (status == MagickFalse)
+ ThrowBinaryException(DrawError,"NonconformingDrawingPrimitiveDefinition",
+ keyword);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w G r a d i e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawGradientImage() draws a linear gradient on the image.
+%
+% The format of the DrawGradientImage method is:
+%
+% MagickBooleanType DrawGradientImage(Image *image,
+% const DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o _info: the draw info.
+%
+*/
+
+static inline MagickRealType GetStopColorOffset(const GradientInfo *gradient,
+ const long x,const long y)
+{
+ switch (gradient->type)
+ {
+ case UndefinedGradient:
+ case LinearGradient:
+ {
+ MagickRealType
+ gamma,
+ length,
+ offset,
+ scale;
+
+ PointInfo
+ p,
+ q;
+
+ const SegmentInfo
+ *gradient_vector;
+
+ gradient_vector=(&gradient->gradient_vector);
+ p.x=gradient_vector->x2-gradient_vector->x1;
+ p.y=gradient_vector->y2-gradient_vector->y1;
+ q.x=(double) x-gradient_vector->x1;
+ q.y=(double) y-gradient_vector->y1;
+ length=sqrt(q.x*q.x+q.y*q.y);
+ gamma=sqrt(p.x*p.x+p.y*p.y)*length;
+ gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
+ scale=p.x*q.x+p.y*q.y;
+ offset=gamma*scale*length;
+ return(offset);
+ }
+ case RadialGradient:
+ {
+ MagickRealType
+ length,
+ offset;
+
+ PointInfo
+ v;
+
+ v.x=(double) x-gradient->center.x;
+ v.y=(double) y-gradient->center.y;
+ length=sqrt(v.x*v.x+v.y*v.y);
+ if (gradient->spread == RepeatSpread)
+ return(length);
+ offset=length/gradient->radius;
+ return(offset);
+ }
+ }
+ return(0.0);
+}
+
+MagickExport MagickBooleanType DrawGradientImage(Image *image,
+ const DrawInfo *draw_info)
+{
+ const GradientInfo
+ *gradient;
+
+ const SegmentInfo
+ *gradient_vector;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ length;
+
+ PointInfo
+ point;
+
+ RectangleInfo
+ bounding_box;
+
+ CacheView
+ *image_view;
+
+ /*
+ Draw linear or radial gradient on image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (const DrawInfo *) NULL);
+ gradient=(&draw_info->gradient);
+ gradient_vector=(&gradient->gradient_vector);
+ point.x=gradient_vector->x2-gradient_vector->x1;
+ point.y=gradient_vector->y2-gradient_vector->y1;
+ length=sqrt(point.x*point.x+point.y*point.y);
+ bounding_box=gradient->bounding_box;
+ status=MagickTrue;
+ exception=(&image->exception);
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=bounding_box.y; y < (long) bounding_box.height; y++)
+ {
+ long
+ j;
+
+ MagickPixelPacket
+ composite,
+ pixel;
+
+ MagickRealType
+ alpha,
+ offset;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ i,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ composite=zero;
+ offset=GetStopColorOffset(gradient,0,y);
+ if (gradient->type != RadialGradient)
+ offset/=length;
+ for (x=bounding_box.x; x < (long) bounding_box.width; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ switch (gradient->spread)
+ {
+ case UndefinedSpread:
+ case PadSpread:
+ {
+ if ((x != (long) (gradient_vector->x1+0.5)) ||
+ (y != (long) (gradient_vector->y1+0.5)))
+ {
+ offset=GetStopColorOffset(gradient,x,y);
+ if (gradient->type != RadialGradient)
+ offset/=length;
+ }
+ for (i=0; i < (long) gradient->number_stops; i++)
+ if (offset < gradient->stops[i].offset)
+ break;
+ if ((offset < 0.0) || (i == 0))
+ composite=gradient->stops[0].color;
+ else
+ if ((offset > 1.0) || (i == (long) gradient->number_stops))
+ composite=gradient->stops[gradient->number_stops-1].color;
+ else
+ {
+ j=i;
+ i--;
+ alpha=(offset-gradient->stops[i].offset)/
+ (gradient->stops[j].offset-gradient->stops[i].offset);
+ MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
+ &gradient->stops[j].color,alpha,&composite);
+ }
+ break;
+ }
+ case ReflectSpread:
+ {
+ if ((x != (long) (gradient_vector->x1+0.5)) ||
+ (y != (long) (gradient_vector->y1+0.5)))
+ {
+ offset=GetStopColorOffset(gradient,x,y);
+ if (gradient->type != RadialGradient)
+ offset/=length;
+ }
+ if (offset < 0.0)
+ offset=(-offset);
+ if ((long) fmod(offset,2.0) == 0)
+ offset=fmod(offset,1.0);
+ else
+ offset=1.0-fmod(offset,1.0);
+ for (i=0; i < (long) gradient->number_stops; i++)
+ if (offset < gradient->stops[i].offset)
+ break;
+ if (i == 0)
+ composite=gradient->stops[0].color;
+ else
+ if (i == (long) gradient->number_stops)
+ composite=gradient->stops[gradient->number_stops-1].color;
+ else
+ {
+ j=i;
+ i--;
+ alpha=(offset-gradient->stops[i].offset)/
+ (gradient->stops[j].offset-gradient->stops[i].offset);
+ MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
+ &gradient->stops[j].color,alpha,&composite);
+ }
+ break;
+ }
+ case RepeatSpread:
+ {
+ MagickBooleanType
+ antialias;
+
+ MagickRealType
+ repeat;
+
+ antialias=MagickFalse;
+ repeat=0.0;
+ if ((x != (long) (gradient_vector->x1+0.5)) ||
+ (y != (long) (gradient_vector->y1+0.5)))
+ {
+ offset=GetStopColorOffset(gradient,x,y);
+ if (gradient->type == LinearGradient)
+ {
+ repeat=fmod(offset,length);
+ if (repeat < 0.0)
+ repeat=length-fmod(-repeat,length);
+ else
+ repeat=fmod(offset,length);
+ antialias=(repeat < length) && ((repeat+1.0) > length) ?
+ MagickTrue : MagickFalse;
+ offset=repeat/length;
+ }
+ else
+ {
+ repeat=fmod(offset,gradient->radius);
+ if (repeat < 0.0)
+ repeat=gradient->radius-fmod(-repeat,gradient->radius);
+ else
+ repeat=fmod(offset,gradient->radius);
+ antialias=repeat+1.0 > gradient->radius ?
+ MagickTrue : MagickFalse;
+ offset=repeat/gradient->radius;
+ }
+ }
+ for (i=0; i < (long) gradient->number_stops; i++)
+ if (offset < gradient->stops[i].offset)
+ break;
+ if (i == 0)
+ composite=gradient->stops[0].color;
+ else
+ if (i == (long) gradient->number_stops)
+ composite=gradient->stops[gradient->number_stops-1].color;
+ else
+ {
+ j=i;
+ i--;
+ alpha=(offset-gradient->stops[i].offset)/
+ (gradient->stops[j].offset-gradient->stops[i].offset);
+ if (antialias != MagickFalse)
+ {
+ if (gradient->type == LinearGradient)
+ alpha=length-repeat;
+ else
+ alpha=gradient->radius-repeat;
+ i=0;
+ j=(long) gradient->number_stops-1L;
+ }
+ MagickPixelCompositeBlend(&gradient->stops[i].color,1.0-alpha,
+ &gradient->stops[j].color,alpha,&composite);
+ }
+ break;
+ }
+ }
+ MagickPixelCompositeOver(&composite,composite.opacity,&pixel,
+ pixel.opacity,&pixel);
+ SetPixelPacket(image,&pixel,q,indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w P a t t e r n P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawPatternPath() draws a pattern.
+%
+% The format of the DrawPatternPath method is:
+%
+% MagickBooleanType DrawPatternPath(Image *image,const DrawInfo *draw_info,
+% const char *name,Image **pattern)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o name: the pattern name.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType DrawPatternPath(Image *image,
+ const DrawInfo *draw_info,const char *name,Image **pattern)
+{
+ char
+ property[MaxTextExtent];
+
+ const char
+ *geometry,
+ *path;
+
+ DrawInfo
+ *clone_info;
+
+ ImageInfo
+ *image_info;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (const DrawInfo *) NULL);
+ assert(name != (const char *) NULL);
+ (void) FormatMagickString(property,MaxTextExtent,"%s",name);
+ path=GetImageArtifact(image,property);
+ if (path == (const char *) NULL)
+ return(MagickFalse);
+ (void) FormatMagickString(property,MaxTextExtent,"%s-geometry",name);
+ geometry=GetImageArtifact(image,property);
+ if (geometry == (const char *) NULL)
+ return(MagickFalse);
+ if ((*pattern) != (Image *) NULL)
+ *pattern=DestroyImage(*pattern);
+ image_info=AcquireImageInfo();
+ image_info->size=AcquireString(geometry);
+ *pattern=AcquireImage(image_info);
+ image_info=DestroyImageInfo(image_info);
+ (void) QueryColorDatabase("#00000000",&(*pattern)->background_color,
+ &image->exception);
+ (void) SetImageBackgroundColor(*pattern);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "begin pattern-path %s %s",name,geometry);
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ clone_info->fill_pattern=NewImageList();
+ clone_info->stroke_pattern=NewImageList();
+ (void) CloneString(&clone_info->primitive,path);
+ status=DrawImage(*pattern,clone_info);
+ clone_info=DestroyDrawInfo(clone_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),"end pattern-path");
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D r a w P o l y g o n P r i m i t i v e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawPolygonPrimitive() draws a polygon on the image.
+%
+% The format of the DrawPolygonPrimitive method is:
+%
+% MagickBooleanType DrawPolygonPrimitive(Image *image,
+% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
+%
+*/
+
+static PolygonInfo **DestroyPolygonThreadSet(PolygonInfo **polygon_info)
+{
+ register long
+ i;
+
+ assert(polygon_info != (PolygonInfo **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (polygon_info[i] != (PolygonInfo *) NULL)
+ polygon_info[i]=DestroyPolygonInfo(polygon_info[i]);
+ polygon_info=(PolygonInfo **) RelinquishAlignedMemory(polygon_info);
+ return(polygon_info);
+}
+
+static PolygonInfo **AcquirePolygonThreadSet(const DrawInfo *draw_info,
+ const PrimitiveInfo *primitive_info)
+{
+ PathInfo
+ *path_info;
+
+ register long
+ i;
+
+ PolygonInfo
+ **polygon_info;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ polygon_info=(PolygonInfo **) AcquireAlignedMemory(number_threads,
+ sizeof(*polygon_info));
+ if (polygon_info == (PolygonInfo **) NULL)
+ return((PolygonInfo **) NULL);
+ (void) ResetMagickMemory(polygon_info,0,GetOpenMPMaximumThreads()*
+ sizeof(*polygon_info));
+ path_info=ConvertPrimitiveToPath(draw_info,primitive_info);
+ if (path_info == (PathInfo *) NULL)
+ return(DestroyPolygonThreadSet(polygon_info));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ polygon_info[i]=ConvertPathToPolygon(draw_info,path_info);
+ if (polygon_info[i] == (PolygonInfo *) NULL)
+ return(DestroyPolygonThreadSet(polygon_info));
+ }
+ path_info=(PathInfo *) RelinquishMagickMemory(path_info);
+ return(polygon_info);
+}
+
+static MagickRealType GetPixelOpacity(PolygonInfo *polygon_info,
+ const MagickRealType mid,const MagickBooleanType fill,
+ const FillRule fill_rule,const long x,const long y,
+ MagickRealType *stroke_opacity)
+{
+ long
+ highwater,
+ j,
+ k,
+ number_edges,
+ number_points,
+ winding_number;
+
+ MagickBooleanType
+ quest;
+
+ MagickRealType
+ distance,
+ midpoint,
+ subpath_opacity;
+
+ PointInfo
+ current_point,
+ delta,
+ point,
+ previous_point;
+
+ register const PointInfo
+ *q;
+
+ register EdgeInfo
+ *p;
+
+ register long
+ i;
+
+ register MagickRealType
+ alpha,
+ beta;
+
+ SegmentInfo
+ edge,
+ bounds;
+
+ /*
+ Compute fill & stroke opacity for this (x,y) point.
+ */
+ *stroke_opacity=0.0;
+ subpath_opacity=0.0;
+ winding_number=0;
+ quest=MagickTrue;
+ point.x=(MagickRealType) x;
+ point.y=(MagickRealType) y;
+ edge.x1=0.0;
+ edge.y1=0.0;
+ edge.x2=0.0;
+ edge.y2=0.0;
+ p=polygon_info->edges;
+ number_edges=(long) polygon_info->number_edges;
+ midpoint=mid+0.5;
+ for (j=0; j < number_edges; j++, p++)
+ {
+ bounds=p->bounds;
+ if (point.y <= (bounds.y1-midpoint))
+ break;
+ if (point.y > (bounds.y2+midpoint-MagickEpsilon))
+ {
+ (void) DestroyEdge(polygon_info,(unsigned long) j);
+ number_edges=(long) polygon_info->number_edges;
+ continue;
+ }
+ if (point.y <= bounds.y1)
+ quest=MagickFalse;
+ else
+ if ((quest != MagickFalse) && (point.y <= bounds.y2) &&
+ (point.x > bounds.x1) && (point.x > bounds.x2))
+ winding_number+=p->direction != 0 ? 1 : -1;
+ if ((point.x <= (bounds.x1-midpoint)) ||
+ (point.x > (bounds.x2+midpoint-MagickEpsilon)))
+ continue;
+ highwater=(long) MagickMax((double) p->highwater,1.0);
+ number_points=(long) p->number_points;
+ k=highwater-1;
+ current_point.y=p->points[k].y;
+ for (i=highwater; i < number_points; i++)
+ {
+ previous_point.y=current_point.y;
+ current_point.y=p->points[i].y;
+ if (point.y < (previous_point.y-midpoint))
+ break;
+ if (point.y > (current_point.y+midpoint-MagickEpsilon))
+ continue;
+ q=p->points+i-1;
+ edge.x1=q->x;
+ edge.y1=q->y;
+ edge.x2=(q+1)->x;
+ edge.y2=(q+1)->y;
+ k=i;
+ if (p->scanline != point.y)
+ {
+ p->scanline=point.y;
+ p->highwater=(unsigned long) i;
+ highwater=i;
+ }
+ /*
+ Compute distance between a point and an edge.
+ */
+ delta.x=edge.x2-edge.x1;
+ delta.y=edge.y2-edge.y1;
+ beta=delta.x*(point.x-edge.x1)+delta.y*(point.y-edge.y1);
+ if (beta < 0.0)
+ {
+ delta.x=point.x-edge.x1;
+ delta.y=point.y-edge.y1;
+ distance=delta.x*delta.x+delta.y*delta.y;
+ }
+ else
+ {
+ alpha=delta.x*delta.x+delta.y*delta.y;
+ if (beta > alpha)
+ {
+ delta.x=point.x-edge.x2;
+ delta.y=point.y-edge.y2;
+ distance=delta.x*delta.x+delta.y*delta.y;
+ }
+ else
+ {
+ beta=delta.x*(point.y-edge.y1)-delta.y*(point.x-edge.x1);
+ distance=beta*beta/alpha;
+ }
+ }
+ /*
+ Compute stroke & subpath opacity.
+ */
+ beta=0.0;
+ if (p->ghostline == MagickFalse)
+ {
+ alpha=midpoint;
+ if ((*stroke_opacity < 1.0) &&
+ (distance <= ((alpha+0.25)*(alpha+0.25))))
+ {
+ alpha=mid-0.5;
+ if (distance <= ((alpha+0.25)*(alpha+0.25)))
+ *stroke_opacity=1.0;
+ else
+ {
+ beta=1.0;
+ if (distance != 1.0)
+ beta=sqrt((double) distance);
+ alpha=beta-midpoint;
+ if (*stroke_opacity < ((alpha-0.25)*(alpha-0.25)))
+ *stroke_opacity=(alpha-0.25)*(alpha-0.25);
+ }
+ }
+ }
+ if ((fill == MagickFalse) || (distance > 1.0) || (subpath_opacity >= 1.0))
+ continue;
+ if (distance <= 0.0)
+ {
+ subpath_opacity=1.0;
+ continue;
+ }
+ if (distance > 1.0)
+ continue;
+ if (beta == 0.0)
+ {
+ beta=1.0;
+ if (distance != 1.0)
+ beta=sqrt((double) distance);
+ }
+ alpha=beta-1.0;
+ if (subpath_opacity < (alpha*alpha-MagickEpsilon))
+ subpath_opacity=alpha*alpha;
+ }
+ /*
+ Determine winding number.
+ */
+ if ((quest == MagickFalse) || (point.y > bounds.y2) ||
+ (point.x <= bounds.x1) || (point.x > bounds.x2))
+ continue;
+ for (i=highwater; i < number_points; i++)
+ if (point.y <= p->points[i].y)
+ break;
+ if (i != k)
+ {
+ q=p->points+i-1;
+ edge.x1=q->x;
+ edge.y1=q->y;
+ edge.x2=(q+1)->x;
+ edge.y2=(q+1)->y;
+ }
+ if (((edge.x2-edge.x1)*(point.y-edge.y1)) <=
+ ((edge.y2-edge.y1)*(point.x-edge.x1)))
+ winding_number+=p->direction != 0 ? 1 : -1;
+ }
+ /*
+ Compute fill opacity.
+ */
+ if (fill == MagickFalse)
+ return(0.0);
+ if (subpath_opacity >= 1.0)
+ return(1.0);
+ if (fill_rule != NonZeroRule)
+ {
+ if ((MagickAbsoluteValue(winding_number) & 0x01) != 0)
+ return(1.0);
+ }
+ else
+ if (MagickAbsoluteValue(winding_number) != 0)
+ return(1.0);
+ return(subpath_opacity);
+}
+
+static MagickBooleanType DrawPolygonPrimitive(Image *image,
+ const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ start,
+ stop,
+ y;
+
+ MagickBooleanType
+ fill,
+ status;
+
+ MagickRealType
+ mid;
+
+ PolygonInfo
+ **polygon_info;
+
+ register EdgeInfo
+ *p;
+
+ register long
+ i;
+
+ SegmentInfo
+ bounds;
+
+ CacheView
+ *image_view;
+
+ /*
+ Compute bounding box.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ assert(primitive_info != (PrimitiveInfo *) NULL);
+ if (primitive_info->coordinates == 0)
+ return(MagickTrue);
+ polygon_info=AcquirePolygonThreadSet(draw_info,primitive_info);
+ if (polygon_info == (PolygonInfo **) NULL)
+ return(MagickFalse);
+ if (0)
+ DrawBoundingRectangles(image,draw_info,polygon_info[0]);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," begin draw-polygon");
+ fill=(primitive_info->method == FillToBorderMethod) ||
+ (primitive_info->method == FloodfillMethod) ? MagickTrue : MagickFalse;
+ mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
+ bounds=polygon_info[0]->edges[0].bounds;
+ for (i=1; i < (long) polygon_info[0]->number_edges; i++)
+ {
+ p=polygon_info[0]->edges+i;
+ if (p->bounds.x1 < bounds.x1)
+ bounds.x1=p->bounds.x1;
+ if (p->bounds.y1 < bounds.y1)
+ bounds.y1=p->bounds.y1;
+ if (p->bounds.x2 > bounds.x2)
+ bounds.x2=p->bounds.x2;
+ if (p->bounds.y2 > bounds.y2)
+ bounds.y2=p->bounds.y2;
+ }
+ bounds.x1-=(mid+1.0);
+ bounds.x1=bounds.x1 < 0.0 ? 0.0 : (unsigned long) (bounds.x1+0.5) >=
+ image->columns ? (double) image->columns-1.0 : bounds.x1;
+ bounds.y1-=(mid+1.0);
+ bounds.y1=bounds.y1 < 0.0 ? 0.0 : (unsigned long) (bounds.y1+0.5) >=
+ image->rows ? (double) image->rows-1.0 : bounds.y1;
+ bounds.x2+=(mid+1.0);
+ bounds.x2=bounds.x2 < 0.0 ? 0.0 : (unsigned long) (bounds.x2+0.5) >=
+ image->columns ? (double) image->columns-1.0 : bounds.x2;
+ bounds.y2+=(mid+1.0);
+ bounds.y2=bounds.y2 < 0.0 ? 0.0 : (unsigned long) (bounds.y2+0.5) >=
+ image->rows ? (double) image->rows-1.0 : bounds.y2;
+ status=MagickTrue;
+ exception=(&image->exception);
+ start=(long) (bounds.x1+0.5);
+ stop=(long) (bounds.x2+0.5);
+ image_view=AcquireCacheView(image);
+ if (primitive_info->coordinates == 1)
+ {
+ /*
+ Draw point.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for shared(status)
+#endif
+ for (y=(long) (bounds.y1+0.5); y <= (long) (bounds.y2+0.5); y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ x=start;
+ q=GetCacheViewAuthenticPixels(image_view,x,y,(unsigned long) (stop-x+1),
+ 1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for ( ; x <= stop; x++)
+ {
+ if ((x == (long) (primitive_info->point.x+0.5)) &&
+ (y == (long) (primitive_info->point.y+0.5)))
+ (void) GetStrokeColor(draw_info,x,y,q);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ polygon_info=DestroyPolygonThreadSet(polygon_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " end draw-polygon");
+ return(status);
+ }
+ /*
+ Draw polygon or line.
+ */
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(status)
+#endif
+ for (y=(long) (bounds.y1+0.5); y <= (long) (bounds.y2+0.5); y++)
+ {
+ MagickRealType
+ fill_opacity,
+ stroke_opacity;
+
+ PixelPacket
+ fill_color,
+ stroke_color;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,start,y,(unsigned long) (stop-
+ start+1),1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ id=GetOpenMPThreadId();
+ for (x=start; x <= stop; x++)
+ {
+ /*
+ Fill and/or stroke.
+ */
+ fill_opacity=GetPixelOpacity(polygon_info[id],mid,fill,
+ draw_info->fill_rule,x,y,&stroke_opacity);
+ if (draw_info->stroke_antialias == MagickFalse)
+ {
+ fill_opacity=fill_opacity > 0.25 ? 1.0 : 0.0;
+ stroke_opacity=stroke_opacity > 0.25 ? 1.0 : 0.0;
+ }
+ (void) GetFillColor(draw_info,x,y,&fill_color);
+ fill_opacity=(MagickRealType) (QuantumRange-fill_opacity*(QuantumRange-
+ fill_color.opacity));
+ MagickCompositeOver(&fill_color,fill_opacity,q,(MagickRealType)
+ q->opacity,q);
+ (void) GetStrokeColor(draw_info,x,y,&stroke_color);
+ stroke_opacity=(MagickRealType) (QuantumRange-stroke_opacity*
+ (QuantumRange-stroke_color.opacity));
+ MagickCompositeOver(&stroke_color,stroke_opacity,q,(MagickRealType)
+ q->opacity,q);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ polygon_info=DestroyPolygonThreadSet(polygon_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-polygon");
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D r a w P r i m i t i v e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawPrimitive() draws a primitive (line, rectangle, ellipse) on the image.
+%
+% The format of the DrawPrimitive method is:
+%
+% MagickBooleanType DrawPrimitive(Image *image,const DrawInfo *draw_info,
+% PrimitiveInfo *primitive_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
+%
+*/
+
+static void LogPrimitiveInfo(const PrimitiveInfo *primitive_info)
+{
+ const char
+ *methods[] =
+ {
+ "point",
+ "replace",
+ "floodfill",
+ "filltoborder",
+ "reset",
+ "?"
+ };
+
+ long
+ coordinates,
+ y;
+
+ PointInfo
+ p,
+ q,
+ point;
+
+ register long
+ i,
+ x;
+
+ x=(long) (primitive_info->point.x+0.5);
+ y=(long) (primitive_info->point.y+0.5);
+ switch (primitive_info->primitive)
+ {
+ case PointPrimitive:
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "PointPrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
+ return;
+ }
+ case ColorPrimitive:
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "ColorPrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
+ return;
+ }
+ case MattePrimitive:
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "MattePrimitive %ld,%ld %s",x,y,methods[primitive_info->method]);
+ return;
+ }
+ case TextPrimitive:
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "TextPrimitive %ld,%ld",x,y);
+ return;
+ }
+ case ImagePrimitive:
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ "ImagePrimitive %ld,%ld",x,y);
+ return;
+ }
+ default:
+ break;
+ }
+ coordinates=0;
+ p=primitive_info[0].point;
+ q.x=(-1.0);
+ q.y=(-1.0);
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++)
+ {
+ point=primitive_info[i].point;
+ if (coordinates <= 0)
+ {
+ coordinates=(long) primitive_info[i].coordinates;
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " begin open (%ld)",coordinates);
+ p=point;
+ }
+ point=primitive_info[i].point;
+ if ((fabs(q.x-point.x) > MagickEpsilon) ||
+ (fabs(q.y-point.y) > MagickEpsilon))
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," %ld: %g,%g",
+ coordinates,point.x,point.y);
+ else
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " %ld: %g,%g (duplicate)",coordinates,point.x,point.y);
+ q=point;
+ coordinates--;
+ if (coordinates > 0)
+ continue;
+ if ((fabs(p.x-point.x) > MagickEpsilon) ||
+ (fabs(p.y-point.y) > MagickEpsilon))
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end last (%ld)",
+ coordinates);
+ else
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end open (%ld)",
+ coordinates);
+ }
+}
+
+MagickExport MagickBooleanType DrawPrimitive(Image *image,
+ const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickStatusType
+ status;
+
+ register long
+ i,
+ x;
+
+ CacheView
+ *image_view;
+
+ if (image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " begin draw-primitive");
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " affine: %g,%g,%g,%g,%g,%g",draw_info->affine.sx,
+ draw_info->affine.rx,draw_info->affine.ry,draw_info->affine.sy,
+ draw_info->affine.tx,draw_info->affine.ty);
+ }
+ status=MagickTrue;
+ exception=(&image->exception);
+ x=(long) (primitive_info->point.x+0.5);
+ y=(long) (primitive_info->point.y+0.5);
+ image_view=AcquireCacheView(image);
+ switch (primitive_info->primitive)
+ {
+ case PointPrimitive:
+ {
+ PixelPacket
+ fill_color;
+
+ PixelPacket
+ *q;
+
+ q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ (void) GetFillColor(draw_info,x,y,&fill_color);
+ MagickCompositeOver(&fill_color,(MagickRealType) fill_color.opacity,q,
+ (MagickRealType) q->opacity,q);
+ (void) SyncCacheViewAuthenticPixels(image_view,exception);
+ break;
+ }
+ case ColorPrimitive:
+ {
+ switch (primitive_info->method)
+ {
+ case PointMethod:
+ default:
+ {
+ PixelPacket
+ *q;
+
+ q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ (void) GetFillColor(draw_info,x,y,q);
+ (void) SyncCacheViewAuthenticPixels(image_view,exception);
+ break;
+ }
+ case ReplaceMethod:
+ {
+ MagickBooleanType
+ sync;
+
+ PixelPacket
+ target;
+
+ (void) GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsColorSimilar(image,q,&target) == MagickFalse)
+ {
+ q++;
+ continue;
+ }
+ (void) GetFillColor(draw_info,x,y,q);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case FloodfillMethod:
+ case FillToBorderMethod:
+ {
+ MagickPixelPacket
+ target;
+
+ (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
+ if (primitive_info->method == FillToBorderMethod)
+ {
+ target.red=(MagickRealType) draw_info->border_color.red;
+ target.green=(MagickRealType) draw_info->border_color.green;
+ target.blue=(MagickRealType) draw_info->border_color.blue;
+ }
+ (void) FloodfillPaintImage(image,DefaultChannels,draw_info,&target,x,
+ y,primitive_info->method == FloodfillMethod ? MagickFalse :
+ MagickTrue);
+ break;
+ }
+ case ResetMethod:
+ {
+ MagickBooleanType
+ sync;
+
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ (void) GetFillColor(draw_info,x,y,q);
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case MattePrimitive:
+ {
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ switch (primitive_info->method)
+ {
+ case PointMethod:
+ default:
+ {
+ PixelPacket
+ pixel;
+
+ PixelPacket
+ *q;
+
+ q=GetCacheViewAuthenticPixels(image_view,x,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ (void) GetFillColor(draw_info,x,y,&pixel);
+ q->opacity=pixel.opacity;
+ (void) SyncCacheViewAuthenticPixels(image_view,exception);
+ break;
+ }
+ case ReplaceMethod:
+ {
+ MagickBooleanType
+ sync;
+
+ PixelPacket
+ pixel,
+ target;
+
+ (void) GetOneCacheViewVirtualPixel(image_view,x,y,&target,exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (IsColorSimilar(image,q,&target) == MagickFalse)
+ {
+ q++;
+ continue;
+ }
+ (void) GetFillColor(draw_info,x,y,&pixel);
+ q->opacity=pixel.opacity;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case FloodfillMethod:
+ case FillToBorderMethod:
+ {
+ MagickPixelPacket
+ target;
+
+ (void) GetOneVirtualMagickPixel(image,x,y,&target,exception);
+ if (primitive_info->method == FillToBorderMethod)
+ {
+ target.red=(MagickRealType) draw_info->border_color.red;
+ target.green=(MagickRealType) draw_info->border_color.green;
+ target.blue=(MagickRealType) draw_info->border_color.blue;
+ }
+ (void) FloodfillPaintImage(image,OpacityChannel,draw_info,&target,x,y,
+ primitive_info->method == FloodfillMethod ? MagickFalse :
+ MagickTrue);
+ break;
+ }
+ case ResetMethod:
+ {
+ MagickBooleanType
+ sync;
+
+ PixelPacket
+ pixel;
+
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ (void) GetFillColor(draw_info,x,y,&pixel);
+ q->opacity=pixel.opacity;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case TextPrimitive:
+ {
+ char
+ geometry[MaxTextExtent];
+
+ DrawInfo
+ *clone_info;
+
+ if (primitive_info->text == (char *) NULL)
+ break;
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ (void) CloneString(&clone_info->text,primitive_info->text);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%+f%+f",
+ primitive_info->point.x,primitive_info->point.y);
+ (void) CloneString(&clone_info->geometry,geometry);
+ status=AnnotateImage(image,clone_info);
+ clone_info=DestroyDrawInfo(clone_info);
+ break;
+ }
+ case ImagePrimitive:
+ {
+ AffineMatrix
+ affine;
+
+ char
+ composite_geometry[MaxTextExtent];
+
+ Image
+ *composite_image;
+
+ ImageInfo
+ *clone_info;
+
+ long
+ x1,
+ y1;
+
+ RectangleInfo
+ geometry;
+
+ if (primitive_info->text == (char *) NULL)
+ break;
+ clone_info=AcquireImageInfo();
+ if (LocaleNCompare(primitive_info->text,"data:",5) == 0)
+ composite_image=ReadInlineImage(clone_info,primitive_info->text,
+ &image->exception);
+ else
+ {
+ (void) CopyMagickString(clone_info->filename,primitive_info->text,
+ MaxTextExtent);
+ composite_image=ReadImage(clone_info,&image->exception);
+ }
+ clone_info=DestroyImageInfo(clone_info);
+ if (composite_image == (Image *) NULL)
+ break;
+ (void) SetImageProgressMonitor(composite_image,(MagickProgressMonitor)
+ NULL,(void *) NULL);
+ x1=(long) (primitive_info[1].point.x+0.5);
+ y1=(long) (primitive_info[1].point.y+0.5);
+ if (((x1 != 0L) && (x1 != (long) composite_image->columns)) ||
+ ((y1 != 0L) && (y1 != (long) composite_image->rows)))
+ {
+ char
+ geometry[MaxTextExtent];
+
+ /*
+ Resize image.
+ */
+ (void) FormatMagickString(geometry,MaxTextExtent,"%gx%g!",
+ primitive_info[1].point.x,primitive_info[1].point.y);
+ composite_image->filter=image->filter;
+ (void) TransformImage(&composite_image,(char *) NULL,geometry);
+ }
+ if (composite_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
+ if (draw_info->opacity != OpaqueOpacity)
+ (void) SetImageOpacity(composite_image,draw_info->opacity);
+ SetGeometry(image,&geometry);
+ image->gravity=draw_info->gravity;
+ geometry.x=x;
+ geometry.y=y;
+ (void) FormatMagickString(composite_geometry,MaxTextExtent,
+ "%lux%lu%+ld%+ld",composite_image->columns,composite_image->rows,
+ geometry.x,geometry.y);
+ (void) ParseGravityGeometry(image,composite_geometry,&geometry,
+ &image->exception);
+ affine=draw_info->affine;
+ affine.tx=(double) geometry.x;
+ affine.ty=(double) geometry.y;
+ composite_image->interpolate=image->interpolate;
+ if (draw_info->compose == OverCompositeOp)
+ (void) DrawAffineImage(image,composite_image,&affine);
+ else
+ (void) CompositeImage(image,draw_info->compose,composite_image,
+ geometry.x,geometry.y);
+ composite_image=DestroyImage(composite_image);
+ break;
+ }
+ default:
+ {
+ MagickRealType
+ mid,
+ scale;
+
+ DrawInfo
+ *clone_info;
+
+ if (IsEventLogging() != MagickFalse)
+ LogPrimitiveInfo(primitive_info);
+ scale=ExpandAffine(&draw_info->affine);
+ if ((draw_info->dash_pattern != (double *) NULL) &&
+ (draw_info->dash_pattern[0] != 0.0) &&
+ ((scale*draw_info->stroke_width) > MagickEpsilon) &&
+ (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
+ {
+ /*
+ Draw dash polygon.
+ */
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ clone_info->stroke_width=0.0;
+ clone_info->stroke.opacity=(Quantum) TransparentOpacity;
+ status=DrawPolygonPrimitive(image,clone_info,primitive_info);
+ clone_info=DestroyDrawInfo(clone_info);
+ (void) DrawDashPolygon(draw_info,primitive_info,image);
+ break;
+ }
+ mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
+ if ((mid > 1.0) &&
+ (draw_info->stroke.opacity != (Quantum) TransparentOpacity))
+ {
+ MagickBooleanType
+ closed_path;
+
+ /*
+ Draw strokes while respecting line cap/join attributes.
+ */
+ for (i=0; primitive_info[i].primitive != UndefinedPrimitive; i++) ;
+ closed_path=
+ (primitive_info[i-1].point.x == primitive_info[0].point.x) &&
+ (primitive_info[i-1].point.y == primitive_info[0].point.y) ?
+ MagickTrue : MagickFalse;
+ i=(long) primitive_info[0].coordinates;
+ if ((((draw_info->linecap == RoundCap) ||
+ (closed_path != MagickFalse)) &&
+ (draw_info->linejoin == RoundJoin)) ||
+ (primitive_info[i].primitive != UndefinedPrimitive))
+ {
+ (void) DrawPolygonPrimitive(image,draw_info,primitive_info);
+ break;
+ }
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ clone_info->stroke_width=0.0;
+ clone_info->stroke.opacity=(Quantum) TransparentOpacity;
+ status=DrawPolygonPrimitive(image,clone_info,primitive_info);
+ clone_info=DestroyDrawInfo(clone_info);
+ status|=DrawStrokePolygon(image,draw_info,primitive_info);
+ break;
+ }
+ status=DrawPolygonPrimitive(image,draw_info,primitive_info);
+ break;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule()," end draw-primitive");
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D r a w S t r o k e P o l y g o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DrawStrokePolygon() draws a stroked polygon (line, rectangle, ellipse) on
+% the image while respecting the line cap and join attributes.
+%
+% The format of the DrawStrokePolygon method is:
+%
+% MagickBooleanType DrawStrokePolygon(Image *image,
+% const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o primitive_info: Specifies a pointer to a PrimitiveInfo structure.
+%
+%
+*/
+
+static void DrawRoundLinecap(Image *image,const DrawInfo *draw_info,
+ const PrimitiveInfo *primitive_info)
+{
+ PrimitiveInfo
+ linecap[5];
+
+ register long
+ i;
+
+ for (i=0; i < 4; i++)
+ linecap[i]=(*primitive_info);
+ linecap[0].coordinates=4;
+ linecap[1].point.x+=(double) (10.0*MagickEpsilon);
+ linecap[2].point.x+=(double) (10.0*MagickEpsilon);
+ linecap[2].point.y+=(double) (10.0*MagickEpsilon);
+ linecap[3].point.y+=(double) (10.0*MagickEpsilon);
+ linecap[4].primitive=UndefinedPrimitive;
+ (void) DrawPolygonPrimitive(image,draw_info,linecap);
+}
+
+static MagickBooleanType DrawStrokePolygon(Image *image,
+ const DrawInfo *draw_info,const PrimitiveInfo *primitive_info)
+{
+ DrawInfo
+ *clone_info;
+
+ MagickBooleanType
+ closed_path,
+ status;
+
+ PrimitiveInfo
+ *stroke_polygon;
+
+ register const PrimitiveInfo
+ *p,
+ *q;
+
+ /*
+ Draw stroked polygon.
+ */
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " begin draw-stroke-polygon");
+ clone_info=CloneDrawInfo((ImageInfo *) NULL,draw_info);
+ clone_info->fill=draw_info->stroke;
+ clone_info->stroke.opacity=(Quantum) TransparentOpacity;
+ clone_info->stroke_width=0.0;
+ clone_info->fill_rule=NonZeroRule;
+ status=MagickTrue;
+ for (p=primitive_info; p->primitive != UndefinedPrimitive; p+=p->coordinates)
+ {
+ stroke_polygon=TraceStrokePolygon(draw_info,p);
+ status=DrawPolygonPrimitive(image,clone_info,stroke_polygon);
+ stroke_polygon=(PrimitiveInfo *) RelinquishMagickMemory(stroke_polygon);
+ q=p+p->coordinates-1;
+ closed_path=(q->point.x == p->point.x) && (q->point.y == p->point.y) ?
+ MagickTrue : MagickFalse;
+ if ((draw_info->linecap == RoundCap) && (closed_path == MagickFalse))
+ {
+ DrawRoundLinecap(image,draw_info,p);
+ DrawRoundLinecap(image,draw_info,q);
+ }
+ }
+ clone_info=DestroyDrawInfo(clone_info);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(DrawEvent,GetMagickModule(),
+ " end draw-stroke-polygon");
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t A f f i n e M a t r i x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAffineMatrix() returns an AffineMatrix initialized to the identity
+% matrix.
+%
+% The format of the GetAffineMatrix method is:
+%
+% void GetAffineMatrix(AffineMatrix *affine_matrix)
+%
+% A description of each parameter follows:
+%
+% o affine_matrix: the affine matrix.
+%
+*/
+MagickExport void GetAffineMatrix(AffineMatrix *affine_matrix)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(affine_matrix != (AffineMatrix *) NULL);
+ (void) ResetMagickMemory(affine_matrix,0,sizeof(*affine_matrix));
+ affine_matrix->sx=1.0;
+ affine_matrix->sy=1.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t D r a w I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetDrawInfo() initializes draw_info to default values.
+%
+% The format of the GetDrawInfo method is:
+%
+% void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info..
+%
+% o draw_info: the draw info.
+%
+*/
+MagickExport void GetDrawInfo(const ImageInfo *image_info,DrawInfo *draw_info)
+{
+ const char
+ *option;
+
+ ExceptionInfo
+ *exception;
+
+ ImageInfo
+ *clone_info;
+
+ /*
+ Initialize draw attributes.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(draw_info != (DrawInfo *) NULL);
+ (void) ResetMagickMemory(draw_info,0,sizeof(*draw_info));
+ clone_info=CloneImageInfo(image_info);
+ GetAffineMatrix(&draw_info->affine);
+ exception=AcquireExceptionInfo();
+ (void) QueryColorDatabase("#000F",&draw_info->fill,exception);
+ (void) QueryColorDatabase("#FFF0",&draw_info->stroke,exception);
+ draw_info->stroke_antialias=clone_info->antialias;
+ draw_info->stroke_width=1.0;
+ draw_info->opacity=OpaqueOpacity;
+ draw_info->fill_rule=EvenOddRule;
+ draw_info->linecap=ButtCap;
+ draw_info->linejoin=MiterJoin;
+ draw_info->miterlimit=10;
+ draw_info->decorate=NoDecoration;
+ if (clone_info->font != (char *) NULL)
+ draw_info->font=AcquireString(clone_info->font);
+ if (clone_info->density != (char *) NULL)
+ draw_info->density=AcquireString(clone_info->density);
+ draw_info->text_antialias=clone_info->antialias;
+ draw_info->pointsize=12.0;
+ if (clone_info->pointsize != 0.0)
+ draw_info->pointsize=clone_info->pointsize;
+ draw_info->undercolor.opacity=(Quantum) TransparentOpacity;
+ draw_info->border_color=clone_info->border_color;
+ draw_info->compose=OverCompositeOp;
+ if (clone_info->server_name != (char *) NULL)
+ draw_info->server_name=AcquireString(clone_info->server_name);
+ draw_info->render=MagickTrue;
+ draw_info->debug=IsEventLogging();
+ option=GetImageOption(clone_info,"encoding");
+ if (option != (const char *) NULL)
+ (void) CloneString(&draw_info->encoding,option);
+ option=GetImageOption(clone_info,"kerning");
+ if (option != (const char *) NULL)
+ draw_info->kerning=atof(option);
+ option=GetImageOption(clone_info,"interword-spacing");
+ if (option != (const char *) NULL)
+ draw_info->interword_spacing=atof(option);
+ option=GetImageOption(clone_info,"fill");
+ if (option != (const char *) NULL)
+ (void) QueryColorDatabase(option,&draw_info->fill,exception);
+ option=GetImageOption(clone_info,"stroke");
+ if (option != (const char *) NULL)
+ (void) QueryColorDatabase(option,&draw_info->stroke,exception);
+ option=GetImageOption(clone_info,"strokewidth");
+ if (option != (const char *) NULL)
+ draw_info->stroke_width=atof(option);
+ option=GetImageOption(clone_info,"undercolor");
+ if (option != (const char *) NULL)
+ (void) QueryColorDatabase(option,&draw_info->undercolor,exception);
+ option=GetImageOption(clone_info,"gravity");
+ if (option != (const char *) NULL)
+ draw_info->gravity=(GravityType) ParseMagickOption(MagickGravityOptions,
+ MagickFalse,option);
+ exception=DestroyExceptionInfo(exception);
+ draw_info->signature=MagickSignature;
+ clone_info=DestroyImageInfo(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P e r m u t a t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Permutate() returns the permuation of the (n,k).
+%
+% The format of the Permutate method is:
+%
+% void Permutate(long n,long k)
+%
+% A description of each parameter follows:
+%
+% o n:
+%
+% o k:
+%
+%
+*/
+static inline MagickRealType Permutate(const long n,const long k)
+{
+ MagickRealType
+ r;
+
+ register long
+ i;
+
+ r=1.0;
+ for (i=k+1; i <= n; i++)
+ r*=i;
+ for (i=1; i <= (n-k); i++)
+ r/=i;
+ return(r);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ T r a c e P r i m i t i v e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TracePrimitive is a collection of methods for generating graphic
+% primitives such as arcs, ellipses, paths, etc.
+%
+*/
+
+static void TraceArc(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo end,const PointInfo degrees)
+{
+ PointInfo
+ center,
+ radii;
+
+ center.x=0.5*(end.x+start.x);
+ center.y=0.5*(end.y+start.y);
+ radii.x=fabs(center.x-start.x);
+ radii.y=fabs(center.y-start.y);
+ TraceEllipse(primitive_info,center,radii,degrees);
+}
+
+static void TraceArcPath(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo end,const PointInfo arc,const MagickRealType angle,
+ const MagickBooleanType large_arc,const MagickBooleanType sweep)
+{
+ MagickRealType
+ alpha,
+ beta,
+ delta,
+ factor,
+ gamma,
+ theta;
+
+ PointInfo
+ center,
+ points[3],
+ radii;
+
+ register MagickRealType
+ cosine,
+ sine;
+
+ register PrimitiveInfo
+ *p;
+
+ register long
+ i;
+
+ unsigned long
+ arc_segments;
+
+ if ((start.x == end.x) && (start.y == end.y))
+ {
+ TracePoint(primitive_info,end);
+ return;
+ }
+ radii.x=fabs(arc.x);
+ radii.y=fabs(arc.y);
+ if ((radii.x == 0.0) || (radii.y == 0.0))
+ {
+ TraceLine(primitive_info,start,end);
+ return;
+ }
+ cosine=cos(DegreesToRadians(fmod((double) angle,360.0)));
+ sine=sin(DegreesToRadians(fmod((double) angle,360.0)));
+ center.x=(double) (cosine*(end.x-start.x)/2+sine*(end.y-start.y)/2);
+ center.y=(double) (cosine*(end.y-start.y)/2-sine*(end.x-start.x)/2);
+ delta=(center.x*center.x)/(radii.x*radii.x)+(center.y*center.y)/
+ (radii.y*radii.y);
+ if (delta < MagickEpsilon)
+ {
+ TraceLine(primitive_info,start,end);
+ return;
+ }
+ if (delta > 1.0)
+ {
+ radii.x*=sqrt((double) delta);
+ radii.y*=sqrt((double) delta);
+ }
+ points[0].x=(double) (cosine*start.x/radii.x+sine*start.y/radii.x);
+ points[0].y=(double) (cosine*start.y/radii.y-sine*start.x/radii.y);
+ points[1].x=(double) (cosine*end.x/radii.x+sine*end.y/radii.x);
+ points[1].y=(double) (cosine*end.y/radii.y-sine*end.x/radii.y);
+ alpha=points[1].x-points[0].x;
+ beta=points[1].y-points[0].y;
+ factor=1.0/(alpha*alpha+beta*beta)-0.25;
+ if (factor <= 0.0)
+ factor=0.0;
+ else
+ {
+ factor=sqrt((double) factor);
+ if (sweep == large_arc)
+ factor=(-factor);
+ }
+ center.x=(double) ((points[0].x+points[1].x)/2-factor*beta);
+ center.y=(double) ((points[0].y+points[1].y)/2+factor*alpha);
+ alpha=atan2(points[0].y-center.y,points[0].x-center.x);
+ theta=atan2(points[1].y-center.y,points[1].x-center.x)-alpha;
+ if ((theta < 0.0) && (sweep != MagickFalse))
+ theta+=(MagickRealType) (2.0*MagickPI);
+ else
+ if ((theta > 0.0) && (sweep == MagickFalse))
+ theta-=(MagickRealType) (2.0*MagickPI);
+ arc_segments=(unsigned long) ceil(fabs((double) (theta/(0.5*MagickPI+
+ MagickEpsilon))));
+ p=primitive_info;
+ for (i=0; i < (long) arc_segments; i++)
+ {
+ beta=0.5*((alpha+(i+1)*theta/arc_segments)-(alpha+i*theta/arc_segments));
+ gamma=(8.0/3.0)*sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))*
+ sin(fmod((double) (0.5*beta),DegreesToRadians(360.0)))/
+ sin(fmod((double) beta,DegreesToRadians(360.0)));
+ points[0].x=(double) (center.x+cos(fmod((double) (alpha+(double) i*theta/
+ arc_segments),DegreesToRadians(360.0)))-gamma*sin(fmod((double) (alpha+
+ (double) i*theta/arc_segments),DegreesToRadians(360.0))));
+ points[0].y=(double) (center.y+sin(fmod((double) (alpha+(double) i*theta/
+ arc_segments),DegreesToRadians(360.0)))+gamma*cos(fmod((double) (alpha+
+ (double) i*theta/arc_segments),DegreesToRadians(360.0))));
+ points[2].x=(double) (center.x+cos(fmod((double) (alpha+(double) (i+1)*
+ theta/arc_segments),DegreesToRadians(360.0))));
+ points[2].y=(double) (center.y+sin(fmod((double) (alpha+(double) (i+1)*
+ theta/arc_segments),DegreesToRadians(360.0))));
+ points[1].x=(double) (points[2].x+gamma*sin(fmod((double) (alpha+(double)
+ (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
+ points[1].y=(double) (points[2].y-gamma*cos(fmod((double) (alpha+(double)
+ (i+1)*theta/arc_segments),DegreesToRadians(360.0))));
+ p->point.x=(p == primitive_info) ? start.x : (p-1)->point.x;
+ p->point.y=(p == primitive_info) ? start.y : (p-1)->point.y;
+ (p+1)->point.x=(double) (cosine*radii.x*points[0].x-sine*radii.y*
+ points[0].y);
+ (p+1)->point.y=(double) (sine*radii.x*points[0].x+cosine*radii.y*
+ points[0].y);
+ (p+2)->point.x=(double) (cosine*radii.x*points[1].x-sine*radii.y*
+ points[1].y);
+ (p+2)->point.y=(double) (sine*radii.x*points[1].x+cosine*radii.y*
+ points[1].y);
+ (p+3)->point.x=(double) (cosine*radii.x*points[2].x-sine*radii.y*
+ points[2].y);
+ (p+3)->point.y=(double) (sine*radii.x*points[2].x+cosine*radii.y*
+ points[2].y);
+ if (i == (long) (arc_segments-1))
+ (p+3)->point=end;
+ TraceBezier(p,4);
+ p+=p->coordinates;
+ }
+ primitive_info->coordinates=(unsigned long) (p-primitive_info);
+ for (i=0; i < (long) primitive_info->coordinates; i++)
+ {
+ p->primitive=primitive_info->primitive;
+ p--;
+ }
+}
+
+static void TraceBezier(PrimitiveInfo *primitive_info,
+ const unsigned long number_coordinates)
+{
+ MagickRealType
+ alpha,
+ *coefficients,
+ weight;
+
+ PointInfo
+ end,
+ point,
+ *points;
+
+ register long
+ i,
+ j;
+
+ register PrimitiveInfo
+ *p;
+
+ unsigned long
+ control_points,
+ quantum;
+
+ /*
+ Allocate coeficients.
+ */
+ quantum=number_coordinates;
+ for (i=0; i < (long) number_coordinates; i++)
+ {
+ for (j=i+1; j < (long) number_coordinates; j++)
+ {
+ alpha=fabs(primitive_info[j].point.x-primitive_info[i].point.x);
+ if (alpha > (MagickRealType) quantum)
+ quantum=(unsigned long) alpha;
+ alpha=fabs(primitive_info[j].point.y-primitive_info[i].point.y);
+ if (alpha > (MagickRealType) quantum)
+ quantum=(unsigned long) alpha;
+ }
+ }
+ quantum=(unsigned long) MagickMin((double) quantum/number_coordinates,
+ (double) BezierQuantum);
+ control_points=quantum*number_coordinates;
+ coefficients=(MagickRealType *) AcquireQuantumMemory((size_t)
+ number_coordinates,sizeof(*coefficients));
+ points=(PointInfo *) AcquireQuantumMemory((size_t) control_points,
+ sizeof(*points));
+ if ((coefficients == (MagickRealType *) NULL) ||
+ (points == (PointInfo *) NULL))
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ /*
+ Compute bezier points.
+ */
+ end=primitive_info[number_coordinates-1].point;
+ for (i=0; i < (long) number_coordinates; i++)
+ coefficients[i]=Permutate((long) number_coordinates-1,i);
+ weight=0.0;
+ for (i=0; i < (long) control_points; i++)
+ {
+ p=primitive_info;
+ point.x=0.0;
+ point.y=0.0;
+ alpha=pow((double) (1.0-weight),(double) number_coordinates-1.0);
+ for (j=0; j < (long) number_coordinates; j++)
+ {
+ point.x+=alpha*coefficients[j]*p->point.x;
+ point.y+=alpha*coefficients[j]*p->point.y;
+ alpha*=weight/(1.0-weight);
+ p++;
+ }
+ points[i]=point;
+ weight+=1.0/control_points;
+ }
+ /*
+ Bezier curves are just short segmented polys.
+ */
+ p=primitive_info;
+ for (i=0; i < (long) control_points; i++)
+ {
+ TracePoint(p,points[i]);
+ p+=p->coordinates;
+ }
+ TracePoint(p,end);
+ p+=p->coordinates;
+ primitive_info->coordinates=(unsigned long) (p-primitive_info);
+ for (i=0; i < (long) primitive_info->coordinates; i++)
+ {
+ p->primitive=primitive_info->primitive;
+ p--;
+ }
+ points=(PointInfo *) RelinquishMagickMemory(points);
+ coefficients=(MagickRealType *) RelinquishMagickMemory(coefficients);
+}
+
+static void TraceCircle(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo end)
+{
+ MagickRealType
+ alpha,
+ beta,
+ radius;
+
+ PointInfo
+ offset,
+ degrees;
+
+ alpha=end.x-start.x;
+ beta=end.y-start.y;
+ radius=hypot((double) alpha,(double) beta);
+ offset.x=(double) radius;
+ offset.y=(double) radius;
+ degrees.x=0.0;
+ degrees.y=360.0;
+ TraceEllipse(primitive_info,start,offset,degrees);
+}
+
+static void TraceEllipse(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo stop,const PointInfo degrees)
+{
+ MagickRealType
+ delta,
+ step,
+ y;
+
+ PointInfo
+ angle,
+ point;
+
+ register PrimitiveInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Ellipses are just short segmented polys.
+ */
+ if ((stop.x == 0.0) && (stop.y == 0.0))
+ {
+ TracePoint(primitive_info,start);
+ return;
+ }
+ delta=2.0/MagickMax(stop.x,stop.y);
+ step=(MagickRealType) (MagickPI/8.0);
+ if (delta < (MagickPI/8.0))
+ step=MagickPI/(4*(MagickPI/delta/2+0.5));
+ angle.x=DegreesToRadians(degrees.x);
+ y=degrees.y;
+ while (y < degrees.x)
+ y+=360.0;
+ angle.y=(double) (DegreesToRadians(y)-MagickEpsilon);
+ for (p=primitive_info; angle.x < angle.y; angle.x+=step)
+ {
+ point.x=cos(fmod(angle.x,DegreesToRadians(360.0)))*stop.x+start.x;
+ point.y=sin(fmod(angle.x,DegreesToRadians(360.0)))*stop.y+start.y;
+ TracePoint(p,point);
+ p+=p->coordinates;
+ }
+ point.x=cos(fmod(angle.y,DegreesToRadians(360.0)))*stop.x+start.x;
+ point.y=sin(fmod(angle.y,DegreesToRadians(360.0)))*stop.y+start.y;
+ TracePoint(p,point);
+ p+=p->coordinates;
+ primitive_info->coordinates=(unsigned long) (p-primitive_info);
+ for (i=0; i < (long) primitive_info->coordinates; i++)
+ {
+ p->primitive=primitive_info->primitive;
+ p--;
+ }
+}
+
+static void TraceLine(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo end)
+{
+ TracePoint(primitive_info,start);
+ if ((fabs(start.x-end.x) <= MagickEpsilon) &&
+ (fabs(start.y-end.y) <= MagickEpsilon))
+ {
+ primitive_info->primitive=PointPrimitive;
+ primitive_info->coordinates=1;
+ return;
+ }
+ TracePoint(primitive_info+1,end);
+ (primitive_info+1)->primitive=primitive_info->primitive;
+ primitive_info->coordinates=2;
+}
+
+static unsigned long TracePath(PrimitiveInfo *primitive_info,const char *path)
+{
+ char
+ token[MaxTextExtent];
+
+ const char
+ *p;
+
+ int
+ attribute,
+ last_attribute;
+
+ MagickRealType
+ x,
+ y;
+
+ PointInfo
+ end,
+ points[4],
+ point,
+ start;
+
+ PrimitiveType
+ primitive_type;
+
+ register PrimitiveInfo
+ *q;
+
+ register long
+ i;
+
+ unsigned long
+ number_coordinates,
+ z_count;
+
+ attribute=0;
+ point.x=0.0;
+ point.y=0.0;
+ start.x=0.0;
+ start.y=0.0;
+ number_coordinates=0;
+ z_count=0;
+ primitive_type=primitive_info->primitive;
+ q=primitive_info;
+ for (p=path; *p != '\0'; )
+ {
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if (*p == '\0')
+ break;
+ last_attribute=attribute;
+ attribute=(int) (*p++);
+ switch (attribute)
+ {
+ case 'a':
+ case 'A':
+ {
+ MagickBooleanType
+ large_arc,
+ sweep;
+
+ MagickRealType
+ angle;
+
+ PointInfo
+ arc;
+
+ /*
+ Compute arc points.
+ */
+ do
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ arc.x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ arc.y=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ angle=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ large_arc=atoi(token) != 0 ? MagickTrue : MagickFalse;
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ sweep=atoi(token) != 0 ? MagickTrue : MagickFalse;
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ end.x=(double) (attribute == (int) 'A' ? x : point.x+x);
+ end.y=(double) (attribute == (int) 'A' ? y : point.y+y);
+ TraceArcPath(q,point,end,arc,angle,large_arc,sweep);
+ q+=q->coordinates;
+ point=end;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'c':
+ case 'C':
+ {
+ /*
+ Compute bezier points.
+ */
+ do
+ {
+ points[0]=point;
+ for (i=1; i < 4; i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ end.x=(double) (attribute == (int) 'C' ? x : point.x+x);
+ end.y=(double) (attribute == (int) 'C' ? y : point.y+y);
+ points[i]=end;
+ }
+ for (i=0; i < 4; i++)
+ (q+i)->point=points[i];
+ TraceBezier(q,4);
+ q+=q->coordinates;
+ point=end;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'H':
+ case 'h':
+ {
+ do
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ point.x=(double) (attribute == (int) 'H' ? x: point.x+x);
+ TracePoint(q,point);
+ q+=q->coordinates;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'l':
+ case 'L':
+ {
+ do
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ point.x=(double) (attribute == (int) 'L' ? x : point.x+x);
+ point.y=(double) (attribute == (int) 'L' ? y : point.y+y);
+ TracePoint(q,point);
+ q+=q->coordinates;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if (q != primitive_info)
+ {
+ primitive_info->coordinates=(unsigned long) (q-primitive_info);
+ number_coordinates+=primitive_info->coordinates;
+ primitive_info=q;
+ }
+ i=0;
+ do
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ point.x=(double) (attribute == (int) 'M' ? x : point.x+x);
+ point.y=(double) (attribute == (int) 'M' ? y : point.y+y);
+ if (i == 0)
+ start=point;
+ i++;
+ TracePoint(q,point);
+ q+=q->coordinates;
+ if (attribute == (int) 'M')
+ {
+ TracePoint(q,point);
+ q+=q->coordinates;
+ }
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'q':
+ case 'Q':
+ {
+ /*
+ Compute bezier points.
+ */
+ do
+ {
+ points[0]=point;
+ for (i=1; i < 3; i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ if (*p == ',')
+ p++;
+ end.x=(double) (attribute == (int) 'Q' ? x : point.x+x);
+ end.y=(double) (attribute == (int) 'Q' ? y : point.y+y);
+ points[i]=end;
+ }
+ for (i=0; i < 3; i++)
+ (q+i)->point=points[i];
+ TraceBezier(q,3);
+ q+=q->coordinates;
+ point=end;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 's':
+ case 'S':
+ {
+ /*
+ Compute bezier points.
+ */
+ do
+ {
+ points[0]=points[3];
+ points[1].x=2.0*points[3].x-points[2].x;
+ points[1].y=2.0*points[3].y-points[2].y;
+ for (i=2; i < 4; i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ if (*p == ',')
+ p++;
+ end.x=(double) (attribute == (int) 'S' ? x : point.x+x);
+ end.y=(double) (attribute == (int) 'S' ? y : point.y+y);
+ points[i]=end;
+ }
+ if (strchr("CcSs",last_attribute) == (char *) NULL)
+ {
+ points[0]=points[2];
+ points[1]=points[3];
+ }
+ for (i=0; i < 4; i++)
+ (q+i)->point=points[i];
+ TraceBezier(q,4);
+ q+=q->coordinates;
+ point=end;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 't':
+ case 'T':
+ {
+ /*
+ Compute bezier points.
+ */
+ do
+ {
+ points[0]=points[2];
+ points[1].x=2.0*points[2].x-points[1].x;
+ points[1].y=2.0*points[2].y-points[1].y;
+ for (i=2; i < 3; i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ x=atof(token);
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ end.x=(double) (attribute == (int) 'T' ? x : point.x+x);
+ end.y=(double) (attribute == (int) 'T' ? y : point.y+y);
+ points[i]=end;
+ }
+ if (strchr("QqTt",last_attribute) == (char *) NULL)
+ {
+ points[0]=points[2];
+ points[1]=points[3];
+ }
+ for (i=0; i < 3; i++)
+ (q+i)->point=points[i];
+ TraceBezier(q,3);
+ q+=q->coordinates;
+ point=end;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'v':
+ case 'V':
+ {
+ do
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ y=atof(token);
+ point.y=(double) (attribute == (int) 'V' ? y : point.y+y);
+ TracePoint(q,point);
+ q+=q->coordinates;
+ } while (IsPoint(p) != MagickFalse);
+ break;
+ }
+ case 'z':
+ case 'Z':
+ {
+ point=start;
+ TracePoint(q,point);
+ q+=q->coordinates;
+ primitive_info->coordinates=(unsigned long) (q-primitive_info);
+ number_coordinates+=primitive_info->coordinates;
+ primitive_info=q;
+ z_count++;
+ break;
+ }
+ default:
+ {
+ if (isalpha((int) ((unsigned char) attribute)) != 0)
+ (void) fprintf(stderr,"attribute not recognized: %c\n",attribute);
+ break;
+ }
+ }
+ }
+ primitive_info->coordinates=(unsigned long) (q-primitive_info);
+ number_coordinates+=primitive_info->coordinates;
+ for (i=0; i < (long) number_coordinates; i++)
+ {
+ q--;
+ q->primitive=primitive_type;
+ if (z_count > 1)
+ q->method=FillToBorderMethod;
+ }
+ q=primitive_info;
+ return(number_coordinates);
+}
+
+static void TraceRectangle(PrimitiveInfo *primitive_info,const PointInfo start,
+ const PointInfo end)
+{
+ PointInfo
+ point;
+
+ register PrimitiveInfo
+ *p;
+
+ register long
+ i;
+
+ p=primitive_info;
+ TracePoint(p,start);
+ p+=p->coordinates;
+ point.x=start.x;
+ point.y=end.y;
+ TracePoint(p,point);
+ p+=p->coordinates;
+ TracePoint(p,end);
+ p+=p->coordinates;
+ point.x=end.x;
+ point.y=start.y;
+ TracePoint(p,point);
+ p+=p->coordinates;
+ TracePoint(p,start);
+ p+=p->coordinates;
+ primitive_info->coordinates=(unsigned long) (p-primitive_info);
+ for (i=0; i < (long) primitive_info->coordinates; i++)
+ {
+ p->primitive=primitive_info->primitive;
+ p--;
+ }
+}
+
+static void TraceRoundRectangle(PrimitiveInfo *primitive_info,
+ const PointInfo start,const PointInfo end,PointInfo arc)
+{
+ PointInfo
+ degrees,
+ offset,
+ point;
+
+ register PrimitiveInfo
+ *p;
+
+ register long
+ i;
+
+ p=primitive_info;
+ offset.x=fabs(end.x-start.x);
+ offset.y=fabs(end.y-start.y);
+ if (arc.x > (0.5*offset.x))
+ arc.x=0.5*offset.x;
+ if (arc.y > (0.5*offset.y))
+ arc.y=0.5*offset.y;
+ point.x=start.x+offset.x-arc.x;
+ point.y=start.y+arc.y;
+ degrees.x=270.0;
+ degrees.y=360.0;
+ TraceEllipse(p,point,arc,degrees);
+ p+=p->coordinates;
+ point.x=start.x+offset.x-arc.x;
+ point.y=start.y+offset.y-arc.y;
+ degrees.x=0.0;
+ degrees.y=90.0;
+ TraceEllipse(p,point,arc,degrees);
+ p+=p->coordinates;
+ point.x=start.x+arc.x;
+ point.y=start.y+offset.y-arc.y;
+ degrees.x=90.0;
+ degrees.y=180.0;
+ TraceEllipse(p,point,arc,degrees);
+ p+=p->coordinates;
+ point.x=start.x+arc.x;
+ point.y=start.y+arc.y;
+ degrees.x=180.0;
+ degrees.y=270.0;
+ TraceEllipse(p,point,arc,degrees);
+ p+=p->coordinates;
+ TracePoint(p,primitive_info->point);
+ p+=p->coordinates;
+ primitive_info->coordinates=(unsigned long) (p-primitive_info);
+ for (i=0; i < (long) primitive_info->coordinates; i++)
+ {
+ p->primitive=primitive_info->primitive;
+ p--;
+ }
+}
+
+static void TraceSquareLinecap(PrimitiveInfo *primitive_info,
+ const unsigned long number_vertices,const MagickRealType offset)
+{
+ MagickRealType
+ distance;
+
+ long
+ j;
+
+ register MagickRealType
+ dx,
+ dy;
+
+ register long
+ i;
+
+ dx=0.0;
+ dy=0.0;
+ for (i=1; i < (long) number_vertices; i++)
+ {
+ dx=primitive_info[0].point.x-primitive_info[i].point.x;
+ dy=primitive_info[0].point.y-primitive_info[i].point.y;
+ if ((fabs((double) dx) >= MagickEpsilon) ||
+ (fabs((double) dy) >= MagickEpsilon))
+ break;
+ }
+ if (i == (long) number_vertices)
+ i=(long) number_vertices-1L;
+ distance=hypot((double) dx,(double) dy);
+ primitive_info[0].point.x=(double) (primitive_info[i].point.x+
+ dx*(distance+offset)/distance);
+ primitive_info[0].point.y=(double) (primitive_info[i].point.y+
+ dy*(distance+offset)/distance);
+ for (j=(long) number_vertices-2; j >= 0; j--)
+ {
+ dx=primitive_info[number_vertices-1].point.x-primitive_info[j].point.x;
+ dy=primitive_info[number_vertices-1].point.y-primitive_info[j].point.y;
+ if ((fabs((double) dx) >= MagickEpsilon) ||
+ (fabs((double) dy) >= MagickEpsilon))
+ break;
+ }
+ distance=hypot((double) dx,(double) dy);
+ primitive_info[number_vertices-1].point.x=(double) (primitive_info[j].point.x+
+ dx*(distance+offset)/distance);
+ primitive_info[number_vertices-1].point.y=(double) (primitive_info[j].point.y+
+ dy*(distance+offset)/distance);
+}
+
+static PrimitiveInfo *TraceStrokePolygon(const DrawInfo *draw_info,
+ const PrimitiveInfo *primitive_info)
+{
+ typedef struct _LineSegment
+ {
+ double
+ p,
+ q;
+ } LineSegment;
+
+ LineSegment
+ dx,
+ dy,
+ inverse_slope,
+ slope,
+ theta;
+
+ long
+ j,
+ n,
+ p,
+ q;
+
+ MagickBooleanType
+ closed_path;
+
+ MagickRealType
+ delta_theta,
+ dot_product,
+ mid,
+ miterlimit;
+
+ PointInfo
+ box_p[5],
+ box_q[5],
+ center,
+ offset,
+ *path_p,
+ *path_q;
+
+ PrimitiveInfo
+ *polygon_primitive,
+ *stroke_polygon;
+
+ register long
+ i;
+
+ unsigned long
+ arc_segments,
+ max_strokes,
+ number_vertices;
+
+ /*
+ Allocate paths.
+ */
+ number_vertices=primitive_info->coordinates;
+ max_strokes=2*number_vertices+6*BezierQuantum+360;
+ path_p=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
+ sizeof(*path_p));
+ path_q=(PointInfo *) AcquireQuantumMemory((size_t) max_strokes,
+ sizeof(*path_q));
+ polygon_primitive=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
+ number_vertices+2UL,sizeof(*polygon_primitive));
+ if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL) ||
+ (polygon_primitive == (PrimitiveInfo *) NULL))
+ return((PrimitiveInfo *) NULL);
+ (void) CopyMagickMemory(polygon_primitive,primitive_info,(size_t)
+ number_vertices*sizeof(*polygon_primitive));
+ closed_path=
+ (primitive_info[number_vertices-1].point.x == primitive_info[0].point.x) &&
+ (primitive_info[number_vertices-1].point.y == primitive_info[0].point.y) ?
+ MagickTrue : MagickFalse;
+ if ((draw_info->linejoin == RoundJoin) ||
+ ((draw_info->linejoin == MiterJoin) && (closed_path != MagickFalse)))
+ {
+ polygon_primitive[number_vertices]=primitive_info[1];
+ number_vertices++;
+ }
+ polygon_primitive[number_vertices].primitive=UndefinedPrimitive;
+ /*
+ Compute the slope for the first line segment, p.
+ */
+ dx.p=0.0;
+ dy.p=0.0;
+ for (n=1; n < (long) number_vertices; n++)
+ {
+ dx.p=polygon_primitive[n].point.x-polygon_primitive[0].point.x;
+ dy.p=polygon_primitive[n].point.y-polygon_primitive[0].point.y;
+ if ((fabs(dx.p) >= MagickEpsilon) || (fabs(dy.p) >= MagickEpsilon))
+ break;
+ }
+ if (n == (long) number_vertices)
+ n=(long) number_vertices-1L;
+ slope.p=0.0;
+ inverse_slope.p=0.0;
+ if (fabs(dx.p) <= MagickEpsilon)
+ {
+ if (dx.p >= 0.0)
+ slope.p=dy.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
+ else
+ slope.p=dy.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
+ }
+ else
+ if (fabs(dy.p) <= MagickEpsilon)
+ {
+ if (dy.p >= 0.0)
+ inverse_slope.p=dx.p < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
+ else
+ inverse_slope.p=dx.p < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
+ }
+ else
+ {
+ slope.p=dy.p/dx.p;
+ inverse_slope.p=(-1.0/slope.p);
+ }
+ mid=ExpandAffine(&draw_info->affine)*draw_info->stroke_width/2.0;
+ miterlimit=(MagickRealType) (draw_info->miterlimit*draw_info->miterlimit*
+ mid*mid);
+ if ((draw_info->linecap == SquareCap) && (closed_path == MagickFalse))
+ TraceSquareLinecap(polygon_primitive,number_vertices,mid);
+ offset.x=sqrt((double) (mid*mid/(inverse_slope.p*inverse_slope.p+1.0)));
+ offset.y=(double) (offset.x*inverse_slope.p);
+ if ((dy.p*offset.x-dx.p*offset.y) > 0.0)
+ {
+ box_p[0].x=polygon_primitive[0].point.x-offset.x;
+ box_p[0].y=polygon_primitive[0].point.y-offset.x*inverse_slope.p;
+ box_p[1].x=polygon_primitive[n].point.x-offset.x;
+ box_p[1].y=polygon_primitive[n].point.y-offset.x*inverse_slope.p;
+ box_q[0].x=polygon_primitive[0].point.x+offset.x;
+ box_q[0].y=polygon_primitive[0].point.y+offset.x*inverse_slope.p;
+ box_q[1].x=polygon_primitive[n].point.x+offset.x;
+ box_q[1].y=polygon_primitive[n].point.y+offset.x*inverse_slope.p;
+ }
+ else
+ {
+ box_p[0].x=polygon_primitive[0].point.x+offset.x;
+ box_p[0].y=polygon_primitive[0].point.y+offset.y;
+ box_p[1].x=polygon_primitive[n].point.x+offset.x;
+ box_p[1].y=polygon_primitive[n].point.y+offset.y;
+ box_q[0].x=polygon_primitive[0].point.x-offset.x;
+ box_q[0].y=polygon_primitive[0].point.y-offset.y;
+ box_q[1].x=polygon_primitive[n].point.x-offset.x;
+ box_q[1].y=polygon_primitive[n].point.y-offset.y;
+ }
+ /*
+ Create strokes for the line join attribute: bevel, miter, round.
+ */
+ p=0;
+ q=0;
+ path_q[p++]=box_q[0];
+ path_p[q++]=box_p[0];
+ for (i=(long) n+1; i < (long) number_vertices; i++)
+ {
+ /*
+ Compute the slope for this line segment, q.
+ */
+ dx.q=polygon_primitive[i].point.x-polygon_primitive[n].point.x;
+ dy.q=polygon_primitive[i].point.y-polygon_primitive[n].point.y;
+ dot_product=dx.q*dx.q+dy.q*dy.q;
+ if (dot_product < 0.25)
+ continue;
+ slope.q=0.0;
+ inverse_slope.q=0.0;
+ if (fabs(dx.q) < MagickEpsilon)
+ {
+ if (dx.q >= 0.0)
+ slope.q=dy.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
+ else
+ slope.q=dy.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
+ }
+ else
+ if (fabs(dy.q) <= MagickEpsilon)
+ {
+ if (dy.q >= 0.0)
+ inverse_slope.q=dx.q < 0.0 ? -1.0/MagickEpsilon : 1.0/MagickEpsilon;
+ else
+ inverse_slope.q=dx.q < 0.0 ? 1.0/MagickEpsilon : -1.0/MagickEpsilon;
+ }
+ else
+ {
+ slope.q=dy.q/dx.q;
+ inverse_slope.q=(-1.0/slope.q);
+ }
+ offset.x=sqrt((double) (mid*mid/(inverse_slope.q*inverse_slope.q+1.0)));
+ offset.y=(double) (offset.x*inverse_slope.q);
+ dot_product=dy.q*offset.x-dx.q*offset.y;
+ if (dot_product > 0.0)
+ {
+ box_p[2].x=polygon_primitive[n].point.x-offset.x;
+ box_p[2].y=polygon_primitive[n].point.y-offset.y;
+ box_p[3].x=polygon_primitive[i].point.x-offset.x;
+ box_p[3].y=polygon_primitive[i].point.y-offset.y;
+ box_q[2].x=polygon_primitive[n].point.x+offset.x;
+ box_q[2].y=polygon_primitive[n].point.y+offset.y;
+ box_q[3].x=polygon_primitive[i].point.x+offset.x;
+ box_q[3].y=polygon_primitive[i].point.y+offset.y;
+ }
+ else
+ {
+ box_p[2].x=polygon_primitive[n].point.x+offset.x;
+ box_p[2].y=polygon_primitive[n].point.y+offset.y;
+ box_p[3].x=polygon_primitive[i].point.x+offset.x;
+ box_p[3].y=polygon_primitive[i].point.y+offset.y;
+ box_q[2].x=polygon_primitive[n].point.x-offset.x;
+ box_q[2].y=polygon_primitive[n].point.y-offset.y;
+ box_q[3].x=polygon_primitive[i].point.x-offset.x;
+ box_q[3].y=polygon_primitive[i].point.y-offset.y;
+ }
+ if (fabs((double) (slope.p-slope.q)) <= MagickEpsilon)
+ {
+ box_p[4]=box_p[1];
+ box_q[4]=box_q[1];
+ }
+ else
+ {
+ box_p[4].x=(double) ((slope.p*box_p[0].x-box_p[0].y-slope.q*box_p[3].x+
+ box_p[3].y)/(slope.p-slope.q));
+ box_p[4].y=(double) (slope.p*(box_p[4].x-box_p[0].x)+box_p[0].y);
+ box_q[4].x=(double) ((slope.p*box_q[0].x-box_q[0].y-slope.q*box_q[3].x+
+ box_q[3].y)/(slope.p-slope.q));
+ box_q[4].y=(double) (slope.p*(box_q[4].x-box_q[0].x)+box_q[0].y);
+ }
+ if (q >= (long) (max_strokes-6*BezierQuantum-360))
+ {
+ max_strokes+=6*BezierQuantum+360;
+ path_p=(PointInfo *) ResizeQuantumMemory(path_p,(size_t) max_strokes,
+ sizeof(*path_p));
+ path_q=(PointInfo *) ResizeQuantumMemory(path_q,(size_t) max_strokes,
+ sizeof(*path_q));
+ if ((path_p == (PointInfo *) NULL) || (path_q == (PointInfo *) NULL))
+ {
+ polygon_primitive=(PrimitiveInfo *)
+ RelinquishMagickMemory(polygon_primitive);
+ return((PrimitiveInfo *) NULL);
+ }
+ }
+ dot_product=dx.q*dy.p-dx.p*dy.q;
+ if (dot_product <= 0.0)
+ switch (draw_info->linejoin)
+ {
+ case BevelJoin:
+ {
+ path_q[q++]=box_q[1];
+ path_q[q++]=box_q[2];
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ path_p[p++]=box_p[4];
+ else
+ {
+ path_p[p++]=box_p[1];
+ path_p[p++]=box_p[2];
+ }
+ break;
+ }
+ case MiterJoin:
+ {
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ {
+ path_q[q++]=box_q[4];
+ path_p[p++]=box_p[4];
+ }
+ else
+ {
+ path_q[q++]=box_q[1];
+ path_q[q++]=box_q[2];
+ path_p[p++]=box_p[1];
+ path_p[p++]=box_p[2];
+ }
+ break;
+ }
+ case RoundJoin:
+ {
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ path_p[p++]=box_p[4];
+ else
+ {
+ path_p[p++]=box_p[1];
+ path_p[p++]=box_p[2];
+ }
+ center=polygon_primitive[n].point;
+ theta.p=atan2(box_q[1].y-center.y,box_q[1].x-center.x);
+ theta.q=atan2(box_q[2].y-center.y,box_q[2].x-center.x);
+ if (theta.q < theta.p)
+ theta.q+=(MagickRealType) (2.0*MagickPI);
+ arc_segments=(unsigned long) ceil((double) ((theta.q-theta.p)/
+ (2.0*sqrt((double) (1.0/mid)))));
+ path_q[q].x=box_q[1].x;
+ path_q[q].y=box_q[1].y;
+ q++;
+ for (j=1; j < (long) arc_segments; j++)
+ {
+ delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
+ path_q[q].x=(double) (center.x+mid*cos(fmod((double)
+ (theta.p+delta_theta),DegreesToRadians(360.0))));
+ path_q[q].y=(double) (center.y+mid*sin(fmod((double)
+ (theta.p+delta_theta),DegreesToRadians(360.0))));
+ q++;
+ }
+ path_q[q++]=box_q[2];
+ break;
+ }
+ default:
+ break;
+ }
+ else
+ switch (draw_info->linejoin)
+ {
+ case BevelJoin:
+ {
+ path_p[p++]=box_p[1];
+ path_p[p++]=box_p[2];
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ path_q[q++]=box_q[4];
+ else
+ {
+ path_q[q++]=box_q[1];
+ path_q[q++]=box_q[2];
+ }
+ break;
+ }
+ case MiterJoin:
+ {
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ {
+ path_q[q++]=box_q[4];
+ path_p[p++]=box_p[4];
+ }
+ else
+ {
+ path_q[q++]=box_q[1];
+ path_q[q++]=box_q[2];
+ path_p[p++]=box_p[1];
+ path_p[p++]=box_p[2];
+ }
+ break;
+ }
+ case RoundJoin:
+ {
+ dot_product=(box_q[4].x-box_p[4].x)*(box_q[4].x-box_p[4].x)+
+ (box_q[4].y-box_p[4].y)*(box_q[4].y-box_p[4].y);
+ if (dot_product <= miterlimit)
+ path_q[q++]=box_q[4];
+ else
+ {
+ path_q[q++]=box_q[1];
+ path_q[q++]=box_q[2];
+ }
+ center=polygon_primitive[n].point;
+ theta.p=atan2(box_p[1].y-center.y,box_p[1].x-center.x);
+ theta.q=atan2(box_p[2].y-center.y,box_p[2].x-center.x);
+ if (theta.p < theta.q)
+ theta.p+=(MagickRealType) (2.0*MagickPI);
+ arc_segments=(unsigned long) ceil((double) ((theta.p-theta.q)/
+ (2.0*sqrt((double) (1.0/mid)))));
+ path_p[p++]=box_p[1];
+ for (j=1; j < (long) arc_segments; j++)
+ {
+ delta_theta=(MagickRealType) (j*(theta.q-theta.p)/arc_segments);
+ path_p[p].x=(double) (center.x+mid*cos(fmod((double)
+ (theta.p+delta_theta),DegreesToRadians(360.0))));
+ path_p[p].y=(double) (center.y+mid*sin(fmod((double)
+ (theta.p+delta_theta),DegreesToRadians(360.0))));
+ p++;
+ }
+ path_p[p++]=box_p[2];
+ break;
+ }
+ default:
+ break;
+ }
+ slope.p=slope.q;
+ inverse_slope.p=inverse_slope.q;
+ box_p[0]=box_p[2];
+ box_p[1]=box_p[3];
+ box_q[0]=box_q[2];
+ box_q[1]=box_q[3];
+ dx.p=dx.q;
+ dy.p=dy.q;
+ n=i;
+ }
+ path_p[p++]=box_p[1];
+ path_q[q++]=box_q[1];
+ /*
+ Trace stroked polygon.
+ */
+ stroke_polygon=(PrimitiveInfo *) AcquireQuantumMemory((size_t)
+ (p+q+2UL*closed_path+2UL),sizeof(*stroke_polygon));
+ if (stroke_polygon != (PrimitiveInfo *) NULL)
+ {
+ for (i=0; i < (long) p; i++)
+ {
+ stroke_polygon[i]=polygon_primitive[0];
+ stroke_polygon[i].point=path_p[i];
+ }
+ if (closed_path != MagickFalse)
+ {
+ stroke_polygon[i]=polygon_primitive[0];
+ stroke_polygon[i].point=stroke_polygon[0].point;
+ i++;
+ }
+ for ( ; i < (long) (p+q+closed_path); i++)
+ {
+ stroke_polygon[i]=polygon_primitive[0];
+ stroke_polygon[i].point=path_q[p+q+closed_path-(i+1)];
+ }
+ if (closed_path != MagickFalse)
+ {
+ stroke_polygon[i]=polygon_primitive[0];
+ stroke_polygon[i].point=stroke_polygon[p+closed_path].point;
+ i++;
+ }
+ stroke_polygon[i]=polygon_primitive[0];
+ stroke_polygon[i].point=stroke_polygon[0].point;
+ i++;
+ stroke_polygon[i].primitive=UndefinedPrimitive;
+ stroke_polygon[0].coordinates=(unsigned long) (p+q+2*closed_path+1);
+ }
+ path_p=(PointInfo *) RelinquishMagickMemory(path_p);
+ path_q=(PointInfo *) RelinquishMagickMemory(path_q);
+ polygon_primitive=(PrimitiveInfo *) RelinquishMagickMemory(polygon_primitive);
+ return(stroke_polygon);
+}
diff --git a/magick/draw.h b/magick/draw.h
new file mode 100644
index 0000000..9098127
--- /dev/null
+++ b/magick/draw.h
@@ -0,0 +1,384 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore drawing methods.
+*/
+#ifndef _MAGICKCORE_DRAW_H
+#define _MAGICKCORE_DRAW_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/pixel.h"
+#include "magick/type.h"
+
+typedef enum
+{
+ UndefinedAlign,
+ LeftAlign,
+ CenterAlign,
+ RightAlign
+} AlignType;
+
+typedef enum
+{
+ UndefinedPathUnits,
+ UserSpace,
+ UserSpaceOnUse,
+ ObjectBoundingBox
+} ClipPathUnits;
+
+typedef enum
+{
+ UndefinedDecoration,
+ NoDecoration,
+ UnderlineDecoration,
+ OverlineDecoration,
+ LineThroughDecoration
+} DecorationType;
+
+typedef enum
+{
+ UndefinedRule,
+#undef EvenOddRule
+ EvenOddRule,
+ NonZeroRule
+} FillRule;
+
+typedef enum
+{
+ UndefinedGradient,
+ LinearGradient,
+ RadialGradient
+} GradientType;
+
+typedef enum
+{
+ UndefinedCap,
+ ButtCap,
+ RoundCap,
+ SquareCap
+} LineCap;
+
+typedef enum
+{
+ UndefinedJoin,
+ MiterJoin,
+ RoundJoin,
+ BevelJoin
+} LineJoin;
+
+typedef enum
+{
+ UndefinedMethod,
+ PointMethod,
+ ReplaceMethod,
+ FloodfillMethod,
+ FillToBorderMethod,
+ ResetMethod
+} PaintMethod;
+
+typedef enum
+{
+ UndefinedPrimitive,
+ PointPrimitive,
+ LinePrimitive,
+ RectanglePrimitive,
+ RoundRectanglePrimitive,
+ ArcPrimitive,
+ EllipsePrimitive,
+ CirclePrimitive,
+ PolylinePrimitive,
+ PolygonPrimitive,
+ BezierPrimitive,
+ ColorPrimitive,
+ MattePrimitive,
+ TextPrimitive,
+ ImagePrimitive,
+ PathPrimitive
+} PrimitiveType;
+
+typedef enum
+{
+ UndefinedReference,
+ GradientReference
+} ReferenceType;
+
+typedef enum
+{
+ UndefinedSpread,
+ PadSpread,
+ ReflectSpread,
+ RepeatSpread
+} SpreadMethod;
+
+typedef struct _PointInfo
+{
+ double
+ x,
+ y;
+} PointInfo;
+
+typedef struct _StopInfo
+{
+ MagickPixelPacket
+ color;
+
+ MagickRealType
+ offset;
+} StopInfo;
+
+typedef struct _GradientInfo
+{
+ GradientType
+ type;
+
+ RectangleInfo
+ bounding_box;
+
+ SegmentInfo
+ gradient_vector;
+
+ StopInfo
+ *stops;
+
+ unsigned long
+ number_stops;
+
+ SpreadMethod
+ spread;
+
+ MagickBooleanType
+ debug;
+
+ unsigned long
+ signature;
+
+ PointInfo
+ center;
+
+ MagickRealType
+ radius;
+} GradientInfo;
+
+typedef struct _ElementReference
+{
+ char
+ *id;
+
+ ReferenceType
+ type;
+
+ GradientInfo
+ gradient;
+
+ unsigned long
+ signature;
+
+ struct _ElementReference
+ *previous,
+ *next;
+} ElementReference;
+
+typedef struct _DrawInfo
+{
+ char
+ *primitive,
+ *geometry;
+
+ RectangleInfo
+ viewbox;
+
+ AffineMatrix
+ affine;
+
+ GravityType
+ gravity;
+
+ PixelPacket
+ fill,
+ stroke;
+
+ double
+ stroke_width;
+
+ GradientInfo
+ gradient;
+
+ Image
+ *fill_pattern,
+ *tile,
+ *stroke_pattern;
+
+ MagickBooleanType
+ stroke_antialias,
+ text_antialias;
+
+ FillRule
+ fill_rule;
+
+ LineCap
+ linecap;
+
+ LineJoin
+ linejoin;
+
+ unsigned long
+ miterlimit;
+
+ double
+ dash_offset;
+
+ DecorationType
+ decorate;
+
+ CompositeOperator
+ compose;
+
+ char
+ *text;
+
+ unsigned long
+ face;
+
+ char
+ *font,
+ *metrics,
+ *family;
+
+ StyleType
+ style;
+
+ StretchType
+ stretch;
+
+ unsigned long
+ weight;
+
+ char
+ *encoding;
+
+ double
+ pointsize;
+
+ char
+ *density;
+
+ AlignType
+ align;
+
+ PixelPacket
+ undercolor,
+ border_color;
+
+ char
+ *server_name;
+
+ double
+ *dash_pattern;
+
+ char
+ *clip_mask;
+
+ SegmentInfo
+ bounds;
+
+ ClipPathUnits
+ clip_units;
+
+ Quantum
+ opacity;
+
+ MagickBooleanType
+ render;
+
+ ElementReference
+ element_reference;
+
+ MagickBooleanType
+ debug;
+
+ unsigned long
+ signature;
+
+ double
+ kerning,
+ interword_spacing;
+} DrawInfo;
+
+typedef struct _PrimitiveInfo
+{
+ PointInfo
+ point;
+
+ unsigned long
+ coordinates;
+
+ PrimitiveType
+ primitive;
+
+ PaintMethod
+ method;
+
+ char
+ *text;
+} PrimitiveInfo;
+
+typedef struct _TypeMetric
+{
+ PointInfo
+ pixels_per_em;
+
+ double
+ ascent,
+ descent,
+ width,
+ height,
+ max_advance,
+ underline_position,
+ underline_thickness;
+
+ SegmentInfo
+ bounds;
+
+ PointInfo
+ origin;
+} TypeMetric;
+
+extern MagickExport DrawInfo
+ *AcquireDrawInfo(void),
+ *CloneDrawInfo(const ImageInfo *,const DrawInfo *),
+ *DestroyDrawInfo(DrawInfo *);
+
+extern MagickExport MagickBooleanType
+ DrawAffineImage(Image *,const Image *,const AffineMatrix *),
+ DrawClipPath(Image *,const DrawInfo *,const char *),
+ DrawGradientImage(Image *,const DrawInfo *),
+ DrawImage(Image *,const DrawInfo *),
+ DrawPatternPath(Image *,const DrawInfo *,const char *,Image **),
+ DrawPrimitive(Image *,const DrawInfo *,const PrimitiveInfo *);
+
+extern MagickExport void
+ GetAffineMatrix(AffineMatrix *),
+ GetDrawInfo(const ImageInfo *,DrawInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/effect.c b/magick/effect.c
new file mode 100644
index 0000000..6b74def
--- /dev/null
+++ b/magick/effect.c
@@ -0,0 +1,4760 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% EEEEE FFFFF FFFFF EEEEE CCCC TTTTT %
+% E F F E C T %
+% EEE FFF FFF EEE C T %
+% E F F E C T %
+% EEEEE F F EEEEE CCCC T %
+% %
+% %
+% MagickCore Image Effects Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/draw.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/effect.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/montage.h"
+#include "magick/paint.h"
+#include "magick/pixel-private.h"
+#include "magick/property.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/random-private.h"
+#include "magick/resample.h"
+#include "magick/resample-private.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/segment.h"
+#include "magick/shear.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/transform.h"
+#include "magick/threshold.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d a p t i v e B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AdaptiveBlurImage() adaptively blurs the image by blurring less
+% intensely near image edges and more intensely far from edges. We blur the
+% image with a Gaussian operator of the given radius and standard deviation
+% (sigma). For reasonable results, radius should be larger than sigma. Use a
+% radius of 0 and AdaptiveBlurImage() selects a suitable radius for you.
+%
+% The format of the AdaptiveBlurImage method is:
+%
+% Image *AdaptiveBlurImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *AdaptiveBlurImageChannel(const Image *image,
+% const ChannelType channel,double radius,const double sigma,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Laplacian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *AdaptiveBlurImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=AdaptiveBlurImageChannel(image,DefaultChannels,radius,sigma,
+ exception);
+ return(blur_image);
+}
+
+MagickExport Image *AdaptiveBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+#define AdaptiveBlurImageTag "Convolve/Image"
+#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
+
+ double
+ **kernel;
+
+ Image
+ *blur_image,
+ *edge_image,
+ *gaussian_image;
+
+ long
+ j,
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ alpha,
+ bias,
+ normalize;
+
+ register long
+ i,
+ u,
+ v;
+
+ unsigned long
+ width;
+
+ CacheView
+ *blur_view,
+ *edge_view,
+ *image_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ blur_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (fabs(sigma) <= MagickEpsilon)
+ return(blur_image);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ /*
+ Edge detect the image brighness channel, level, blur, and level again.
+ */
+ edge_image=EdgeImage(image,radius,exception);
+ if (edge_image == (Image *) NULL)
+ {
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ (void) LevelImage(edge_image,"20%,95%");
+ gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
+ if (gaussian_image != (Image *) NULL)
+ {
+ edge_image=DestroyImage(edge_image);
+ edge_image=gaussian_image;
+ }
+ (void) LevelImage(edge_image,"10%,95%");
+ /*
+ Create a set of kernels from maximum (radius,sigma) to minimum.
+ */
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
+ if (kernel == (double **) NULL)
+ {
+ edge_image=DestroyImage(edge_image);
+ blur_image=DestroyImage(blur_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
+ for (i=0; i < (long) width; i+=2)
+ {
+ kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
+ sizeof(**kernel));
+ if (kernel[i] == (double *) NULL)
+ break;
+ j=0;
+ for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
+ {
+ for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i][j]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
+ j++;
+ }
+ }
+ normalize=0.0;
+ for (j=0; j < (long) ((width-i)*(width-i)); j++)
+ normalize+=kernel[i][j];
+ if (fabs(normalize) <= MagickEpsilon)
+ normalize=1.0;
+ normalize=1.0/normalize;
+ for (j=0; j < (long) ((width-i)*(width-i)); j++)
+ kernel[i][j]=(double) (normalize*kernel[i][j]);
+ }
+ if (i < (long) width)
+ {
+ for (i-=2; i >= 0; i-=2)
+ kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
+ kernel=(double **) RelinquishMagickMemory(kernel);
+ edge_image=DestroyImage(edge_image);
+ blur_image=DestroyImage(blur_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Adaptively blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ bias=image->bias;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ edge_view=AcquireCacheView(edge_image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) blur_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict r;
+
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (long) blur_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ alpha,
+ gamma;
+
+ register const double
+ *__restrict k;
+
+ register long
+ i,
+ u,
+ v;
+
+ gamma=0.0;
+ i=(long) (width*QuantumScale*PixelIntensity(r)+0.5);
+ if (i < 0)
+ i=0;
+ else
+ if (i > (long) width)
+ i=(long) width;
+ if ((i & 0x01) != 0)
+ i--;
+ p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
+ ((width-i)/2L),width-i,width-i,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ pixel=zero;
+ k=kernel[i];
+ for (v=0; v < (long) (width-i); v++)
+ {
+ for (u=0; u < (long) (width-i); u++)
+ {
+ alpha=1.0;
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ if ((channel & RedChannel) != 0)
+ pixel.red+=(*k)*alpha*p->red;
+ if ((channel & GreenChannel) != 0)
+ pixel.green+=(*k)*alpha*p->green;
+ if ((channel & BlueChannel) != 0)
+ pixel.blue+=(*k)*alpha*p->blue;
+ if ((channel & OpacityChannel) != 0)
+ pixel.opacity+=(*k)*p->opacity;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
+ gamma+=(*k)*alpha;
+ k++;
+ p++;
+ }
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ q++;
+ r++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AdaptiveBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,AdaptiveBlurImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_image->type=image->type;
+ blur_view=DestroyCacheView(blur_view);
+ edge_view=DestroyCacheView(edge_view);
+ image_view=DestroyCacheView(image_view);
+ edge_image=DestroyImage(edge_image);
+ for (i=0; i < (long) width; i+=2)
+ kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
+ kernel=(double **) RelinquishMagickMemory(kernel);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d a p t i v e S h a r p e n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AdaptiveSharpenImage() adaptively sharpens the image by sharpening more
+% intensely near image edges and less intensely far from edges. We sharpen the
+% image with a Gaussian operator of the given radius and standard deviation
+% (sigma). For reasonable results, radius should be larger than sigma. Use a
+% radius of 0 and AdaptiveSharpenImage() selects a suitable radius for you.
+%
+% The format of the AdaptiveSharpenImage method is:
+%
+% Image *AdaptiveSharpenImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *AdaptiveSharpenImageChannel(const Image *image,
+% const ChannelType channel,double radius,const double sigma,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Laplacian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *AdaptiveSharpenImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *sharp_image;
+
+ sharp_image=AdaptiveSharpenImageChannel(image,DefaultChannels,radius,sigma,
+ exception);
+ return(sharp_image);
+}
+
+MagickExport Image *AdaptiveSharpenImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+#define AdaptiveSharpenImageTag "Convolve/Image"
+#define MagickSigma (fabs(sigma) <= MagickEpsilon ? 1.0 : sigma)
+
+ double
+ **kernel;
+
+ Image
+ *sharp_image,
+ *edge_image,
+ *gaussian_image;
+
+ long
+ j,
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ alpha,
+ bias,
+ normalize;
+
+ register long
+ i,
+ u,
+ v;
+
+ unsigned long
+ width;
+
+ CacheView
+ *sharp_view,
+ *edge_view,
+ *image_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ sharp_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (sharp_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (fabs(sigma) <= MagickEpsilon)
+ return(sharp_image);
+ if (SetImageStorageClass(sharp_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&sharp_image->exception);
+ sharp_image=DestroyImage(sharp_image);
+ return((Image *) NULL);
+ }
+ /*
+ Edge detect the image brighness channel, level, sharp, and level again.
+ */
+ edge_image=EdgeImage(image,radius,exception);
+ if (edge_image == (Image *) NULL)
+ {
+ sharp_image=DestroyImage(sharp_image);
+ return((Image *) NULL);
+ }
+ (void) LevelImage(edge_image,"20%,95%");
+ gaussian_image=GaussianBlurImage(edge_image,radius,sigma,exception);
+ if (gaussian_image != (Image *) NULL)
+ {
+ edge_image=DestroyImage(edge_image);
+ edge_image=gaussian_image;
+ }
+ (void) LevelImage(edge_image,"10%,95%");
+ /*
+ Create a set of kernels from maximum (radius,sigma) to minimum.
+ */
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double **) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
+ if (kernel == (double **) NULL)
+ {
+ edge_image=DestroyImage(edge_image);
+ sharp_image=DestroyImage(sharp_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
+ for (i=0; i < (long) width; i+=2)
+ {
+ kernel[i]=(double *) AcquireQuantumMemory((size_t) (width-i),(width-i)*
+ sizeof(**kernel));
+ if (kernel[i] == (double *) NULL)
+ break;
+ j=0;
+ for (v=(-((long) (width-i)/2)); v <= (long) ((width-i)/2); v++)
+ {
+ for (u=(-((long) (width-i)/2)); u <= (long) ((width-i)/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i][j]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
+ j++;
+ }
+ }
+ normalize=0.0;
+ for (j=0; j < (long) ((width-i)*(width-i)); j++)
+ normalize+=kernel[i][j];
+ if (fabs(normalize) <= MagickEpsilon)
+ normalize=1.0;
+ normalize=1.0/normalize;
+ for (j=0; j < (long) ((width-i)*(width-i)); j++)
+ kernel[i][j]=(double) (normalize*kernel[i][j]);
+ }
+ if (i < (long) width)
+ {
+ for (i-=2; i >= 0; i-=2)
+ kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
+ kernel=(double **) RelinquishMagickMemory(kernel);
+ edge_image=DestroyImage(edge_image);
+ sharp_image=DestroyImage(sharp_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Adaptively sharpen image.
+ */
+ status=MagickTrue;
+ progress=0;
+ bias=image->bias;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ edge_view=AcquireCacheView(edge_image);
+ sharp_view=AcquireCacheView(sharp_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) sharp_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict r;
+
+ register IndexPacket
+ *__restrict sharp_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ r=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(sharp_view,0,y,sharp_image->columns,1,
+ exception);
+ if ((r == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ sharp_indexes=GetCacheViewAuthenticIndexQueue(sharp_view);
+ for (x=0; x < (long) sharp_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ alpha,
+ gamma;
+
+ register const double
+ *__restrict k;
+
+ register long
+ i,
+ u,
+ v;
+
+ gamma=0.0;
+ i=(long) (width*(QuantumRange-QuantumScale*PixelIntensity(r))+0.5);
+ if (i < 0)
+ i=0;
+ else
+ if (i > (long) width)
+ i=(long) width;
+ if ((i & 0x01) != 0)
+ i--;
+ p=GetCacheViewVirtualPixels(image_view,x-((long) (width-i)/2L),y-(long)
+ ((width-i)/2L),width-i,width-i,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ k=kernel[i];
+ pixel=zero;
+ for (v=0; v < (long) (width-i); v++)
+ {
+ for (u=0; u < (long) (width-i); u++)
+ {
+ alpha=1.0;
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte != MagickFalse))
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ if ((channel & RedChannel) != 0)
+ pixel.red+=(*k)*alpha*p->red;
+ if ((channel & GreenChannel) != 0)
+ pixel.green+=(*k)*alpha*p->green;
+ if ((channel & BlueChannel) != 0)
+ pixel.blue+=(*k)*alpha*p->blue;
+ if ((channel & OpacityChannel) != 0)
+ pixel.opacity+=(*k)*p->opacity;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ pixel.index+=(*k)*alpha*indexes[x+(width-i)*v+u];
+ gamma+=(*k)*alpha;
+ k++;
+ p++;
+ }
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ sharp_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ q++;
+ r++;
+ }
+ if (SyncCacheViewAuthenticPixels(sharp_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AdaptiveSharpenImageChannel)
+#endif
+ proceed=SetImageProgress(image,AdaptiveSharpenImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ sharp_image->type=image->type;
+ sharp_view=DestroyCacheView(sharp_view);
+ edge_view=DestroyCacheView(edge_view);
+ image_view=DestroyCacheView(image_view);
+ edge_image=DestroyImage(edge_image);
+ for (i=0; i < (long) width; i+=2)
+ kernel[i]=(double *) RelinquishMagickMemory(kernel[i]);
+ kernel=(double **) RelinquishMagickMemory(kernel);
+ if (status == MagickFalse)
+ sharp_image=DestroyImage(sharp_image);
+ return(sharp_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BlurImage() blurs an image. We convolve the image with a Gaussian operator
+% of the given radius and standard deviation (sigma). For reasonable results,
+% the radius should be larger than sigma. Use a radius of 0 and BlurImage()
+% selects a suitable radius for you.
+%
+% BlurImage() differs from GaussianBlurImage() in that it uses a separable
+% kernel which is faster but mathematically equivalent to the non-separable
+% kernel.
+%
+% The format of the BlurImage method is:
+%
+% Image *BlurImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *BlurImageChannel(const Image *image,const ChannelType channel,
+% const double radius,const double sigma,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *BlurImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=BlurImageChannel(image,DefaultChannels,radius,sigma,exception);
+ return(blur_image);
+}
+
+static double *GetBlurKernel(unsigned long width,const MagickRealType sigma)
+{
+#define KernelRank 3
+
+ double
+ *kernel;
+
+ long
+ bias;
+
+ MagickRealType
+ alpha,
+ normalize;
+
+ register long
+ i;
+
+ /*
+ Generate a 1-D convolution kernel.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ return(0);
+ (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
+ bias=KernelRank*(long) width/2;
+ for (i=(-bias); i <= bias; i++)
+ {
+ alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
+ MagickSigma*MagickSigma)));
+ kernel[(i+bias)/KernelRank]+=(double) (alpha/(MagickSQ2PI*sigma));
+ }
+ normalize=0.0;
+ for (i=0; i < (long) width; i++)
+ normalize+=kernel[i];
+ for (i=0; i < (long) width; i++)
+ kernel[i]/=normalize;
+ return(kernel);
+}
+
+MagickExport Image *BlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+#define BlurImageTag "Blur/Image"
+
+ double
+ *kernel;
+
+ Image
+ *blur_image;
+
+ long
+ progress,
+ x,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ bias;
+
+ register long
+ i;
+
+ unsigned long
+ width;
+
+ CacheView
+ *blur_view,
+ *image_view;
+
+ /*
+ Initialize blur image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (fabs(sigma) <= MagickEpsilon)
+ return(blur_image);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ width=GetOptimalKernelWidth1D(radius,sigma);
+ kernel=GetBlurKernel(width,sigma);
+ if (kernel == (double *) NULL)
+ {
+ blur_image=DestroyImage(blur_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ *message;
+
+ register const double
+ *k;
+
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " BlurImage with %ld kernel:",width);
+ message=AcquireString("");
+ k=kernel;
+ for (i=0; i < (long) width; i++)
+ {
+ *message='\0';
+ (void) FormatMagickString(format,MaxTextExtent,"%ld: ",i);
+ (void) ConcatenateString(&message,format);
+ (void) FormatMagickString(format,MaxTextExtent,"%g ",*k++);
+ (void) ConcatenateString(&message,format);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
+ }
+ message=DestroyString(message);
+ }
+ /*
+ Blur rows.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ bias=image->bias;
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) blur_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y,image->columns+
+ width,1,exception);
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (long) blur_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const double
+ *__restrict k;
+
+ register const PixelPacket
+ *__restrict kernel_pixels;
+
+ register long
+ i;
+
+ pixel=zero;
+ k=kernel;
+ kernel_pixels=p;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.red+=(*k)*kernel_pixels->red;
+ pixel.green+=(*k)*kernel_pixels->green;
+ pixel.blue+=(*k)*kernel_pixels->blue;
+ k++;
+ kernel_pixels++;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=kernel;
+ kernel_pixels=p;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels->opacity;
+ k++;
+ kernel_pixels++;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=kernel;
+ kernel_indexes=indexes;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.index+=(*k)*(*kernel_indexes);
+ k++;
+ kernel_indexes++;
+ }
+ blur_indexes[x]=RoundToQuantum(pixel.index+bias);
+ }
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ gamma=0.0;
+ for (i=0; i < (long) width; i++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels->opacity));
+ pixel.red+=(*k)*alpha*kernel_pixels->red;
+ pixel.green+=(*k)*alpha*kernel_pixels->green;
+ pixel.blue+=(*k)*alpha*kernel_pixels->blue;
+ gamma+=(*k)*alpha;
+ k++;
+ kernel_pixels++;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=kernel;
+ kernel_pixels=p;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels->opacity;
+ k++;
+ kernel_pixels++;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=kernel;
+ kernel_pixels=p;
+ kernel_indexes=indexes;
+ for (i=0; i < (long) width; i++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels->opacity));
+ pixel.index+=(*k)*alpha*(*kernel_indexes);
+ k++;
+ kernel_pixels++;
+ kernel_indexes++;
+ }
+ blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ }
+ }
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_BlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
+ blur_image->columns);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ /*
+ Blur columns.
+ */
+ image_view=AcquireCacheView(blur_image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for shared(progress,status)
+#endif
+ for (x=0; x < (long) blur_image->columns; x++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,x,-((long) width/2L),1,image->rows+
+ width,exception);
+ q=GetCacheViewAuthenticPixels(blur_view,x,0,1,blur_image->rows,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (y=0; y < (long) blur_image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const double
+ *__restrict k;
+
+ register const PixelPacket
+ *__restrict kernel_pixels;
+
+ register long
+ i;
+
+ pixel=zero;
+ k=kernel;
+ kernel_pixels=p;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.red+=(*k)*kernel_pixels->red;
+ pixel.green+=(*k)*kernel_pixels->green;
+ pixel.blue+=(*k)*kernel_pixels->blue;
+ k++;
+ kernel_pixels++;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=kernel;
+ kernel_pixels=p;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels->opacity;
+ k++;
+ kernel_pixels++;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=kernel;
+ kernel_indexes=indexes;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.index+=(*k)*(*kernel_indexes);
+ k++;
+ kernel_indexes++;
+ }
+ blur_indexes[y]=RoundToQuantum(pixel.index+bias);
+ }
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ gamma=0.0;
+ for (i=0; i < (long) width; i++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels->opacity));
+ pixel.red+=(*k)*alpha*kernel_pixels->red;
+ pixel.green+=(*k)*alpha*kernel_pixels->green;
+ pixel.blue+=(*k)*alpha*kernel_pixels->blue;
+ gamma+=(*k)*alpha;
+ k++;
+ kernel_pixels++;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=kernel;
+ kernel_pixels=p;
+ for (i=0; i < (long) width; i++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels->opacity;
+ k++;
+ kernel_pixels++;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=kernel;
+ kernel_pixels=p;
+ kernel_indexes=indexes;
+ for (i=0; i < (long) width; i++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels->opacity));
+ pixel.index+=(*k)*alpha*(*kernel_indexes);
+ k++;
+ kernel_pixels++;
+ kernel_indexes++;
+ }
+ blur_indexes[y]=RoundToQuantum(gamma*pixel.index+bias);
+ }
+ }
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_BlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,blur_image->rows+
+ blur_image->columns);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ blur_image->type=image->type;
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s p e c k l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DespeckleImage() reduces the speckle noise in an image while perserving the
+% edges of the original image.
+%
+% The format of the DespeckleImage method is:
+%
+% Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static Quantum **DestroyPixelThreadSet(Quantum **pixels)
+{
+ register long
+ i;
+
+ assert(pixels != (Quantum **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (pixels[i] != (Quantum *) NULL)
+ pixels[i]=(Quantum *) RelinquishMagickMemory(pixels[i]);
+ pixels=(Quantum **) RelinquishAlignedMemory(pixels);
+ return(pixels);
+}
+
+static Quantum **AcquirePixelThreadSet(const size_t count)
+{
+ register long
+ i;
+
+ Quantum
+ **pixels;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ pixels=(Quantum **) AcquireAlignedMemory(number_threads,sizeof(*pixels));
+ if (pixels == (Quantum **) NULL)
+ return((Quantum **) NULL);
+ (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ pixels[i]=(Quantum *) AcquireQuantumMemory(count,sizeof(**pixels));
+ if (pixels[i] == (Quantum *) NULL)
+ return(DestroyPixelThreadSet(pixels));
+ }
+ return(pixels);
+}
+
+static void Hull(const long x_offset,const long y_offset,
+ const unsigned long columns,const unsigned long rows,Quantum *f,Quantum *g,
+ const int polarity)
+{
+ long
+ y;
+
+ MagickRealType
+ v;
+
+ register long
+ x;
+
+ register Quantum
+ *p,
+ *q,
+ *r,
+ *s;
+
+ assert(f != (Quantum *) NULL);
+ assert(g != (Quantum *) NULL);
+ p=f+(columns+2);
+ q=g+(columns+2);
+ r=p+(y_offset*((long) columns+2)+x_offset);
+ for (y=0; y < (long) rows; y++)
+ {
+ p++;
+ q++;
+ r++;
+ if (polarity > 0)
+ for (x=(long) columns; x != 0; x--)
+ {
+ v=(MagickRealType) (*p);
+ if ((MagickRealType) *r >= (v+(MagickRealType) ScaleCharToQuantum(2)))
+ v+=ScaleCharToQuantum(1);
+ *q=(Quantum) v;
+ p++;
+ q++;
+ r++;
+ }
+ else
+ for (x=(long) columns; x != 0; x--)
+ {
+ v=(MagickRealType) (*p);
+ if ((MagickRealType) *r <= (v-(MagickRealType) ScaleCharToQuantum(2)))
+ v-=(long) ScaleCharToQuantum(1);
+ *q=(Quantum) v;
+ p++;
+ q++;
+ r++;
+ }
+ p++;
+ q++;
+ r++;
+ }
+ p=f+(columns+2);
+ q=g+(columns+2);
+ r=q+(y_offset*((long) columns+2)+x_offset);
+ s=q-(y_offset*((long) columns+2)+x_offset);
+ for (y=0; y < (long) rows; y++)
+ {
+ p++;
+ q++;
+ r++;
+ s++;
+ if (polarity > 0)
+ for (x=(long) columns; x != 0; x--)
+ {
+ v=(MagickRealType) (*q);
+ if (((MagickRealType) *s >=
+ (v+(MagickRealType) ScaleCharToQuantum(2))) &&
+ ((MagickRealType) *r > v))
+ v+=ScaleCharToQuantum(1);
+ *p=(Quantum) v;
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+ else
+ for (x=(long) columns; x != 0; x--)
+ {
+ v=(MagickRealType) (*q);
+ if (((MagickRealType) *s <=
+ (v-(MagickRealType) ScaleCharToQuantum(2))) &&
+ ((MagickRealType) *r < v))
+ v-=(MagickRealType) ScaleCharToQuantum(1);
+ *p=(Quantum) v;
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+ p++;
+ q++;
+ r++;
+ s++;
+ }
+}
+
+MagickExport Image *DespeckleImage(const Image *image,ExceptionInfo *exception)
+{
+#define DespeckleImageTag "Despeckle/Image"
+
+ Image
+ *despeckle_image;
+
+ long
+ channel;
+
+ MagickBooleanType
+ status;
+
+ Quantum
+ **buffers,
+ **pixels;
+
+ size_t
+ length;
+
+ static const int
+ X[4]= {0, 1, 1,-1},
+ Y[4]= {1, 0, 1, 1};
+
+ CacheView
+ *despeckle_view,
+ *image_view;
+
+ /*
+ Allocate despeckled image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ despeckle_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (despeckle_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(despeckle_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&despeckle_image->exception);
+ despeckle_image=DestroyImage(despeckle_image);
+ return((Image *) NULL);
+ }
+ /*
+ Allocate image buffers.
+ */
+ length=(size_t) ((image->columns+2)*(image->rows+2));
+ pixels=AcquirePixelThreadSet(length);
+ buffers=AcquirePixelThreadSet(length);
+ if ((pixels == (Quantum **) NULL) || (buffers == (Quantum **) NULL))
+ {
+ if (buffers != (Quantum **) NULL)
+ buffers=DestroyPixelThreadSet(buffers);
+ if (pixels != (Quantum **) NULL)
+ pixels=DestroyPixelThreadSet(pixels);
+ despeckle_image=DestroyImage(despeckle_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Reduce speckle in the image.
+ */
+ status=MagickTrue;
+ image_view=AcquireCacheView(image);
+ despeckle_view=AcquireCacheView(despeckle_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,1) shared(status)
+#endif
+ for (channel=0; channel <= 3; channel++)
+ {
+ long
+ j,
+ y;
+
+ register long
+ i,
+ x;
+
+ register Quantum
+ *buffer,
+ *pixel;
+
+ if (status == MagickFalse)
+ continue;
+ pixel=pixels[GetOpenMPThreadId()];
+ (void) ResetMagickMemory(pixel,0,length*sizeof(*pixel));
+ j=(long) image->columns+2;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ j++;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ switch (channel)
+ {
+ case 0: pixel[j]=p->red; break;
+ case 1: pixel[j]=p->green; break;
+ case 2: pixel[j]=p->blue; break;
+ case 3: pixel[j]=p->opacity; break;
+ default: break;
+ }
+ p++;
+ j++;
+ }
+ j++;
+ }
+ buffer=buffers[GetOpenMPThreadId()];
+ (void) ResetMagickMemory(buffer,0,length*sizeof(*buffer));
+ for (i=0; i < 4; i++)
+ {
+ Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,1);
+ Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,1);
+ Hull(-X[i],-Y[i],image->columns,image->rows,pixel,buffer,-1);
+ Hull(X[i],Y[i],image->columns,image->rows,pixel,buffer,-1);
+ }
+ j=(long) image->columns+2;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(despeckle_view,0,y,despeckle_image->columns,
+ 1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ j++;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ switch (channel)
+ {
+ case 0: q->red=pixel[j]; break;
+ case 1: q->green=pixel[j]; break;
+ case 2: q->blue=pixel[j]; break;
+ case 3: q->opacity=pixel[j]; break;
+ default: break;
+ }
+ q++;
+ j++;
+ }
+ sync=SyncCacheViewAuthenticPixels(despeckle_view,exception);
+ if (sync == MagickFalse)
+ {
+ status=MagickFalse;
+ break;
+ }
+ j++;
+ }
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_DespeckleImage)
+#endif
+ proceed=SetImageProgress(image,DespeckleImageTag,channel,3);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ despeckle_view=DestroyCacheView(despeckle_view);
+ image_view=DestroyCacheView(image_view);
+ buffers=DestroyPixelThreadSet(buffers);
+ pixels=DestroyPixelThreadSet(pixels);
+ despeckle_image->type=image->type;
+ if (status == MagickFalse)
+ despeckle_image=DestroyImage(despeckle_image);
+ return(despeckle_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E d g e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EdgeImage() finds edges in an image. Radius defines the radius of the
+% convolution filter. Use a radius of 0 and EdgeImage() selects a suitable
+% radius for you.
+%
+% The format of the EdgeImage method is:
+%
+% Image *EdgeImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *EdgeImage(const Image *image,const double radius,
+ ExceptionInfo *exception)
+{
+ Image
+ *edge_image;
+
+ double
+ *kernel;
+
+ register long
+ i;
+
+ unsigned long
+ width;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth1D(radius,0.5);
+ kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ for (i=0; i < (long) (width*width); i++)
+ kernel[i]=(-1.0);
+ kernel[i/2]=(double) (width*width-1.0);
+ edge_image=ConvolveImage(image,width,kernel,exception);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ return(edge_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E m b o s s I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EmbossImage() returns a grayscale image with a three-dimensional effect.
+% We convolve the image with a Gaussian operator of the given radius and
+% standard deviation (sigma). For reasonable results, radius should be
+% larger than sigma. Use a radius of 0 and Emboss() selects a suitable
+% radius for you.
+%
+% The format of the EmbossImage method is:
+%
+% Image *EmbossImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *EmbossImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ double
+ *kernel;
+
+ Image
+ *emboss_image;
+
+ long
+ j;
+
+ MagickRealType
+ alpha;
+
+ register long
+ i,
+ u,
+ v;
+
+ unsigned long
+ width;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ i=0;
+ j=(long) width/2;
+ for (v=(-((long) width/2)); v <= (long) (width/2); v++)
+ {
+ for (u=(-((long) width/2)); u <= (long) (width/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i]=(double) (((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
+ (2.0*MagickPI*MagickSigma*MagickSigma));
+ if (u != j)
+ kernel[i]=0.0;
+ i++;
+ }
+ j--;
+ }
+ emboss_image=ConvolveImage(image,width,kernel,exception);
+ if (emboss_image != (Image *) NULL)
+ (void) EqualizeImage(emboss_image);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ return(emboss_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G a u s s i a n B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GaussianBlurImage() blurs an image. We convolve the image with a
+% Gaussian operator of the given radius and standard deviation (sigma).
+% For reasonable results, the radius should be larger than sigma. Use a
+% radius of 0 and GaussianBlurImage() selects a suitable radius for you
+%
+% The format of the GaussianBlurImage method is:
+%
+% Image *GaussianBlurImage(const Image *image,onst double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *GaussianBlurImageChannel(const Image *image,
+% const ChannelType channel,const double radius,const double sigma,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *GaussianBlurImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=GaussianBlurImageChannel(image,DefaultChannels,radius,sigma,
+ exception);
+ return(blur_image);
+}
+
+MagickExport Image *GaussianBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+ double
+ *kernel;
+
+ Image
+ *blur_image;
+
+ MagickRealType
+ alpha;
+
+ register long
+ i,
+ u,
+ v;
+
+ unsigned long
+ width;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ i=0;
+ for (v=(-((long) width/2)); v <= (long) (width/2); v++)
+ {
+ for (u=(-((long) width/2)); u <= (long) (width/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
+ i++;
+ }
+ }
+ blur_image=ConvolveImageChannel(image,channel,width,kernel,exception);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M e d i a n F i l t e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MedianFilterImage() applies a digital filter that improves the quality
+% of a noisy image. Each pixel is replaced by the median in a set of
+% neighboring pixels as defined by radius.
+%
+% The algorithm was contributed by Mike Edmonds and implements an insertion
+% sort for selecting median color-channel values. For more on this algorithm
+% see "Skip Lists: A probabilistic Alternative to Balanced Trees" by William
+% Pugh in the June 1990 of Communications of the ACM.
+%
+% The format of the MedianFilterImage method is:
+%
+% Image *MedianFilterImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#define MedianListChannels 5
+
+typedef struct _MedianListNode
+{
+ unsigned long
+ next[9],
+ count,
+ signature;
+} MedianListNode;
+
+typedef struct _MedianSkipList
+{
+ long
+ level;
+
+ MedianListNode
+ *nodes;
+} MedianSkipList;
+
+typedef struct _MedianPixelList
+{
+ unsigned long
+ center,
+ seed,
+ signature;
+
+ MedianSkipList
+ lists[MedianListChannels];
+} MedianPixelList;
+
+static MedianPixelList *DestroyMedianPixelList(MedianPixelList *pixel_list)
+{
+ register long
+ i;
+
+ if (pixel_list == (MedianPixelList *) NULL)
+ return((MedianPixelList *) NULL);
+ for (i=0; i < MedianListChannels; i++)
+ if (pixel_list->lists[i].nodes != (MedianListNode *) NULL)
+ pixel_list->lists[i].nodes=(MedianListNode *) RelinquishMagickMemory(
+ pixel_list->lists[i].nodes);
+ pixel_list=(MedianPixelList *) RelinquishAlignedMemory(pixel_list);
+ return(pixel_list);
+}
+
+static MedianPixelList **DestroyMedianPixelListThreadSet(
+ MedianPixelList **pixel_list)
+{
+ register long
+ i;
+
+ assert(pixel_list != (MedianPixelList **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (pixel_list[i] != (MedianPixelList *) NULL)
+ pixel_list[i]=DestroyMedianPixelList(pixel_list[i]);
+ pixel_list=(MedianPixelList **) RelinquishAlignedMemory(pixel_list);
+ return(pixel_list);
+}
+
+static MedianPixelList *AcquireMedianPixelList(const unsigned long width)
+{
+ MedianPixelList
+ *pixel_list;
+
+ register long
+ i;
+
+ pixel_list=(MedianPixelList *) AcquireAlignedMemory(1,sizeof(*pixel_list));
+ if (pixel_list == (MedianPixelList *) NULL)
+ return(pixel_list);
+ (void) ResetMagickMemory((void *) pixel_list,0,sizeof(*pixel_list));
+ pixel_list->center=width*width/2;
+ for (i=0; i < MedianListChannels; i++)
+ {
+ pixel_list->lists[i].nodes=(MedianListNode *) AcquireQuantumMemory(65537UL,
+ sizeof(*pixel_list->lists[i].nodes));
+ if (pixel_list->lists[i].nodes == (MedianListNode *) NULL)
+ return(DestroyMedianPixelList(pixel_list));
+ (void) ResetMagickMemory(pixel_list->lists[i].nodes,0,65537UL*
+ sizeof(*pixel_list->lists[i].nodes));
+ }
+ pixel_list->signature=MagickSignature;
+ return(pixel_list);
+}
+
+static MedianPixelList **AcquireMedianPixelListThreadSet(
+ const unsigned long width)
+{
+ register long
+ i;
+
+ MedianPixelList
+ **pixel_list;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ pixel_list=(MedianPixelList **) AcquireAlignedMemory(number_threads,
+ sizeof(*pixel_list));
+ if (pixel_list == (MedianPixelList **) NULL)
+ return((MedianPixelList **) NULL);
+ (void) ResetMagickMemory(pixel_list,0,number_threads*sizeof(*pixel_list));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ pixel_list[i]=AcquireMedianPixelList(width);
+ if (pixel_list[i] == (MedianPixelList *) NULL)
+ return(DestroyMedianPixelListThreadSet(pixel_list));
+ }
+ return(pixel_list);
+}
+
+static void AddNodeMedianPixelList(MedianPixelList *pixel_list,
+ const long channel,const unsigned long color)
+{
+ register long
+ level;
+
+ register MedianSkipList
+ *list;
+
+ unsigned long
+ search,
+ update[9];
+
+ /*
+ Initialize the node.
+ */
+ list=pixel_list->lists+channel;
+ list->nodes[color].signature=pixel_list->signature;
+ list->nodes[color].count=1;
+ /*
+ Determine where it belongs in the list.
+ */
+ search=65536UL;
+ for (level=list->level; level >= 0; level--)
+ {
+ while (list->nodes[search].next[level] < color)
+ search=list->nodes[search].next[level];
+ update[level]=search;
+ }
+ /*
+ Generate a pseudo-random level for this node.
+ */
+ for (level=0; ; level++)
+ {
+ pixel_list->seed=(pixel_list->seed*42893621L)+1L;
+ if ((pixel_list->seed & 0x300) != 0x300)
+ break;
+ }
+ if (level > 8)
+ level=8;
+ if (level > (list->level+2))
+ level=list->level+2;
+ /*
+ If we're raising the list's level, link back to the root node.
+ */
+ while (level > list->level)
+ {
+ list->level++;
+ update[list->level]=65536UL;
+ }
+ /*
+ Link the node into the skip-list.
+ */
+ do
+ {
+ list->nodes[color].next[level]=list->nodes[update[level]].next[level];
+ list->nodes[update[level]].next[level]=color;
+ }
+ while (level-- > 0);
+}
+
+static MagickPixelPacket GetMedianPixelList(MedianPixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
+
+ register long
+ channel;
+
+ register MedianSkipList
+ *list;
+
+ unsigned long
+ center,
+ color,
+ count;
+
+ unsigned short
+ channels[MedianListChannels];
+
+ /*
+ Find the median value for each of the color.
+ */
+ center=pixel_list->center;
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ color=65536UL;
+ count=0;
+ do
+ {
+ color=list->nodes[color].next[0];
+ count+=list->nodes[color].count;
+ }
+ while (count <= center);
+ channels[channel]=(unsigned short) color;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
+
+static inline void InsertMedianPixelList(const Image *image,
+ const PixelPacket *pixel,const IndexPacket *indexes,
+ MedianPixelList *pixel_list)
+{
+ unsigned long
+ signature;
+
+ unsigned short
+ index;
+
+ index=ScaleQuantumToShort(pixel->red);
+ signature=pixel_list->lists[0].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[0].nodes[index].count++;
+ else
+ AddNodeMedianPixelList(pixel_list,0,index);
+ index=ScaleQuantumToShort(pixel->green);
+ signature=pixel_list->lists[1].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[1].nodes[index].count++;
+ else
+ AddNodeMedianPixelList(pixel_list,1,index);
+ index=ScaleQuantumToShort(pixel->blue);
+ signature=pixel_list->lists[2].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[2].nodes[index].count++;
+ else
+ AddNodeMedianPixelList(pixel_list,2,index);
+ index=ScaleQuantumToShort(pixel->opacity);
+ signature=pixel_list->lists[3].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[3].nodes[index].count++;
+ else
+ AddNodeMedianPixelList(pixel_list,3,index);
+ if (image->colorspace == CMYKColorspace)
+ index=ScaleQuantumToShort(*indexes);
+ signature=pixel_list->lists[4].nodes[index].signature;
+ if (signature == pixel_list->signature)
+ pixel_list->lists[4].nodes[index].count++;
+ else
+ AddNodeMedianPixelList(pixel_list,4,index);
+}
+
+static void ResetMedianPixelList(MedianPixelList *pixel_list)
+{
+ int
+ level;
+
+ register long
+ channel;
+
+ register MedianListNode
+ *root;
+
+ register MedianSkipList
+ *list;
+
+ /*
+ Reset the skip-list.
+ */
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ root=list->nodes+65536UL;
+ list->level=0;
+ for (level=0; level < 9; level++)
+ root->next[level]=65536UL;
+ }
+ pixel_list->seed=pixel_list->signature++;
+}
+
+MagickExport Image *MedianFilterImage(const Image *image,const double radius,
+ ExceptionInfo *exception)
+{
+#define MedianFilterImageTag "MedianFilter/Image"
+
+ Image
+ *median_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MedianPixelList
+ **pixel_list;
+
+ unsigned long
+ width;
+
+ CacheView
+ *image_view,
+ *median_view;
+
+ /*
+ Initialize median image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,0.5);
+ if ((image->columns < width) || (image->rows < width))
+ ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
+ median_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (median_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(median_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&median_image->exception);
+ median_image=DestroyImage(median_image);
+ return((Image *) NULL);
+ }
+ pixel_list=AcquireMedianPixelListThreadSet(width);
+ if (pixel_list == (MedianPixelList **) NULL)
+ {
+ median_image=DestroyImage(median_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Median filter each image row.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ median_view=AcquireCacheView(median_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) median_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict median_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
+ 2L),image->columns+width,width,exception);
+ q=QueueCacheViewAuthenticPixels(median_view,0,y,median_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ median_indexes=GetCacheViewAuthenticIndexQueue(median_view);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) median_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const PixelPacket
+ *__restrict r;
+
+ register const IndexPacket
+ *__restrict s;
+
+ register long
+ u,
+ v;
+
+ r=p;
+ s=indexes+x;
+ ResetMedianPixelList(pixel_list[id]);
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
+ r+=image->columns+width;
+ s+=image->columns+width;
+ }
+ pixel=GetMedianPixelList(pixel_list[id]);
+ SetPixelPacket(median_image,&pixel,q,median_indexes+x);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(median_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_MedianFilterImage)
+#endif
+ proceed=SetImageProgress(image,MedianFilterImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ median_view=DestroyCacheView(median_view);
+ image_view=DestroyCacheView(image_view);
+ pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
+ return(median_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o t i o n B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MotionBlurImage() simulates motion blur. We convolve the image with a
+% Gaussian operator of the given radius and standard deviation (sigma).
+% For reasonable results, radius should be larger than sigma. Use a
+% radius of 0 and MotionBlurImage() selects a suitable radius for you.
+% Angle gives the angle of the blurring motion.
+%
+% Andrew Protano contributed this effect.
+%
+% The format of the MotionBlurImage method is:
+%
+% Image *MotionBlurImage(const Image *image,const double radius,
+% const double sigma,const double angle,ExceptionInfo *exception)
+% Image *MotionBlurImageChannel(const Image *image,const ChannelType channel,
+% const double radius,const double sigma,const double angle,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% o radius: the radius of the Gaussian, in pixels, not counting
+% the center pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o angle: Apply the effect along this angle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static double *GetMotionBlurKernel(unsigned long width,
+ const MagickRealType sigma)
+{
+#define KernelRank 3
+
+ double
+ *kernel;
+
+ long
+ bias;
+
+ MagickRealType
+ alpha,
+ normalize;
+
+ register long
+ i;
+
+ /*
+ Generate a 1-D convolution kernel.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ kernel=(double *) AcquireQuantumMemory((size_t) width,sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ return(kernel);
+ (void) ResetMagickMemory(kernel,0,(size_t) width*sizeof(*kernel));
+ bias=(long) (KernelRank*width);
+ for (i=0; i < (long) bias; i++)
+ {
+ alpha=exp((-((double) (i*i))/(double) (2.0*KernelRank*KernelRank*
+ MagickSigma*MagickSigma)));
+ kernel[i/KernelRank]+=(double) alpha/(MagickSQ2PI*sigma);
+ }
+ normalize=0.0;
+ for (i=0; i < (long) width; i++)
+ normalize+=kernel[i];
+ for (i=0; i < (long) width; i++)
+ kernel[i]/=normalize;
+ return(kernel);
+}
+
+MagickExport Image *MotionBlurImage(const Image *image,const double radius,
+ const double sigma,const double angle,ExceptionInfo *exception)
+{
+ Image
+ *motion_blur;
+
+ motion_blur=MotionBlurImageChannel(image,DefaultChannels,radius,sigma,angle,
+ exception);
+ return(motion_blur);
+}
+
+MagickExport Image *MotionBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ const double angle,ExceptionInfo *exception)
+{
+ typedef struct _OffsetInfo
+ {
+ long
+ x,
+ y;
+ } OffsetInfo;
+
+ double
+ *kernel;
+
+ Image
+ *blur_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ OffsetInfo
+ *offset;
+
+ PointInfo
+ point;
+
+ register long
+ i;
+
+ unsigned long
+ width;
+
+ CacheView
+ *blur_view,
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ width=GetOptimalKernelWidth1D(radius,sigma);
+ kernel=GetMotionBlurKernel(width,sigma);
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ offset=(OffsetInfo *) AcquireQuantumMemory(width,sizeof(*offset));
+ if (offset == (OffsetInfo *) NULL)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ return((Image *) NULL);
+ }
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ point.x=(double) width*sin(DegreesToRadians(angle));
+ point.y=(double) width*cos(DegreesToRadians(angle));
+ for (i=0; i < (long) width; i++)
+ {
+ offset[i].x=(long) ((i*point.y)/hypot(point.x,point.y)+0.5);
+ offset[i].y=(long) ((i*point.x)/hypot(point.x,point.y)+0.5);
+ }
+ /*
+ Motion blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickPixelPacket
+ qixel;
+
+ PixelPacket
+ pixel;
+
+ register double
+ *__restrict k;
+
+ register long
+ i;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ k=kernel;
+ qixel=zero;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (long) width; i++)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
+ offset[i].y,&pixel,exception);
+ qixel.red+=(*k)*pixel.red;
+ qixel.green+=(*k)*pixel.green;
+ qixel.blue+=(*k)*pixel.blue;
+ qixel.opacity+=(*k)*pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*k)*(*indexes);
+ }
+ k++;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(qixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(qixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(qixel.blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(qixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ blur_indexes[x]=(IndexPacket) RoundToQuantum(qixel.index);
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ alpha=0.0;
+ gamma=0.0;
+ for (i=0; i < (long) width; i++)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,x+offset[i].x,y+
+ offset[i].y,&pixel,exception);
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
+ qixel.red+=(*k)*alpha*pixel.red;
+ qixel.green+=(*k)*alpha*pixel.green;
+ qixel.blue+=(*k)*alpha*pixel.blue;
+ qixel.opacity+=(*k)*pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*k)*alpha*(*indexes);
+ }
+ gamma+=(*k)*alpha;
+ k++;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*qixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*qixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*qixel.blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(qixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_MotionBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ offset=(OffsetInfo *) RelinquishMagickMemory(offset);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r e v i e w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PreviewImage() tiles 9 thumbnails of the specified image with an image
+% processing operation applied with varying parameters. This may be helpful
+% pin-pointing an appropriate parameter for a particular image processing
+% operation.
+%
+% The format of the PreviewImages method is:
+%
+% Image *PreviewImages(const Image *image,const PreviewType preview,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o preview: the image processing operation.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *PreviewImage(const Image *image,const PreviewType preview,
+ ExceptionInfo *exception)
+{
+#define NumberTiles 9
+#define PreviewImageTag "Preview/Image"
+#define DefaultPreviewGeometry "204x204+10+10"
+
+ char
+ factor[MaxTextExtent],
+ label[MaxTextExtent];
+
+ double
+ degrees,
+ gamma,
+ percentage,
+ radius,
+ sigma,
+ threshold;
+
+ Image
+ *images,
+ *montage_image,
+ *preview_image,
+ *thumbnail;
+
+ ImageInfo
+ *preview_info;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ MontageInfo
+ *montage_info;
+
+ QuantizeInfo
+ quantize_info;
+
+ RectangleInfo
+ geometry;
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ colors;
+
+ /*
+ Open output image file.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ colors=2;
+ degrees=0.0;
+ gamma=(-0.2f);
+ preview_info=AcquireImageInfo();
+ SetGeometry(image,&geometry);
+ (void) ParseMetaGeometry(DefaultPreviewGeometry,&geometry.x,&geometry.y,
+ &geometry.width,&geometry.height);
+ images=NewImageList();
+ percentage=12.5;
+ GetQuantizeInfo(&quantize_info);
+ radius=0.0;
+ sigma=1.0;
+ threshold=0.0;
+ x=0;
+ y=0;
+ for (i=0; i < NumberTiles; i++)
+ {
+ thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
+ if (thumbnail == (Image *) NULL)
+ break;
+ (void) SetImageProgressMonitor(thumbnail,(MagickProgressMonitor) NULL,
+ (void *) NULL);
+ (void) SetImageProperty(thumbnail,"label",DefaultTileLabel);
+ if (i == (NumberTiles/2))
+ {
+ (void) QueryColorDatabase("#dfdfdf",&thumbnail->matte_color,exception);
+ AppendImageToList(&images,thumbnail);
+ continue;
+ }
+ switch (preview)
+ {
+ case RotatePreview:
+ {
+ degrees+=45.0;
+ preview_image=RotateImage(thumbnail,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"rotate %g",degrees);
+ break;
+ }
+ case ShearPreview:
+ {
+ degrees+=5.0;
+ preview_image=ShearImage(thumbnail,degrees,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"shear %gx%g",
+ degrees,2.0*degrees);
+ break;
+ }
+ case RollPreview:
+ {
+ x=(long) ((i+1)*thumbnail->columns)/NumberTiles;
+ y=(long) ((i+1)*thumbnail->rows)/NumberTiles;
+ preview_image=RollImage(thumbnail,x,y,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"roll %ldx%ld",x,y);
+ break;
+ }
+ case HuePreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(factor,MaxTextExtent,"100,100,%g",
+ 2.0*percentage);
+ (void) ModulateImage(preview_image,factor);
+ (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
+ break;
+ }
+ case SaturationPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(factor,MaxTextExtent,"100,%g",2.0*percentage);
+ (void) ModulateImage(preview_image,factor);
+ (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
+ break;
+ }
+ case BrightnessPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(factor,MaxTextExtent,"%g",2.0*percentage);
+ (void) ModulateImage(preview_image,factor);
+ (void) FormatMagickString(label,MaxTextExtent,"modulate %s",factor);
+ break;
+ }
+ case GammaPreview:
+ default:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ gamma+=0.4f;
+ (void) GammaImageChannel(preview_image,DefaultChannels,gamma);
+ (void) FormatMagickString(label,MaxTextExtent,"gamma %g",gamma);
+ break;
+ }
+ case SpiffPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image != (Image *) NULL)
+ for (x=0; x < i; x++)
+ (void) ContrastImage(preview_image,MagickTrue);
+ (void) FormatMagickString(label,MaxTextExtent,"contrast (%ld)",i+1);
+ break;
+ }
+ case DullPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ for (x=0; x < i; x++)
+ (void) ContrastImage(preview_image,MagickFalse);
+ (void) FormatMagickString(label,MaxTextExtent,"+contrast (%ld)",i+1);
+ break;
+ }
+ case GrayscalePreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ colors<<=1;
+ quantize_info.number_colors=colors;
+ quantize_info.colorspace=GRAYColorspace;
+ (void) QuantizeImage(&quantize_info,preview_image);
+ (void) FormatMagickString(label,MaxTextExtent,
+ "-colorspace gray -colors %ld",colors);
+ break;
+ }
+ case QuantizePreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ colors<<=1;
+ quantize_info.number_colors=colors;
+ (void) QuantizeImage(&quantize_info,preview_image);
+ (void) FormatMagickString(label,MaxTextExtent,"colors %ld",colors);
+ break;
+ }
+ case DespecklePreview:
+ {
+ for (x=0; x < (i-1); x++)
+ {
+ preview_image=DespeckleImage(thumbnail,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ thumbnail=DestroyImage(thumbnail);
+ thumbnail=preview_image;
+ }
+ preview_image=DespeckleImage(thumbnail,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) FormatMagickString(label,MaxTextExtent,"despeckle (%ld)",i+1);
+ break;
+ }
+ case ReduceNoisePreview:
+ {
+ preview_image=ReduceNoiseImage(thumbnail,radius,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"noise %g",radius);
+ break;
+ }
+ case AddNoisePreview:
+ {
+ switch ((int) i)
+ {
+ case 0:
+ {
+ (void) CopyMagickString(factor,"uniform",MaxTextExtent);
+ break;
+ }
+ case 1:
+ {
+ (void) CopyMagickString(factor,"gaussian",MaxTextExtent);
+ break;
+ }
+ case 2:
+ {
+ (void) CopyMagickString(factor,"multiplicative",MaxTextExtent);
+ break;
+ }
+ case 3:
+ {
+ (void) CopyMagickString(factor,"impulse",MaxTextExtent);
+ break;
+ }
+ case 4:
+ {
+ (void) CopyMagickString(factor,"laplacian",MaxTextExtent);
+ break;
+ }
+ case 5:
+ {
+ (void) CopyMagickString(factor,"Poisson",MaxTextExtent);
+ break;
+ }
+ default:
+ {
+ (void) CopyMagickString(thumbnail->magick,"NULL",MaxTextExtent);
+ break;
+ }
+ }
+ preview_image=ReduceNoiseImage(thumbnail,(double) i,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"+noise %s",factor);
+ break;
+ }
+ case SharpenPreview:
+ {
+ preview_image=SharpenImage(thumbnail,radius,sigma,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"sharpen %gx%g",radius,
+ sigma);
+ break;
+ }
+ case BlurPreview:
+ {
+ preview_image=BlurImage(thumbnail,radius,sigma,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"blur %gx%g",radius,
+ sigma);
+ break;
+ }
+ case ThresholdPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) BilevelImage(thumbnail,
+ (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
+ (void) FormatMagickString(label,MaxTextExtent,"threshold %g",
+ (double) (percentage*((MagickRealType) QuantumRange+1.0))/100.0);
+ break;
+ }
+ case EdgeDetectPreview:
+ {
+ preview_image=EdgeImage(thumbnail,radius,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"edge %g",radius);
+ break;
+ }
+ case SpreadPreview:
+ {
+ preview_image=SpreadImage(thumbnail,radius,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"spread %g",radius+0.5);
+ break;
+ }
+ case SolarizePreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) SolarizeImage(preview_image,(double) QuantumRange*
+ percentage/100.0);
+ (void) FormatMagickString(label,MaxTextExtent,"solarize %g",
+ (QuantumRange*percentage)/100.0);
+ break;
+ }
+ case ShadePreview:
+ {
+ degrees+=10.0;
+ preview_image=ShadeImage(thumbnail,MagickTrue,degrees,degrees,
+ exception);
+ (void) FormatMagickString(label,MaxTextExtent,"shade %gx%g",degrees,
+ degrees);
+ break;
+ }
+ case RaisePreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ geometry.width=(unsigned long) (2*i+2);
+ geometry.height=(unsigned long) (2*i+2);
+ geometry.x=i/2;
+ geometry.y=i/2;
+ (void) RaiseImage(preview_image,&geometry,MagickTrue);
+ (void) FormatMagickString(label,MaxTextExtent,"raise %lux%lu%+ld%+ld",
+ geometry.width,geometry.height,geometry.x,geometry.y);
+ break;
+ }
+ case SegmentPreview:
+ {
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ threshold+=0.4f;
+ (void) SegmentImage(preview_image,RGBColorspace,MagickFalse,threshold,
+ threshold);
+ (void) FormatMagickString(label,MaxTextExtent,"segment %gx%g",
+ threshold,threshold);
+ break;
+ }
+ case SwirlPreview:
+ {
+ preview_image=SwirlImage(thumbnail,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"swirl %g",degrees);
+ degrees+=45.0;
+ break;
+ }
+ case ImplodePreview:
+ {
+ degrees+=0.1f;
+ preview_image=ImplodeImage(thumbnail,degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"implode %g",degrees);
+ break;
+ }
+ case WavePreview:
+ {
+ degrees+=5.0f;
+ preview_image=WaveImage(thumbnail,0.5*degrees,2.0*degrees,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"wave %gx%g",0.5*degrees,
+ 2.0*degrees);
+ break;
+ }
+ case OilPaintPreview:
+ {
+ preview_image=OilPaintImage(thumbnail,(double) radius,exception);
+ (void) FormatMagickString(label,MaxTextExtent,"paint %g",radius);
+ break;
+ }
+ case CharcoalDrawingPreview:
+ {
+ preview_image=CharcoalImage(thumbnail,(double) radius,(double) sigma,
+ exception);
+ (void) FormatMagickString(label,MaxTextExtent,"charcoal %gx%g",radius,
+ sigma);
+ break;
+ }
+ case JPEGPreview:
+ {
+ char
+ filename[MaxTextExtent];
+
+ int
+ file;
+
+ MagickBooleanType
+ status;
+
+ preview_image=CloneImage(thumbnail,0,0,MagickTrue,exception);
+ if (preview_image == (Image *) NULL)
+ break;
+ preview_info->quality=(unsigned long) percentage;
+ (void) FormatMagickString(factor,MaxTextExtent,"%lu",
+ preview_info->quality);
+ file=AcquireUniqueFileResource(filename);
+ if (file != -1)
+ file=close(file)-1;
+ (void) FormatMagickString(preview_image->filename,MaxTextExtent,
+ "jpeg:%s",filename);
+ status=WriteImage(preview_info,preview_image);
+ if (status != MagickFalse)
+ {
+ Image
+ *quality_image;
+
+ (void) CopyMagickString(preview_info->filename,
+ preview_image->filename,MaxTextExtent);
+ quality_image=ReadImage(preview_info,exception);
+ if (quality_image != (Image *) NULL)
+ {
+ preview_image=DestroyImage(preview_image);
+ preview_image=quality_image;
+ }
+ }
+ (void) RelinquishUniqueFileResource(preview_image->filename);
+ if ((GetBlobSize(preview_image)/1024) >= 1024)
+ (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gmb ",
+ factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
+ 1024.0/1024.0);
+ else
+ if (GetBlobSize(preview_image) >= 1024)
+ (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%gkb ",
+ factor,(double) ((MagickOffsetType) GetBlobSize(preview_image))/
+ 1024.0);
+ else
+ (void) FormatMagickString(label,MaxTextExtent,"quality %s\n%lub ",
+ factor,(unsigned long) GetBlobSize(thumbnail));
+ break;
+ }
+ }
+ thumbnail=DestroyImage(thumbnail);
+ percentage+=12.5;
+ radius+=0.5;
+ sigma+=0.25;
+ if (preview_image == (Image *) NULL)
+ break;
+ (void) DeleteImageProperty(preview_image,"label");
+ (void) SetImageProperty(preview_image,"label",label);
+ AppendImageToList(&images,preview_image);
+ proceed=SetImageProgress(image,PreviewImageTag,i,NumberTiles);
+ if (proceed == MagickFalse)
+ break;
+ }
+ if (images == (Image *) NULL)
+ {
+ preview_info=DestroyImageInfo(preview_info);
+ return((Image *) NULL);
+ }
+ /*
+ Create the montage.
+ */
+ montage_info=CloneMontageInfo(preview_info,(MontageInfo *) NULL);
+ (void) CopyMagickString(montage_info->filename,image->filename,MaxTextExtent);
+ montage_info->shadow=MagickTrue;
+ (void) CloneString(&montage_info->tile,"3x3");
+ (void) CloneString(&montage_info->geometry,DefaultPreviewGeometry);
+ (void) CloneString(&montage_info->frame,DefaultTileFrame);
+ montage_image=MontageImages(images,montage_info,exception);
+ montage_info=DestroyMontageInfo(montage_info);
+ images=DestroyImageList(images);
+ if (montage_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ if (montage_image->montage != (char *) NULL)
+ {
+ /*
+ Free image directory.
+ */
+ montage_image->montage=(char *) RelinquishMagickMemory(
+ montage_image->montage);
+ if (image->directory != (char *) NULL)
+ montage_image->directory=(char *) RelinquishMagickMemory(
+ montage_image->directory);
+ }
+ preview_info=DestroyImageInfo(preview_info);
+ return(montage_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R a d i a l B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RadialBlurImage() applies a radial blur to the image.
+%
+% Andrew Protano contributed this effect.
+%
+% The format of the RadialBlurImage method is:
+%
+% Image *RadialBlurImage(const Image *image,const double angle,
+% ExceptionInfo *exception)
+% Image *RadialBlurImageChannel(const Image *image,const ChannelType channel,
+% const double angle,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o angle: the angle of the radial blur.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *RadialBlurImage(const Image *image,const double angle,
+ ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=RadialBlurImageChannel(image,DefaultChannels,angle,exception);
+ return(blur_image);
+}
+
+MagickExport Image *RadialBlurImageChannel(const Image *image,
+ const ChannelType channel,const double angle,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ blur_radius,
+ *cos_theta,
+ offset,
+ *sin_theta,
+ theta;
+
+ PointInfo
+ blur_center;
+
+ register long
+ i;
+
+ unsigned long
+ n;
+
+ CacheView
+ *blur_view,
+ *image_view;
+
+ /*
+ Allocate blur image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ blur_center.x=(double) image->columns/2.0;
+ blur_center.y=(double) image->rows/2.0;
+ blur_radius=hypot(blur_center.x,blur_center.y);
+ n=(unsigned long) fabs(4.0*DegreesToRadians(angle)*sqrt((double) blur_radius)+
+ 2UL);
+ theta=DegreesToRadians(angle)/(MagickRealType) (n-1);
+ cos_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
+ sizeof(*cos_theta));
+ sin_theta=(MagickRealType *) AcquireQuantumMemory((size_t) n,
+ sizeof(*sin_theta));
+ if ((cos_theta == (MagickRealType *) NULL) ||
+ (sin_theta == (MagickRealType *) NULL))
+ {
+ blur_image=DestroyImage(blur_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ offset=theta*(MagickRealType) (n-1)/2.0;
+ for (i=0; i < (long) n; i++)
+ {
+ cos_theta[i]=cos((double) (theta*i-offset));
+ sin_theta[i]=sin((double) (theta*i-offset));
+ }
+ /*
+ Radial blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) blur_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (long) blur_image->columns; x++)
+ {
+ MagickPixelPacket
+ qixel;
+
+ MagickRealType
+ normalize,
+ radius;
+
+ PixelPacket
+ pixel;
+
+ PointInfo
+ center;
+
+ register long
+ i;
+
+ unsigned long
+ step;
+
+ center.x=(double) x-blur_center.x;
+ center.y=(double) y-blur_center.y;
+ radius=hypot((double) center.x,center.y);
+ if (radius == 0)
+ step=1;
+ else
+ {
+ step=(unsigned long) (blur_radius/radius);
+ if (step == 0)
+ step=1;
+ else
+ if (step >= n)
+ step=n-1;
+ }
+ normalize=0.0;
+ qixel=zero;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (i=0; i < (long) n; i+=step)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
+ center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
+ blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
+ &pixel,exception);
+ qixel.red+=pixel.red;
+ qixel.green+=pixel.green;
+ qixel.blue+=pixel.blue;
+ qixel.opacity+=pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=(*indexes);
+ }
+ normalize+=1.0;
+ }
+ normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
+ normalize);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(normalize*qixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(normalize*qixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(normalize*qixel.blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(normalize*qixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ blur_indexes[x]=(IndexPacket) RoundToQuantum(normalize*qixel.index);
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ alpha=1.0;
+ gamma=0.0;
+ for (i=0; i < (long) n; i+=step)
+ {
+ (void) GetOneCacheViewVirtualPixel(image_view,(long) (blur_center.x+
+ center.x*cos_theta[i]-center.y*sin_theta[i]+0.5),(long) (
+ blur_center.y+center.x*sin_theta[i]+center.y*cos_theta[i]+0.5),
+ &pixel,exception);
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity));
+ qixel.red+=alpha*pixel.red;
+ qixel.green+=alpha*pixel.green;
+ qixel.blue+=alpha*pixel.blue;
+ qixel.opacity+=pixel.opacity;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ qixel.index+=alpha*(*indexes);
+ }
+ gamma+=alpha;
+ normalize+=1.0;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ normalize=1.0/(fabs((double) normalize) <= MagickEpsilon ? 1.0 :
+ normalize);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*qixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*qixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*qixel.blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(normalize*qixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ blur_indexes[x]=(IndexPacket) RoundToQuantum(gamma*qixel.index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(blur_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RadialBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,BlurImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ cos_theta=(MagickRealType *) RelinquishMagickMemory(cos_theta);
+ sin_theta=(MagickRealType *) RelinquishMagickMemory(sin_theta);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e d u c e N o i s e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReduceNoiseImage() smooths the contours of an image while still preserving
+% edge information. The algorithm works by replacing each pixel with its
+% neighbor closest in value. A neighbor is defined by radius. Use a radius
+% of 0 and ReduceNoise() selects a suitable radius for you.
+%
+% The format of the ReduceNoiseImage method is:
+%
+% Image *ReduceNoiseImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static MagickPixelPacket GetNonpeakMedianPixelList(MedianPixelList *pixel_list)
+{
+ MagickPixelPacket
+ pixel;
+
+ register long
+ channel;
+
+ register MedianSkipList
+ *list;
+
+ unsigned long
+ center,
+ color,
+ count,
+ previous,
+ next;
+
+ unsigned short
+ channels[5];
+
+ /*
+ Finds the median value for each of the color.
+ */
+ center=pixel_list->center;
+ for (channel=0; channel < 5; channel++)
+ {
+ list=pixel_list->lists+channel;
+ color=65536UL;
+ next=list->nodes[color].next[0];
+ count=0;
+ do
+ {
+ previous=color;
+ color=next;
+ next=list->nodes[color].next[0];
+ count+=list->nodes[color].count;
+ }
+ while (count <= center);
+ if ((previous == 65536UL) && (next != 65536UL))
+ color=next;
+ else
+ if ((previous != 65536UL) && (next == 65536UL))
+ color=previous;
+ channels[channel]=(unsigned short) color;
+ }
+ GetMagickPixelPacket((const Image *) NULL,&pixel);
+ pixel.red=(MagickRealType) ScaleShortToQuantum(channels[0]);
+ pixel.green=(MagickRealType) ScaleShortToQuantum(channels[1]);
+ pixel.blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
+ pixel.opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
+ pixel.index=(MagickRealType) ScaleShortToQuantum(channels[4]);
+ return(pixel);
+}
+
+MagickExport Image *ReduceNoiseImage(const Image *image,const double radius,
+ ExceptionInfo *exception)
+{
+#define ReduceNoiseImageTag "ReduceNoise/Image"
+
+ Image
+ *noise_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MedianPixelList
+ **pixel_list;
+
+ unsigned long
+ width;
+
+ CacheView
+ *image_view,
+ *noise_view;
+
+ /*
+ Initialize noise image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,0.5);
+ if ((image->columns < width) || (image->rows < width))
+ ThrowImageException(OptionError,"ImageSmallerThanKernelRadius");
+ noise_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (noise_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&noise_image->exception);
+ noise_image=DestroyImage(noise_image);
+ return((Image *) NULL);
+ }
+ pixel_list=AcquireMedianPixelListThreadSet(width);
+ if (pixel_list == (MedianPixelList **) NULL)
+ {
+ noise_image=DestroyImage(noise_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Reduce noise image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ noise_view=AcquireCacheView(noise_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) noise_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict noise_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
+ 2L),image->columns+width,width,exception);
+ q=QueueCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) noise_image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const PixelPacket
+ *__restrict r;
+
+ register const IndexPacket
+ *__restrict s;
+
+ register long
+ u,
+ v;
+
+ r=p;
+ s=indexes+x;
+ ResetMedianPixelList(pixel_list[id]);
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ InsertMedianPixelList(image,r+u,s+u,pixel_list[id]);
+ r+=image->columns+width;
+ s+=image->columns+width;
+ }
+ pixel=GetNonpeakMedianPixelList(pixel_list[id]);
+ SetPixelPacket(noise_image,&pixel,q,noise_indexes+x);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(noise_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ReduceNoiseImage)
+#endif
+ proceed=SetImageProgress(image,ReduceNoiseImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ noise_view=DestroyCacheView(noise_view);
+ image_view=DestroyCacheView(image_view);
+ pixel_list=DestroyMedianPixelListThreadSet(pixel_list);
+ return(noise_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e l e c t i v e B l u r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SelectiveBlurImage() selectively blur pixels within a contrast threshold.
+% It is similar to the unsharpen mask that sharpens everything with contrast
+% above a certain threshold.
+%
+% The format of the SelectiveBlurImage method is:
+%
+% Image *SelectiveBlurImage(const Image *image,const double radius,
+% const double sigma,const double threshold,ExceptionInfo *exception)
+% Image *SelectiveBlurImageChannel(const Image *image,
+% const ChannelType channel,const double radius,const double sigma,
+% const double threshold,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o threshold: only pixels within this contrast threshold are included
+% in the blur operation.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType SelectiveContrast(const PixelPacket *p,
+ const PixelPacket *q,const double threshold)
+{
+ if (fabs(PixelIntensity(p)-PixelIntensity(q)) < threshold)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+MagickExport Image *SelectiveBlurImage(const Image *image,const double radius,
+ const double sigma,const double threshold,ExceptionInfo *exception)
+{
+ Image
+ *blur_image;
+
+ blur_image=SelectiveBlurImageChannel(image,DefaultChannels,radius,sigma,
+ threshold,exception);
+ return(blur_image);
+}
+
+MagickExport Image *SelectiveBlurImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ const double threshold,ExceptionInfo *exception)
+{
+#define SelectiveBlurImageTag "SelectiveBlur/Image"
+
+ double
+ alpha,
+ *kernel;
+
+ Image
+ *blur_image;
+
+ long
+ progress,
+ v,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ bias;
+
+ register long
+ i,
+ u;
+
+ unsigned long
+ width;
+
+ CacheView
+ *blur_view,
+ *image_view;
+
+ /*
+ Initialize blur image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth1D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width,width*sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ i=0;
+ for (v=(-((long) width/2)); v <= (long) (width/2); v++)
+ {
+ for (u=(-((long) width/2)); u <= (long) (width/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i]=(double) (alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
+ i++;
+ }
+ }
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ *message;
+
+ long
+ u,
+ v;
+
+ register const double
+ *k;
+
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " SelectiveBlurImage with %ldx%ld kernel:",width,width);
+ message=AcquireString("");
+ k=kernel;
+ for (v=0; v < (long) width; v++)
+ {
+ *message='\0';
+ (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
+ (void) ConcatenateString(&message,format);
+ for (u=0; u < (long) width; u++)
+ {
+ (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
+ (void) ConcatenateString(&message,format);
+ }
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
+ }
+ message=DestroyString(message);
+ }
+ blur_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(blur_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&blur_image->exception);
+ blur_image=DestroyImage(blur_image);
+ return((Image *) NULL);
+ }
+ /*
+ Threshold blur image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ bias=image->bias;
+ image_view=AcquireCacheView(image);
+ blur_view=AcquireCacheView(blur_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickRealType
+ gamma;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict blur_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
+ 2L),image->columns+width,width,exception);
+ q=GetCacheViewAuthenticPixels(blur_view,0,y,blur_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ blur_indexes=GetCacheViewAuthenticIndexQueue(blur_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ long
+ j,
+ v;
+
+ MagickPixelPacket
+ pixel;
+
+ register const double
+ *__restrict k;
+
+ register long
+ u;
+
+ pixel=zero;
+ k=kernel;
+ gamma=0.0;
+ j=0;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.red+=(*k)*(p+u+j)->red;
+ pixel.green+=(*k)*(p+u+j)->green;
+ pixel.blue+=(*k)*(p+u+j)->blue;
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.opacity+=(*k)*(p+u+j)->opacity;
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ q->opacity=RoundToQuantum(gamma*pixel.opacity+bias);
+ }
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.index+=(*k)*indexes[x+u+j];
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ }
+ }
+ }
+ else
+ {
+ MagickRealType
+ alpha;
+
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ (p+u+j)->opacity));
+ pixel.red+=(*k)*alpha*(p+u+j)->red;
+ pixel.green+=(*k)*alpha*(p+u+j)->green;
+ pixel.blue+=(*k)*alpha*(p+u+j)->blue;
+ pixel.opacity+=(*k)*(p+u+j)->opacity;
+ gamma+=(*k)*alpha;
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ pixel.opacity+=(*k)*(p+u+j)->opacity;
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ gamma=0.0;
+ j=0;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ if (SelectiveContrast(p+u+j,q,threshold) != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ (p+u+j)->opacity));
+ pixel.index+=(*k)*alpha*indexes[x+u+j];
+ gamma+=(*k);
+ k++;
+ }
+ }
+ j+=image->columns+width;
+ }
+ if (gamma != 0.0)
+ {
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 :
+ gamma);
+ blur_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ }
+ }
+ }
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(blur_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SelectiveBlurImageChannel)
+#endif
+ proceed=SetImageProgress(image,SelectiveBlurImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ blur_image->type=image->type;
+ blur_view=DestroyCacheView(blur_view);
+ image_view=DestroyCacheView(image_view);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ if (status == MagickFalse)
+ blur_image=DestroyImage(blur_image);
+ return(blur_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h a d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ShadeImage() shines a distant light on an image to create a
+% three-dimensional effect. You control the positioning of the light with
+% azimuth and elevation; azimuth is measured in degrees off the x axis
+% and elevation is measured in pixels above the Z axis.
+%
+% The format of the ShadeImage method is:
+%
+% Image *ShadeImage(const Image *image,const MagickBooleanType gray,
+% const double azimuth,const double elevation,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o gray: A value other than zero shades the intensity of each pixel.
+%
+% o azimuth, elevation: Define the light source direction.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ShadeImage(const Image *image,const MagickBooleanType gray,
+ const double azimuth,const double elevation,ExceptionInfo *exception)
+{
+#define ShadeImageTag "Shade/Image"
+
+ Image
+ *shade_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ PrimaryInfo
+ light;
+
+ CacheView
+ *image_view,
+ *shade_view;
+
+ /*
+ Initialize shaded image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ shade_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (shade_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(shade_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&shade_image->exception);
+ shade_image=DestroyImage(shade_image);
+ return((Image *) NULL);
+ }
+ /*
+ Compute the light vector.
+ */
+ light.x=(double) QuantumRange*cos(DegreesToRadians(azimuth))*
+ cos(DegreesToRadians(elevation));
+ light.y=(double) QuantumRange*sin(DegreesToRadians(azimuth))*
+ cos(DegreesToRadians(elevation));
+ light.z=(double) QuantumRange*sin(DegreesToRadians(elevation));
+ /*
+ Shade image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ shade_view=AcquireCacheView(shade_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickRealType
+ distance,
+ normal_distance,
+ shade;
+
+ PrimaryInfo
+ normal;
+
+ register const PixelPacket
+ *__restrict p,
+ *__restrict s0,
+ *__restrict s1,
+ *__restrict s2;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-1,y-1,image->columns+2,3,exception);
+ q=QueueCacheViewAuthenticPixels(shade_view,0,y,shade_image->columns,1,
+ exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ /*
+ Shade this row of pixels.
+ */
+ normal.z=2.0*(double) QuantumRange; /* constant Z of surface normal */
+ s0=p+1;
+ s1=s0+image->columns+2;
+ s2=s1+image->columns+2;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Determine the surface normal and compute shading.
+ */
+ normal.x=(double) (PixelIntensity(s0-1)+PixelIntensity(s1-1)+
+ PixelIntensity(s2-1)-PixelIntensity(s0+1)-PixelIntensity(s1+1)-
+ PixelIntensity(s2+1));
+ normal.y=(double) (PixelIntensity(s2-1)+PixelIntensity(s2)+
+ PixelIntensity(s2+1)-PixelIntensity(s0-1)-PixelIntensity(s0)-
+ PixelIntensity(s0+1));
+ if ((normal.x == 0.0) && (normal.y == 0.0))
+ shade=light.z;
+ else
+ {
+ shade=0.0;
+ distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
+ if (distance > MagickEpsilon)
+ {
+ normal_distance=
+ normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
+ if (normal_distance > (MagickEpsilon*MagickEpsilon))
+ shade=distance/sqrt((double) normal_distance);
+ }
+ }
+ if (gray != MagickFalse)
+ {
+ q->red=(Quantum) shade;
+ q->green=(Quantum) shade;
+ q->blue=(Quantum) shade;
+ }
+ else
+ {
+ q->red=RoundToQuantum(QuantumScale*shade*s1->red);
+ q->green=RoundToQuantum(QuantumScale*shade*s1->green);
+ q->blue=RoundToQuantum(QuantumScale*shade*s1->blue);
+ }
+ q->opacity=s1->opacity;
+ s0++;
+ s1++;
+ s2++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(shade_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ShadeImage)
+#endif
+ proceed=SetImageProgress(image,ShadeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ shade_view=DestroyCacheView(shade_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ shade_image=DestroyImage(shade_image);
+ return(shade_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h a r p e n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SharpenImage() sharpens the image. We convolve the image with a Gaussian
+% operator of the given radius and standard deviation (sigma). For
+% reasonable results, radius should be larger than sigma. Use a radius of 0
+% and SharpenImage() selects a suitable radius for you.
+%
+% Using a separable kernel would be faster, but the negative weights cancel
+% out on the corners of the kernel producing often undesirable ringing in the
+% filtered result; this can be avoided by using a 2D gaussian shaped image
+% sharpening kernel instead.
+%
+% The format of the SharpenImage method is:
+%
+% Image *SharpenImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+% Image *SharpenImageChannel(const Image *image,const ChannelType channel,
+% const double radius,const double sigma,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Laplacian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *SharpenImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *sharp_image;
+
+ sharp_image=SharpenImageChannel(image,DefaultChannels,radius,sigma,exception);
+ return(sharp_image);
+}
+
+MagickExport Image *SharpenImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ ExceptionInfo *exception)
+{
+ double
+ *kernel;
+
+ Image
+ *sharp_image;
+
+ MagickRealType
+ alpha,
+ normalize;
+
+ register long
+ i,
+ u,
+ v;
+
+ unsigned long
+ width;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,sigma);
+ kernel=(double *) AcquireQuantumMemory((size_t) width*width,sizeof(*kernel));
+ if (kernel == (double *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ i=0;
+ normalize=0.0;
+ for (v=(-((long) width/2)); v <= (long) (width/2); v++)
+ {
+ for (u=(-((long) width/2)); u <= (long) (width/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*MagickSigma*MagickSigma));
+ kernel[i]=(double) (-alpha/(2.0*MagickPI*MagickSigma*MagickSigma));
+ normalize+=kernel[i];
+ i++;
+ }
+ }
+ kernel[i/2]=(double) ((-2.0)*normalize);
+ sharp_image=ConvolveImageChannel(image,channel,width,kernel,exception);
+ kernel=(double *) RelinquishMagickMemory(kernel);
+ return(sharp_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p r e a d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SpreadImage() is a special effects method that randomly displaces each
+% pixel in a block defined by the radius parameter.
+%
+% The format of the SpreadImage method is:
+%
+% Image *SpreadImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: Choose a random pixel in a neighborhood of this extent.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SpreadImage(const Image *image,const double radius,
+ ExceptionInfo *exception)
+{
+#define SpreadImageTag "Spread/Image"
+
+ Image
+ *spread_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ RandomInfo
+ **random_info;
+
+ ResampleFilter
+ **resample_filter;
+
+ unsigned long
+ width;
+
+ CacheView
+ *image_view;
+
+ /*
+ Initialize spread image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ spread_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (spread_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(spread_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&spread_image->exception);
+ spread_image=DestroyImage(spread_image);
+ return((Image *) NULL);
+ }
+ /*
+ Spread image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(spread_image,&zero);
+ width=GetOptimalKernelWidth1D(radius,0.5);
+ resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
+ random_info=AcquireRandomInfoThreadSet();
+ image_view=AcquireCacheView(spread_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) spread_image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(image_view,0,y,spread_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ pixel=zero;
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) spread_image->columns; x++)
+ {
+ (void) ResamplePixelColor(resample_filter[id],(double) x+width*
+ (GetPseudoRandomValue(random_info[id])-0.5),(double) y+width*
+ (GetPseudoRandomValue(random_info[id])-0.5),&pixel);
+ SetPixelPacket(spread_image,&pixel,q,indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SpreadImage)
+#endif
+ proceed=SetImageProgress(image,SpreadImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ return(spread_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n s h a r p M a s k I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnsharpMaskImage() sharpens one or more image channels. We convolve the
+% image with a Gaussian operator of the given radius and standard deviation
+% (sigma). For reasonable results, radius should be larger than sigma. Use a
+% radius of 0 and UnsharpMaskImage() selects a suitable radius for you.
+%
+% The format of the UnsharpMaskImage method is:
+%
+% Image *UnsharpMaskImage(const Image *image,const double radius,
+% const double sigma,const double amount,const double threshold,
+% ExceptionInfo *exception)
+% Image *UnsharpMaskImageChannel(const Image *image,
+% const ChannelType channel,const double radius,const double sigma,
+% const double amount,const double threshold,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o amount: the percentage of the difference between the original and the
+% blur image that is added back into the original.
+%
+% o threshold: the threshold in pixels needed to apply the diffence amount.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *UnsharpMaskImage(const Image *image,const double radius,
+ const double sigma,const double amount,const double threshold,
+ ExceptionInfo *exception)
+{
+ Image
+ *sharp_image;
+
+ sharp_image=UnsharpMaskImageChannel(image,DefaultChannels,radius,sigma,amount,
+ threshold,exception);
+ return(sharp_image);
+}
+
+MagickExport Image *UnsharpMaskImageChannel(const Image *image,
+ const ChannelType channel,const double radius,const double sigma,
+ const double amount,const double threshold,ExceptionInfo *exception)
+{
+#define SharpenImageTag "Sharpen/Image"
+
+ Image
+ *unsharp_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ quantum_threshold;
+
+ CacheView
+ *image_view,
+ *unsharp_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ unsharp_image=BlurImageChannel(image,channel,radius,sigma,exception);
+ if (unsharp_image == (Image *) NULL)
+ return((Image *) NULL);
+ quantum_threshold=(MagickRealType) QuantumRange*threshold;
+ /*
+ Unsharp-mask image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+ unsharp_view=AcquireCacheView(unsharp_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict unsharp_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewAuthenticPixels(unsharp_view,0,y,unsharp_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ unsharp_indexes=GetCacheViewAuthenticIndexQueue(unsharp_view);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ pixel.red=p->red-(MagickRealType) q->red;
+ if (fabs(2.0*pixel.red) < quantum_threshold)
+ pixel.red=(MagickRealType) p->red;
+ else
+ pixel.red=(MagickRealType) p->red+(pixel.red*amount);
+ q->red=RoundToQuantum(pixel.red);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ pixel.green=p->green-(MagickRealType) q->green;
+ if (fabs(2.0*pixel.green) < quantum_threshold)
+ pixel.green=(MagickRealType) p->green;
+ else
+ pixel.green=(MagickRealType) p->green+(pixel.green*amount);
+ q->green=RoundToQuantum(pixel.green);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ pixel.blue=p->blue-(MagickRealType) q->blue;
+ if (fabs(2.0*pixel.blue) < quantum_threshold)
+ pixel.blue=(MagickRealType) p->blue;
+ else
+ pixel.blue=(MagickRealType) p->blue+(pixel.blue*amount);
+ q->blue=RoundToQuantum(pixel.blue);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ pixel.opacity=p->opacity-(MagickRealType) q->opacity;
+ if (fabs(2.0*pixel.opacity) < quantum_threshold)
+ pixel.opacity=(MagickRealType) p->opacity;
+ else
+ pixel.opacity=p->opacity+(pixel.opacity*amount);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ pixel.index=unsharp_indexes[x]-(MagickRealType) indexes[x];
+ if (fabs(2.0*pixel.index) < quantum_threshold)
+ pixel.index=(MagickRealType) unsharp_indexes[x];
+ else
+ pixel.index=(MagickRealType) unsharp_indexes[x]+(pixel.index*
+ amount);
+ unsharp_indexes[x]=RoundToQuantum(pixel.index);
+ }
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(unsharp_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_UnsharpMaskImageChannel)
+#endif
+ proceed=SetImageProgress(image,SharpenImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ unsharp_image->type=image->type;
+ unsharp_view=DestroyCacheView(unsharp_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ unsharp_image=DestroyImage(unsharp_image);
+ return(unsharp_image);
+}
diff --git a/magick/effect.h b/magick/effect.h
new file mode 100644
index 0000000..a775231
--- /dev/null
+++ b/magick/effect.h
@@ -0,0 +1,105 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image effects methods.
+*/
+#ifndef _MAGICKCORE_EFFECT_H
+#define _MAGICKCORE_EFFECT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedPreview,
+ RotatePreview,
+ ShearPreview,
+ RollPreview,
+ HuePreview,
+ SaturationPreview,
+ BrightnessPreview,
+ GammaPreview,
+ SpiffPreview,
+ DullPreview,
+ GrayscalePreview,
+ QuantizePreview,
+ DespecklePreview,
+ ReduceNoisePreview,
+ AddNoisePreview,
+ SharpenPreview,
+ BlurPreview,
+ ThresholdPreview,
+ EdgeDetectPreview,
+ SpreadPreview,
+ SolarizePreview,
+ ShadePreview,
+ RaisePreview,
+ SegmentPreview,
+ SwirlPreview,
+ ImplodePreview,
+ WavePreview,
+ OilPaintPreview,
+ CharcoalDrawingPreview,
+ JPEGPreview
+} PreviewType;
+
+extern MagickExport Image
+ *AdaptiveBlurImage(const Image *,const double,const double,ExceptionInfo *),
+ *AdaptiveBlurImageChannel(const Image *,const ChannelType,const double,
+ const double,ExceptionInfo *),
+ *AdaptiveSharpenImage(const Image *,const double,const double,
+ ExceptionInfo *),
+ *AdaptiveSharpenImageChannel(const Image *,const ChannelType,const double,
+ const double,ExceptionInfo *),
+ *BlurImage(const Image *,const double,const double,ExceptionInfo *),
+ *BlurImageChannel(const Image *,const ChannelType,const double,const double,
+ ExceptionInfo *),
+ *DespeckleImage(const Image *,ExceptionInfo *),
+ *EdgeImage(const Image *,const double,ExceptionInfo *),
+ *EmbossImage(const Image *,const double,const double,ExceptionInfo *),
+ *GaussianBlurImage(const Image *,const double,const double,ExceptionInfo *),
+ *GaussianBlurImageChannel(const Image *,const ChannelType,const double,
+ const double,ExceptionInfo *),
+ *MedianFilterImage(const Image *,const double,ExceptionInfo *),
+ *MotionBlurImage(const Image *,const double,const double,const double,
+ ExceptionInfo *),
+ *MotionBlurImageChannel(const Image *,const ChannelType,const double,
+ const double,const double,ExceptionInfo *),
+ *PreviewImage(const Image *,const PreviewType,ExceptionInfo *),
+ *RadialBlurImage(const Image *,const double,ExceptionInfo *),
+ *RadialBlurImageChannel(const Image *,const ChannelType,const double,
+ ExceptionInfo *),
+ *ReduceNoiseImage(const Image *,const double,ExceptionInfo *),
+ *SelectiveBlurImage(const Image *,const double,const double,const double,
+ ExceptionInfo *),
+ *SelectiveBlurImageChannel(const Image *,const ChannelType,const double,
+ const double,const double,ExceptionInfo *),
+ *ShadeImage(const Image *,const MagickBooleanType,const double,const double,
+ ExceptionInfo *),
+ *SharpenImage(const Image *,const double,const double,ExceptionInfo *),
+ *SharpenImageChannel(const Image *,const ChannelType,const double,
+ const double,ExceptionInfo *),
+ *SpreadImage(const Image *,const double,ExceptionInfo *),
+ *UnsharpMaskImage(const Image *,const double,const double,const double,
+ const double,ExceptionInfo *),
+ *UnsharpMaskImageChannel(const Image *,const ChannelType,const double,
+ const double,const double,const double,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/enhance.c b/magick/enhance.c
new file mode 100644
index 0000000..f1a9612
--- /dev/null
+++ b/magick/enhance.c
@@ -0,0 +1,3680 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% EEEEE N N H H AAA N N CCCC EEEEE %
+% E NN N H H A A NN N C E %
+% EEE N N N HHHHH AAAAA N N N C EEE %
+% E N NN H H A A N NN C E %
+% EEEEE N N H H A A N N CCCC EEEEE %
+% %
+% %
+% MagickCore Image Enhancement Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/composite-private.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/histogram.h"
+#include "magick/image.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/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/resample.h"
+#include "magick/resample-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+#include "magick/xml-tree.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A u t o G a m m a I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AutoGammaImage() extract the 'mean' from the image and adjust the image
+% to try make set its gamma appropriatally.
+%
+% The format of the LevelImage method is:
+%
+% MagickBooleanType AutoGammaImage(Image *image)
+% MagickBooleanType AutoGammaImageChannel(Image *image,
+% const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: The image to auto-level
+%
+% o channel: The channels to auto-level. If the special 'SyncChannels'
+% flag is set all given channels is adjusted in the same way using the
+% mean average of those channels.
+%
+*/
+
+MagickExport MagickBooleanType AutoGammaImage(Image *image)
+{
+ return(AutoGammaImageChannel(image,DefaultChannels));
+}
+
+MagickExport MagickBooleanType AutoGammaImageChannel(Image *image,
+ const ChannelType channel)
+{
+ MagickStatusType
+ status;
+
+ double
+ mean,junk,gamma;
+
+ if ((channel & SyncChannels) != 0 )
+ {
+ /*
+ Apply gamma correction equally accross all given channels
+ */
+ GetImageChannelMean(image, channel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //return GammaImageChannel(image, channel, gamma);
+ return LevelImageChannel(image, channel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+
+ /*
+ auto-gamma each channel separateally
+ */
+ status = MagickTrue;
+ if ((channel & RedChannel) != 0)
+ {
+ GetImageChannelMean(image, RedChannel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //status = status && GammaImageChannel(image, RedChannel, gamma);
+ status = status && LevelImageChannel(image, RedChannel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ GetImageChannelMean(image, GreenChannel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //status = status && GammaImageChannel(image, GreenChannel, gamma);
+ status = status && LevelImageChannel(image, GreenChannel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ GetImageChannelMean(image, BlueChannel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //status = status && GammaImageChannel(image, BlueChannel, gamma);
+ status = status && LevelImageChannel(image, BlueChannel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ {
+ GetImageChannelMean(image, OpacityChannel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //status = status && GammaImageChannel(image, OpacityChannel, gamma);
+ status = status && LevelImageChannel(image, OpacityChannel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ GetImageChannelMean(image, IndexChannel, &mean, &junk, &image->exception);
+ gamma = log(mean*QuantumScale)/log(0.5);
+ //status = status && GammaImageChannel(image, IndexChannel, gamma);
+ status = status && LevelImageChannel(image, IndexChannel,
+ 0.0, (double)QuantumRange, gamma);
+ }
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A u t o L e v e l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AutoLevelImage() adjusts the levels of a particular image channel by
+% scaling the minimum and maximum values to the full quantum range.
+%
+% The format of the LevelImage method is:
+%
+% MagickBooleanType AutoLevelImage(Image *image)
+% MagickBooleanType AutoLevelImageChannel(Image *image,
+% const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: The image to auto-level
+%
+% o channel: The channels to auto-level. If the special 'SyncChannels'
+% flag is set the min/max/mean value of all given channels is used for
+% all given channels, to all channels in the same way.
+%
+*/
+
+MagickExport MagickBooleanType AutoLevelImage(Image *image)
+{
+ return(AutoLevelImageChannel(image,DefaultChannels));
+}
+
+MagickExport MagickBooleanType AutoLevelImageChannel(Image *image,
+ const ChannelType channel)
+{
+ /*
+ This is simply a convenience function around a Min/Max Histogram Stretch
+ */
+ return MinMaxStretchImage(image, channel, 0.0, 0.0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o l o r D e c i s i o n L i s t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ColorDecisionListImage() accepts a lightweight Color Correction Collection
+% (CCC) file which solely contains one or more color corrections and applies
+% the correction to the image. Here is a sample CCC file:
+%
+% <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
+% <ColorCorrection id="cc03345">
+% <SOPNode>
+% <Slope> 0.9 1.2 0.5 </Slope>
+% <Offset> 0.4 -0.5 0.6 </Offset>
+% <Power> 1.0 0.8 1.5 </Power>
+% </SOPNode>
+% <SATNode>
+% <Saturation> 0.85 </Saturation>
+% </SATNode>
+% </ColorCorrection>
+% </ColorCorrectionCollection>
+%
+% which includes the slop, offset, and power for each of the RGB channels
+% as well as the saturation.
+%
+% The format of the ColorDecisionListImage method is:
+%
+% MagickBooleanType ColorDecisionListImage(Image *image,
+% const char *color_correction_collection)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o color_correction_collection: the color correction collection in XML.
+%
+*/
+MagickExport MagickBooleanType ColorDecisionListImage(Image *image,
+ const char *color_correction_collection)
+{
+#define ColorDecisionListCorrectImageTag "ColorDecisionList/Image"
+
+ typedef struct _Correction
+ {
+ double
+ slope,
+ offset,
+ power;
+ } Correction;
+
+ typedef struct _ColorCorrection
+ {
+ Correction
+ red,
+ green,
+ blue;
+
+ double
+ saturation;
+ } ColorCorrection;
+
+ char
+ token[MaxTextExtent];
+
+ ColorCorrection
+ color_correction;
+
+ const char
+ *content,
+ *p;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ PixelPacket
+ *cdl_map;
+
+ register long
+ i;
+
+ XMLTreeInfo
+ *cc,
+ *ccc,
+ *sat,
+ *sop;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize cdl maps.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (color_correction_collection == (const char *) NULL)
+ return(MagickFalse);
+ ccc=NewXMLTree((const char *) color_correction_collection,&image->exception);
+ if (ccc == (XMLTreeInfo *) NULL)
+ return(MagickFalse);
+ cc=GetXMLTreeChild(ccc,"ColorCorrection");
+ if (cc == (XMLTreeInfo *) NULL)
+ {
+ ccc=DestroyXMLTree(ccc);
+ return(MagickFalse);
+ }
+ color_correction.red.slope=1.0;
+ color_correction.red.offset=0.0;
+ color_correction.red.power=1.0;
+ color_correction.green.slope=1.0;
+ color_correction.green.offset=0.0;
+ color_correction.green.power=1.0;
+ color_correction.blue.slope=1.0;
+ color_correction.blue.offset=0.0;
+ color_correction.blue.power=1.0;
+ color_correction.saturation=0.0;
+ sop=GetXMLTreeChild(cc,"SOPNode");
+ if (sop != (XMLTreeInfo *) NULL)
+ {
+ XMLTreeInfo
+ *offset,
+ *power,
+ *slope;
+
+ slope=GetXMLTreeChild(sop,"Slope");
+ if (slope != (XMLTreeInfo *) NULL)
+ {
+ content=GetXMLTreeContent(slope);
+ p=(const char *) content;
+ for (i=0; (*p != '\0') && (i < 3); i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ switch (i)
+ {
+ case 0: color_correction.red.slope=atof(token); break;
+ case 1: color_correction.green.slope=atof(token); break;
+ case 2: color_correction.blue.slope=atof(token); break;
+ }
+ }
+ }
+ offset=GetXMLTreeChild(sop,"Offset");
+ if (offset != (XMLTreeInfo *) NULL)
+ {
+ content=GetXMLTreeContent(offset);
+ p=(const char *) content;
+ for (i=0; (*p != '\0') && (i < 3); i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ switch (i)
+ {
+ case 0: color_correction.red.offset=atof(token); break;
+ case 1: color_correction.green.offset=atof(token); break;
+ case 2: color_correction.blue.offset=atof(token); break;
+ }
+ }
+ }
+ power=GetXMLTreeChild(sop,"Power");
+ if (power != (XMLTreeInfo *) NULL)
+ {
+ content=GetXMLTreeContent(power);
+ p=(const char *) content;
+ for (i=0; (*p != '\0') && (i < 3); i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ switch (i)
+ {
+ case 0: color_correction.red.power=atof(token); break;
+ case 1: color_correction.green.power=atof(token); break;
+ case 2: color_correction.blue.power=atof(token); break;
+ }
+ }
+ }
+ }
+ sat=GetXMLTreeChild(cc,"SATNode");
+ if (sat != (XMLTreeInfo *) NULL)
+ {
+ XMLTreeInfo
+ *saturation;
+
+ saturation=GetXMLTreeChild(sat,"Saturation");
+ if (saturation != (XMLTreeInfo *) NULL)
+ {
+ content=GetXMLTreeContent(saturation);
+ p=(const char *) content;
+ GetMagickToken(p,&p,token);
+ color_correction.saturation=atof(token);
+ }
+ }
+ ccc=DestroyXMLTree(ccc);
+ if (image->debug != MagickFalse)
+ {
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " Color Correction Collection:");
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.red.slope: %g",color_correction.red.slope);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.red.offset: %g",color_correction.red.offset);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.red.power: %g",color_correction.red.power);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.green.slope: %g",color_correction.green.slope);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.green.offset: %g",color_correction.green.offset);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.green.power: %g",color_correction.green.power);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.blue.slope: %g",color_correction.blue.slope);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.blue.offset: %g",color_correction.blue.offset);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.blue.power: %g",color_correction.blue.power);
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " color_correction.saturation: %g",color_correction.saturation);
+ }
+ cdl_map=(PixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*cdl_map));
+ if (cdl_map == (PixelPacket *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(guided)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ cdl_map[i].red=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
+ MagickRealType) (MaxMap*(pow(color_correction.red.slope*i/MaxMap+
+ color_correction.red.offset,color_correction.red.power)))));
+ cdl_map[i].green=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
+ MagickRealType) (MaxMap*(pow(color_correction.green.slope*i/MaxMap+
+ color_correction.green.offset,color_correction.green.power)))));
+ cdl_map[i].blue=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
+ MagickRealType) (MaxMap*(pow(color_correction.blue.slope*i/MaxMap+
+ color_correction.blue.offset,color_correction.blue.power)))));
+ }
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Apply transfer function to colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ double
+ luma;
+
+ luma=0.2126*image->colormap[i].red+0.7152*image->colormap[i].green+
+ 0.0722*image->colormap[i].blue;
+ image->colormap[i].red=RoundToQuantum(luma+color_correction.saturation*
+ cdl_map[ScaleQuantumToMap(image->colormap[i].red)].red-luma);
+ image->colormap[i].green=RoundToQuantum(luma+
+ color_correction.saturation*cdl_map[ScaleQuantumToMap(
+ image->colormap[i].green)].green-luma);
+ image->colormap[i].blue=RoundToQuantum(luma+color_correction.saturation*
+ cdl_map[ScaleQuantumToMap(image->colormap[i].blue)].blue-luma);
+ }
+ }
+ /*
+ Apply transfer function to image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ luma;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ luma=0.2126*q->red+0.7152*q->green+0.0722*q->blue;
+ q->red=RoundToQuantum(luma+color_correction.saturation*
+ (cdl_map[ScaleQuantumToMap(q->red)].red-luma));
+ q->green=RoundToQuantum(luma+color_correction.saturation*
+ (cdl_map[ScaleQuantumToMap(q->green)].green-luma));
+ q->blue=RoundToQuantum(luma+color_correction.saturation*
+ (cdl_map[ScaleQuantumToMap(q->blue)].blue-luma));
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ColorDecisionListImageChannel)
+#endif
+ proceed=SetImageProgress(image,ColorDecisionListCorrectImageTag,
+ progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ cdl_map=(PixelPacket *) RelinquishMagickMemory(cdl_map);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l u t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClutImage() replaces each color value in the given image, by using it as an
+% index to lookup a replacement color value in a Color Look UP Table in the
+% form of an image. The values are extracted along a diagonal of the CLUT
+% image so either a horizontal or vertial gradient image can be used.
+%
+% Typically this is used to either re-color a gray-scale image according to a
+% color gradient in the CLUT image, or to perform a freeform histogram
+% (level) adjustment according to the (typically gray-scale) gradient in the
+% CLUT image.
+%
+% When the 'channel' mask includes the matte/alpha transparency channel but
+% one image has no such channel it is assumed that that image is a simple
+% gray-scale image that will effect the alpha channel values, either for
+% gray-scale coloring (with transparent or semi-transparent colors), or
+% a histogram adjustment of existing alpha channel values. If both images
+% have matte channels, direct and normal indexing is applied, which is rarely
+% used.
+%
+% The format of the ClutImage method is:
+%
+% MagickBooleanType ClutImage(Image *image,Image *clut_image)
+% MagickBooleanType ClutImageChannel(Image *image,
+% const ChannelType channel,Image *clut_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image, which is replaced by indexed CLUT values
+%
+% o clut_image: the color lookup table image for replacement color values.
+%
+% o channel: the channel.
+%
+*/
+
+MagickExport MagickBooleanType ClutImage(Image *image,const Image *clut_image)
+{
+ return(ClutImageChannel(image,DefaultChannels,clut_image));
+}
+
+MagickExport MagickBooleanType ClutImageChannel(Image *image,
+ const ChannelType channel,const Image *clut_image)
+{
+#define ClutImageTag "Clut/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ adjust,
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ ResampleFilter
+ **resample_filter;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(clut_image != (Image *) NULL);
+ assert(clut_image->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Clut image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(clut_image,&zero);
+ adjust=clut_image->interpolate == IntegerInterpolatePixel ? 0 : 1;
+ exception=(&image->exception);
+ resample_filter=AcquireResampleFilterThreadSet(clut_image,MagickTrue,
+ exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ PROGRAMMERS WARNING:
+
+ Apply OpacityChannel BEFORE the color channels. Do not re-order.
+
+ The handling special case 2 (coloring gray-scale), requires access to
+ the unmodified colors of the original image to determine the index
+ value. As such alpha/matte channel handling must be performed BEFORE,
+ any of the color channels are modified.
+
+ */
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (clut_image->matte == MagickFalse)
+ {
+ /*
+ A gray-scale LUT replacement for an image alpha channel.
+ */
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*
+ (QuantumRange-q->opacity)*(clut_image->columns+adjust),
+ QuantumScale*(QuantumRange-q->opacity)*(clut_image->rows+
+ adjust),&pixel);
+ q->opacity=(Quantum) (QuantumRange-MagickPixelIntensityToQuantum(
+ &pixel));
+ }
+ else
+ if (image->matte == MagickFalse)
+ {
+ /*
+ A greyscale image being colored by a LUT with transparency.
+ */
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*
+ PixelIntensity(q)*(clut_image->columns-adjust),QuantumScale*
+ PixelIntensity(q)*(clut_image->rows-adjust),&pixel);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ }
+ else
+ {
+ /*
+ Direct alpha channel lookup.
+ */
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*
+ q->opacity*(clut_image->columns-adjust),QuantumScale*
+ q->opacity* (clut_image->rows-adjust),&pixel);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ }
+ }
+ if ((channel & RedChannel) != 0)
+ {
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->red*
+ (clut_image->columns-adjust),QuantumScale*q->red*
+ (clut_image->rows-adjust),&pixel);
+ q->red=RoundToQuantum(pixel.red);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->green*
+ (clut_image->columns-adjust),QuantumScale*q->green*
+ (clut_image->rows-adjust),&pixel);
+ q->green=RoundToQuantum(pixel.green);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*q->blue*
+ (clut_image->columns-adjust),QuantumScale*q->blue*
+ (clut_image->rows-adjust),&pixel);
+ q->blue=RoundToQuantum(pixel.blue);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ (void) ResamplePixelColor(resample_filter[id],QuantumScale*indexes[x]*
+ (clut_image->columns-adjust),QuantumScale*indexes[x]*
+ (clut_image->rows-adjust),&pixel);
+ indexes[x]=RoundToQuantum(pixel.index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ClutImageChannel)
+#endif
+ proceed=SetImageProgress(image,ClutImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ /*
+ Enable alpha channel if CLUT image could enable it.
+ */
+ if ((clut_image->matte != MagickFalse) && ((channel & OpacityChannel) != 0))
+ (void) SetImageAlphaChannel(image,ActivateAlphaChannel);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n t r a s t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ContrastImage() enhances the intensity differences between the lighter and
+% darker elements of the image. Set sharpen to a MagickTrue to increase the
+% image contrast otherwise the contrast is reduced.
+%
+% The format of the ContrastImage method is:
+%
+% MagickBooleanType ContrastImage(Image *image,
+% const MagickBooleanType sharpen)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o sharpen: Increase or decrease image contrast.
+%
+*/
+
+static void Contrast(const int sign,Quantum *red,Quantum *green,Quantum *blue)
+{
+ double
+ brightness,
+ hue,
+ saturation;
+
+ /*
+ Enhance contrast: dark color become darker, light color become lighter.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ hue=0.0;
+ saturation=0.0;
+ brightness=0.0;
+ ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
+ brightness+=0.5*sign*(0.5*(sin(MagickPI*(brightness-0.5))+1.0)-brightness);
+ if (brightness > 1.0)
+ brightness=1.0;
+ else
+ if (brightness < 0.0)
+ brightness=0.0;
+ ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
+}
+
+MagickExport MagickBooleanType ContrastImage(Image *image,
+ const MagickBooleanType sharpen)
+{
+#define ContrastImageTag "Contrast/Image"
+
+ ExceptionInfo
+ *exception;
+
+ int
+ sign;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ sign=sharpen != MagickFalse ? 1 : -1;
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Contrast enhance colormap.
+ */
+ for (i=0; i < (long) image->colors; i++)
+ Contrast(sign,&image->colormap[i].red,&image->colormap[i].green,
+ &image->colormap[i].blue);
+ }
+ /*
+ Contrast enhance image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ Contrast(sign,&q->red,&q->green,&q->blue);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ContrastImage)
+#endif
+ proceed=SetImageProgress(image,ContrastImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n t r a s t S t r e t c h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The ContrastStretchImage() is a simple image enhancement technique that
+% attempts to improve the contrast in an image by `stretching' the range of
+% intensity values it contains to span a desired range of values. It differs
+% from the more sophisticated histogram equalization in that it can only
+% apply % a linear scaling function to the image pixel values. As a result
+% the `enhancement' is less harsh.
+%
+% The format of the ContrastStretchImage method is:
+%
+% MagickBooleanType ContrastStretchImage(Image *image,
+% const char *levels)
+% MagickBooleanType ContrastStretchImageChannel(Image *image,
+% const unsigned long channel,const double black_point,
+% const double white_point)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o black_point: the black point.
+%
+% o white_point: the white point.
+%
+% o levels: Specify the levels where the black and white points have the
+% range of 0 to number-of-pixels (e.g. 1%, 10x90%, etc.).
+%
+*/
+
+MagickExport MagickBooleanType ContrastStretchImage(Image *image,
+ const char *levels)
+{
+ double
+ black_point,
+ white_point;
+
+ GeometryInfo
+ geometry_info;
+
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Parse levels.
+ */
+ if (levels == (char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(levels,&geometry_info);
+ black_point=geometry_info.rho;
+ white_point=(double) image->columns*image->rows;
+ if ((flags & SigmaValue) != 0)
+ white_point=geometry_info.sigma;
+ if ((flags & PercentValue) != 0)
+ {
+ black_point*=(double) QuantumRange/100.0;
+ white_point*=(double) QuantumRange/100.0;
+ }
+ if ((flags & SigmaValue) == 0)
+ white_point=(double) image->columns*image->rows-black_point;
+ status=ContrastStretchImageChannel(image,DefaultChannels,black_point,
+ white_point);
+ return(status);
+}
+
+MagickExport MagickBooleanType ContrastStretchImageChannel(Image *image,
+ const ChannelType channel,const double black_point,const double white_point)
+{
+#define MaxRange(color) ((MagickRealType) ScaleQuantumToMap((Quantum) (color)))
+#define ContrastStretchImageTag "ContrastStretch/Image"
+
+ double
+ intensity;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ black,
+ *histogram,
+ *stretch_map,
+ white;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate histogram and stretch map.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*histogram));
+ stretch_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*stretch_map));
+ if ((histogram == (MagickPixelPacket *) NULL) ||
+ (stretch_map == (MagickPixelPacket *) NULL))
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ /*
+ Form histogram.
+ */
+ status=MagickTrue;
+ exception=(&image->exception);
+ (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ if (channel == DefaultChannels)
+ for (x=0; x < (long) image->columns; x++)
+ {
+ Quantum
+ intensity;
+
+ intensity=PixelIntensityToQuantum(p);
+ histogram[ScaleQuantumToMap(intensity)].red++;
+ histogram[ScaleQuantumToMap(intensity)].green++;
+ histogram[ScaleQuantumToMap(intensity)].blue++;
+ histogram[ScaleQuantumToMap(intensity)].index++;
+ p++;
+ }
+ else
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ histogram[ScaleQuantumToMap(p->red)].red++;
+ if ((channel & GreenChannel) != 0)
+ histogram[ScaleQuantumToMap(p->green)].green++;
+ if ((channel & BlueChannel) != 0)
+ histogram[ScaleQuantumToMap(p->blue)].blue++;
+ if ((channel & OpacityChannel) != 0)
+ histogram[ScaleQuantumToMap(p->opacity)].opacity++;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ histogram[ScaleQuantumToMap(indexes[x])].index++;
+ p++;
+ }
+ }
+ /*
+ Find the histogram boundaries by locating the black/white levels.
+ */
+ black.red=0.0;
+ white.red=MaxRange(QuantumRange);
+ if ((channel & RedChannel) != 0)
+ {
+ intensity=0.0;
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ intensity+=histogram[i].red;
+ if (intensity > black_point)
+ break;
+ }
+ black.red=(MagickRealType) i;
+ intensity=0.0;
+ for (i=(long) MaxMap; i != 0; i--)
+ {
+ intensity+=histogram[i].red;
+ if (intensity > ((double) image->columns*image->rows-white_point))
+ break;
+ }
+ white.red=(MagickRealType) i;
+ }
+ black.green=0.0;
+ white.green=MaxRange(QuantumRange);
+ if ((channel & GreenChannel) != 0)
+ {
+ intensity=0.0;
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ intensity+=histogram[i].green;
+ if (intensity > black_point)
+ break;
+ }
+ black.green=(MagickRealType) i;
+ intensity=0.0;
+ for (i=(long) MaxMap; i != 0; i--)
+ {
+ intensity+=histogram[i].green;
+ if (intensity > ((double) image->columns*image->rows-white_point))
+ break;
+ }
+ white.green=(MagickRealType) i;
+ }
+ black.blue=0.0;
+ white.blue=MaxRange(QuantumRange);
+ if ((channel & BlueChannel) != 0)
+ {
+ intensity=0.0;
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ intensity+=histogram[i].blue;
+ if (intensity > black_point)
+ break;
+ }
+ black.blue=(MagickRealType) i;
+ intensity=0.0;
+ for (i=(long) MaxMap; i != 0; i--)
+ {
+ intensity+=histogram[i].blue;
+ if (intensity > ((double) image->columns*image->rows-white_point))
+ break;
+ }
+ white.blue=(MagickRealType) i;
+ }
+ black.opacity=0.0;
+ white.opacity=MaxRange(QuantumRange);
+ if ((channel & OpacityChannel) != 0)
+ {
+ intensity=0.0;
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ intensity+=histogram[i].opacity;
+ if (intensity > black_point)
+ break;
+ }
+ black.opacity=(MagickRealType) i;
+ intensity=0.0;
+ for (i=(long) MaxMap; i != 0; i--)
+ {
+ intensity+=histogram[i].opacity;
+ if (intensity > ((double) image->columns*image->rows-white_point))
+ break;
+ }
+ white.opacity=(MagickRealType) i;
+ }
+ black.index=0.0;
+ white.index=MaxRange(QuantumRange);
+ if (((channel & IndexChannel) != 0) && (image->colorspace == CMYKColorspace))
+ {
+ intensity=0.0;
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ intensity+=histogram[i].index;
+ if (intensity > black_point)
+ break;
+ }
+ black.index=(MagickRealType) i;
+ intensity=0.0;
+ for (i=(long) MaxMap; i != 0; i--)
+ {
+ intensity+=histogram[i].index;
+ if (intensity > ((double) image->columns*image->rows-white_point))
+ break;
+ }
+ white.index=(MagickRealType) i;
+ }
+ histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
+ /*
+ Stretch the histogram to create the stretched image mapping.
+ */
+ (void) ResetMagickMemory(stretch_map,0,(MaxMap+1)*sizeof(*stretch_map));
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ if (i < (long) black.red)
+ stretch_map[i].red=0.0;
+ else
+ if (i > (long) white.red)
+ stretch_map[i].red=(MagickRealType) QuantumRange;
+ else
+ if (black.red != white.red)
+ stretch_map[i].red=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) (MaxMap*(i-black.red)/(white.red-black.red)));
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ if (i < (long) black.green)
+ stretch_map[i].green=0.0;
+ else
+ if (i > (long) white.green)
+ stretch_map[i].green=(MagickRealType) QuantumRange;
+ else
+ if (black.green != white.green)
+ stretch_map[i].green=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) (MaxMap*(i-black.green)/(white.green-
+ black.green)));
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ if (i < (long) black.blue)
+ stretch_map[i].blue=0.0;
+ else
+ if (i > (long) white.blue)
+ stretch_map[i].blue=(MagickRealType) QuantumRange;
+ else
+ if (black.blue != white.blue)
+ stretch_map[i].blue=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) (MaxMap*(i-black.blue)/(white.blue-
+ black.blue)));
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (i < (long) black.opacity)
+ stretch_map[i].opacity=0.0;
+ else
+ if (i > (long) white.opacity)
+ stretch_map[i].opacity=(MagickRealType) QuantumRange;
+ else
+ if (black.opacity != white.opacity)
+ stretch_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) (MaxMap*(i-black.opacity)/(white.opacity-
+ black.opacity)));
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ if (i < (long) black.index)
+ stretch_map[i].index=0.0;
+ else
+ if (i > (long) white.index)
+ stretch_map[i].index=(MagickRealType) QuantumRange;
+ else
+ if (black.index != white.index)
+ stretch_map[i].index=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) (MaxMap*(i-black.index)/(white.index-
+ black.index)));
+ }
+ }
+ /*
+ Stretch the image.
+ */
+ if (((channel & OpacityChannel) != 0) || (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace)))
+ image->storage_class=DirectClass;
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Stretch colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ if (black.red != white.red)
+ image->colormap[i].red=RoundToQuantum(stretch_map[
+ ScaleQuantumToMap(image->colormap[i].red)].red);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ if (black.green != white.green)
+ image->colormap[i].green=RoundToQuantum(stretch_map[
+ ScaleQuantumToMap(image->colormap[i].green)].green);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ if (black.blue != white.blue)
+ image->colormap[i].blue=RoundToQuantum(stretch_map[
+ ScaleQuantumToMap(image->colormap[i].blue)].blue);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (black.opacity != white.opacity)
+ image->colormap[i].opacity=RoundToQuantum(stretch_map[
+ ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
+ }
+ }
+ }
+ /*
+ Stretch image.
+ */
+ status=MagickTrue;
+ progress=0;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ if (black.red != white.red)
+ q->red=RoundToQuantum(stretch_map[ScaleQuantumToMap(q->red)].red);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ if (black.green != white.green)
+ q->green=RoundToQuantum(stretch_map[ScaleQuantumToMap(
+ q->green)].green);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ if (black.blue != white.blue)
+ q->blue=RoundToQuantum(stretch_map[ScaleQuantumToMap(
+ q->blue)].blue);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (black.opacity != white.opacity)
+ q->opacity=RoundToQuantum(stretch_map[ScaleQuantumToMap(
+ q->opacity)].opacity);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ if (black.index != white.index)
+ indexes[x]=(IndexPacket) RoundToQuantum(stretch_map[
+ ScaleQuantumToMap(indexes[x])].index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ContrastStretchImageChannel)
+#endif
+ proceed=SetImageProgress(image,ContrastStretchImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ stretch_map=(MagickPixelPacket *) RelinquishMagickMemory(stretch_map);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E n h a n c e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EnhanceImage() applies a digital filter that improves the quality of a
+% noisy image.
+%
+% The format of the EnhanceImage method is:
+%
+% Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *EnhanceImage(const Image *image,ExceptionInfo *exception)
+{
+#define Enhance(weight) \
+ mean=((MagickRealType) r->red+pixel.red)/2; \
+ distance=(MagickRealType) r->red-(MagickRealType) pixel.red; \
+ distance_squared=QuantumScale*(2.0*((MagickRealType) QuantumRange+1.0)+ \
+ mean)*distance*distance; \
+ mean=((MagickRealType) r->green+pixel.green)/2; \
+ distance=(MagickRealType) r->green-(MagickRealType) pixel.green; \
+ distance_squared+=4.0*distance*distance; \
+ mean=((MagickRealType) r->blue+pixel.blue)/2; \
+ distance=(MagickRealType) r->blue-(MagickRealType) pixel.blue; \
+ distance_squared+=QuantumScale*(3.0*((MagickRealType) \
+ QuantumRange+1.0)-1.0-mean)*distance*distance; \
+ mean=((MagickRealType) r->opacity+pixel.opacity)/2; \
+ distance=(MagickRealType) r->opacity-(MagickRealType) pixel.opacity; \
+ distance_squared+=QuantumScale*(3.0*((MagickRealType) \
+ QuantumRange+1.0)-1.0-mean)*distance*distance; \
+ if (distance_squared < ((MagickRealType) QuantumRange*(MagickRealType) \
+ QuantumRange/25.0f)) \
+ { \
+ aggregate.red+=(weight)*r->red; \
+ aggregate.green+=(weight)*r->green; \
+ aggregate.blue+=(weight)*r->blue; \
+ aggregate.opacity+=(weight)*r->opacity; \
+ total_weight+=(weight); \
+ } \
+ r++;
+#define EnhanceImageTag "Enhance/Image"
+
+ Image
+ *enhance_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ CacheView
+ *enhance_view,
+ *image_view;
+
+ /*
+ Initialize enhanced image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((image->columns < 5) || (image->rows < 5))
+ return((Image *) NULL);
+ enhance_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (enhance_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(enhance_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&enhance_image->exception);
+ enhance_image=DestroyImage(enhance_image);
+ return((Image *) NULL);
+ }
+ /*
+ Enhance image.
+ */
+ status=MagickTrue;
+ progress=0;
+ (void) ResetMagickMemory(&zero,0,sizeof(zero));
+ image_view=AcquireCacheView(image);
+ enhance_view=AcquireCacheView(enhance_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Read another scan line.
+ */
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-2,y-2,image->columns+4,5,exception);
+ q=QueueCacheViewAuthenticPixels(enhance_view,0,y,enhance_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickPixelPacket
+ aggregate;
+
+ MagickRealType
+ distance,
+ distance_squared,
+ mean,
+ total_weight;
+
+ PixelPacket
+ pixel;
+
+ register const PixelPacket
+ *__restrict r;
+
+ /*
+ Compute weighted average of target pixel color components.
+ */
+ aggregate=zero;
+ total_weight=0.0;
+ r=p+2*(image->columns+4)+2;
+ pixel=(*r);
+ r=p;
+ Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
+ r=p+(image->columns+4);
+ Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
+ r=p+2*(image->columns+4);
+ Enhance(10.0); Enhance(40.0); Enhance(80.0); Enhance(40.0); Enhance(10.0);
+ r=p+3*(image->columns+4);
+ Enhance(8.0); Enhance(20.0); Enhance(40.0); Enhance(20.0); Enhance(8.0);
+ r=p+4*(image->columns+4);
+ Enhance(5.0); Enhance(8.0); Enhance(10.0); Enhance(8.0); Enhance(5.0);
+ q->red=(Quantum) ((aggregate.red+(total_weight/2)-1)/total_weight);
+ q->green=(Quantum) ((aggregate.green+(total_weight/2)-1)/total_weight);
+ q->blue=(Quantum) ((aggregate.blue+(total_weight/2)-1)/total_weight);
+ q->opacity=(Quantum) ((aggregate.opacity+(total_weight/2)-1)/
+ total_weight);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(enhance_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_EnhanceImage)
+#endif
+ proceed=SetImageProgress(image,EnhanceImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ enhance_view=DestroyCacheView(enhance_view);
+ image_view=DestroyCacheView(image_view);
+ return(enhance_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E q u a l i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EqualizeImage() applies a histogram equalization to the image.
+%
+% The format of the EqualizeImage method is:
+%
+% MagickBooleanType EqualizeImage(Image *image)
+% MagickBooleanType EqualizeImageChannel(Image *image,
+% const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+*/
+
+MagickExport MagickBooleanType EqualizeImage(Image *image)
+{
+ return(EqualizeImageChannel(image,DefaultChannels));
+}
+
+MagickExport MagickBooleanType EqualizeImageChannel(Image *image,
+ const ChannelType channel)
+{
+#define EqualizeImageTag "Equalize/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ black,
+ *equalize_map,
+ *histogram,
+ intensity,
+ *map,
+ white;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize histogram arrays.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ equalize_map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*equalize_map));
+ histogram=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*histogram));
+ map=(MagickPixelPacket *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*map));
+ if ((equalize_map == (MagickPixelPacket *) NULL) ||
+ (histogram == (MagickPixelPacket *) NULL) ||
+ (map == (MagickPixelPacket *) NULL))
+ {
+ if (map != (MagickPixelPacket *) NULL)
+ map=(MagickPixelPacket *) RelinquishMagickMemory(map);
+ if (histogram != (MagickPixelPacket *) NULL)
+ histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
+ if (equalize_map != (MagickPixelPacket *) NULL)
+ equalize_map=(MagickPixelPacket *) RelinquishMagickMemory(equalize_map);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ /*
+ Form histogram.
+ */
+ (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
+ exception=(&image->exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ histogram[ScaleQuantumToMap(p->red)].red++;
+ if ((channel & GreenChannel) != 0)
+ histogram[ScaleQuantumToMap(p->green)].green++;
+ if ((channel & BlueChannel) != 0)
+ histogram[ScaleQuantumToMap(p->blue)].blue++;
+ if ((channel & OpacityChannel) != 0)
+ histogram[ScaleQuantumToMap(p->opacity)].opacity++;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ histogram[ScaleQuantumToMap(indexes[x])].index++;
+ p++;
+ }
+ }
+ /*
+ Integrate the histogram to get the equalization map.
+ */
+ (void) ResetMagickMemory(&intensity,0,sizeof(intensity));
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ intensity.red+=histogram[i].red;
+ if ((channel & GreenChannel) != 0)
+ intensity.green+=histogram[i].green;
+ if ((channel & BlueChannel) != 0)
+ intensity.blue+=histogram[i].blue;
+ if ((channel & OpacityChannel) != 0)
+ intensity.opacity+=histogram[i].opacity;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ intensity.index+=histogram[i].index;
+ map[i]=intensity;
+ }
+ black=map[0];
+ white=map[(int) MaxMap];
+ (void) ResetMagickMemory(equalize_map,0,(MaxMap+1)*sizeof(*equalize_map));
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ if (((channel & RedChannel) != 0) && (white.red != black.red))
+ equalize_map[i].red=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ ((MaxMap*(map[i].red-black.red))/(white.red-black.red)));
+ if (((channel & GreenChannel) != 0) && (white.green != black.green))
+ equalize_map[i].green=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ ((MaxMap*(map[i].green-black.green))/(white.green-black.green)));
+ if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
+ equalize_map[i].blue=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ ((MaxMap*(map[i].blue-black.blue))/(white.blue-black.blue)));
+ if (((channel & OpacityChannel) != 0) && (white.opacity != black.opacity))
+ equalize_map[i].opacity=(MagickRealType) ScaleMapToQuantum(
+ (MagickRealType) ((MaxMap*(map[i].opacity-black.opacity))/
+ (white.opacity-black.opacity)));
+ if ((((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace)) &&
+ (white.index != black.index))
+ equalize_map[i].index=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ ((MaxMap*(map[i].index-black.index))/(white.index-black.index)));
+ }
+ histogram=(MagickPixelPacket *) RelinquishMagickMemory(histogram);
+ map=(MagickPixelPacket *) RelinquishMagickMemory(map);
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Equalize colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (((channel & RedChannel) != 0) && (white.red != black.red))
+ image->colormap[i].red=RoundToQuantum(equalize_map[
+ ScaleQuantumToMap(image->colormap[i].red)].red);
+ if (((channel & GreenChannel) != 0) && (white.green != black.green))
+ image->colormap[i].green=RoundToQuantum(equalize_map[
+ ScaleQuantumToMap(image->colormap[i].green)].green);
+ if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
+ image->colormap[i].blue=RoundToQuantum(equalize_map[
+ ScaleQuantumToMap(image->colormap[i].blue)].blue);
+ if (((channel & OpacityChannel) != 0) &&
+ (white.opacity != black.opacity))
+ image->colormap[i].opacity=RoundToQuantum(equalize_map[
+ ScaleQuantumToMap(image->colormap[i].opacity)].opacity);
+ }
+ }
+ /*
+ Equalize image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if (((channel & RedChannel) != 0) && (white.red != black.red))
+ q->red=RoundToQuantum(equalize_map[ScaleQuantumToMap(q->red)].red);
+ if (((channel & GreenChannel) != 0) && (white.green != black.green))
+ q->green=RoundToQuantum(equalize_map[ScaleQuantumToMap(
+ q->green)].green);
+ if (((channel & BlueChannel) != 0) && (white.blue != black.blue))
+ q->blue=RoundToQuantum(equalize_map[ScaleQuantumToMap(q->blue)].blue);
+ if (((channel & OpacityChannel) != 0) && (white.opacity != black.opacity))
+ q->opacity=RoundToQuantum(equalize_map[ScaleQuantumToMap(
+ q->opacity)].opacity);
+ if ((((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace)) &&
+ (white.index != black.index))
+ indexes[x]=RoundToQuantum(equalize_map[ScaleQuantumToMap(
+ indexes[x])].index);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_EqualizeImageChannel)
+#endif
+ proceed=SetImageProgress(image,EqualizeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ equalize_map=(MagickPixelPacket *) RelinquishMagickMemory(equalize_map);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G a m m a I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GammaImage() gamma-corrects a particular image channel. The same
+% image viewed on different devices will have perceptual differences in the
+% way the image's intensities are represented on the screen. Specify
+% individual gamma levels for the red, green, and blue channels, or adjust
+% all three with the gamma parameter. Values typically range from 0.8 to 2.3.
+%
+% You can also reduce the influence of a particular channel with a gamma
+% value of 0.
+%
+% The format of the GammaImage method is:
+%
+% MagickBooleanType GammaImage(Image *image,const double gamma)
+% MagickBooleanType GammaImageChannel(Image *image,
+% const ChannelType channel,const double gamma)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o gamma: the image gamma.
+%
+*/
+MagickExport MagickBooleanType GammaImage(Image *image,const char *level)
+{
+ GeometryInfo
+ geometry_info;
+
+ MagickPixelPacket
+ gamma;
+
+ MagickStatusType
+ flags,
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (level == (char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(level,&geometry_info);
+ gamma.red=geometry_info.rho;
+ gamma.green=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ gamma.green=gamma.red;
+ gamma.blue=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ gamma.blue=gamma.red;
+ if ((gamma.red == 1.0) && (gamma.green == 1.0) && (gamma.blue == 1.0))
+ return(MagickTrue);
+ if ((gamma.red == gamma.green) && (gamma.green == gamma.blue))
+ status=GammaImageChannel(image,(const ChannelType) (RedChannel |
+ GreenChannel | BlueChannel),(double) gamma.red);
+ else
+ {
+ status=GammaImageChannel(image,RedChannel,(double) gamma.red);
+ status|=GammaImageChannel(image,GreenChannel,(double) gamma.green);
+ status|=GammaImageChannel(image,BlueChannel,(double) gamma.blue);
+ }
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+MagickExport MagickBooleanType GammaImageChannel(Image *image,
+ const ChannelType channel,const double gamma)
+{
+#define GammaCorrectImageTag "GammaCorrect/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ Quantum
+ *gamma_map;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize gamma maps.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (gamma == 1.0)
+ return(MagickTrue);
+ gamma_map=(Quantum *) AcquireQuantumMemory(MaxMap+1UL,sizeof(*gamma_map));
+ if (gamma_map == (Quantum *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ (void) ResetMagickMemory(gamma_map,0,(MaxMap+1)*sizeof(*gamma_map));
+ if (gamma != 0.0)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(guided)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ gamma_map[i]=RoundToQuantum((MagickRealType) ScaleMapToQuantum((
+ MagickRealType) (MaxMap*pow((double) i/MaxMap,1.0/gamma))));
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Gamma-correct colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ image->colormap[i].red=gamma_map[
+ ScaleQuantumToMap(image->colormap[i].red)];
+ if ((channel & GreenChannel) != 0)
+ image->colormap[i].green=gamma_map[
+ ScaleQuantumToMap(image->colormap[i].green)];
+ if ((channel & BlueChannel) != 0)
+ image->colormap[i].blue=gamma_map[
+ ScaleQuantumToMap(image->colormap[i].blue)];
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte == MagickFalse)
+ image->colormap[i].opacity=gamma_map[
+ ScaleQuantumToMap(image->colormap[i].opacity)];
+ else
+ image->colormap[i].opacity=(Quantum) QuantumRange-
+ gamma_map[ScaleQuantumToMap((Quantum) (QuantumRange-
+ image->colormap[i].opacity))];
+ }
+ }
+ }
+ /*
+ Gamma-correct image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=gamma_map[ScaleQuantumToMap(q->red)];
+ if ((channel & GreenChannel) != 0)
+ q->green=gamma_map[ScaleQuantumToMap(q->green)];
+ if ((channel & BlueChannel) != 0)
+ q->blue=gamma_map[ScaleQuantumToMap(q->blue)];
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte == MagickFalse)
+ q->opacity=gamma_map[ScaleQuantumToMap(q->opacity)];
+ else
+ q->opacity=(Quantum) QuantumRange-gamma_map[
+ ScaleQuantumToMap((Quantum) (QuantumRange-q->opacity))];
+ }
+ q++;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ for (x=0; x < (long) image->columns; x++)
+ indexes[x]=gamma_map[ScaleQuantumToMap(indexes[x])];
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GammaImageChannel)
+#endif
+ proceed=SetImageProgress(image,GammaCorrectImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ gamma_map=(Quantum *) RelinquishMagickMemory(gamma_map);
+ if (image->gamma != 0.0)
+ image->gamma*=gamma;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H a l d C l u t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% HaldClutImage() applies a Hald color lookup table to the image. A Hald
+% color lookup table is a 3-dimensional color cube mapped to 2 dimensions.
+% Create it with the HALD coder. You can apply any color transformation to
+% the Hald image and then use this method to apply the transform to the
+% image.
+%
+% The format of the HaldClutImage method is:
+%
+% MagickBooleanType HaldClutImage(Image *image,Image *hald_image)
+% MagickBooleanType HaldClutImageChannel(Image *image,
+% const ChannelType channel,Image *hald_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image, which is replaced by indexed CLUT values
+%
+% o hald_image: the color lookup table image for replacement color values.
+%
+% o channel: the channel.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType HaldClutImage(Image *image,
+ const Image *hald_image)
+{
+ return(HaldClutImageChannel(image,DefaultChannels,hald_image));
+}
+
+MagickExport MagickBooleanType HaldClutImageChannel(Image *image,
+ const ChannelType channel,const Image *hald_image)
+{
+#define HaldClutImageTag "Clut/Image"
+
+ typedef struct _HaldInfo
+ {
+ MagickRealType
+ x,
+ y,
+ z;
+ } HaldInfo;
+
+ double
+ width;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ ResampleFilter
+ **resample_filter;
+
+ size_t
+ cube_size,
+ length,
+ level;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(hald_image != (Image *) NULL);
+ assert(hald_image->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ /*
+ Hald clut image.
+ */
+ status=MagickTrue;
+ progress=0;
+ length=MagickMin(hald_image->columns,hald_image->rows);
+ for (level=2; (level*level*level) < length; level++) ;
+ level*=level;
+ cube_size=level*level;
+ width=(double) hald_image->columns;
+ GetMagickPixelPacket(hald_image,&zero);
+ exception=(&image->exception);
+ resample_filter=AcquireResampleFilterThreadSet(hald_image,MagickTrue,
+ exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ offset;
+
+ HaldInfo
+ point;
+
+ MagickPixelPacket
+ pixel,
+ pixel1,
+ pixel2,
+ pixel3,
+ pixel4;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ pixel1=zero;
+ pixel2=zero;
+ pixel3=zero;
+ pixel4=zero;
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ point.x=QuantumScale*(level-1.0)*q->red;
+ point.y=QuantumScale*(level-1.0)*q->green;
+ point.z=QuantumScale*(level-1.0)*q->blue;
+ offset=point.x+level*floor(point.y)+cube_size*floor(point.z);
+ point.x-=floor(point.x);
+ point.y-=floor(point.y);
+ point.z-=floor(point.z);
+ (void) ResamplePixelColor(resample_filter[id],fmod(offset,width),
+ floor(offset/width),&pixel1);
+ (void) ResamplePixelColor(resample_filter[id],fmod(offset+level,width),
+ floor((offset+level)/width),&pixel2);
+ MagickPixelCompositeAreaBlend(&pixel1,pixel1.opacity,&pixel2,
+ pixel2.opacity,point.y,&pixel3);
+ offset+=cube_size;
+ (void) ResamplePixelColor(resample_filter[id],fmod(offset,width),
+ floor(offset/width),&pixel1);
+ (void) ResamplePixelColor(resample_filter[id],fmod(offset+level,width),
+ floor((offset+level)/width),&pixel2);
+ MagickPixelCompositeAreaBlend(&pixel1,pixel1.opacity,&pixel2,
+ pixel2.opacity,point.y,&pixel4);
+ MagickPixelCompositeAreaBlend(&pixel3,pixel3.opacity,&pixel4,
+ pixel4.opacity,point.z,&pixel);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(pixel.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(pixel.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(pixel.blue);
+ if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
+ q->opacity=RoundToQuantum(pixel.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=RoundToQuantum(pixel.index);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_HaldClutImageChannel)
+#endif
+ proceed=SetImageProgress(image,HaldClutImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L e v e l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LevelImage() adjusts the levels of a particular image channel by
+% scaling the colors falling between specified white and black points to
+% the full available quantum range.
+%
+% The parameters provided represent the black, and white points. The black
+% point specifies the darkest color in the image. Colors darker than the
+% black point are set to zero. White point specifies the lightest color in
+% the image. Colors brighter than the white point are set to the maximum
+% quantum value.
+%
+% If a '!' flag is given, map black and white colors to the given levels
+% rather than mapping those levels to black and white. See
+% LevelizeImageChannel() and LevelizeImageChannel(), below.
+%
+% Gamma specifies a gamma correction to apply to the image.
+%
+% The format of the LevelImage method is:
+%
+% MagickBooleanType LevelImage(Image *image,const char *levels)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o levels: Specify the levels where the black and white points have the
+% range of 0-QuantumRange, and gamma has the range 0-10 (e.g. 10x90%+2).
+% A '!' flag inverts the re-mapping.
+%
+*/
+
+MagickExport MagickBooleanType LevelImage(Image *image,const char *levels)
+{
+ double
+ black_point,
+ gamma,
+ white_point;
+
+ GeometryInfo
+ geometry_info;
+
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Parse levels.
+ */
+ if (levels == (char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(levels,&geometry_info);
+ black_point=geometry_info.rho;
+ white_point=(double) QuantumRange;
+ if ((flags & SigmaValue) != 0)
+ white_point=geometry_info.sigma;
+ gamma=1.0;
+ if ((flags & XiValue) != 0)
+ gamma=geometry_info.xi;
+ if ((flags & PercentValue) != 0)
+ {
+ black_point*=(double) image->columns*image->rows/100.0;
+ white_point*=(double) image->columns*image->rows/100.0;
+ }
+ if ((flags & SigmaValue) == 0)
+ white_point=(double) QuantumRange-black_point;
+ if ((flags & AspectValue ) == 0)
+ status=LevelImageChannel(image,DefaultChannels,black_point,white_point,
+ gamma);
+ else
+ status=LevelizeImageChannel(image,DefaultChannels,black_point,white_point,
+ gamma);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L e v e l I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LevelImageChannel() applies the normal LevelImage() operation to just the
+% Specific channels specified, spreading out the values between the black and
+% white points over the entire range of values. Gamma correction is also
+% applied after the values has been mapped.
+%
+% It is typically used to improve image contrast, or to provide a controlled
+% linear threshold for the image. If the black and white points are set to
+% the minimum and maximum values found in the image, the image can be
+% normalized. or by swapping black and white values, negate the image.
+%
+% The format of the LevelizeImageChannel method is:
+%
+% MagickBooleanType LevelImageChannel(Image *image,
+% const ChannelType channel,black_point,white_point,gamma)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o black_point: The level which is to be mapped to zero (black)
+%
+% o white_point: The level which is to be mapped to QuantiumRange (white)
+%
+% o gamma: adjust gamma by this factor before mapping values.
+% use 1.0 for purely linear stretching of image color values
+%
+*/
+MagickExport MagickBooleanType LevelImageChannel(Image *image,
+ const ChannelType channel,const double black_point,const double white_point,
+ const double gamma)
+{
+#define LevelImageTag "Level/Image"
+#define LevelValue(x) (RoundToQuantum((MagickRealType) QuantumRange* \
+ pow(((double) (x)-black_point)/(white_point-black_point),1.0/gamma)))
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize levels map.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->storage_class == PseudoClass)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ /*
+ Level colormap.
+ */
+ if ((channel & RedChannel) != 0)
+ image->colormap[i].red=LevelValue(image->colormap[i].red);
+ if ((channel & GreenChannel) != 0)
+ image->colormap[i].green=LevelValue(image->colormap[i].green);
+ if ((channel & BlueChannel) != 0)
+ image->colormap[i].blue=LevelValue(image->colormap[i].blue);
+ if ((channel & OpacityChannel) != 0)
+ image->colormap[i].opacity=LevelValue(image->colormap[i].opacity);
+ }
+ /*
+ Level image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=LevelValue(q->red);
+ if ((channel & GreenChannel) != 0)
+ q->green=LevelValue(q->green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=LevelValue(q->blue);
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ q->opacity=LevelValue(q->opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=LevelValue(indexes[x]);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_LevelImageChannel)
+#endif
+ proceed=SetImageProgress(image,LevelImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L e v e l i z e I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LevelizeImageChannel() applies the reversed LevelImage() operation to just
+% the specific channels specified. It compresses the full range of color
+% values, so that they lie between the given black and white points. Gamma is
+% applied before the values are mapped.
+%
+% LevelizeImageChannel() can be called with by using a +level command line
+% API option, or using a '!' on a -level or LevelImage() geometry string.
+%
+% It can be used for example de-contrast a greyscale image to the exact
+% levels specified. Or by using specific levels for each channel of an image
+% you can convert a gray-scale image to any linear color gradient, according
+% to those levels.
+%
+% The format of the LevelizeImageChannel method is:
+%
+% MagickBooleanType LevelizeImageChannel(Image *image,
+% const ChannelType channel,const char *levels)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o black_point: The level to map zero (black) to.
+%
+% o white_point: The level to map QuantiumRange (white) to.
+%
+% o gamma: adjust gamma by this factor before mapping values.
+%
+*/
+MagickExport MagickBooleanType LevelizeImageChannel(Image *image,
+ const ChannelType channel,const double black_point,const double white_point,
+ const double gamma)
+{
+#define LevelizeImageTag "Levelize/Image"
+#define LevelizeValue(x) (RoundToQuantum(((MagickRealType) \
+ pow((double)(QuantumScale*(x)),1.0/gamma))*(white_point-black_point)+ \
+ black_point))
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize levels map.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->storage_class == PseudoClass)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ /*
+ Level colormap.
+ */
+ if ((channel & RedChannel) != 0)
+ image->colormap[i].red=LevelizeValue(image->colormap[i].red);
+ if ((channel & GreenChannel) != 0)
+ image->colormap[i].green=LevelizeValue(image->colormap[i].green);
+ if ((channel & BlueChannel) != 0)
+ image->colormap[i].blue=LevelizeValue(image->colormap[i].blue);
+ if ((channel & OpacityChannel) != 0)
+ image->colormap[i].opacity=LevelizeValue(image->colormap[i].opacity);
+ }
+ /*
+ Level image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=LevelizeValue(q->red);
+ if ((channel & GreenChannel) != 0)
+ q->green=LevelizeValue(q->green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=LevelizeValue(q->blue);
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ q->opacity=LevelizeValue(q->opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=LevelizeValue(indexes[x]);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_LevelizeImageChannel)
+#endif
+ proceed=SetImageProgress(image,LevelizeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L e v e l I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LevelImageColor() will map the given color to "black" and "white"
+% values, limearly spreading out the colors, and level values on a channel by
+% channel bases, as per LevelImage(). The given colors allows you to specify
+% different level ranges for each of the color channels seperatally.
+%
+% If the boolean 'invert' is set true the image values will modifyed in the
+% reverse direction. That is any existing "black" and "white" colors in the
+% image will become the color values given, with all other values compressed
+% appropriatally. This effectivally maps a greyscale gradient into the given
+% color gradient.
+%
+% The format of the LevelImageColors method is:
+%
+% MagickBooleanType LevelImageColors(Image *image,const ChannelType channel,
+% const MagickPixelPacket *black_color,const MagickPixelPacket *white_color,
+% const MagickBooleanType invert)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o black_color: The color to map black to/from
+%
+% o white_point: The color to map white to/from
+%
+% o invert: if true map the colors (levelize), rather than from (level)
+%
+*/
+MagickBooleanType LevelImageColors(Image *image,const ChannelType channel,
+ const MagickPixelPacket *black_color,const MagickPixelPacket *white_color,
+ const MagickBooleanType invert)
+{
+#define LevelColorImageTag "LevelColor/Image"
+
+ MagickStatusType
+ status;
+
+ /*
+ Allocate and initialize levels map.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=MagickFalse;
+ if (invert == MagickFalse)
+ {
+ if ((channel & RedChannel) != 0)
+ status|=LevelImageChannel(image,RedChannel,
+ black_color->red,white_color->red,(double) 1.0);
+ if ((channel & GreenChannel) != 0)
+ status|=LevelImageChannel(image,GreenChannel,
+ black_color->green,white_color->green,(double) 1.0);
+ if ((channel & BlueChannel) != 0)
+ status|=LevelImageChannel(image,BlueChannel,
+ black_color->blue,white_color->blue,(double) 1.0);
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ status|=LevelImageChannel(image,OpacityChannel,
+ black_color->opacity,white_color->opacity,(double) 1.0);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ status|=LevelImageChannel(image,IndexChannel,
+ black_color->index,white_color->index,(double) 1.0);
+ }
+ else
+ {
+ if ((channel & RedChannel) != 0)
+ status|=LevelizeImageChannel(image,RedChannel,
+ black_color->red,white_color->red,(double) 1.0);
+ if ((channel & GreenChannel) != 0)
+ status|=LevelizeImageChannel(image,GreenChannel,
+ black_color->green,white_color->green,(double) 1.0);
+ if ((channel & BlueChannel) != 0)
+ status|=LevelizeImageChannel(image,BlueChannel,
+ black_color->blue,white_color->blue,(double) 1.0);
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ status|=LevelizeImageChannel(image,OpacityChannel,
+ black_color->opacity,white_color->opacity,(double) 1.0);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ status|=LevelizeImageChannel(image,IndexChannel,
+ black_color->index,white_color->index,(double) 1.0);
+ }
+ return(status == 0 ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i n e a r S t r e t c h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The LinearStretchImage() discards any pixels below the black point and
+% above the white point and levels the remaining pixels.
+%
+% The format of the LinearStretchImage method is:
+%
+% MagickBooleanType LinearStretchImage(Image *image,
+% const double black_point,const double white_point)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o black_point: the black point.
+%
+% o white_point: the white point.
+%
+*/
+MagickExport MagickBooleanType LinearStretchImage(Image *image,
+ const double black_point,const double white_point)
+{
+#define LinearStretchImageTag "LinearStretch/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ black,
+ white,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ *histogram,
+ intensity;
+
+ MagickSizeType
+ number_pixels;
+
+ /*
+ Allocate histogram and linear map.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ histogram=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*histogram));
+ if (histogram == (MagickRealType *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ /*
+ Form histogram.
+ */
+ (void) ResetMagickMemory(histogram,0,(MaxMap+1)*sizeof(*histogram));
+ exception=(&image->exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=(long) image->columns-1; x >= 0; x--)
+ {
+ histogram[ScaleQuantumToMap(PixelIntensityToQuantum(p))]++;
+ p++;
+ }
+ }
+ /*
+ Find the histogram boundaries by locating the black and white point levels.
+ */
+ number_pixels=(MagickSizeType) image->columns*image->rows;
+ intensity=0.0;
+ for (black=0; black < (long) MaxMap; black++)
+ {
+ intensity+=histogram[black];
+ if (intensity >= black_point)
+ break;
+ }
+ intensity=0.0;
+ for (white=(long) MaxMap; white != 0; white--)
+ {
+ intensity+=histogram[white];
+ if (intensity >= white_point)
+ break;
+ }
+ histogram=(MagickRealType *) RelinquishMagickMemory(histogram);
+ status=LevelImageChannel(image,DefaultChannels,(double) black,(double) white,
+ 1.0);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o d u l a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ModulateImage() lets you control the brightness, saturation, and hue
+% of an image. Modulate represents the brightness, saturation, and hue
+% as one parameter (e.g. 90,150,100). If the image colorspace is HSL, the
+% modulation is lightness, saturation, and hue. And if the colorspace is
+% HWB, use blackness, whiteness, and hue.
+%
+% The format of the ModulateImage method is:
+%
+% MagickBooleanType ModulateImage(Image *image,const char *modulate)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o modulate: Define the percent change in brightness, saturation, and
+% hue.
+%
+*/
+
+static void ModulateHSB(const double percent_hue,
+ const double percent_saturation,const double percent_brightness,
+ Quantum *red,Quantum *green,Quantum *blue)
+{
+ double
+ brightness,
+ hue,
+ saturation;
+
+ /*
+ Increase or decrease color brightness, saturation, or hue.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ ConvertRGBToHSB(*red,*green,*blue,&hue,&saturation,&brightness);
+ hue+=0.5*(0.01*percent_hue-1.0);
+ while (hue < 0.0)
+ hue+=1.0;
+ while (hue > 1.0)
+ hue-=1.0;
+ saturation*=0.01*percent_saturation;
+ brightness*=0.01*percent_brightness;
+ ConvertHSBToRGB(hue,saturation,brightness,red,green,blue);
+}
+
+static void ModulateHSL(const double percent_hue,
+ const double percent_saturation,const double percent_lightness,
+ Quantum *red,Quantum *green,Quantum *blue)
+{
+ double
+ hue,
+ lightness,
+ saturation;
+
+ /*
+ Increase or decrease color lightness, saturation, or hue.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ ConvertRGBToHSL(*red,*green,*blue,&hue,&saturation,&lightness);
+ hue+=0.5*(0.01*percent_hue-1.0);
+ while (hue < 0.0)
+ hue+=1.0;
+ while (hue > 1.0)
+ hue-=1.0;
+ saturation*=0.01*percent_saturation;
+ lightness*=0.01*percent_lightness;
+ ConvertHSLToRGB(hue,saturation,lightness,red,green,blue);
+}
+
+static void ModulateHWB(const double percent_hue,const double percent_whiteness, const double percent_blackness,Quantum *red,Quantum *green,Quantum *blue)
+{
+ double
+ blackness,
+ hue,
+ whiteness;
+
+ /*
+ Increase or decrease color blackness, whiteness, or hue.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ ConvertRGBToHWB(*red,*green,*blue,&hue,&whiteness,&blackness);
+ hue+=0.5*(0.01*percent_hue-1.0);
+ while (hue < 0.0)
+ hue+=1.0;
+ while (hue > 1.0)
+ hue-=1.0;
+ blackness*=0.01*percent_blackness;
+ whiteness*=0.01*percent_whiteness;
+ ConvertHWBToRGB(hue,whiteness,blackness,red,green,blue);
+}
+
+MagickExport MagickBooleanType ModulateImage(Image *image,const char *modulate)
+{
+#define ModulateImageTag "Modulate/Image"
+
+ ColorspaceType
+ colorspace;
+
+ const char
+ *artifact;
+
+ double
+ percent_brightness,
+ percent_hue,
+ percent_saturation;
+
+ ExceptionInfo
+ *exception;
+
+ GeometryInfo
+ geometry_info;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Initialize gamma table.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (modulate == (char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(modulate,&geometry_info);
+ percent_brightness=geometry_info.rho;
+ percent_saturation=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ percent_saturation=100.0;
+ percent_hue=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ percent_hue=100.0;
+ colorspace=UndefinedColorspace;
+ artifact=GetImageArtifact(image,"modulate:colorspace");
+ if (artifact != (const char *) NULL)
+ colorspace=(ColorspaceType) ParseMagickOption(MagickColorspaceOptions,
+ MagickFalse,artifact);
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Modulate colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ switch (colorspace)
+ {
+ case HSBColorspace:
+ {
+ ModulateHSB(percent_hue,percent_saturation,percent_brightness,
+ &image->colormap[i].red,&image->colormap[i].green,
+ &image->colormap[i].blue);
+ break;
+ }
+ case HSLColorspace:
+ default:
+ {
+ ModulateHSL(percent_hue,percent_saturation,percent_brightness,
+ &image->colormap[i].red,&image->colormap[i].green,
+ &image->colormap[i].blue);
+ break;
+ }
+ case HWBColorspace:
+ {
+ ModulateHWB(percent_hue,percent_saturation,percent_brightness,
+ &image->colormap[i].red,&image->colormap[i].green,
+ &image->colormap[i].blue);
+ break;
+ }
+ }
+ }
+ /*
+ Modulate image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ switch (colorspace)
+ {
+ case HSBColorspace:
+ {
+ ModulateHSB(percent_hue,percent_saturation,percent_brightness,
+ &q->red,&q->green,&q->blue);
+ break;
+ }
+ case HSLColorspace:
+ default:
+ {
+ ModulateHSL(percent_hue,percent_saturation,percent_brightness,
+ &q->red,&q->green,&q->blue);
+ break;
+ }
+ case HWBColorspace:
+ {
+ ModulateHWB(percent_hue,percent_saturation,percent_brightness,
+ &q->red,&q->green,&q->blue);
+ break;
+ }
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ModulateImage)
+#endif
+ proceed=SetImageProgress(image,ModulateImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e g a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NegateImage() negates the colors in the reference image. The grayscale
+% option means that only grayscale values within the image are negated.
+%
+% The format of the NegateImageChannel method is:
+%
+% MagickBooleanType NegateImage(Image *image,
+% const MagickBooleanType grayscale)
+% MagickBooleanType NegateImageChannel(Image *image,
+% const ChannelType channel,const MagickBooleanType grayscale)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o grayscale: If MagickTrue, only negate grayscale pixels within the image.
+%
+*/
+
+MagickExport MagickBooleanType NegateImage(Image *image,
+ const MagickBooleanType grayscale)
+{
+ MagickBooleanType
+ status;
+
+ status=NegateImageChannel(image,DefaultChannels,grayscale);
+ return(status);
+}
+
+MagickExport MagickBooleanType NegateImageChannel(Image *image,
+ const ChannelType channel,const MagickBooleanType grayscale)
+{
+#define NegateImageTag "Negate/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Negate colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (grayscale != MagickFalse)
+ if ((image->colormap[i].red != image->colormap[i].green) ||
+ (image->colormap[i].green != image->colormap[i].blue))
+ continue;
+ if ((channel & RedChannel) != 0)
+ image->colormap[i].red=(Quantum) QuantumRange-
+ image->colormap[i].red;
+ if ((channel & GreenChannel) != 0)
+ image->colormap[i].green=(Quantum) QuantumRange-
+ image->colormap[i].green;
+ if ((channel & BlueChannel) != 0)
+ image->colormap[i].blue=(Quantum) QuantumRange-
+ image->colormap[i].blue;
+ }
+ }
+ /*
+ Negate image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ if (grayscale != MagickFalse)
+ {
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((q->red != q->green) || (q->green != q->blue))
+ {
+ q++;
+ continue;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=(Quantum) QuantumRange-q->red;
+ if ((channel & GreenChannel) != 0)
+ q->green=(Quantum) QuantumRange-q->green;
+ if ((channel & BlueChannel) != 0)
+ q->blue=(Quantum) QuantumRange-q->blue;
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=(Quantum) QuantumRange-q->opacity;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=(IndexPacket) QuantumRange-indexes[x];
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_NegateImageChannel)
+#endif
+ proceed=SetImageProgress(image,NegateImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(MagickTrue);
+ }
+ /*
+ Negate image.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=(Quantum) QuantumRange-q->red;
+ if ((channel & GreenChannel) != 0)
+ q->green=(Quantum) QuantumRange-q->green;
+ if ((channel & BlueChannel) != 0)
+ q->blue=(Quantum) QuantumRange-q->blue;
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=(Quantum) QuantumRange-q->opacity;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=(IndexPacket) QuantumRange-indexes[x];
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_NegateImageChannel)
+#endif
+ proceed=SetImageProgress(image,NegateImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N o r m a l i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The NormalizeImage() method enhances the contrast of a color image by
+% mapping the darkest 2 percent of all pixel to black and the brightest
+% 1 percent to white.
+%
+% The format of the NormalizeImage method is:
+%
+% MagickBooleanType NormalizeImage(Image *image)
+% MagickBooleanType NormalizeImageChannel(Image *image,
+% const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+*/
+
+MagickExport MagickBooleanType NormalizeImage(Image *image)
+{
+ MagickBooleanType
+ status;
+
+ status=NormalizeImageChannel(image,DefaultChannels);
+ return(status);
+}
+
+MagickExport MagickBooleanType NormalizeImageChannel(Image *image,
+ const ChannelType channel)
+{
+ double
+ black_point,
+ white_point;
+
+ black_point=(double) image->columns*image->rows*0.02;
+ white_point=(double) image->columns*image->rows*0.99;
+ return(ContrastStretchImageChannel(image,channel,black_point,white_point));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S i g m o i d a l C o n t r a s t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SigmoidalContrastImage() adjusts the contrast of an image with a non-linear
+% sigmoidal contrast algorithm. Increase the contrast of the image using a
+% sigmoidal transfer function without saturating highlights or shadows.
+% Contrast indicates how much to increase the contrast (0 is none; 3 is
+% typical; 20 is pushing it); mid-point indicates where midtones fall in the
+% resultant image (0 is white; 50% is middle-gray; 100% is black). Set
+% sharpen to MagickTrue to increase the image contrast otherwise the contrast
+% is reduced.
+%
+% The format of the SigmoidalContrastImage method is:
+%
+% MagickBooleanType SigmoidalContrastImage(Image *image,
+% const MagickBooleanType sharpen,const char *levels)
+% MagickBooleanType SigmoidalContrastImageChannel(Image *image,
+% const ChannelType channel,const MagickBooleanType sharpen,
+% const double contrast,const double midpoint)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o sharpen: Increase or decrease image contrast.
+%
+% o contrast: control the "shoulder" of the contast curve.
+%
+% o midpoint: control the "toe" of the contast curve.
+%
+*/
+
+MagickExport MagickBooleanType SigmoidalContrastImage(Image *image,
+ const MagickBooleanType sharpen,const char *levels)
+{
+ GeometryInfo
+ geometry_info;
+
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ flags=ParseGeometry(levels,&geometry_info);
+ if ((flags & SigmaValue) == 0)
+ geometry_info.sigma=1.0*QuantumRange/2.0;
+ if ((flags & PercentValue) != 0)
+ geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
+ status=SigmoidalContrastImageChannel(image,DefaultChannels,sharpen,
+ geometry_info.rho,geometry_info.sigma);
+ return(status);
+}
+
+MagickExport MagickBooleanType SigmoidalContrastImageChannel(Image *image,
+ const ChannelType channel,const MagickBooleanType sharpen,
+ const double contrast,const double midpoint)
+{
+#define SigmoidalContrastImageTag "SigmoidalContrast/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ *sigmoidal_map;
+
+ register long
+ i;
+
+ CacheView
+ *image_view;
+
+ /*
+ Allocate and initialize sigmoidal maps.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ sigmoidal_map=(MagickRealType *) AcquireQuantumMemory(MaxMap+1UL,
+ sizeof(*sigmoidal_map));
+ if (sigmoidal_map == (MagickRealType *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ (void) ResetMagickMemory(sigmoidal_map,0,(MaxMap+1)*sizeof(*sigmoidal_map));
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i <= (long) MaxMap; i++)
+ {
+ if (sharpen != MagickFalse)
+ {
+ sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ (MaxMap*((1.0/(1.0+exp(contrast*(midpoint/(double) QuantumRange-
+ (double) i/MaxMap))))-(1.0/(1.0+exp(contrast*(midpoint/
+ (double) QuantumRange)))))/((1.0/(1.0+exp(contrast*(midpoint/
+ (double) QuantumRange-1.0))))-(1.0/(1.0+exp(contrast*(midpoint/
+ (double) QuantumRange)))))+0.5));
+ continue;
+ }
+ sigmoidal_map[i]=(MagickRealType) ScaleMapToQuantum((MagickRealType)
+ (MaxMap*(QuantumScale*midpoint-log((1.0-(1.0/(1.0+exp(midpoint/
+ (double) QuantumRange*contrast))+((double) i/MaxMap)*((1.0/
+ (1.0+exp(contrast*(midpoint/(double) QuantumRange-1.0))))-(1.0/
+ (1.0+exp(midpoint/(double) QuantumRange*contrast))))))/
+ (1.0/(1.0+exp(midpoint/(double) QuantumRange*contrast))+
+ ((double) i/MaxMap)*((1.0/(1.0+exp(contrast*(midpoint/
+ (double) QuantumRange-1.0))))-(1.0/(1.0+exp(midpoint/
+ (double) QuantumRange*contrast))))))/contrast)));
+ }
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Sigmoidal-contrast enhance colormap.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ image->colormap[i].red=RoundToQuantum(sigmoidal_map[
+ ScaleQuantumToMap(image->colormap[i].red)]);
+ if ((channel & GreenChannel) != 0)
+ image->colormap[i].green=RoundToQuantum(sigmoidal_map[
+ ScaleQuantumToMap(image->colormap[i].green)]);
+ if ((channel & BlueChannel) != 0)
+ image->colormap[i].blue=RoundToQuantum(sigmoidal_map[
+ ScaleQuantumToMap(image->colormap[i].blue)]);
+ if ((channel & OpacityChannel) != 0)
+ image->colormap[i].opacity=RoundToQuantum(sigmoidal_map[
+ ScaleQuantumToMap(image->colormap[i].opacity)]);
+ }
+ }
+ /*
+ Sigmoidal-contrast enhance image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->red)]);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->green)]);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->blue)]);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(sigmoidal_map[ScaleQuantumToMap(q->opacity)]);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=(IndexPacket) RoundToQuantum(sigmoidal_map[
+ ScaleQuantumToMap(indexes[x])]);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SigmoidalContrastImageChannel)
+#endif
+ proceed=SetImageProgress(image,SigmoidalContrastImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ sigmoidal_map=(MagickRealType *) RelinquishMagickMemory(sigmoidal_map);
+ return(status);
+}
diff --git a/magick/enhance.h b/magick/enhance.h
new file mode 100644
index 0000000..a2ca92e
--- /dev/null
+++ b/magick/enhance.h
@@ -0,0 +1,67 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image enhance methods.
+*/
+#ifndef _MAGICKCORE_ENHANCE_H
+#define _MAGICKCORE_ENHANCE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ AutoGammaImage(Image *),
+ AutoGammaImageChannel(Image *,const ChannelType),
+ AutoLevelImage(Image *),
+ AutoLevelImageChannel(Image *,const ChannelType),
+ ClutImage(Image *,const Image *),
+ ClutImageChannel(Image *,const ChannelType,const Image *),
+ ColorDecisionListImage(Image *,const char *),
+ ContrastImage(Image *,const MagickBooleanType),
+ ContrastStretchImage(Image *,const char *),
+ ContrastStretchImageChannel(Image *,const ChannelType,const double,
+ const double),
+ EqualizeImage(Image *image),
+ EqualizeImageChannel(Image *image,const ChannelType),
+ GammaImage(Image *,const char *),
+ GammaImageChannel(Image *,const ChannelType,const double),
+ HaldClutImage(Image *,const Image *),
+ HaldClutImageChannel(Image *,const ChannelType,const Image *),
+ LevelImage(Image *,const char *),
+ LevelImageChannel(Image *,const ChannelType,const double,const double,
+ const double),
+ LevelizeImageChannel(Image *,const ChannelType,const double,const double,
+ const double),
+ LevelImageColors(Image *,const ChannelType,const MagickPixelPacket *,
+ const MagickPixelPacket *, const MagickBooleanType),
+ LinearStretchImage(Image *,const double,const double),
+ ModulateImage(Image *,const char *),
+ NegateImage(Image *,const MagickBooleanType),
+ NegateImageChannel(Image *,const ChannelType,const MagickBooleanType),
+ NormalizeImage(Image *),
+ NormalizeImageChannel(Image *,const ChannelType),
+ SigmoidalContrastImage(Image *,const MagickBooleanType,const char *),
+ SigmoidalContrastImageChannel(Image *,const ChannelType,
+ const MagickBooleanType,const double,const double);
+
+extern MagickExport Image
+ *EnhanceImage(const Image *,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/exception-private.h b/magick/exception-private.h
new file mode 100644
index 0000000..5b64870
--- /dev/null
+++ b/magick/exception-private.h
@@ -0,0 +1,84 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore exception private methods.
+*/
+#ifndef _MAGICKCORE_EXCEPTION_PRIVATE_H
+#define _MAGICKCORE_EXCEPTION_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/log.h"
+
+#define ThrowBinaryException(severity,tag,context) \
+{ \
+ if (image != (Image *) NULL) \
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s'",context); \
+ return(MagickFalse); \
+}
+#define ThrowFatalException(severity,tag) \
+{ \
+ ExceptionInfo \
+ exception; \
+ \
+ GetExceptionInfo(&exception); \
+ (void) ThrowMagickException(&exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s'",strerror(errno)); \
+ CatchException(&exception); \
+ (void) DestroyExceptionInfo(&exception); \
+ _exit(1); \
+}
+#define ThrowFileException(exception,severity,tag,context) \
+{ \
+ (void) ThrowMagickException(exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context, \
+ strerror(errno)); \
+}
+#define ThrowImageException(severity,tag) \
+{ \
+ (void) ThrowMagickException(exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s'",image->filename); \
+ return((Image *) NULL); \
+}
+#define ThrowReaderException(severity,tag) \
+{ \
+ (void) ThrowMagickException(exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s'",image_info->filename); \
+ if ((image) != (Image *) NULL) \
+ { \
+ (void) CloseBlob(image); \
+ image=DestroyImageList(image); \
+ } \
+ return((Image *) NULL); \
+}
+#define ThrowWriterException(severity,tag) \
+{ \
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s'",image->filename); \
+ if (image_info->adjoin != MagickFalse) \
+ while (image->previous != (Image *) NULL) \
+ image=image->previous; \
+ (void) CloseBlob(image); \
+ return(MagickFalse); \
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/exception.c b/magick/exception.c
new file mode 100644
index 0000000..a3e7a22
--- /dev/null
+++ b/magick/exception.c
@@ -0,0 +1,990 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% EEEEE X X CCCC EEEEE PPPP TTTTT IIIII OOO N N %
+% E X X C E P P T I O O NN N %
+% EEE X C EEE PPPP T I O O N N N %
+% E X X C E P T I O O N NN %
+% EEEEE X X CCCC EEEEE P T IIIII OOO N N %
+% %
+% %
+% MagickCore Exception Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/client.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+ Forward declarations.
+*/
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static void
+ DefaultErrorHandler(const ExceptionType,const char *,const char *),
+ DefaultFatalErrorHandler(const ExceptionType,const char *,const char *),
+ DefaultWarningHandler(const ExceptionType,const char *,const char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+/*
+ Global declarations.
+*/
+static ErrorHandler
+ error_handler = DefaultErrorHandler;
+
+static FatalErrorHandler
+ fatal_error_handler = DefaultFatalErrorHandler;
+
+static WarningHandler
+ warning_handler = DefaultWarningHandler;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e E x c e p t i o n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireExceptionInfo() allocates the ExceptionInfo structure.
+%
+% The format of the AcquireExceptionInfo method is:
+%
+% ExceptionInfo *AcquireExceptionInfo(void)
+%
+*/
+MagickExport ExceptionInfo *AcquireExceptionInfo(void)
+{
+ ExceptionInfo
+ *exception;
+
+ exception=(ExceptionInfo *) AcquireMagickMemory(sizeof(*exception));
+ if (exception == (ExceptionInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetExceptionInfo(exception);
+ exception->relinquish=MagickTrue;
+ return(exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l e a r M a g i c k E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClearMagickException() clears any exception that may not have been caught
+% yet.
+%
+% The format of the ClearMagickException method is:
+%
+% ClearMagickException(ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+*/
+
+static void *DestroyExceptionElement(void *exception)
+{
+ register ExceptionInfo
+ *p;
+
+ p=(ExceptionInfo *) exception;
+ if (p->reason != (char *) NULL)
+ p->reason=DestroyString(p->reason);
+ if (p->description != (char *) NULL)
+ p->description=DestroyString(p->description);
+ p=(ExceptionInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void ClearMagickException(ExceptionInfo *exception)
+{
+ register ExceptionInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (exception->exceptions == (void *) NULL)
+ return;
+ AcquireSemaphoreInfo(&exception->semaphore);
+ p=(ExceptionInfo *) RemoveLastElementFromLinkedList((LinkedListInfo *)
+ exception->exceptions);
+ while (p != (ExceptionInfo *) NULL)
+ {
+ (void) DestroyExceptionElement(p);
+ p=(ExceptionInfo *) RemoveLastElementFromLinkedList((LinkedListInfo *)
+ exception->exceptions);
+ }
+ exception->severity=UndefinedException;
+ exception->reason=(char *) NULL;
+ exception->description=(char *) NULL;
+ RelinquishSemaphoreInfo(exception->semaphore);
+ errno=0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C a t c h E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CatchException() returns if no exceptions is found otherwise it reports
+% the exception as a warning, error, or fatal depending on the severity.
+%
+% The format of the CatchException method is:
+%
+% CatchException(ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+*/
+MagickExport void CatchException(ExceptionInfo *exception)
+{
+ register const ExceptionInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (exception->exceptions == (void *) NULL)
+ return;
+ AcquireSemaphoreInfo(&exception->semaphore);
+ ResetLinkedListIterator((LinkedListInfo *) exception->exceptions);
+ p=(const ExceptionInfo *) GetNextValueInLinkedList((LinkedListInfo *)
+ exception->exceptions);
+ while (p != (const ExceptionInfo *) NULL)
+ {
+ if ((p->severity >= WarningException) && (p->severity < ErrorException))
+ MagickWarning(p->severity,p->reason,p->description);
+ if ((p->severity >= ErrorException) && (p->severity < FatalErrorException))
+ MagickError(p->severity,p->reason,p->description);
+ if (exception->severity >= FatalErrorException)
+ MagickFatalError(p->severity,p->reason,p->description);
+ p=(const ExceptionInfo *) GetNextValueInLinkedList((LinkedListInfo *)
+ exception->exceptions);
+ }
+ RelinquishSemaphoreInfo(exception->semaphore);
+ ClearMagickException(exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f a u l t E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefaultErrorHandler() displays an error reason.
+%
+% The format of the DefaultErrorHandler method is:
+%
+% void MagickError(const ExceptionType severity,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o severity: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+static void DefaultErrorHandler(const ExceptionType magick_unused(severity),
+ const char *reason,const char *description)
+{
+ if (reason == (char *) NULL)
+ return;
+ (void) fprintf(stderr,"%s: %s",GetClientName(),reason);
+ if (description != (char *) NULL)
+ (void) fprintf(stderr," (%s)",description);
+ (void) fprintf(stderr,".\n");
+ (void) fflush(stderr);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f a u l t F a t a l E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefaultFatalErrorHandler() displays an error reason and then terminates the
+% program.
+%
+% The format of the DefaultFatalErrorHandler method is:
+%
+% void MagickFatalError(const ExceptionType severity,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o severity: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+static void DefaultFatalErrorHandler(
+ const ExceptionType magick_unused(severity),
+ const char *reason,const char *description)
+{
+ if (reason == (char *) NULL)
+ return;
+ (void) fprintf(stderr,"%s: %s",GetClientName(),reason);
+ if (description != (char *) NULL)
+ (void) fprintf(stderr," (%s)",description);
+ (void) fprintf(stderr,".\n");
+ (void) fflush(stderr);
+ MagickCoreTerminus();
+ exit(1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f a u l t W a r n i n g H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefaultWarningHandler() displays a warning reason.
+%
+% The format of the DefaultWarningHandler method is:
+%
+% void DefaultWarningHandler(const ExceptionType warning,
+% const char *reason,const char *description)
+%
+% A description of each parameter follows:
+%
+% o warning: Specifies the numeric warning category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+static void DefaultWarningHandler(const ExceptionType magick_unused(severity),
+ const char *reason,const char *description)
+{
+ if (reason == (char *) NULL)
+ return;
+ (void) fprintf(stderr,"%s: %s",GetClientName(),reason);
+ if (description != (char *) NULL)
+ (void) fprintf(stderr," (%s)",description);
+ (void) fprintf(stderr,".\n");
+ (void) fflush(stderr);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y E x c e p t i o n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyExceptionInfo() deallocates memory associated with an exception.
+%
+% The format of the DestroyExceptionInfo method is:
+%
+% ExceptionInfo *DestroyExceptionInfo(ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+*/
+MagickExport ExceptionInfo *DestroyExceptionInfo(ExceptionInfo *exception)
+{
+ MagickBooleanType
+ relinquish;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ AcquireSemaphoreInfo(&exception->semaphore);
+ exception->severity=UndefinedException;
+ if (exception->exceptions != (void *) NULL)
+ exception->exceptions=(void *) DestroyLinkedList((LinkedListInfo *)
+ exception->exceptions,DestroyExceptionElement);
+ relinquish=exception->relinquish;
+ if (exception->relinquish != MagickFalse)
+ exception->signature=(~MagickSignature);
+ RelinquishSemaphoreInfo(exception->semaphore);
+ DestroySemaphoreInfo(&exception->semaphore);
+ if (relinquish != MagickFalse)
+ exception=(ExceptionInfo *) RelinquishMagickMemory(exception);
+ return(exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t E x c e p t i o n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetExceptionInfo() initializes an exception to default values.
+%
+% The format of the GetExceptionInfo method is:
+%
+% GetExceptionInfo(ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+*/
+MagickExport void GetExceptionInfo(ExceptionInfo *exception)
+{
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) ResetMagickMemory(exception,0,sizeof(*exception));
+ exception->severity=UndefinedException;
+ exception->exceptions=(void *) NewLinkedList(0);
+ exception->signature=MagickSignature;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t E x c e p t i o n M e s s a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetExceptionMessage() returns the error message defined by the specified
+% error code.
+%
+% The format of the GetExceptionMessage method is:
+%
+% char *GetExceptionMessage(const int error)
+%
+% A description of each parameter follows:
+%
+% o error: the error code.
+%
+*/
+MagickExport char *GetExceptionMessage(const int error)
+{
+ char
+ exception[MaxTextExtent];
+
+#if defined(MAGICKCORE_HAVE_STRERROR_R)
+ (void) strerror_r(error,exception,sizeof(exception));
+#else
+ (void) CopyMagickString(exception,strerror(error),sizeof(exception));
+#endif
+ return(ConstantString(exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e E x c e p t i o n M e s s a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleExceptionMessage() converts a enumerated exception severity and tag
+% to a message in the current locale.
+%
+% The format of the GetLocaleExceptionMessage method is:
+%
+% const char *GetLocaleExceptionMessage(const ExceptionType severity,
+% const char *tag)
+%
+% A description of each parameter follows:
+%
+% o severity: the severity of the exception.
+%
+% o tag: the message tag.
+%
+*/
+
+static const char *ExceptionSeverityToTag(const ExceptionType severity)
+{
+ switch (severity)
+ {
+ case ResourceLimitWarning: return("Resource/Limit/Warning/");
+ case TypeWarning: return("Type/Warning/");
+ case OptionWarning: return("Option/Warning/");
+ case DelegateWarning: return("Delegate/Warning/");
+ case MissingDelegateWarning: return("Missing/Delegate/Warning/");
+ case CorruptImageWarning: return("Corrupt/Image/Warning/");
+ case FileOpenWarning: return("File/Open/Warning/");
+ case BlobWarning: return("Blob/Warning/");
+ case StreamWarning: return("Stream/Warning/");
+ case CacheWarning: return("Cache/Warning/");
+ case CoderWarning: return("Coder/Warning/");
+ case ModuleWarning: return("Module/Warning/");
+ case DrawWarning: return("Draw/Warning/");
+ case ImageWarning: return("Image/Warning/");
+ case WandWarning: return("Wand/Warning/");
+ case XServerWarning: return("XServer/Warning/");
+ case MonitorWarning: return("Monitor/Warning/");
+ case RegistryWarning: return("Registry/Warning/");
+ case ConfigureWarning: return("Configure/Warning/");
+ case PolicyWarning: return("Policy/Warning/");
+ case ResourceLimitError: return("Resource/Limit/Error/");
+ case TypeError: return("Type/Error/");
+ case OptionError: return("Option/Error/");
+ case DelegateError: return("Delegate/Error/");
+ case MissingDelegateError: return("Missing/Delegate/Error/");
+ case CorruptImageError: return("Corrupt/Image/Error/");
+ case FileOpenError: return("File/Open/Error/");
+ case BlobError: return("Blob/Error/");
+ case StreamError: return("Stream/Error/");
+ case CacheError: return("Cache/Error/");
+ case CoderError: return("Coder/Error/");
+ case ModuleError: return("Module/Error/");
+ case DrawError: return("Draw/Error/");
+ case ImageError: return("Image/Error/");
+ case WandError: return("Wand/Error/");
+ case XServerError: return("XServer/Error/");
+ case MonitorError: return("Monitor/Error/");
+ case RegistryError: return("Registry/Error/");
+ case ConfigureError: return("Configure/Error/");
+ case PolicyError: return("Policy/Error/");
+ case ResourceLimitFatalError: return("Resource/Limit/FatalError/");
+ case TypeFatalError: return("Type/FatalError/");
+ case OptionFatalError: return("Option/FatalError/");
+ case DelegateFatalError: return("Delegate/FatalError/");
+ case MissingDelegateFatalError: return("Missing/Delegate/FatalError/");
+ case CorruptImageFatalError: return("Corrupt/Image/FatalError/");
+ case FileOpenFatalError: return("File/Open/FatalError/");
+ case BlobFatalError: return("Blob/FatalError/");
+ case StreamFatalError: return("Stream/FatalError/");
+ case CacheFatalError: return("Cache/FatalError/");
+ case CoderFatalError: return("Coder/FatalError/");
+ case ModuleFatalError: return("Module/FatalError/");
+ case DrawFatalError: return("Draw/FatalError/");
+ case ImageFatalError: return("Image/FatalError/");
+ case WandFatalError: return("Wand/FatalError/");
+ case XServerFatalError: return("XServer/FatalError/");
+ case MonitorFatalError: return("Monitor/FatalError/");
+ case RegistryFatalError: return("Registry/FatalError/");
+ case ConfigureFatalError: return("Configure/FatalError/");
+ case PolicyFatalError: return("Policy/FatalError/");
+ default: break;
+ }
+ return("");
+}
+
+MagickExport const char *GetLocaleExceptionMessage(const ExceptionType severity,
+ const char *tag)
+{
+ char
+ message[MaxTextExtent];
+
+ const char
+ *locale_message;
+
+ assert(tag != (const char *) NULL);
+ (void) FormatMagickString(message,MaxTextExtent,"Exception/%s%s",
+ ExceptionSeverityToTag(severity),tag);
+ locale_message=GetLocaleMessage(message);
+ if (locale_message == (const char *) NULL)
+ return(tag);
+ if (locale_message == message)
+ return(tag);
+ return(locale_message);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n h e r i t E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InheritException() inherits an exception from a related exception.
+%
+% The format of the InheritException method is:
+%
+% InheritException(ExceptionInfo *exception,const ExceptionInfo *relative)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+% o relative: the related exception info.
+%
+*/
+MagickExport void InheritException(ExceptionInfo *exception,
+ const ExceptionInfo *relative)
+{
+ register const ExceptionInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert(relative != (ExceptionInfo *) NULL);
+ assert(relative->signature == MagickSignature);
+ if (relative->exceptions == (void *) NULL)
+ return;
+ AcquireSemaphoreInfo(&exception->semaphore);
+ ResetLinkedListIterator((LinkedListInfo *) relative->exceptions);
+ p=(const ExceptionInfo *) GetNextValueInLinkedList((LinkedListInfo *)
+ relative->exceptions);
+ while (p != (const ExceptionInfo *) NULL)
+ {
+ (void) ThrowException(exception,p->severity,p->reason,p->description);
+ p=(const ExceptionInfo *) GetNextValueInLinkedList((LinkedListInfo *)
+ relative->exceptions);
+ }
+ RelinquishSemaphoreInfo(exception->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickError() calls the exception handler methods with an error reason.
+%
+% The format of the MagickError method is:
+%
+% void MagickError(const ExceptionType error,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o exception: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+MagickExport void MagickError(const ExceptionType error,const char *reason,
+ const char *description)
+{
+ if (error_handler != (ErrorHandler) NULL)
+ (*error_handler)(error,reason,description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k F a t al E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickFatalError() calls the fatal exception handler methods with an error
+% reason.
+%
+% The format of the MagickError method is:
+%
+% void MagickFatalError(const ExceptionType error,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o exception: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+MagickExport void MagickFatalError(const ExceptionType error,const char *reason,
+ const char *description)
+{
+ if (fatal_error_handler != (ErrorHandler) NULL)
+ (*fatal_error_handler)(error,reason,description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k W a r n i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickWarning() calls the warning handler methods with a warning reason.
+%
+% The format of the MagickWarning method is:
+%
+% void MagickWarning(const ExceptionType warning,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o warning: the warning severity.
+%
+% o reason: Define the reason for the warning.
+%
+% o description: Describe the warning.
+%
+*/
+MagickExport void MagickWarning(const ExceptionType warning,const char *reason,
+ const char *description)
+{
+ if (warning_handler != (WarningHandler) NULL)
+ (*warning_handler)(warning,reason,description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetErrorHandler() sets the exception handler to the specified method
+% and returns the previous exception handler.
+%
+% The format of the SetErrorHandler method is:
+%
+% ErrorHandler SetErrorHandler(ErrorHandler handler)
+%
+% A description of each parameter follows:
+%
+% o handler: the method to handle errors.
+%
+*/
+MagickExport ErrorHandler SetErrorHandler(ErrorHandler handler)
+{
+ ErrorHandler
+ previous_handler;
+
+ previous_handler=error_handler;
+ error_handler=handler;
+ return(previous_handler);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t F a t a l E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetFatalErrorHandler() sets the fatal exception handler to the specified
+% method and returns the previous fatal exception handler.
+%
+% The format of the SetErrorHandler method is:
+%
+% ErrorHandler SetErrorHandler(ErrorHandler handler)
+%
+% A description of each parameter follows:
+%
+% o handler: the method to handle errors.
+%
+*/
+MagickExport FatalErrorHandler SetFatalErrorHandler(FatalErrorHandler handler)
+{
+ FatalErrorHandler
+ previous_handler;
+
+ previous_handler=fatal_error_handler;
+ fatal_error_handler=handler;
+ return(previous_handler);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t W a r n i n g H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetWarningHandler() sets the warning handler to the specified method
+% and returns the previous warning handler.
+%
+% The format of the SetWarningHandler method is:
+%
+% ErrorHandler SetWarningHandler(ErrorHandler handler)
+%
+% A description of each parameter follows:
+%
+% o handler: the method to handle warnings.
+%
+*/
+MagickExport WarningHandler SetWarningHandler(WarningHandler handler)
+{
+ WarningHandler
+ previous_handler;
+
+ previous_handler=warning_handler;
+ warning_handler=handler;
+ return(previous_handler);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T h r o w E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ThrowException() throws an exception with the specified severity code,
+% reason, and optional description.
+%
+% The format of the ThrowException method is:
+%
+% MagickBooleanType ThrowException(ExceptionInfo *exception,
+% const ExceptionType severity,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+% o severity: the severity of the exception.
+%
+% o reason: the reason for the exception.
+%
+% o description: the exception description.
+%
+*/
+MagickExport MagickBooleanType ThrowException(ExceptionInfo *exception,
+ const ExceptionType severity,const char *reason,const char *description)
+{
+ register ExceptionInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ p=(ExceptionInfo *) GetLastValueInLinkedList((LinkedListInfo *)
+ exception->exceptions);
+ if ((p != (ExceptionInfo *) NULL) && (p->severity == severity) &&
+ (LocaleCompare(exception->reason,reason) == 0) &&
+ (LocaleCompare(exception->description,description) == 0))
+ return(MagickTrue);
+ p=(ExceptionInfo *) AcquireMagickMemory(sizeof(*p));
+ if (p == (ExceptionInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(p,0,sizeof(*p));
+ p->severity=severity;
+ if (reason != (const char *) NULL)
+ p->reason=ConstantString(reason);
+ if (description != (const char *) NULL)
+ p->description=ConstantString(description);
+ p->signature=MagickSignature;
+ (void) AppendValueToLinkedList((LinkedListInfo *) exception->exceptions,p);
+ exception->severity=p->severity;
+ exception->reason=p->reason;
+ exception->description=p->description;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T h r o w M a g i c k E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ThrowMagickException logs an exception as determined by the log configuration
+% file. If an error occurs, MagickFalse is returned otherwise MagickTrue.
+%
+% The format of the ThrowMagickException method is:
+%
+% MagickBooleanType ThrowFileException(ExceptionInfo *exception,
+% const char *module,const char *function,const unsigned long line,
+% const ExceptionType severity,const char *tag,const char *format,...)
+%
+% A description of each parameter follows:
+%
+% o exception: the exception info.
+%
+% o filename: the source module filename.
+%
+% o function: the function name.
+%
+% o line: the line number of the source module.
+%
+% o severity: Specifies the numeric error category.
+%
+% o tag: the locale tag.
+%
+% o format: the output format.
+%
+*/
+
+MagickExport MagickBooleanType ThrowMagickExceptionList(
+ ExceptionInfo *exception,const char *module,const char *function,
+ const unsigned long line,const ExceptionType severity,const char *tag,
+ const char *format,va_list operands)
+{
+ char
+ message[MaxTextExtent],
+ path[MaxTextExtent],
+ reason[MaxTextExtent];
+
+ const char
+ *locale;
+
+ int
+ n;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ length;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ locale=GetLocaleExceptionMessage(severity,tag);
+ (void) CopyMagickString(reason,locale,MaxTextExtent);
+ (void) ConcatenateMagickString(reason," ",MaxTextExtent);
+ length=strlen(reason);
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(reason+length,MaxTextExtent-length,format,operands);
+#else
+ n=vsprintf(reason+length,format,operands);
+#endif
+ if (n < 0)
+ reason[MaxTextExtent-1]='\0';
+ status=LogMagickEvent(ExceptionEvent,module,function,line,"%s",reason);
+ GetPathComponent(module,TailPath,path);
+ (void) FormatMagickString(message,MaxTextExtent,"%s @ %s/%s/%ld",reason,path,
+ function,line);
+ (void) ThrowException(exception,severity,message,(char *) NULL);
+ return(status);
+}
+
+MagickExport MagickBooleanType ThrowMagickException(ExceptionInfo *exception,
+ const char *module,const char *function,const unsigned long line,
+ const ExceptionType severity,const char *tag,const char *format,...)
+{
+ MagickBooleanType
+ status;
+
+ va_list
+ operands;
+
+ va_start(operands,format);
+ status=ThrowMagickExceptionList(exception,module,function,line,severity,tag,
+ format,operands);
+ va_end(operands);
+ return(status);
+}
diff --git a/magick/exception.h b/magick/exception.h
new file mode 100644
index 0000000..f5a3759
--- /dev/null
+++ b/magick/exception.h
@@ -0,0 +1,175 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore exception methods.
+*/
+#ifndef _MAGICKCORE_EXCEPTION_H
+#define _MAGICKCORE_EXCEPTION_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include "magick/semaphore.h"
+
+typedef enum
+{
+ UndefinedException,
+ WarningException = 300,
+ ResourceLimitWarning = 300,
+ TypeWarning = 305,
+ OptionWarning = 310,
+ DelegateWarning = 315,
+ MissingDelegateWarning = 320,
+ CorruptImageWarning = 325,
+ FileOpenWarning = 330,
+ BlobWarning = 335,
+ StreamWarning = 340,
+ CacheWarning = 345,
+ CoderWarning = 350,
+ ModuleWarning = 355,
+ DrawWarning = 360,
+ ImageWarning = 365,
+ WandWarning = 370,
+ RandomWarning = 375,
+ XServerWarning = 380,
+ MonitorWarning = 385,
+ RegistryWarning = 390,
+ ConfigureWarning = 395,
+ PolicyWarning = 399,
+ ErrorException = 400,
+ ResourceLimitError = 400,
+ TypeError = 405,
+ OptionError = 410,
+ DelegateError = 415,
+ MissingDelegateError = 420,
+ CorruptImageError = 425,
+ FileOpenError = 430,
+ BlobError = 435,
+ StreamError = 440,
+ CacheError = 445,
+ CoderError = 450,
+ ModuleError = 455,
+ DrawError = 460,
+ ImageError = 465,
+ WandError = 470,
+ RandomError = 475,
+ XServerError = 480,
+ MonitorError = 485,
+ RegistryError = 490,
+ ConfigureError = 495,
+ PolicyError = 499,
+ FatalErrorException = 700,
+ ResourceLimitFatalError = 700,
+ TypeFatalError = 705,
+ OptionFatalError = 710,
+ DelegateFatalError = 715,
+ MissingDelegateFatalError = 720,
+ CorruptImageFatalError = 725,
+ FileOpenFatalError = 730,
+ BlobFatalError = 735,
+ StreamFatalError = 740,
+ CacheFatalError = 745,
+ CoderFatalError = 750,
+ ModuleFatalError = 755,
+ DrawFatalError = 760,
+ ImageFatalError = 765,
+ WandFatalError = 770,
+ RandomFatalError = 775,
+ XServerFatalError = 780,
+ MonitorFatalError = 785,
+ RegistryFatalError = 790,
+ ConfigureFatalError = 795,
+ PolicyFatalError = 799
+} ExceptionType;
+
+struct _ExceptionInfo
+{
+ ExceptionType
+ severity;
+
+ int
+ error_number;
+
+ char
+ *reason,
+ *description;
+
+ void
+ *exceptions;
+
+ MagickBooleanType
+ relinquish;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+typedef void
+ (*ErrorHandler)(const ExceptionType,const char *,const char *);
+
+typedef void
+ (*FatalErrorHandler)(const ExceptionType,const char *,const char *);
+
+typedef void
+ (*WarningHandler)(const ExceptionType,const char *,const char *);
+
+extern MagickExport char
+ *GetExceptionMessage(const int);
+
+extern MagickExport const char
+ *GetLocaleExceptionMessage(const ExceptionType,const char *);
+
+extern MagickExport ErrorHandler
+ SetErrorHandler(ErrorHandler);
+
+extern MagickExport ExceptionInfo
+ *AcquireExceptionInfo(void),
+ *DestroyExceptionInfo(ExceptionInfo *);
+
+extern MagickExport FatalErrorHandler
+ SetFatalErrorHandler(FatalErrorHandler);
+
+extern MagickExport MagickBooleanType
+ ThrowException(ExceptionInfo *,const ExceptionType,const char *,
+ const char *),
+ ThrowMagickException(ExceptionInfo *,const char *,const char *,
+ const unsigned long,const ExceptionType,const char *,const char *,...)
+ magick_attribute((format (printf,7,8))),
+ ThrowMagickExceptionList(ExceptionInfo *,const char *,const char *,
+ const unsigned long,const ExceptionType,const char *,const char *,va_list)
+ magick_attribute((format (printf,7,0)));
+
+extern MagickExport void
+ CatchException(ExceptionInfo *),
+ ClearMagickException(ExceptionInfo *),
+ GetExceptionInfo(ExceptionInfo *),
+ InheritException(ExceptionInfo *,const ExceptionInfo *),
+ MagickError(const ExceptionType,const char *,const char *),
+ MagickFatalError(const ExceptionType,const char *,const char *),
+ MagickWarning(const ExceptionType,const char *,const char *);
+
+extern MagickExport WarningHandler
+ SetWarningHandler(WarningHandler);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/fourier.c b/magick/fourier.c
new file mode 100644
index 0000000..bbb7996
--- /dev/null
+++ b/magick/fourier.c
@@ -0,0 +1,1261 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% FFFFF OOO U U RRRR IIIII EEEEE RRRR %
+% F O O U U R R I E R R %
+% FFF O O U U RRRR I EEE RRRR %
+% F O O U U R R I E R R %
+% F OOO UUU R R IIIII EEEEE R R %
+% %
+% %
+% MagickCore Discrete Fourier Transform Methods %
+% %
+% Software Design %
+% Sean Burke %
+% Fred Weinhaus %
+% John Cristy %
+% July 2009 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/fourier.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/property.h"
+#include "magick/thread-private.h"
+#if defined(MAGICKCORE_FFTW_DELEGATE)
+#include <complex.h>
+#include <fftw3.h>
+#endif
+
+/*
+ Typedef declarations.
+*/
+typedef struct _FourierInfo
+{
+ ChannelType
+ channel;
+
+ MagickBooleanType
+ modulus;
+
+ unsigned long
+ width,
+ height;
+
+ long
+ center;
+} FourierInfo;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r w a r d F o u r i e r T r a n s f o r m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ForwardFourierTransformImage() implements the discrete Fourier transform
+% (DFT) of the image either as a magnitude / phase or real / imaginary image
+% pair.
+%
+% The format of the ForwadFourierTransformImage method is:
+%
+% Image *ForwardFourierTransformImage(const Image *image,
+% const MagickBooleanType modulus,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o modulus: if true, return as transform as a magnitude / phase pair
+% otherwise a real / imaginary image pair.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(MAGICKCORE_FFTW_DELEGATE)
+
+static MagickBooleanType RollFourier(const unsigned long width,
+ const unsigned long height,const long x_offset,const long y_offset,
+ double *fourier)
+{
+ double
+ *roll;
+
+ long
+ u,
+ v,
+ y;
+
+ register long
+ i,
+ x;
+
+ /*
+ Move the zero frequency (DC) from (0,0) to (width/2,height/2).
+ */
+ roll=(double *) AcquireQuantumMemory((size_t) width,height*sizeof(*roll));
+ if (roll == (double *) NULL)
+ return(MagickFalse);
+ i=0L;
+ for (y=0L; y < (long) height; y++)
+ {
+ if (y_offset < 0L)
+ v=((y+y_offset) < 0L) ? y+y_offset+(long) height : y+y_offset;
+ else
+ v=((y+y_offset) > ((long) height-1L)) ? y+y_offset-(long) height :
+ y+y_offset;
+ for (x=0L; x < (long) width; x++)
+ {
+ if (x_offset < 0L)
+ u=((x+x_offset) < 0L) ? x+x_offset+(long) width : x+x_offset;
+ else
+ u=((x+x_offset) > ((long) width-1L)) ? x+x_offset-(long) width :
+ x+x_offset;
+ roll[v*width+u]=fourier[i++];
+ }
+ }
+ (void) CopyMagickMemory(fourier,roll,width*height*sizeof(*roll));
+ roll=(double *) RelinquishMagickMemory(roll);
+ return(MagickTrue);
+}
+
+static MagickBooleanType ForwardQuadrantSwap(const unsigned long width,
+ const unsigned long height,double *source,double *destination)
+{
+ long
+ center,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ x;
+
+ /*
+ Swap quadrants.
+ */
+ center=(long) floor((double) width/2L)+1L;
+ status=RollFourier((unsigned long) center,height,0L,(long) height/2L,source);
+ if (status == MagickFalse)
+ return(MagickFalse);
+ for (y=0L; y < (long) height; y++)
+ for (x=0L; x < (long) (width/2L-1L); x++)
+ destination[width*y+x+width/2L]=source[center*y+x];
+ for (y=1; y < (long) height; y++)
+ for (x=0L; x < (long) (width/2L-1L); x++)
+ destination[width*(height-y)+width/2L-x-1L]=source[center*y+x+1L];
+ for (x=0L; x < (long) (width/2L); x++)
+ destination[-x+width/2L-1L]=destination[x+width/2L+1L];
+ return(MagickTrue);
+}
+
+static void CorrectPhaseLHS(const unsigned long width,
+ const unsigned long height,double *fourier)
+{
+ long
+ y;
+
+ register long
+ x;
+
+ for (y=0L; y < (long) height; y++)
+ for (x=0L; x < (long) (width/2L); x++)
+ fourier[y*width+x]*=(-1.0);
+}
+
+static MagickBooleanType ForwardFourier(const FourierInfo *fourier_info,
+ Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
+{
+ CacheView
+ *magnitude_view,
+ *phase_view;
+
+ double
+ *magnitude_source,
+ *phase_source;
+
+ Image
+ *magnitude_image,
+ *phase_image;
+
+ long
+ i,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *q;
+
+ magnitude_image=GetFirstImageInList(image);
+ phase_image=GetNextImageInList(image);
+ if (phase_image == (Image *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ImageSequenceRequired","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ /*
+ Create "Fourier Transform" image from constituent arrays.
+ */
+ magnitude_source=(double *) AcquireQuantumMemory((size_t)
+ fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
+ if (magnitude_source == (double *) NULL)
+ return(MagickFalse);
+ (void) ResetMagickMemory(magnitude_source,0,fourier_info->width*
+ fourier_info->height*sizeof(*magnitude_source));
+ phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
+ fourier_info->width*sizeof(*phase_source));
+ if (magnitude_source == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
+ return(MagickFalse);
+ }
+ status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,
+ magnitude,magnitude_source);
+ if (status != MagickFalse)
+ status=ForwardQuadrantSwap(fourier_info->height,fourier_info->height,phase,
+ phase_source);
+ CorrectPhaseLHS(fourier_info->height,fourier_info->height,phase_source);
+ if (fourier_info->modulus != MagickFalse)
+ {
+ i=0L;
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ phase_source[i]/=(2.0*MagickPI);
+ phase_source[i]+=0.5;
+ i++;
+ }
+ }
+ magnitude_view=AcquireCacheView(magnitude_image);
+ phase_view=AcquireCacheView(phase_image);
+ i=0L;
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ q=GetCacheViewAuthenticPixels(magnitude_view,0L,y,fourier_info->height,1UL,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ q->red=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ break;
+ }
+ case GreenChannel:
+ {
+ q->green=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ break;
+ }
+ case BlueChannel:
+ {
+ q->blue=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ break;
+ }
+ case OpacityChannel:
+ {
+ q->opacity=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ break;
+ }
+ case IndexChannel:
+ {
+ indexes[x]=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ break;
+ }
+ case GrayChannels:
+ {
+ q->red=RoundToQuantum(QuantumRange*magnitude_source[i]);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ }
+ i++;
+ q++;
+ }
+ status=SyncCacheViewAuthenticPixels(magnitude_view,exception);
+ if (status == MagickFalse)
+ break;
+ }
+ i=0L;
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ q=GetCacheViewAuthenticPixels(phase_view,0L,y,fourier_info->height,1UL,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(phase_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ q->red=RoundToQuantum(QuantumRange*phase_source[i]);
+ break;
+ }
+ case GreenChannel:
+ {
+ q->green=RoundToQuantum(QuantumRange*phase_source[i]);
+ break;
+ }
+ case BlueChannel:
+ {
+ q->blue=RoundToQuantum(QuantumRange*phase_source[i]);
+ break;
+ }
+ case OpacityChannel:
+ {
+ q->opacity=RoundToQuantum(QuantumRange*phase_source[i]);
+ break;
+ }
+ case IndexChannel:
+ {
+ indexes[x]=RoundToQuantum(QuantumRange*phase_source[i]);
+ break;
+ }
+ case GrayChannels:
+ {
+ q->red=RoundToQuantum(QuantumRange*phase_source[i]);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ }
+ i++;
+ q++;
+ }
+ status=SyncCacheViewAuthenticPixels(phase_view,exception);
+ if (status == MagickFalse)
+ break;
+ }
+ phase_view=DestroyCacheView(phase_view);
+ magnitude_view=DestroyCacheView(magnitude_view);
+ phase_source=(double *) RelinquishMagickMemory(phase_source);
+ magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
+ return(status);
+}
+
+static MagickBooleanType ForwardFourierTransform(FourierInfo *fourier_info,
+ const Image *image,double *magnitude,double *phase,ExceptionInfo *exception)
+{
+ CacheView
+ *image_view;
+
+ double
+ n,
+ *source;
+
+ fftw_complex
+ *fourier;
+
+ fftw_plan
+ fftw_r2c_plan;
+
+ long
+ y;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ /*
+ Generate the forward Fourier transform.
+ */
+ source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
+ fourier_info->width*sizeof(*source));
+ if (source == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ ResetMagickMemory(source,0,fourier_info->width*fourier_info->height*
+ sizeof(*source));
+ i=0L;
+ image_view=AcquireCacheView(image);
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0L,y,fourier_info->width,1UL,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ source[i]=QuantumScale*p->red;
+ break;
+ }
+ case GreenChannel:
+ {
+ source[i]=QuantumScale*p->green;
+ break;
+ }
+ case BlueChannel:
+ {
+ source[i]=QuantumScale*p->blue;
+ break;
+ }
+ case OpacityChannel:
+ {
+ source[i]=QuantumScale*p->opacity;
+ break;
+ }
+ case IndexChannel:
+ {
+ source[i]=QuantumScale*indexes[x];
+ break;
+ }
+ case GrayChannels:
+ {
+ source[i]=QuantumScale*p->red;
+ break;
+ }
+ }
+ i++;
+ p++;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info->height,
+ fourier_info->center*sizeof(*fourier));
+ if (fourier == (fftw_complex *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ source=(double *) RelinquishMagickMemory(source);
+ return(MagickFalse);
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ForwardFourierTransform)
+#endif
+ fftw_r2c_plan=fftw_plan_dft_r2c_2d(fourier_info->width,fourier_info->width,
+ source,fourier,FFTW_ESTIMATE);
+ fftw_execute(fftw_r2c_plan);
+ fftw_destroy_plan(fftw_r2c_plan);
+ source=(double *) RelinquishMagickMemory(source);
+ /*
+ Normalize Fourier transform.
+ */
+ n=(double) fourier_info->width*(double) fourier_info->width;
+ i=0L;
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->center; x++)
+ fourier[i++]/=n;
+ /*
+ Generate magnitude and phase (or real and imaginary).
+ */
+ i=0L;
+ if (fourier_info->modulus != MagickFalse)
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->center; x++)
+ {
+ magnitude[i]=cabs(fourier[i]);
+ phase[i]=carg(fourier[i]);
+ i++;
+ }
+ else
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->center; x++)
+ {
+ magnitude[i]=creal(fourier[i]);
+ phase[i]=cimag(fourier[i]);
+ i++;
+ }
+ fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
+ return(MagickTrue);
+}
+
+static MagickBooleanType ForwardFourierTransformChannel(const Image *image,
+ const ChannelType channel,const MagickBooleanType modulus,
+ Image *fourier_image,ExceptionInfo *exception)
+{
+ double
+ *magnitude,
+ *phase;
+
+ fftw_complex
+ *fourier;
+
+ MagickBooleanType
+ status;
+
+ FourierInfo
+ fourier_info;
+
+ size_t
+ extent;
+
+ fourier_info.width=image->columns;
+ if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
+ ((image->rows % 2) != 0))
+ {
+ extent=image->columns < image->rows ? image->rows : image->columns;
+ fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
+ }
+ fourier_info.height=fourier_info.width;
+ fourier_info.center=(long) floor((double) fourier_info.width/2.0)+1L;
+ fourier_info.channel=channel;
+ fourier_info.modulus=modulus;
+ magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(*magnitude));
+ if (magnitude == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(*phase));
+ if (phase == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(MagickFalse);
+ }
+ fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(*fourier));
+ if (fourier == (fftw_complex *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ phase=(double *) RelinquishMagickMemory(phase);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(MagickFalse);
+ }
+ status=ForwardFourierTransform(&fourier_info,image,magnitude,phase,exception);
+ if (status != MagickFalse)
+ status=ForwardFourier(&fourier_info,fourier_image,magnitude,phase,
+ exception);
+ fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
+ phase=(double *) RelinquishMagickMemory(phase);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(status);
+}
+#endif
+
+MagickExport Image *ForwardFourierTransformImage(const Image *image,
+ const MagickBooleanType modulus,ExceptionInfo *exception)
+{
+ Image
+ *fourier_image;
+
+ fourier_image=NewImageList();
+#if !defined(MAGICKCORE_FFTW_DELEGATE)
+ (void) modulus;
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
+ image->filename);
+#else
+ {
+ Image
+ *magnitude_image;
+
+ unsigned long
+ extent,
+ width;
+
+ width=image->columns;
+ if ((image->columns != image->rows) || ((image->columns % 2) != 0) ||
+ ((image->rows % 2) != 0))
+ {
+ extent=image->columns < image->rows ? image->rows : image->columns;
+ width=(extent & 0x01) == 1 ? extent+1UL : extent;
+ }
+ magnitude_image=CloneImage(image,width,width,MagickFalse,exception);
+ if (magnitude_image != (Image *) NULL)
+ {
+ Image
+ *phase_image;
+
+ magnitude_image->storage_class=DirectClass;
+ magnitude_image->depth=32UL;
+ phase_image=CloneImage(image,width,width,MagickFalse,exception);
+ if (phase_image == (Image *) NULL)
+ magnitude_image=DestroyImage(magnitude_image);
+ else
+ {
+ MagickBooleanType
+ is_gray,
+ status;
+
+ register long
+ i;
+
+ phase_image->storage_class=DirectClass;
+ phase_image->depth=32UL;
+ AppendImageToList(&fourier_image,magnitude_image);
+ AppendImageToList(&fourier_image,phase_image);
+ status=MagickTrue;
+ is_gray=IsGrayImage(image,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,1) shared(status)
+#endif
+ for (i=0L; i < 5L; i++)
+ {
+ MagickBooleanType
+ thread_status;
+
+ thread_status=MagickTrue;
+ switch (i)
+ {
+ case 0:
+ {
+ if (is_gray != MagickFalse)
+ {
+ thread_status=ForwardFourierTransformChannel(image,
+ GrayChannels,modulus,fourier_image,exception);
+ break;
+ }
+ thread_status=ForwardFourierTransformChannel(image,RedChannel,
+ modulus,fourier_image,exception);
+ break;
+ }
+ case 1:
+ {
+ if (is_gray == MagickFalse)
+ thread_status=ForwardFourierTransformChannel(image,
+ GreenChannel,modulus,fourier_image,exception);
+ break;
+ }
+ case 2:
+ {
+ if (is_gray == MagickFalse)
+ thread_status=ForwardFourierTransformChannel(image,
+ BlueChannel,modulus,fourier_image,exception);
+ break;
+ }
+ case 4:
+ {
+ if (image->matte != MagickFalse)
+ thread_status=ForwardFourierTransformChannel(image,
+ OpacityChannel,modulus,fourier_image,exception);
+ break;
+ }
+ case 5:
+ {
+ if (image->colorspace == CMYKColorspace)
+ thread_status=ForwardFourierTransformChannel(image,
+ IndexChannel,modulus,fourier_image,exception);
+ break;
+ }
+ }
+ if (thread_status == MagickFalse)
+ status=thread_status;
+ }
+ if (status == MagickFalse)
+ fourier_image=DestroyImageList(fourier_image);
+ fftw_cleanup();
+ }
+ }
+ }
+#endif
+ return(fourier_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n v e r s e F o u r i e r T r a n s f o r m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InverseFourierTransformImage() implements the inverse discrete Fourier
+% transform (DFT) of the image either as a magnitude / phase or real /
+% imaginary image pair.
+%
+% The format of the InverseFourierTransformImage method is:
+%
+% Image *InverseFourierTransformImage(const Image *images,
+% const MagickBooleanType modulus,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image sequence.
+%
+% o modulus: if true, return transform as a magnitude / phase pair
+% otherwise a real / imaginary image pair.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(MAGICKCORE_FFTW_DELEGATE)
+static MagickBooleanType InverseQuadrantSwap(const unsigned long width,
+ const unsigned long height,const double *source,double *destination)
+{
+ long
+ center,
+ y;
+
+ register long
+ x;
+
+ /*
+ Swap quadrants.
+ */
+ center=(long) floor((double) width/2.0)+1L;
+ for (y=1L; y < (long) height; y++)
+ for (x=0L; x < (long) (width/2L+1L); x++)
+ destination[center*(height-y)-x+width/2L]=source[y*width+x];
+ for (y=0L; y < (long) height; y++)
+ destination[center*y]=source[y*width+width/2L];
+ for (x=0L; x < center; x++)
+ destination[x]=source[center-x-1L];
+ return(RollFourier(center,height,0L,(long) height/-2L,destination));
+}
+
+static MagickBooleanType InverseFourier(FourierInfo *fourier_info,
+ const Image *images,fftw_complex *fourier,ExceptionInfo *exception)
+{
+ CacheView
+ *magnitude_view,
+ *phase_view;
+
+ double
+ *magnitude,
+ *phase,
+ *magnitude_source,
+ *phase_source;
+
+ Image
+ *magnitude_image,
+ *phase_image;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ /*
+ Inverse fourier - read image and break down into a double array.
+ */
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ magnitude_image=GetFirstImageInList(images),
+ phase_image=GetNextImageInList(images);
+ if (phase_image == (Image *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ImageSequenceRequired","`%s'",images->filename);
+ return(MagickFalse);
+ }
+ magnitude_source=(double *) AcquireQuantumMemory((size_t)
+ fourier_info->height,fourier_info->width*sizeof(*magnitude_source));
+ if (magnitude_source == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ return(MagickFalse);
+ }
+ phase_source=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
+ fourier_info->height*sizeof(*phase_source));
+ if (phase_source == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
+ return(MagickFalse);
+ }
+ i=0L;
+ magnitude_view=AcquireCacheView(magnitude_image);
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ p=GetCacheViewVirtualPixels(magnitude_view,0L,y,fourier_info->width,1UL,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(magnitude_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ magnitude_source[i]=QuantumScale*p->red;
+ break;
+ }
+ case GreenChannel:
+ {
+ magnitude_source[i]=QuantumScale*p->green;
+ break;
+ }
+ case BlueChannel:
+ {
+ magnitude_source[i]=QuantumScale*p->blue;
+ break;
+ }
+ case OpacityChannel:
+ {
+ magnitude_source[i]=QuantumScale*p->opacity;
+ break;
+ }
+ case IndexChannel:
+ {
+ magnitude_source[i]=QuantumScale*indexes[x];
+ break;
+ }
+ case GrayChannels:
+ {
+ magnitude_source[i]=QuantumScale*p->red;
+ break;
+ }
+ }
+ i++;
+ p++;
+ }
+ }
+ i=0L;
+ phase_view=AcquireCacheView(phase_image);
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ p=GetCacheViewVirtualPixels(phase_view,0,y,fourier_info->width,1,
+ exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(phase_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ phase_source[i]=QuantumScale*p->red;
+ break;
+ }
+ case GreenChannel:
+ {
+ phase_source[i]=QuantumScale*p->green;
+ break;
+ }
+ case BlueChannel:
+ {
+ phase_source[i]=QuantumScale*p->blue;
+ break;
+ }
+ case OpacityChannel:
+ {
+ phase_source[i]=QuantumScale*p->opacity;
+ break;
+ }
+ case IndexChannel:
+ {
+ phase_source[i]=QuantumScale*indexes[x];
+ break;
+ }
+ case GrayChannels:
+ {
+ phase_source[i]=QuantumScale*p->red;
+ break;
+ }
+ }
+ i++;
+ p++;
+ }
+ }
+ if (fourier_info->modulus != MagickFalse)
+ {
+ i=0L;
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ phase_source[i]-=0.5;
+ phase_source[i]*=(2.0*MagickPI);
+ i++;
+ }
+ }
+ magnitude_view=DestroyCacheView(magnitude_view);
+ phase_view=DestroyCacheView(phase_view);
+ magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info->height,
+ fourier_info->center*sizeof(*magnitude));
+ if (magnitude == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
+ phase_source=(double *) RelinquishMagickMemory(phase_source);
+ return(MagickFalse);
+ }
+ status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
+ magnitude_source,magnitude);
+ magnitude_source=(double *) RelinquishMagickMemory(magnitude_source);
+ phase=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
+ fourier_info->height*sizeof(*phase));
+ if (phase == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ phase_source=(double *) RelinquishMagickMemory(phase_source);
+ return(MagickFalse);
+ }
+ CorrectPhaseLHS(fourier_info->width,fourier_info->width,phase_source);
+ if (status != MagickFalse)
+ status=InverseQuadrantSwap(fourier_info->width,fourier_info->height,
+ phase_source,phase);
+ phase_source=(double *) RelinquishMagickMemory(phase_source);
+ /*
+ Merge two sets.
+ */
+ i=0L;
+ if (fourier_info->modulus != MagickFalse)
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->center; x++)
+ {
+ fourier[i]=magnitude[i]*cos(phase[i])+I*magnitude[i]*sin(phase[i]);
+ i++;
+ }
+ else
+ for (y=0L; y < (long) fourier_info->height; y++)
+ for (x=0L; x < (long) fourier_info->center; x++)
+ {
+ fourier[i]=magnitude[i]+I*phase[i];
+ i++;
+ }
+ phase=(double *) RelinquishMagickMemory(phase);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(status);
+}
+
+static MagickBooleanType InverseFourierTransform(FourierInfo *fourier_info,
+ fftw_complex *fourier,Image *image,ExceptionInfo *exception)
+{
+ CacheView
+ *image_view;
+
+ double
+ *source;
+
+ fftw_plan
+ fftw_c2r_plan;
+
+ long
+ y;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ i,
+ x;
+
+ register PixelPacket
+ *q;
+
+ source=(double *) AcquireQuantumMemory((size_t) fourier_info->width,
+ fourier_info->height*sizeof(double));
+ if (source == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_InverseFourierTransform)
+#endif
+ fftw_c2r_plan=fftw_plan_dft_c2r_2d(fourier_info->width,fourier_info->height,
+ fourier,source,FFTW_ESTIMATE);
+ fftw_execute(fftw_c2r_plan);
+ fftw_destroy_plan(fftw_c2r_plan);
+ i=0L;
+ image_view=AcquireCacheView(image);
+ for (y=0L; y < (long) fourier_info->height; y++)
+ {
+ q=GetCacheViewAuthenticPixels(image_view,0L,y,fourier_info->width,1UL,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0L; x < (long) fourier_info->width; x++)
+ {
+ switch (fourier_info->channel)
+ {
+ case RedChannel:
+ default:
+ {
+ q->red=RoundToQuantum(QuantumRange*source[i]);
+ break;
+ }
+ case GreenChannel:
+ {
+ q->green=RoundToQuantum(QuantumRange*source[i]);
+ break;
+ }
+ case BlueChannel:
+ {
+ q->blue=RoundToQuantum(QuantumRange*source[i]);
+ break;
+ }
+ case OpacityChannel:
+ {
+ q->opacity=RoundToQuantum(QuantumRange*source[i]);
+ break;
+ }
+ case IndexChannel:
+ {
+ indexes[x]=RoundToQuantum(QuantumRange*source[i]);
+ break;
+ }
+ case GrayChannels:
+ {
+ q->red=RoundToQuantum(QuantumRange*source[i]);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ }
+ i++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ source=(double *) RelinquishMagickMemory(source);
+ return(MagickTrue);
+}
+
+static MagickBooleanType InverseFourierTransformChannel(const Image *images,
+ const ChannelType channel,const MagickBooleanType modulus,
+ Image *fourier_image,ExceptionInfo *exception)
+{
+ double
+ *magnitude,
+ *phase;
+
+ fftw_complex
+ *fourier;
+
+ FourierInfo
+ fourier_info;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ extent;
+
+ fourier_info.width=images->columns;
+ if ((images->columns != images->rows) || ((images->columns % 2) != 0) ||
+ ((images->rows % 2) != 0))
+ {
+ extent=images->columns < images->rows ? images->rows : images->columns;
+ fourier_info.width=(extent & 0x01) == 1 ? extent+1UL : extent;
+ }
+ fourier_info.height=fourier_info.width;
+ fourier_info.center=(long) floor((double) fourier_info.width/2.0)+1L;
+ fourier_info.channel=channel;
+ fourier_info.modulus=modulus;
+ magnitude=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(double));
+ if (magnitude == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ return(MagickFalse);
+ }
+ phase=(double *) AcquireQuantumMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(double));
+ if (phase == (double *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(MagickFalse);
+ }
+ fourier=(fftw_complex *) AcquireAlignedMemory((size_t) fourier_info.height,
+ fourier_info.center*sizeof(*fourier));
+ if (fourier == (fftw_complex *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ phase=(double *) RelinquishMagickMemory(phase);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(MagickFalse);
+ }
+ status=InverseFourier(&fourier_info,images,fourier,exception);
+ if (status != MagickFalse)
+ status=InverseFourierTransform(&fourier_info,fourier,fourier_image,
+ exception);
+ fourier=(fftw_complex *) RelinquishAlignedMemory(fourier);
+ phase=(double *) RelinquishMagickMemory(phase);
+ magnitude=(double *) RelinquishMagickMemory(magnitude);
+ return(status);
+}
+#endif
+
+MagickExport Image *InverseFourierTransformImage(const Image *images,
+ const MagickBooleanType modulus,ExceptionInfo *exception)
+{
+ Image
+ *fourier_image;
+
+#if !defined(MAGICKCORE_FFTW_DELEGATE)
+ fourier_image=(Image *) NULL;
+ (void) modulus;
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (FFTW)",
+ images->filename);
+#else
+ {
+ fourier_image=CloneImage(images,images->columns,images->rows,MagickFalse,
+ exception);
+ if (fourier_image != (Image *) NULL)
+ {
+ MagickBooleanType
+ is_gray,
+ status;
+
+ register long
+ i;
+
+ status=MagickTrue;
+ is_gray=IsGrayImage(images,exception);
+ if ((is_gray != MagickFalse) && (images->next != (Image *) NULL))
+ is_gray=IsGrayImage(images->next,exception);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,1) shared(status)
+#endif
+ for (i=0L; i < 5L; i++)
+ {
+ MagickBooleanType
+ thread_status;
+
+ thread_status=MagickTrue;
+ switch (i)
+ {
+ case 0:
+ {
+ if (is_gray != MagickFalse)
+ {
+ thread_status=InverseFourierTransformChannel(images,
+ GrayChannels,modulus,fourier_image,exception);
+ break;
+ }
+ thread_status=InverseFourierTransformChannel(images,RedChannel,
+ modulus,fourier_image,exception);
+ break;
+ }
+ case 1:
+ {
+ if (is_gray == MagickFalse)
+ thread_status=InverseFourierTransformChannel(images,
+ GreenChannel,modulus,fourier_image,exception);
+ break;
+ }
+ case 2:
+ {
+ if (is_gray == MagickFalse)
+ thread_status=InverseFourierTransformChannel(images,BlueChannel,
+ modulus,fourier_image,exception);
+ break;
+ }
+ case 3:
+ {
+ if (images->matte != MagickFalse)
+ thread_status=InverseFourierTransformChannel(images,
+ OpacityChannel,modulus,fourier_image,exception);
+ break;
+ }
+ case 4:
+ {
+ if (images->colorspace == CMYKColorspace)
+ thread_status=InverseFourierTransformChannel(images,
+ IndexChannel,modulus,fourier_image,exception);
+ break;
+ }
+ }
+ if (thread_status == MagickFalse)
+ status=thread_status;
+ }
+ if (status == MagickFalse)
+ fourier_image=DestroyImage(fourier_image);
+ }
+ fftw_cleanup();
+ }
+#endif
+ return(fourier_image);
+}
diff --git a/magick/fourier.h b/magick/fourier.h
new file mode 100644
index 0000000..783fbd7
--- /dev/null
+++ b/magick/fourier.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore discrete Fourier transform (DFT) methods.
+*/
+#ifndef _MAGICKCORE_FFT_H
+#define _MAGICKCORE_FFT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport Image
+ *ForwardFourierTransformImage(const Image *,const MagickBooleanType,
+ ExceptionInfo *),
+ *InverseFourierTransformImage(const Image *,const MagickBooleanType,
+ ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/fx-private.h b/magick/fx-private.h
new file mode 100644
index 0000000..e4a94e1
--- /dev/null
+++ b/magick/fx-private.h
@@ -0,0 +1,41 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private image f/x methods.
+*/
+#ifndef _MAGICKCORE_FX_PRIVATE_H
+#define _MAGICKCORE_FX_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _FxInfo
+ FxInfo;
+
+extern MagickExport FxInfo
+ *AcquireFxInfo(const Image *,const char *),
+ *DestroyFxInfo(FxInfo *);
+
+extern MagickExport MagickBooleanType
+ FxEvaluateExpression(FxInfo *,MagickRealType *,ExceptionInfo *),
+ FxEvaluateChannelExpression(FxInfo *,const ChannelType,const long,const long,
+ MagickRealType *,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/fx.c b/magick/fx.c
new file mode 100644
index 0000000..1dc9ce9
--- /dev/null
+++ b/magick/fx.c
@@ -0,0 +1,6219 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% FFFFF X X %
+% F X X %
+% FFF X %
+% F X X %
+% F X X %
+% %
+% %
+% MagickCore Image Special Effects Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/annotate.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/composite.h"
+#include "magick/decorate.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/fx-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/image.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-private.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/random-private.h"
+#include "magick/resample.h"
+#include "magick/resample-private.h"
+#include "magick/resize.h"
+#include "magick/shear.h"
+#include "magick/splay-tree.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+
+/*
+ Define declarations.
+*/
+#define LeftShiftOperator 0xf5
+#define RightShiftOperator 0xf6
+#define LessThanEqualOperator 0xf7
+#define GreaterThanEqualOperator 0xf8
+#define EqualOperator 0xf9
+#define NotEqualOperator 0xfa
+#define LogicalAndOperator 0xfb
+#define LogicalOrOperator 0xfc
+
+struct _FxInfo
+{
+ const Image
+ *images;
+
+ MagickBooleanType
+ matte;
+
+ char
+ *expression;
+
+ FILE
+ *file;
+
+ SplayTreeInfo
+ *colors,
+ *symbols;
+
+ ResampleFilter
+ **resample_filter;
+
+ RandomInfo
+ *random_info;
+
+ ExceptionInfo
+ *exception;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e F x I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireFxInfo() allocates the FxInfo structure.
+%
+% The format of the AcquireFxInfo method is:
+%
+% FxInfo *AcquireFxInfo(Image *image,const char *expression)
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o expression: the expression.
+%
+*/
+MagickExport FxInfo *AcquireFxInfo(const Image *image,const char *expression)
+{
+ char
+ fx_op[2];
+
+ FxInfo
+ *fx_info;
+
+ register long
+ i;
+
+ fx_info=(FxInfo *) AcquireMagickMemory(sizeof(*fx_info));
+ if (fx_info == (FxInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(fx_info,0,sizeof(*fx_info));
+ fx_info->exception=AcquireExceptionInfo();
+ fx_info->images=image;
+ fx_info->matte=image->matte;
+ fx_info->colors=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
+ RelinquishMagickMemory);
+ fx_info->symbols=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
+ RelinquishMagickMemory);
+ fx_info->resample_filter=(ResampleFilter **) AcquireQuantumMemory(
+ GetImageListLength(fx_info->images),sizeof(*fx_info->resample_filter));
+ if (fx_info->resample_filter == (ResampleFilter **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
+ {
+ fx_info->resample_filter[i]=AcquireResampleFilter(GetImageFromList(
+ fx_info->images,i),fx_info->exception);
+ SetResampleFilter(fx_info->resample_filter[i],PointFilter,1.0);
+ }
+ fx_info->random_info=AcquireRandomInfo();
+ fx_info->expression=ConstantString(expression);
+ fx_info->file=stderr;
+ (void) SubstituteString(&fx_info->expression," ",""); /* compact string */
+ if ((strstr(fx_info->expression,"e+") != (char *) NULL) ||
+ (strstr(fx_info->expression,"e-") != (char *) NULL))
+ {
+ /*
+ Convert scientific notation.
+ */
+ (void) SubstituteString(&fx_info->expression,"0e+","0*10^");
+ (void) SubstituteString(&fx_info->expression,"1e+","1*10^");
+ (void) SubstituteString(&fx_info->expression,"2e+","2*10^");
+ (void) SubstituteString(&fx_info->expression,"3e+","3*10^");
+ (void) SubstituteString(&fx_info->expression,"4e+","4*10^");
+ (void) SubstituteString(&fx_info->expression,"5e+","5*10^");
+ (void) SubstituteString(&fx_info->expression,"6e+","6*10^");
+ (void) SubstituteString(&fx_info->expression,"7e+","7*10^");
+ (void) SubstituteString(&fx_info->expression,"8e+","8*10^");
+ (void) SubstituteString(&fx_info->expression,"9e+","9*10^");
+ (void) SubstituteString(&fx_info->expression,"0e-","0*10^-");
+ (void) SubstituteString(&fx_info->expression,"1e-","1*10^-");
+ (void) SubstituteString(&fx_info->expression,"2e-","2*10^-");
+ (void) SubstituteString(&fx_info->expression,"3e-","3*10^-");
+ (void) SubstituteString(&fx_info->expression,"4e-","4*10^-");
+ (void) SubstituteString(&fx_info->expression,"5e-","5*10^-");
+ (void) SubstituteString(&fx_info->expression,"6e-","6*10^-");
+ (void) SubstituteString(&fx_info->expression,"7e-","7*10^-");
+ (void) SubstituteString(&fx_info->expression,"8e-","8*10^-");
+ (void) SubstituteString(&fx_info->expression,"9e-","9*10^-");
+ }
+ /*
+ Convert complex to simple operators.
+ */
+ fx_op[1]='\0';
+ *fx_op=(char) LeftShiftOperator;
+ (void) SubstituteString(&fx_info->expression,"<<",fx_op);
+ *fx_op=(char) RightShiftOperator;
+ (void) SubstituteString(&fx_info->expression,">>",fx_op);
+ *fx_op=(char) LessThanEqualOperator;
+ (void) SubstituteString(&fx_info->expression,"<=",fx_op);
+ *fx_op=(char) GreaterThanEqualOperator;
+ (void) SubstituteString(&fx_info->expression,">=",fx_op);
+ *fx_op=(char) EqualOperator;
+ (void) SubstituteString(&fx_info->expression,"==",fx_op);
+ *fx_op=(char) NotEqualOperator;
+ (void) SubstituteString(&fx_info->expression,"!=",fx_op);
+ *fx_op=(char) LogicalAndOperator;
+ (void) SubstituteString(&fx_info->expression,"&&",fx_op);
+ *fx_op=(char) LogicalOrOperator;
+ (void) SubstituteString(&fx_info->expression,"||",fx_op);
+ return(fx_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d d N o i s e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AddNoiseImage() adds random noise to the image.
+%
+% The format of the AddNoiseImage method is:
+%
+% Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
+% ExceptionInfo *exception)
+% Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
+% const NoiseType noise_type,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
+% Impulse, Laplacian, or Poisson.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
+ ExceptionInfo *exception)
+{
+ Image
+ *noise_image;
+
+ noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
+ return(noise_image);
+}
+
+MagickExport Image *AddNoiseImageChannel(const Image *image,
+ const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
+{
+#define AddNoiseImageTag "AddNoise/Image"
+
+ const char
+ *option;
+
+ Image
+ *noise_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ attenuate;
+
+ RandomInfo
+ **random_info;
+
+ CacheView
+ *image_view,
+ *noise_view;
+
+ /*
+ Initialize noise image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ noise_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (noise_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&noise_image->exception);
+ noise_image=DestroyImage(noise_image);
+ return((Image *) NULL);
+ }
+ /*
+ Add noise in each row.
+ */
+ attenuate=1.0;
+ option=GetImageArtifact(image,"attenuate");
+ if (option != (char *) NULL)
+ attenuate=atof(option);
+ status=MagickTrue;
+ progress=0;
+ random_info=AcquireRandomInfoThreadSet();
+ image_view=AcquireCacheView(image);
+ noise_view=AcquireCacheView(noise_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT_DEBUG)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict noise_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
+ exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
+ p->red,noise_type,attenuate));
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
+ p->green,noise_type,attenuate));
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
+ p->blue,noise_type,attenuate));
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(GenerateDifferentialNoise(random_info[id],
+ p->opacity,noise_type,attenuate));
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ noise_indexes[x]=(IndexPacket) RoundToQuantum(GenerateDifferentialNoise(
+ random_info[id],indexes[x],noise_type,attenuate));
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(noise_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AverageImages)
+#endif
+ proceed=SetImageProgress(image,AddNoiseImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ noise_view=DestroyCacheView(noise_view);
+ image_view=DestroyCacheView(image_view);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ if (status == MagickFalse)
+ noise_image=DestroyImage(noise_image);
+ return(noise_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B l u e S h i f t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BlueShiftImage() mutes the colors of the image to simulate a scene at
+% nighttime in the moonlight.
+%
+% The format of the BlueShiftImage method is:
+%
+% Image *BlueShiftImage(const Image *image,const double factor,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o factor: the shift factor.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *BlueShiftImage(const Image *image,const double factor,
+ ExceptionInfo *exception)
+{
+#define BlueShiftImageTag "BlueShift/Image"
+
+ Image
+ *shift_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view,
+ *shift_view;
+
+ /*
+ Allocate blue shift image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ shift_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (shift_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&shift_image->exception);
+ shift_image=DestroyImage(shift_image);
+ return((Image *) NULL);
+ }
+ /*
+ Blue-shift DirectClass image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ shift_view=AcquireCacheView(shift_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT_DEBUG)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ MagickPixelPacket
+ pixel;
+
+ Quantum
+ quantum;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ quantum=p->red;
+ if (p->green < quantum)
+ quantum=p->green;
+ if (p->blue < quantum)
+ quantum=p->blue;
+ pixel.red=0.5*(p->red+factor*quantum);
+ pixel.green=0.5*(p->green+factor*quantum);
+ pixel.blue=0.5*(p->blue+factor*quantum);
+ quantum=p->red;
+ if (p->green > quantum)
+ quantum=p->green;
+ if (p->blue > quantum)
+ quantum=p->blue;
+ pixel.red=0.5*(pixel.red+factor*quantum);
+ pixel.green=0.5*(pixel.green+factor*quantum);
+ pixel.blue=0.5*(pixel.blue+factor*quantum);
+ q->red=RoundToQuantum(pixel.red);
+ q->green=RoundToQuantum(pixel.green);
+ q->blue=RoundToQuantum(pixel.blue);
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(shift_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT_DEBUG)
+ #pragma omp critical (MagickCore_BlueShiftImage)
+#endif
+ proceed=SetImageProgress(image,BlueShiftImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ shift_view=DestroyCacheView(shift_view);
+ if (status == MagickFalse)
+ shift_image=DestroyImage(shift_image);
+ return(shift_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C h a r c o a l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CharcoalImage() creates a new image that is a copy of an existing one with
+% the edge highlighted. It allocates the memory necessary for the new Image
+% structure and returns a pointer to the new image.
+%
+% The format of the CharcoalImage method is:
+%
+% Image *CharcoalImage(const Image *image,const double radius,
+% const double sigma,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CharcoalImage(const Image *image,const double radius,
+ const double sigma,ExceptionInfo *exception)
+{
+ Image
+ *charcoal_image,
+ *clone_image,
+ *edge_image;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ clone_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (clone_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) SetImageType(clone_image,GrayscaleType);
+ edge_image=EdgeImage(clone_image,radius,exception);
+ clone_image=DestroyImage(clone_image);
+ if (edge_image == (Image *) NULL)
+ return((Image *) NULL);
+ charcoal_image=BlurImage(edge_image,radius,sigma,exception);
+ edge_image=DestroyImage(edge_image);
+ if (charcoal_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) NormalizeImage(charcoal_image);
+ (void) NegateImage(charcoal_image,MagickFalse);
+ (void) SetImageType(charcoal_image,GrayscaleType);
+ return(charcoal_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o l o r i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ColorizeImage() blends the fill color with each pixel in the image.
+% A percentage blend is specified with opacity. Control the application
+% of different color components by specifying a different percentage for
+% each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
+%
+% The format of the ColorizeImage method is:
+%
+% Image *ColorizeImage(const Image *image,const char *opacity,
+% const PixelPacket colorize,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o opacity: A character string indicating the level of opacity as a
+% percentage.
+%
+% o colorize: A color value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
+ const PixelPacket colorize,ExceptionInfo *exception)
+{
+#define ColorizeImageTag "Colorize/Image"
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *colorize_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ pixel;
+
+ MagickStatusType
+ flags;
+
+ CacheView
+ *colorize_view,
+ *image_view;
+
+ /*
+ Allocate colorized image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ colorize_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (colorize_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&colorize_image->exception);
+ colorize_image=DestroyImage(colorize_image);
+ return((Image *) NULL);
+ }
+ if (opacity == (const char *) NULL)
+ return(colorize_image);
+ /*
+ Determine RGB values of the pen color.
+ */
+ flags=ParseGeometry(opacity,&geometry_info);
+ pixel.red=geometry_info.rho;
+ pixel.green=geometry_info.rho;
+ pixel.blue=geometry_info.rho;
+ pixel.opacity=(MagickRealType) OpaqueOpacity;
+ if ((flags & SigmaValue) != 0)
+ pixel.green=geometry_info.sigma;
+ if ((flags & XiValue) != 0)
+ pixel.blue=geometry_info.xi;
+ if ((flags & PsiValue) != 0)
+ pixel.opacity=geometry_info.psi;
+ /*
+ Colorize DirectClass image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ colorize_view=AcquireCacheView(colorize_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT_DEBUG)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=(Quantum) ((p->red*(100.0-pixel.red)+
+ colorize.red*pixel.red)/100.0);
+ q->green=(Quantum) ((p->green*(100.0-pixel.green)+
+ colorize.green*pixel.green)/100.0);
+ q->blue=(Quantum) ((p->blue*(100.0-pixel.blue)+
+ colorize.blue*pixel.blue)/100.0);
+ q->opacity=(Quantum) ((p->opacity*(100.0-pixel.opacity)+
+ colorize.opacity*pixel.opacity)/100.0);
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT_DEBUG)
+ #pragma omp critical (MagickCore_ColorizeImage)
+#endif
+ proceed=SetImageProgress(image,ColorizeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ colorize_view=DestroyCacheView(colorize_view);
+ if (status == MagickFalse)
+ colorize_image=DestroyImage(colorize_image);
+ return(colorize_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v o l v e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvolveImage() applies a custom convolution kernel to the image.
+%
+% The format of the ConvolveImage method is:
+%
+% Image *ConvolveImage(const Image *image,const unsigned long order,
+% const double *kernel,ExceptionInfo *exception)
+% Image *ConvolveImageChannel(const Image *image,const ChannelType channel,
+% const unsigned long order,const double *kernel,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o order: the number of columns and rows in the filter kernel.
+%
+% o kernel: An array of double representing the convolution kernel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *ConvolveImage(const Image *image,const unsigned long order,
+ const double *kernel,ExceptionInfo *exception)
+{
+ Image
+ *convolve_image;
+
+ convolve_image=ConvolveImageChannel(image,DefaultChannels,order,kernel,
+ exception);
+ return(convolve_image);
+}
+
+MagickExport Image *ConvolveImageChannel(const Image *image,
+ const ChannelType channel,const unsigned long order,const double *kernel,
+ ExceptionInfo *exception)
+{
+#define ConvolveImageTag "Convolve/Image"
+
+ double
+ *normal_kernel;
+
+ Image
+ *convolve_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ bias,
+ gamma;
+
+ register long
+ i;
+
+ unsigned long
+ width;
+
+ CacheView
+ *convolve_view,
+ *image_view;
+
+ /*
+ Initialize convolve image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=order;
+ if ((width % 2) == 0)
+ ThrowImageException(OptionError,"KernelWidthMustBeAnOddNumber");
+ convolve_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (convolve_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(convolve_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&convolve_image->exception);
+ convolve_image=DestroyImage(convolve_image);
+ return((Image *) NULL);
+ }
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ *message;
+
+ long
+ u,
+ v;
+
+ register const double
+ *k;
+
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " ConvolveImage with %ldx%ld kernel:",width,width);
+ message=AcquireString("");
+ k=kernel;
+ for (v=0; v < (long) width; v++)
+ {
+ *message='\0';
+ (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
+ (void) ConcatenateString(&message,format);
+ for (u=0; u < (long) width; u++)
+ {
+ (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
+ (void) ConcatenateString(&message,format);
+ }
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
+ }
+ message=DestroyString(message);
+ }
+ /*
+ Normalize kernel.
+ */
+ normal_kernel=(double *) AcquireQuantumMemory(width*width,
+ sizeof(*normal_kernel));
+ if (normal_kernel == (double *) NULL)
+ {
+ convolve_image=DestroyImage(convolve_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ gamma=0.0;
+ for (i=0; i < (long) (width*width); i++)
+ gamma+=kernel[i];
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ for (i=0; i < (long) (width*width); i++)
+ normal_kernel[i]=gamma*kernel[i];
+ /*
+ Convolve image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ bias=image->bias;
+ image_view=AcquireCacheView(image);
+ convolve_view=AcquireCacheView(convolve_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict convolve_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
+ 2L),image->columns+width,width,exception);
+ q=GetCacheViewAuthenticPixels(convolve_view,0,y,convolve_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ convolve_indexes=GetCacheViewAuthenticIndexQueue(convolve_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ long
+ v;
+
+ MagickPixelPacket
+ pixel;
+
+ register const double
+ *__restrict k;
+
+ register const PixelPacket
+ *__restrict kernel_pixels;
+
+ register long
+ u;
+
+ pixel=zero;
+ k=normal_kernel;
+ kernel_pixels=p;
+ if (((channel & OpacityChannel) == 0) || (image->matte == MagickFalse))
+ {
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ pixel.red+=(*k)*kernel_pixels[u].red;
+ pixel.green+=(*k)*kernel_pixels[u].green;
+ pixel.blue+=(*k)*kernel_pixels[u].blue;
+ k++;
+ }
+ kernel_pixels+=image->columns+width;
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=normal_kernel;
+ kernel_pixels=p;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels[u].opacity;
+ k++;
+ }
+ kernel_pixels+=image->columns+width;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=normal_kernel;
+ kernel_indexes=indexes;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ pixel.index+=(*k)*kernel_indexes[u];
+ k++;
+ }
+ kernel_indexes+=image->columns+width;
+ }
+ convolve_indexes[x]=RoundToQuantum(pixel.index+bias);
+ }
+ }
+ else
+ {
+ MagickRealType
+ alpha,
+ gamma;
+
+ gamma=0.0;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels[u].opacity));
+ pixel.red+=(*k)*alpha*kernel_pixels[u].red;
+ pixel.green+=(*k)*alpha*kernel_pixels[u].green;
+ pixel.blue+=(*k)*alpha*kernel_pixels[u].blue;
+ pixel.opacity+=(*k)*kernel_pixels[u].opacity;
+ gamma+=(*k)*alpha;
+ k++;
+ }
+ kernel_pixels+=image->columns+width;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(gamma*pixel.red+bias);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(gamma*pixel.green+bias);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(gamma*pixel.blue+bias);
+ if ((channel & OpacityChannel) != 0)
+ {
+ k=normal_kernel;
+ kernel_pixels=p;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ pixel.opacity+=(*k)*kernel_pixels[u].opacity;
+ k++;
+ }
+ kernel_pixels+=image->columns+width;
+ }
+ q->opacity=RoundToQuantum(pixel.opacity+bias);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ register const IndexPacket
+ *__restrict kernel_indexes;
+
+ k=normal_kernel;
+ kernel_pixels=p;
+ kernel_indexes=indexes;
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-
+ kernel_pixels[u].opacity));
+ pixel.index+=(*k)*alpha*kernel_indexes[u];
+ k++;
+ }
+ kernel_pixels+=image->columns+width;
+ kernel_indexes+=image->columns+width;
+ }
+ convolve_indexes[x]=RoundToQuantum(gamma*pixel.index+bias);
+ }
+ }
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(convolve_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ConvolveImageChannel)
+#endif
+ proceed=SetImageProgress(image,ConvolveImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ convolve_image->type=image->type;
+ convolve_view=DestroyCacheView(convolve_view);
+ image_view=DestroyCacheView(image_view);
+ normal_kernel=(double *) RelinquishMagickMemory(normal_kernel);
+ if (status == MagickFalse)
+ convolve_image=DestroyImage(convolve_image);
+ return(convolve_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y F x I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyFxInfo() deallocates memory associated with an FxInfo structure.
+%
+% The format of the DestroyFxInfo method is:
+%
+% ImageInfo *DestroyFxInfo(ImageInfo *fx_info)
+%
+% A description of each parameter follows:
+%
+% o fx_info: the fx info.
+%
+*/
+MagickExport FxInfo *DestroyFxInfo(FxInfo *fx_info)
+{
+ register long
+ i;
+
+ fx_info->exception=DestroyExceptionInfo(fx_info->exception);
+ fx_info->expression=DestroyString(fx_info->expression);
+ fx_info->symbols=DestroySplayTree(fx_info->symbols);
+ fx_info->colors=DestroySplayTree(fx_info->colors);
+ for (i=0; i < (long) GetImageListLength(fx_info->images); i++)
+ fx_info->resample_filter[i]=DestroyResampleFilter(
+ fx_info->resample_filter[i]);
+ fx_info->resample_filter=(ResampleFilter **) RelinquishMagickMemory(
+ fx_info->resample_filter);
+ fx_info->random_info=DestroyRandomInfo(fx_info->random_info);
+ fx_info=(FxInfo *) RelinquishMagickMemory(fx_info);
+ return(fx_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E v a l u a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EvaluateImage() applies a value to the image with an arithmetic, relational,
+% or logical operator to an image. Use these operations to lighten or darken
+% an image, to increase or decrease contrast in an image, or to produce the
+% "negative" of an image.
+%
+% The format of the EvaluateImageChannel method is:
+%
+% MagickBooleanType EvaluateImage(Image *image,
+% const MagickEvaluateOperator op,const double value,
+% ExceptionInfo *exception)
+% MagickBooleanType EvaluateImageChannel(Image *image,
+% const ChannelType channel,const MagickEvaluateOperator op,
+% const double value,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o op: A channel op.
+%
+% o value: A value value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static Quantum ApplyEvaluateOperator(RandomInfo *random_info,Quantum pixel,
+ const MagickEvaluateOperator op,const MagickRealType value)
+{
+ MagickRealType
+ result;
+
+ result=0.0;
+ switch (op)
+ {
+ case UndefinedEvaluateOperator:
+ break;
+ case AddEvaluateOperator:
+ {
+ result=(MagickRealType) (pixel+value);
+ break;
+ }
+ case AddModulusEvaluateOperator:
+ {
+ /* This will return a 'floored modulus' of the addition which will
+ * always return a positive result, regardless of if the result is
+ * positive or negative, and thus in the 0 to QuantumRange range.
+ *
+ * WARNING: this is NOT the same as a % or fmod() which returns a
+ * 'truncated modulus' result, where floor() is replaced by trunc()
+ * and could return a negative result, which will be clipped.
+ */
+ result = pixel+value;
+ result -= (QuantumRange+1)*floor(result/(QuantumRange+1));
+ break;
+ }
+ case AndEvaluateOperator:
+ {
+ result=(MagickRealType) ((unsigned long) pixel & (unsigned long)
+ (value+0.5));
+ break;
+ }
+ case CosineEvaluateOperator:
+ {
+ result=(MagickRealType) (QuantumRange*(0.5*cos((double) (2.0*MagickPI*
+ QuantumScale*pixel*value))+0.5));
+ break;
+ }
+ case DivideEvaluateOperator:
+ {
+ result=pixel/(value == 0.0 ? 1.0 : value);
+ break;
+ }
+ case GaussianNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ GaussianNoise,value);
+ break;
+ }
+ case ImpulseNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ ImpulseNoise,value);
+ break;
+ }
+ case LaplacianNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ LaplacianNoise,value);
+ break;
+ }
+ case LeftShiftEvaluateOperator:
+ {
+ result=(MagickRealType) ((unsigned long) pixel << (unsigned long)
+ (value+0.5));
+ break;
+ }
+ case LogEvaluateOperator:
+ {
+ result=(MagickRealType) (QuantumRange*log((double) (QuantumScale*value*
+ pixel+1.0))/log((double) (value+1.0)));
+ break;
+ }
+ case MaxEvaluateOperator:
+ {
+ result=(MagickRealType) MagickMax((double) pixel,value);
+ break;
+ }
+ case MinEvaluateOperator:
+ {
+ result=(MagickRealType) MagickMin((double) pixel,value);
+ break;
+ }
+ case MultiplicativeNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ MultiplicativeGaussianNoise,value);
+ break;
+ }
+ case MultiplyEvaluateOperator:
+ {
+ result=(MagickRealType) (value*pixel);
+ break;
+ }
+ case OrEvaluateOperator:
+ {
+ result=(MagickRealType) ((unsigned long) pixel | (unsigned long)
+ (value+0.5));
+ break;
+ }
+ case PoissonNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ PoissonNoise,value);
+ break;
+ }
+ case PowEvaluateOperator:
+ {
+ result=(MagickRealType) (QuantumRange*pow((double) (QuantumScale*pixel),
+ (double) value));
+ break;
+ }
+ case RightShiftEvaluateOperator:
+ {
+ result=(MagickRealType) ((unsigned long) pixel >> (unsigned long)
+ (value+0.5));
+ break;
+ }
+ case SetEvaluateOperator:
+ {
+ result=value;
+ break;
+ }
+ case SineEvaluateOperator:
+ {
+ result=(MagickRealType) (QuantumRange*(0.5*sin((double) (2.0*MagickPI*
+ QuantumScale*pixel*value))+0.5));
+ break;
+ }
+ case SubtractEvaluateOperator:
+ {
+ result=(MagickRealType) (pixel-value);
+ break;
+ }
+ case ThresholdEvaluateOperator:
+ {
+ result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 :
+ QuantumRange);
+ break;
+ }
+ case ThresholdBlackEvaluateOperator:
+ {
+ result=(MagickRealType) (((MagickRealType) pixel <= value) ? 0 : pixel);
+ break;
+ }
+ case ThresholdWhiteEvaluateOperator:
+ {
+ result=(MagickRealType) (((MagickRealType) pixel > value) ? QuantumRange :
+ pixel);
+ break;
+ }
+ case UniformNoiseEvaluateOperator:
+ {
+ result=(MagickRealType) GenerateDifferentialNoise(random_info,pixel,
+ UniformNoise,value);
+ break;
+ }
+ case XorEvaluateOperator:
+ {
+ result=(MagickRealType) ((unsigned long) pixel ^ (unsigned long)
+ (value+0.5));
+ break;
+ }
+ }
+ return(RoundToQuantum(result));
+}
+
+MagickExport MagickBooleanType EvaluateImage(Image *image,
+ const MagickEvaluateOperator op,const double value,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=EvaluateImageChannel(image,AllChannels,op,value,exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType EvaluateImageChannel(Image *image,
+ const ChannelType channel,const MagickEvaluateOperator op,const double value,
+ ExceptionInfo *exception)
+{
+#define EvaluateImageTag "Evaluate/Image "
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RandomInfo
+ **random_info;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&image->exception);
+ return(MagickFalse);
+ }
+ status=MagickTrue;
+ progress=0;
+ random_info=AcquireRandomInfoThreadSet();
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=ApplyEvaluateOperator(random_info[id],q->red,op,value);
+ if ((channel & GreenChannel) != 0)
+ q->green=ApplyEvaluateOperator(random_info[id],q->green,op,value);
+ if ((channel & BlueChannel) != 0)
+ q->blue=ApplyEvaluateOperator(random_info[id],q->blue,op,value);
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte == MagickFalse)
+ q->opacity=ApplyEvaluateOperator(random_info[id],q->opacity,op,
+ value);
+ else
+ q->opacity=(Quantum) QuantumRange-ApplyEvaluateOperator(
+ random_info[id],(Quantum) (QuantumRange-q->opacity),op,value);
+ }
+ if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
+ indexes[x]=(IndexPacket) ApplyEvaluateOperator(random_info[id],
+ indexes[x],op,value);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_EvaluateImageChannel)
+#endif
+ proceed=SetImageProgress(image,EvaluateImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F u n c t i o n I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FunctionImage() applies a value to the image with an arithmetic, relational,
+% or logical operator to an image. Use these operations to lighten or darken
+% an image, to increase or decrease contrast in an image, or to produce the
+% "negative" of an image.
+%
+% The format of the FunctionImageChannel method is:
+%
+% MagickBooleanType FunctionImage(Image *image,
+% const MagickFunction function,const long number_parameters,
+% const double *parameters,ExceptionInfo *exception)
+% MagickBooleanType FunctionImageChannel(Image *image,
+% const ChannelType channel,const MagickFunction function,
+% const long number_parameters,const double *argument,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o function: A channel function.
+%
+% o parameters: one or more parameters.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static Quantum ApplyFunction(Quantum pixel,const MagickFunction function,
+ const unsigned long number_parameters,const MagickRealType *parameters,
+ ExceptionInfo *exception)
+{
+ MagickRealType
+ result;
+
+ register long
+ i;
+
+ (void) exception;
+ result=0.0;
+ switch (function)
+ {
+ case PolynomialFunction:
+ {
+ /*
+ * Polynomial
+ * Parameters: polynomial constants, highest to lowest order
+ * For example: c0*x^3 + c1*x^2 + c2*x + c3
+ */
+ result=0.0;
+ for (i=0; i < (long) number_parameters; i++)
+ result = result*QuantumScale*pixel + parameters[i];
+ result *= QuantumRange;
+ break;
+ }
+ case SinusoidFunction:
+ {
+ /* Sinusoid Function
+ * Parameters: Freq, Phase, Ampl, bias
+ */
+ double freq,phase,ampl,bias;
+ freq = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
+ phase = ( number_parameters >= 2 ) ? parameters[1] : 0.0;
+ ampl = ( number_parameters >= 3 ) ? parameters[2] : 0.5;
+ bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
+ result=(MagickRealType) (QuantumRange*(ampl*sin((double) (2.0*MagickPI*
+ (freq*QuantumScale*pixel + phase/360.0) )) + bias ) );
+ break;
+ }
+ case ArcsinFunction:
+ {
+ /* Arcsin Function (peged at range limits for invalid results)
+ * Parameters: Width, Center, Range, Bias
+ */
+ double width,range,center,bias;
+ width = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
+ center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
+ range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
+ bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
+ result = 2.0/width*(QuantumScale*pixel - center);
+ if ( result <= -1.0 )
+ result = bias - range/2.0;
+ else if ( result >= 1.0 )
+ result = bias + range/2.0;
+ else
+ result=range/MagickPI*asin((double)result) + bias;
+ result *= QuantumRange;
+ break;
+ }
+ case ArctanFunction:
+ {
+ /* Arctan Function
+ * Parameters: Slope, Center, Range, Bias
+ */
+ double slope,range,center,bias;
+ slope = ( number_parameters >= 1 ) ? parameters[0] : 1.0;
+ center = ( number_parameters >= 2 ) ? parameters[1] : 0.5;
+ range = ( number_parameters >= 3 ) ? parameters[2] : 1.0;
+ bias = ( number_parameters >= 4 ) ? parameters[3] : 0.5;
+ result = MagickPI*slope*(QuantumScale*pixel - center);
+ result=(MagickRealType) (QuantumRange*(range/MagickPI*atan((double)
+ result) + bias ) );
+ break;
+ }
+ case UndefinedFunction:
+ break;
+ }
+ return(RoundToQuantum(result));
+}
+
+MagickExport MagickBooleanType FunctionImage(Image *image,
+ const MagickFunction function,const unsigned long number_parameters,
+ const double *parameters,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=FunctionImageChannel(image,AllChannels,function,number_parameters,
+ parameters,exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType FunctionImageChannel(Image *image,
+ const ChannelType channel,const MagickFunction function,
+ const unsigned long number_parameters,const double *parameters,
+ ExceptionInfo *exception)
+{
+#define FunctionImageTag "Function/Image "
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&image->exception);
+ return(MagickFalse);
+ }
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=ApplyFunction(q->red,function,number_parameters,parameters,
+ exception);
+ if ((channel & GreenChannel) != 0)
+ q->green=ApplyFunction(q->green,function,number_parameters,parameters,
+ exception);
+ if ((channel & BlueChannel) != 0)
+ q->blue=ApplyFunction(q->blue,function,number_parameters,parameters,
+ exception);
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte == MagickFalse)
+ q->opacity=ApplyFunction(q->opacity,function,number_parameters,
+ parameters,exception);
+ else
+ q->opacity=(Quantum) QuantumRange-ApplyFunction((Quantum) (
+ QuantumRange-q->opacity),function,number_parameters,parameters,
+ exception);
+ }
+ if (((channel & IndexChannel) != 0) && (indexes != (IndexPacket *) NULL))
+ indexes[x]=(IndexPacket) ApplyFunction(indexes[x],function,
+ number_parameters,parameters,exception);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_FunctionImageChannel)
+#endif
+ proceed=SetImageProgress(image,FunctionImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F x E v a l u a t e C h a n n e l E x p r e s s i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FxEvaluateChannelExpression() evaluates an expression and returns the
+% results.
+%
+% The format of the FxEvaluateExpression method is:
+%
+% MagickRealType FxEvaluateChannelExpression(FxInfo *fx_info,
+% const ChannelType channel,const long x,const long y,
+% MagickRealType *alpha,Exceptioninfo *exception)
+% MagickRealType FxEvaluateExpression(FxInfo *fx_info,
+% MagickRealType *alpha,Exceptioninfo *exception)
+%
+% A description of each parameter follows:
+%
+% o fx_info: the fx info.
+%
+% o channel: the channel.
+%
+% o x,y: the pixel position.
+%
+% o alpha: the result.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static MagickRealType FxChannelStatistics(FxInfo *fx_info,const Image *image,
+ ChannelType channel,const char *symbol,ExceptionInfo *exception)
+{
+ char
+ key[MaxTextExtent],
+ statistic[MaxTextExtent];
+
+ const char
+ *value;
+
+ register const char
+ *p;
+
+ for (p=symbol; (*p != '.') && (*p != '\0'); p++) ;
+ if (*p == '.')
+ switch (*++p) /* e.g. depth.r */
+ {
+ case 'r': channel=RedChannel; break;
+ case 'g': channel=GreenChannel; break;
+ case 'b': channel=BlueChannel; break;
+ case 'c': channel=CyanChannel; break;
+ case 'm': channel=MagentaChannel; break;
+ case 'y': channel=YellowChannel; break;
+ case 'k': channel=BlackChannel; break;
+ default: break;
+ }
+ (void) FormatMagickString(key,MaxTextExtent,"%p.%ld.%s",image,(long) channel,
+ symbol);
+ value=(const char *) GetValueFromSplayTree(fx_info->symbols,key);
+ if (value != (const char *) NULL)
+ return(QuantumScale*atof(value));
+ (void) DeleteNodeFromSplayTree(fx_info->symbols,key);
+ if (LocaleNCompare(symbol,"depth",5) == 0)
+ {
+ unsigned long
+ depth;
+
+ depth=GetImageChannelDepth(image,channel,exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%lu",depth);
+ }
+ if (LocaleNCompare(symbol,"kurtosis",8) == 0)
+ {
+ double
+ kurtosis,
+ skewness;
+
+ (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
+ exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",kurtosis);
+ }
+ if (LocaleNCompare(symbol,"maxima",6) == 0)
+ {
+ double
+ maxima,
+ minima;
+
+ (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",maxima);
+ }
+ if (LocaleNCompare(symbol,"mean",4) == 0)
+ {
+ double
+ mean,
+ standard_deviation;
+
+ (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
+ exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",mean);
+ }
+ if (LocaleNCompare(symbol,"minima",6) == 0)
+ {
+ double
+ maxima,
+ minima;
+
+ (void) GetImageChannelRange(image,channel,&minima,&maxima,exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",minima);
+ }
+ if (LocaleNCompare(symbol,"skewness",8) == 0)
+ {
+ double
+ kurtosis,
+ skewness;
+
+ (void) GetImageChannelKurtosis(image,channel,&kurtosis,&skewness,
+ exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",skewness);
+ }
+ if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
+ {
+ double
+ mean,
+ standard_deviation;
+
+ (void) GetImageChannelMean(image,channel,&mean,&standard_deviation,
+ exception);
+ (void) FormatMagickString(statistic,MaxTextExtent,"%g",
+ standard_deviation);
+ }
+ (void) AddValueToSplayTree(fx_info->symbols,ConstantString(key),
+ ConstantString(statistic));
+ return(QuantumScale*atof(statistic));
+}
+
+static MagickRealType
+ FxEvaluateSubexpression(FxInfo *,const ChannelType,const long,const long,
+ const char *,MagickRealType *,ExceptionInfo *);
+
+static inline MagickRealType FxMax(FxInfo *fx_info,const ChannelType channel,
+ const long x,const long y,const char *expression,ExceptionInfo *exception)
+{
+ MagickRealType
+ alpha,
+ beta;
+
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
+ return((MagickRealType) MagickMax((double) alpha,(double) beta));
+}
+
+static inline MagickRealType FxMin(FxInfo *fx_info,ChannelType channel,
+ const long x,const long y,const char *expression,ExceptionInfo *exception)
+{
+ MagickRealType
+ alpha,
+ beta;
+
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression,&beta,exception);
+ return((MagickRealType) MagickMin((double) alpha,(double) beta));
+}
+
+static inline const char *FxSubexpression(const char *expression,
+ ExceptionInfo *exception)
+{
+ const char
+ *subexpression;
+
+ register long
+ level;
+
+ level=0;
+ subexpression=expression;
+ while ((*subexpression != '\0') &&
+ ((level != 1) || (strchr(")",(int) *subexpression) == (char *) NULL)))
+ {
+ if (strchr("(",(int) *subexpression) != (char *) NULL)
+ level++;
+ else
+ if (strchr(")",(int) *subexpression) != (char *) NULL)
+ level--;
+ subexpression++;
+ }
+ if (*subexpression == '\0')
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnbalancedParenthesis","`%s'",expression);
+ return(subexpression);
+}
+
+static MagickRealType FxGetSymbol(FxInfo *fx_info,const ChannelType channel,
+ const long x,const long y,const char *expression,ExceptionInfo *exception)
+{
+ char
+ *q,
+ subexpression[MaxTextExtent],
+ symbol[MaxTextExtent];
+
+ const char
+ *p,
+ *value;
+
+ Image
+ *image;
+
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ alpha,
+ beta;
+
+ PointInfo
+ point;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ unsigned long
+ level;
+
+ p=expression;
+ i=GetImageIndexInList(fx_info->images);
+ level=0;
+ point.x=(double) x;
+ point.y=(double) y;
+ if (isalpha((int) *(p+1)) == 0)
+ {
+ if (strchr("suv",(int) *p) != (char *) NULL)
+ {
+ switch (*p)
+ {
+ case 's':
+ default:
+ {
+ i=GetImageIndexInList(fx_info->images);
+ break;
+ }
+ case 'u': i=0; break;
+ case 'v': i=1; break;
+ }
+ p++;
+ if (*p == '[')
+ {
+ level++;
+ q=subexpression;
+ for (p++; *p != '\0'; )
+ {
+ if (*p == '[')
+ level++;
+ else
+ if (*p == ']')
+ {
+ level--;
+ if (level == 0)
+ break;
+ }
+ *q++=(*p++);
+ }
+ *q='\0';
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
+ &beta,exception);
+ i=(long) (alpha+0.5);
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ }
+ if ((isalpha((int) *(p+1)) == 0) && (*p == 'p'))
+ {
+ p++;
+ if (*p == '{')
+ {
+ level++;
+ q=subexpression;
+ for (p++; *p != '\0'; )
+ {
+ if (*p == '{')
+ level++;
+ else
+ if (*p == '}')
+ {
+ level--;
+ if (level == 0)
+ break;
+ }
+ *q++=(*p++);
+ }
+ *q='\0';
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
+ &beta,exception);
+ point.x=alpha;
+ point.y=beta;
+ p++;
+ }
+ else
+ if (*p == '[')
+ {
+ level++;
+ q=subexpression;
+ for (p++; *p != '\0'; )
+ {
+ if (*p == '[')
+ level++;
+ else
+ if (*p == ']')
+ {
+ level--;
+ if (level == 0)
+ break;
+ }
+ *q++=(*p++);
+ }
+ *q='\0';
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,
+ &beta,exception);
+ point.x+=alpha;
+ point.y+=beta;
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ }
+ }
+ length=GetImageListLength(fx_info->images);
+ while (i < 0)
+ i+=(long) length;
+ i%=length;
+ image=GetImageFromList(fx_info->images,i);
+ if (image == (Image *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "NoSuchImage","`%s'",expression);
+ return(0.0);
+ }
+ (void) ResamplePixelColor(fx_info->resample_filter[i],point.x,point.y,&pixel);
+ if ((strlen(p) > 2) &&
+ (LocaleCompare(p,"intensity") != 0) &&
+ (LocaleCompare(p,"luminance") != 0) &&
+ (LocaleCompare(p,"hue") != 0) &&
+ (LocaleCompare(p,"saturation") != 0) &&
+ (LocaleCompare(p,"lightness") != 0))
+ {
+ char
+ name[MaxTextExtent];
+
+ (void) CopyMagickString(name,p,MaxTextExtent);
+ for (q=name+(strlen(name)-1); q > name; q--)
+ {
+ if (*q == ')')
+ break;
+ if (*q == '.')
+ {
+ *q='\0';
+ break;
+ }
+ }
+ if ((strlen(name) > 2) &&
+ (GetValueFromSplayTree(fx_info->symbols,name) == (const char *) NULL))
+ {
+ MagickPixelPacket
+ *color;
+
+ color=(MagickPixelPacket *) GetValueFromSplayTree(fx_info->colors,
+ name);
+ if (color != (MagickPixelPacket *) NULL)
+ {
+ pixel=(*color);
+ p+=strlen(name);
+ }
+ else
+ if (QueryMagickColor(name,&pixel,fx_info->exception) != MagickFalse)
+ {
+ (void) AddValueToSplayTree(fx_info->colors,ConstantString(name),
+ CloneMagickPixelPacket(&pixel));
+ p+=strlen(name);
+ }
+ }
+ }
+ (void) CopyMagickString(symbol,p,MaxTextExtent);
+ StripString(symbol);
+ if (*symbol == '\0')
+ {
+ switch (channel)
+ {
+ case RedChannel: return(QuantumScale*pixel.red);
+ case GreenChannel: return(QuantumScale*pixel.green);
+ case BlueChannel: return(QuantumScale*pixel.blue);
+ case OpacityChannel:
+ {
+ if (pixel.matte == MagickFalse)
+ {
+ fx_info->matte=MagickFalse;
+ return(1.0);
+ }
+ return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
+ }
+ case IndexChannel:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"ColorSeparatedImageRequired","`%s'",
+ image->filename);
+ return(0.0);
+ }
+ return(QuantumScale*pixel.index);
+ }
+ default:
+ break;
+ }
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnableToParseExpression","`%s'",p);
+ return(0.0);
+ }
+ switch (*symbol)
+ {
+ case 'A':
+ case 'a':
+ {
+ if (LocaleCompare(symbol,"a") == 0)
+ return((MagickRealType) (QuantumScale*(QuantumRange-pixel.opacity)));
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ if (LocaleCompare(symbol,"b") == 0)
+ return(QuantumScale*pixel.blue);
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ if (LocaleNCompare(symbol,"channel",7) == 0)
+ {
+ GeometryInfo
+ channel_info;
+
+ MagickStatusType
+ flags;
+
+ flags=ParseGeometry(symbol+7,&channel_info);
+ if (image->colorspace == CMYKColorspace)
+ switch (channel)
+ {
+ case CyanChannel:
+ {
+ if ((flags & RhoValue) == 0)
+ return(0.0);
+ return(channel_info.rho);
+ }
+ case MagentaChannel:
+ {
+ if ((flags & SigmaValue) == 0)
+ return(0.0);
+ return(channel_info.sigma);
+ }
+ case YellowChannel:
+ {
+ if ((flags & XiValue) == 0)
+ return(0.0);
+ return(channel_info.xi);
+ }
+ case BlackChannel:
+ {
+ if ((flags & PsiValue) == 0)
+ return(0.0);
+ return(channel_info.psi);
+ }
+ case OpacityChannel:
+ {
+ if ((flags & ChiValue) == 0)
+ return(0.0);
+ return(channel_info.chi);
+ }
+ default:
+ return(0.0);
+ }
+ switch (channel)
+ {
+ case RedChannel:
+ {
+ if ((flags & RhoValue) == 0)
+ return(0.0);
+ return(channel_info.rho);
+ }
+ case GreenChannel:
+ {
+ if ((flags & SigmaValue) == 0)
+ return(0.0);
+ return(channel_info.sigma);
+ }
+ case BlueChannel:
+ {
+ if ((flags & XiValue) == 0)
+ return(0.0);
+ return(channel_info.xi);
+ }
+ case OpacityChannel:
+ {
+ if ((flags & PsiValue) == 0)
+ return(0.0);
+ return(channel_info.psi);
+ }
+ case IndexChannel:
+ {
+ if ((flags & ChiValue) == 0)
+ return(0.0);
+ return(channel_info.chi);
+ }
+ default:
+ return(0.0);
+ }
+ return(0.0);
+ }
+ if (LocaleCompare(symbol,"c") == 0)
+ return(QuantumScale*pixel.red);
+ break;
+ }
+ case 'D':
+ case 'd':
+ {
+ if (LocaleNCompare(symbol,"depth",5) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ break;
+ }
+ case 'G':
+ case 'g':
+ {
+ if (LocaleCompare(symbol,"g") == 0)
+ return(QuantumScale*pixel.green);
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ if (LocaleNCompare(symbol,"kurtosis",8) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ if (LocaleCompare(symbol,"k") == 0)
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"ColorSeparatedImageRequired","`%s'",
+ image->filename);
+ return(0.0);
+ }
+ return(QuantumScale*pixel.index);
+ }
+ break;
+ }
+ case 'H':
+ case 'h':
+ {
+ if (LocaleCompare(symbol,"h") == 0)
+ return((MagickRealType) image->rows);
+ if (LocaleCompare(symbol,"hue") == 0)
+ {
+ double
+ hue,
+ lightness,
+ saturation;
+
+ ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
+ RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
+ return(hue);
+ }
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ if ((LocaleCompare(symbol,"image.depth") == 0) ||
+ (LocaleCompare(symbol,"image.minima") == 0) ||
+ (LocaleCompare(symbol,"image.maxima") == 0) ||
+ (LocaleCompare(symbol,"image.mean") == 0) ||
+ (LocaleCompare(symbol,"image.kurtosis") == 0) ||
+ (LocaleCompare(symbol,"image.skewness") == 0) ||
+ (LocaleCompare(symbol,"image.standard_deviation") == 0))
+ return(FxChannelStatistics(fx_info,image,channel,symbol+6,exception));
+ if (LocaleCompare(symbol,"image.resolution.x") == 0)
+ return(image->x_resolution);
+ if (LocaleCompare(symbol,"image.resolution.y") == 0)
+ return(image->y_resolution);
+ if (LocaleCompare(symbol,"intensity") == 0)
+ return(QuantumScale*MagickPixelIntensityToQuantum(&pixel));
+ if (LocaleCompare(symbol,"i") == 0)
+ return((MagickRealType) x);
+ break;
+ }
+ case 'J':
+ case 'j':
+ {
+ if (LocaleCompare(symbol,"j") == 0)
+ return((MagickRealType) y);
+ break;
+ }
+ case 'L':
+ case 'l':
+ {
+ if (LocaleCompare(symbol,"lightness") == 0)
+ {
+ double
+ hue,
+ lightness,
+ saturation;
+
+ ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
+ RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
+ return(lightness);
+ }
+ if (LocaleCompare(symbol,"luminance") == 0)
+ {
+ double
+ luminence;
+
+ luminence=0.2126*pixel.red+0.7152*pixel.green+0.0722*pixel.blue;
+ return(QuantumScale*luminence);
+ }
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if (LocaleNCompare(symbol,"maxima",6) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ if (LocaleNCompare(symbol,"mean",4) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ if (LocaleNCompare(symbol,"minima",6) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ if (LocaleCompare(symbol,"m") == 0)
+ return(QuantumScale*pixel.blue);
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare(symbol,"n") == 0)
+ return((MagickRealType) GetImageListLength(fx_info->images));
+ break;
+ }
+ case 'O':
+ case 'o':
+ {
+ if (LocaleCompare(symbol,"o") == 0)
+ return(QuantumScale*pixel.opacity);
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ if (LocaleCompare(symbol,"page.height") == 0)
+ return((MagickRealType) image->page.height);
+ if (LocaleCompare(symbol,"page.width") == 0)
+ return((MagickRealType) image->page.width);
+ if (LocaleCompare(symbol,"page.x") == 0)
+ return((MagickRealType) image->page.x);
+ if (LocaleCompare(symbol,"page.y") == 0)
+ return((MagickRealType) image->page.y);
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ if (LocaleCompare(symbol,"resolution.x") == 0)
+ return(image->x_resolution);
+ if (LocaleCompare(symbol,"resolution.y") == 0)
+ return(image->y_resolution);
+ if (LocaleCompare(symbol,"r") == 0)
+ return(QuantumScale*pixel.red);
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare(symbol,"saturation") == 0)
+ {
+ double
+ hue,
+ lightness,
+ saturation;
+
+ ConvertRGBToHSL(RoundToQuantum(pixel.red),RoundToQuantum(pixel.green),
+ RoundToQuantum(pixel.blue),&hue,&saturation,&lightness);
+ return(saturation);
+ }
+ if (LocaleNCompare(symbol,"skewness",8) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ if (LocaleNCompare(symbol,"standard_deviation",18) == 0)
+ return(FxChannelStatistics(fx_info,image,channel,symbol,exception));
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if (LocaleCompare(symbol,"t") == 0)
+ return((MagickRealType) fx_info->images->scene);
+ break;
+ }
+ case 'W':
+ case 'w':
+ {
+ if (LocaleCompare(symbol,"w") == 0)
+ return((MagickRealType) image->columns);
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ if (LocaleCompare(symbol,"y") == 0)
+ return(QuantumScale*pixel.green);
+ break;
+ }
+ case 'Z':
+ case 'z':
+ {
+ if (LocaleCompare(symbol,"z") == 0)
+ {
+ MagickRealType
+ depth;
+
+ depth=(MagickRealType) GetImageChannelDepth(image,channel,
+ fx_info->exception);
+ return(depth);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ value=(const char *) GetValueFromSplayTree(fx_info->symbols,symbol);
+ if (value != (const char *) NULL)
+ return((MagickRealType) atof(value));
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnableToParseExpression","`%s'",symbol);
+ return(0.0);
+}
+
+static const char *FxOperatorPrecedence(const char *expression,
+ ExceptionInfo *exception)
+{
+ typedef enum
+ {
+ UndefinedPrecedence,
+ NullPrecedence,
+ BitwiseComplementPrecedence,
+ ExponentPrecedence,
+ MultiplyPrecedence,
+ AdditionPrecedence,
+ ShiftPrecedence,
+ RelationalPrecedence,
+ EquivalencyPrecedence,
+ BitwiseAndPrecedence,
+ BitwiseOrPrecedence,
+ LogicalAndPrecedence,
+ LogicalOrPrecedence,
+ TernaryPrecedence,
+ AssignmentPrecedence,
+ CommaPrecedence,
+ SeparatorPrecedence
+ } FxPrecedence;
+
+ FxPrecedence
+ precedence,
+ target;
+
+ register const char
+ *subexpression;
+
+ register int
+ c;
+
+ unsigned long
+ level;
+
+ c=0;
+ level=0;
+ subexpression=(const char *) NULL;
+ target=NullPrecedence;
+ while (*expression != '\0')
+ {
+ precedence=UndefinedPrecedence;
+ if ((isspace((int) ((char) *expression)) != 0) || (c == (int) '@'))
+ {
+ expression++;
+ continue;
+ }
+ if (LocaleNCompare(expression,"atan2",5) == 0)
+ {
+ expression+=5;
+ continue;
+ }
+ if ((c == (int) '{') || (c == (int) '['))
+ level++;
+ else
+ if ((c == (int) '}') || (c == (int) ']'))
+ level--;
+ if (level == 0)
+ switch ((unsigned char) *expression)
+ {
+ case '~':
+ case '!':
+ {
+ precedence=BitwiseComplementPrecedence;
+ break;
+ }
+ case '^':
+ {
+ precedence=ExponentPrecedence;
+ break;
+ }
+ default:
+ {
+ if (((c != 0) && ((isdigit((int) ((char) c)) != 0) ||
+ (strchr(")",c) != (char *) NULL))) &&
+ (((islower((int) ((char) *expression)) != 0) ||
+ (strchr("(",(int) *expression) != (char *) NULL)) ||
+ ((isdigit((int) ((char) c)) == 0) &&
+ (isdigit((int) ((char) *expression)) != 0))) &&
+ (strchr("xy",(int) *expression) == (char *) NULL))
+ precedence=MultiplyPrecedence;
+ break;
+ }
+ case '*':
+ case '/':
+ case '%':
+ {
+ precedence=MultiplyPrecedence;
+ break;
+ }
+ case '+':
+ case '-':
+ {
+ if ((strchr("(+-/*%:&^|<>~,",c) == (char *) NULL) ||
+ (isalpha(c) != 0))
+ precedence=AdditionPrecedence;
+ break;
+ }
+ case LeftShiftOperator:
+ case RightShiftOperator:
+ {
+ precedence=ShiftPrecedence;
+ break;
+ }
+ case '<':
+ case LessThanEqualOperator:
+ case GreaterThanEqualOperator:
+ case '>':
+ {
+ precedence=RelationalPrecedence;
+ break;
+ }
+ case EqualOperator:
+ case NotEqualOperator:
+ {
+ precedence=EquivalencyPrecedence;
+ break;
+ }
+ case '&':
+ {
+ precedence=BitwiseAndPrecedence;
+ break;
+ }
+ case '|':
+ {
+ precedence=BitwiseOrPrecedence;
+ break;
+ }
+ case LogicalAndOperator:
+ {
+ precedence=LogicalAndPrecedence;
+ break;
+ }
+ case LogicalOrOperator:
+ {
+ precedence=LogicalOrPrecedence;
+ break;
+ }
+ case ':':
+ case '?':
+ {
+ precedence=TernaryPrecedence;
+ break;
+ }
+ case '=':
+ {
+ precedence=AssignmentPrecedence;
+ break;
+ }
+ case ',':
+ {
+ precedence=CommaPrecedence;
+ break;
+ }
+ case ';':
+ {
+ precedence=SeparatorPrecedence;
+ break;
+ }
+ }
+ if ((precedence == BitwiseComplementPrecedence) ||
+ (precedence == TernaryPrecedence) ||
+ (precedence == AssignmentPrecedence))
+ {
+ if (precedence > target)
+ {
+ /*
+ Right-to-left associativity.
+ */
+ target=precedence;
+ subexpression=expression;
+ }
+ }
+ else
+ if (precedence >= target)
+ {
+ /*
+ Left-to-right associativity.
+ */
+ target=precedence;
+ subexpression=expression;
+ }
+ if (strchr("(",(int) *expression) != (char *) NULL)
+ expression=FxSubexpression(expression,exception);
+ c=(int) (*expression++);
+ }
+ return(subexpression);
+}
+
+static MagickRealType FxEvaluateSubexpression(FxInfo *fx_info,
+ const ChannelType channel,const long x,const long y,const char *expression,
+ MagickRealType *beta,ExceptionInfo *exception)
+{
+ char
+ *q,
+ subexpression[MaxTextExtent];
+
+ MagickRealType
+ alpha,
+ gamma;
+
+ register const char
+ *p;
+
+ *beta=0.0;
+ if (exception->severity != UndefinedException)
+ return(0.0);
+ while (isspace((int) *expression) != 0)
+ expression++;
+ if (*expression == '\0')
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "MissingExpression","`%s'",expression);
+ return(0.0);
+ }
+ p=FxOperatorPrecedence(expression,exception);
+ if (p != (const char *) NULL)
+ {
+ (void) CopyMagickString(subexpression,expression,(size_t)
+ (p-expression+1));
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
+ exception);
+ switch ((unsigned char) *p)
+ {
+ case '~':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(MagickRealType) (~(unsigned long) *beta);
+ return(*beta);
+ }
+ case '!':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(*beta == 0.0 ? 1.0 : 0.0);
+ }
+ case '^':
+ {
+ *beta=pow((double) alpha,(double) FxEvaluateSubexpression(fx_info,
+ channel,x,y,++p,beta,exception));
+ return(*beta);
+ }
+ case '*':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha*(*beta));
+ }
+ case '/':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ if (*beta == 0.0)
+ {
+ if (exception->severity == UndefinedException)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"DivideByZero","`%s'",expression);
+ return(0.0);
+ }
+ return(alpha/(*beta));
+ }
+ case '%':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=fabs(floor(((double) *beta)+0.5));
+ if (*beta == 0.0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"DivideByZero","`%s'",expression);
+ return(0.0);
+ }
+ return(fmod((double) alpha,(double) *beta));
+ }
+ case '+':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha+(*beta));
+ }
+ case '-':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha-(*beta));
+ }
+ case LeftShiftOperator:
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(MagickRealType) ((unsigned long) (alpha+0.5) << (unsigned long)
+ (gamma+0.5));
+ return(*beta);
+ }
+ case RightShiftOperator:
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(MagickRealType) ((unsigned long) (alpha+0.5) >> (unsigned long)
+ (gamma+0.5));
+ return(*beta);
+ }
+ case '<':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha < *beta ? 1.0 : 0.0);
+ }
+ case LessThanEqualOperator:
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha <= *beta ? 1.0 : 0.0);
+ }
+ case '>':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha > *beta ? 1.0 : 0.0);
+ }
+ case GreaterThanEqualOperator:
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha >= *beta ? 1.0 : 0.0);
+ }
+ case EqualOperator:
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(fabs(alpha-(*beta)) <= MagickEpsilon ? 1.0 : 0.0);
+ }
+ case NotEqualOperator:
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(fabs(alpha-(*beta)) > MagickEpsilon ? 1.0 : 0.0);
+ }
+ case '&':
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(MagickRealType) ((unsigned long) (alpha+0.5) & (unsigned long)
+ (gamma+0.5));
+ return(*beta);
+ }
+ case '|':
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(MagickRealType) ((unsigned long) (alpha+0.5) | (unsigned long)
+ (gamma+0.5));
+ return(*beta);
+ }
+ case LogicalAndOperator:
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(alpha > 0.0) && (gamma > 0.0) ? 1.0 : 0.0;
+ return(*beta);
+ }
+ case LogicalOrOperator:
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ *beta=(alpha > 0.0) || (gamma > 0.0) ? 1.0 : 0.0;
+ return(*beta);
+ }
+ case '?':
+ {
+ MagickRealType
+ gamma;
+
+ (void) CopyMagickString(subexpression,++p,MaxTextExtent);
+ q=subexpression;
+ p=StringToken(":",&q);
+ if (q == (char *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"UnableToParseExpression","`%s'",subexpression);
+ return(0.0);
+ }
+ if (fabs((double) alpha) > MagickEpsilon)
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,exception);
+ else
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,q,beta,exception);
+ return(gamma);
+ }
+ case '=':
+ {
+ char
+ numeric[MaxTextExtent];
+
+ q=subexpression;
+ while (isalpha((int) ((unsigned char) *q)) != 0)
+ q++;
+ if (*q != '\0')
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionError,"UnableToParseExpression","`%s'",subexpression);
+ return(0.0);
+ }
+ ClearMagickException(exception);
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ (void) FormatMagickString(numeric,MaxTextExtent,"%g",(double) *beta);
+ (void) DeleteNodeFromSplayTree(fx_info->symbols,subexpression);
+ (void) AddValueToSplayTree(fx_info->symbols,ConstantString(
+ subexpression),ConstantString(numeric));
+ return(*beta);
+ }
+ case ',':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(alpha);
+ }
+ case ';':
+ {
+ *beta=FxEvaluateSubexpression(fx_info,channel,x,y,++p,beta,exception);
+ return(*beta);
+ }
+ default:
+ {
+ gamma=alpha*FxEvaluateSubexpression(fx_info,channel,x,y,p,beta,
+ exception);
+ return(gamma);
+ }
+ }
+ }
+ if (strchr("(",(int) *expression) != (char *) NULL)
+ {
+ (void) CopyMagickString(subexpression,expression+1,MaxTextExtent);
+ subexpression[strlen(subexpression)-1]='\0';
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,subexpression,beta,
+ exception);
+ return(gamma);
+ }
+ switch (*expression)
+ {
+ case '+':
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
+ exception);
+ return(1.0*gamma);
+ }
+ case '-':
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
+ exception);
+ return(-1.0*gamma);
+ }
+ case '~':
+ {
+ gamma=FxEvaluateSubexpression(fx_info,channel,x,y,expression+1,beta,
+ exception);
+ return((MagickRealType) (~(unsigned long) (gamma+0.5)));
+ }
+ case 'A':
+ case 'a':
+ {
+ if (LocaleNCompare(expression,"abs",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) fabs((double) alpha));
+ }
+ if (LocaleNCompare(expression,"acos",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) acos((double) alpha));
+ }
+ if (LocaleNCompare(expression,"asin",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) asin((double) alpha));
+ }
+ if (LocaleNCompare(expression,"alt",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return(((long) alpha) & 0x01 ? -1.0 : 1.0);
+ }
+ if (LocaleNCompare(expression,"atan2",5) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
+ exception);
+ return((MagickRealType) atan2((double) alpha,(double) *beta));
+ }
+ if (LocaleNCompare(expression,"atan",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) atan((double) alpha));
+ }
+ if (LocaleCompare(expression,"a") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ if (LocaleCompare(expression,"b") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ if (LocaleNCompare(expression,"ceil",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) ceil((double) alpha));
+ }
+ if (LocaleNCompare(expression,"cosh",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) cosh((double) alpha));
+ }
+ if (LocaleNCompare(expression,"cos",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) cos((double) alpha));
+ }
+ if (LocaleCompare(expression,"c") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'D':
+ case 'd':
+ {
+ if (LocaleNCompare(expression,"debug",5) == 0)
+ {
+ const char
+ *type;
+
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
+ exception);
+ if (fx_info->images->colorspace == CMYKColorspace)
+ switch (channel)
+ {
+ case CyanChannel: type="cyan"; break;
+ case MagentaChannel: type="magenta"; break;
+ case YellowChannel: type="yellow"; break;
+ case OpacityChannel: type="opacity"; break;
+ case BlackChannel: type="black"; break;
+ default: type="unknown"; break;
+ }
+ else
+ switch (channel)
+ {
+ case RedChannel: type="red"; break;
+ case GreenChannel: type="green"; break;
+ case BlueChannel: type="blue"; break;
+ case OpacityChannel: type="opacity"; break;
+ default: type="unknown"; break;
+ }
+ (void) CopyMagickString(subexpression,expression+6,MaxTextExtent);
+ if (strlen(subexpression) > 1)
+ subexpression[strlen(subexpression)-1]='\0';
+ if (fx_info->file != (FILE *) NULL)
+ (void) fprintf(fx_info->file,"%s[%ld,%ld].%s: %s=%g\n",
+ fx_info->images->filename,x,y,type,subexpression,(double) alpha);
+ return(0.0);
+ }
+ break;
+ }
+ case 'E':
+ case 'e':
+ {
+ if (LocaleCompare(expression,"epsilon") == 0)
+ return((MagickRealType) MagickEpsilon);
+ if (LocaleNCompare(expression,"exp",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) exp((double) alpha));
+ }
+ if (LocaleCompare(expression,"e") == 0)
+ return((MagickRealType) 2.7182818284590452354);
+ break;
+ }
+ case 'F':
+ case 'f':
+ {
+ if (LocaleNCompare(expression,"floor",5) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
+ exception);
+ return((MagickRealType) floor((double) alpha));
+ }
+ break;
+ }
+ case 'G':
+ case 'g':
+ {
+ if (LocaleCompare(expression,"g") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'H':
+ case 'h':
+ {
+ if (LocaleCompare(expression,"h") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ if (LocaleCompare(expression,"hue") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ if (LocaleNCompare(expression,"hypot",5) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
+ exception);
+ return((MagickRealType) hypot((double) alpha,(double) *beta));
+ }
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ if (LocaleCompare(expression,"k") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ if (LocaleCompare(expression,"intensity") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ if (LocaleNCompare(expression,"int",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) floor(alpha+0.5));
+ }
+ if (LocaleCompare(expression,"i") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'J':
+ case 'j':
+ {
+ if (LocaleCompare(expression,"j") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'L':
+ case 'l':
+ {
+ if (LocaleNCompare(expression,"ln",2) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+2,beta,
+ exception);
+ return((MagickRealType) log((double) alpha));
+ }
+ if (LocaleNCompare(expression,"logtwo",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) log10((double) alpha))/log10(2.0);
+ }
+ if (LocaleNCompare(expression,"log",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) log10((double) alpha));
+ }
+ if (LocaleCompare(expression,"lightness") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if (LocaleCompare(expression,"MaxRGB") == 0)
+ return((MagickRealType) QuantumRange);
+ if (LocaleNCompare(expression,"maxima",6) == 0)
+ break;
+ if (LocaleNCompare(expression,"max",3) == 0)
+ return(FxMax(fx_info,channel,x,y,expression+3,exception));
+ if (LocaleNCompare(expression,"minima",6) == 0)
+ break;
+ if (LocaleNCompare(expression,"min",3) == 0)
+ return(FxMin(fx_info,channel,x,y,expression+3,exception));
+ if (LocaleNCompare(expression,"mod",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) fmod((double) alpha,(double) *beta));
+ }
+ if (LocaleCompare(expression,"m") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare(expression,"n") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'O':
+ case 'o':
+ {
+ if (LocaleCompare(expression,"Opaque") == 0)
+ return(1.0);
+ if (LocaleCompare(expression,"o") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ if (LocaleCompare(expression,"pi") == 0)
+ return((MagickRealType) MagickPI);
+ if (LocaleNCompare(expression,"pow",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) pow((double) alpha,(double) *beta));
+ }
+ if (LocaleCompare(expression,"p") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'Q':
+ case 'q':
+ {
+ if (LocaleCompare(expression,"QuantumRange") == 0)
+ return((MagickRealType) QuantumRange);
+ if (LocaleCompare(expression,"QuantumScale") == 0)
+ return((MagickRealType) QuantumScale);
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ if (LocaleNCompare(expression,"rand",4) == 0)
+ return((MagickRealType) GetPseudoRandomValue(fx_info->random_info));
+ if (LocaleNCompare(expression,"round",5) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+5,beta,
+ exception);
+ if (alpha >= 0.0)
+ return((MagickRealType) floor((double) alpha+0.5));
+ return((MagickRealType) ceil((double) alpha-0.5));
+ }
+ if (LocaleCompare(expression,"r") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare(expression,"saturation") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ if (LocaleNCompare(expression,"sign",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return(alpha < 0.0 ? -1.0 : 1.0);
+ }
+ if (LocaleNCompare(expression,"sinh",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) sinh((double) alpha));
+ }
+ if (LocaleNCompare(expression,"sin",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) sin((double) alpha));
+ }
+ if (LocaleNCompare(expression,"sqrt",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) sqrt((double) alpha));
+ }
+ if (LocaleCompare(expression,"s") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if (LocaleNCompare(expression,"tanh",4) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+4,beta,
+ exception);
+ return((MagickRealType) tanh((double) alpha));
+ }
+ if (LocaleNCompare(expression,"tan",3) == 0)
+ {
+ alpha=FxEvaluateSubexpression(fx_info,channel,x,y,expression+3,beta,
+ exception);
+ return((MagickRealType) tan((double) alpha));
+ }
+ if (LocaleCompare(expression,"Transparent") == 0)
+ return(0.0);
+ if (LocaleCompare(expression,"t") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'U':
+ case 'u':
+ {
+ if (LocaleCompare(expression,"u") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'V':
+ case 'v':
+ {
+ if (LocaleCompare(expression,"v") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'W':
+ case 'w':
+ {
+ if (LocaleCompare(expression,"w") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ if (LocaleCompare(expression,"y") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ case 'Z':
+ case 'z':
+ {
+ if (LocaleCompare(expression,"z") == 0)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ break;
+ }
+ default:
+ break;
+ }
+ q=(char *) expression;
+ alpha=strtod(expression,&q);
+ if (q == expression)
+ return(FxGetSymbol(fx_info,channel,x,y,expression,exception));
+ return(alpha);
+}
+
+MagickExport MagickBooleanType FxEvaluateExpression(FxInfo *fx_info,
+ MagickRealType *alpha,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType FxPreprocessExpression(FxInfo *fx_info,
+ MagickRealType *alpha,ExceptionInfo *exception)
+{
+ FILE
+ *file;
+
+ MagickBooleanType
+ status;
+
+ file=fx_info->file;
+ fx_info->file=(FILE *) NULL;
+ status=FxEvaluateChannelExpression(fx_info,GrayChannel,0,0,alpha,exception);
+ fx_info->file=file;
+ return(status);
+}
+
+MagickExport MagickBooleanType FxEvaluateChannelExpression(FxInfo *fx_info,
+ const ChannelType channel,const long x,const long y,MagickRealType *alpha,
+ ExceptionInfo *exception)
+{
+ MagickRealType
+ beta;
+
+ beta=0.0;
+ *alpha=FxEvaluateSubexpression(fx_info,channel,x,y,fx_info->expression,&beta,
+ exception);
+ return(exception->severity == OptionError ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F x I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FxImage() applies a mathematical expression to the specified image.
+%
+% The format of the FxImage method is:
+%
+% Image *FxImage(const Image *image,const char *expression,
+% ExceptionInfo *exception)
+% Image *FxImageChannel(const Image *image,const ChannelType channel,
+% const char *expression,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o expression: A mathematical expression.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static FxInfo **DestroyFxThreadSet(FxInfo **fx_info)
+{
+ register long
+ i;
+
+ assert(fx_info != (FxInfo **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (fx_info[i] != (FxInfo *) NULL)
+ fx_info[i]=DestroyFxInfo(fx_info[i]);
+ fx_info=(FxInfo **) RelinquishAlignedMemory(fx_info);
+ return(fx_info);
+}
+
+static FxInfo **AcquireFxThreadSet(const Image *image,const char *expression,
+ ExceptionInfo *exception)
+{
+ char
+ *fx_expression;
+
+ FxInfo
+ **fx_info;
+
+ MagickRealType
+ alpha;
+
+ register long
+ i;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ fx_info=(FxInfo **) AcquireAlignedMemory(number_threads,sizeof(*fx_info));
+ if (fx_info == (FxInfo **) NULL)
+ return((FxInfo **) NULL);
+ (void) ResetMagickMemory(fx_info,0,number_threads*sizeof(*fx_info));
+ if (*expression != '@')
+ fx_expression=ConstantString(expression);
+ else
+ fx_expression=FileToString(expression+1,~0,exception);
+ for (i=0; i < (long) number_threads; i++)
+ {
+ fx_info[i]=AcquireFxInfo(image,fx_expression);
+ if (fx_info[i] == (FxInfo *) NULL)
+ return(DestroyFxThreadSet(fx_info));
+ (void) FxPreprocessExpression(fx_info[i],&alpha,fx_info[i]->exception);
+ }
+ fx_expression=DestroyString(fx_expression);
+ return(fx_info);
+}
+
+MagickExport Image *FxImage(const Image *image,const char *expression,
+ ExceptionInfo *exception)
+{
+ Image
+ *fx_image;
+
+ fx_image=FxImageChannel(image,GrayChannel,expression,exception);
+ return(fx_image);
+}
+
+MagickExport Image *FxImageChannel(const Image *image,const ChannelType channel,
+ const char *expression,ExceptionInfo *exception)
+{
+#define FxImageTag "Fx/Image"
+
+ FxInfo
+ **fx_info;
+
+ Image
+ *fx_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickRealType
+ alpha;
+
+ CacheView
+ *fx_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ fx_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (fx_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&fx_image->exception);
+ fx_image=DestroyImage(fx_image);
+ return((Image *) NULL);
+ }
+ fx_info=AcquireFxThreadSet(image,expression,exception);
+ if (fx_info == (FxInfo **) NULL)
+ {
+ fx_image=DestroyImage(fx_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ status=FxPreprocessExpression(fx_info[0],&alpha,exception);
+ if (status == MagickFalse)
+ {
+ fx_image=DestroyImage(fx_image);
+ fx_info=DestroyFxThreadSet(fx_info);
+ return((Image *) NULL);
+ }
+ /*
+ Fx image.
+ */
+ status=MagickTrue;
+ progress=0;
+ fx_view=AcquireCacheView(fx_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) fx_image->rows; y++)
+ {
+ MagickRealType
+ alpha;
+
+ register IndexPacket
+ *__restrict fx_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(fx_view,0,y,fx_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);
+ id=GetOpenMPThreadId();
+ alpha=0.0;
+ for (x=0; x < (long) fx_image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ (void) FxEvaluateChannelExpression(fx_info[id],RedChannel,x,y,
+ &alpha,exception);
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*alpha);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ (void) FxEvaluateChannelExpression(fx_info[id],GreenChannel,x,y,
+ &alpha,exception);
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*alpha);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ (void) FxEvaluateChannelExpression(fx_info[id],BlueChannel,x,y,
+ &alpha,exception);
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*alpha);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ (void) FxEvaluateChannelExpression(fx_info[id],OpacityChannel,x,y,
+ &alpha,exception);
+ if (image->matte == MagickFalse)
+ q->opacity=RoundToQuantum((MagickRealType) QuantumRange*alpha);
+ else
+ q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-
+ QuantumRange*alpha));
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (fx_image->colorspace == CMYKColorspace))
+ {
+ (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y,
+ &alpha,exception);
+ fx_indexes[x]=(IndexPacket) RoundToQuantum((MagickRealType)
+ QuantumRange*alpha);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(fx_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_FxImageChannel)
+#endif
+ proceed=SetImageProgress(image,FxImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ fx_image->matte=fx_info[0]->matte;
+ fx_view=DestroyCacheView(fx_view);
+ fx_info=DestroyFxThreadSet(fx_info);
+ if (status == MagickFalse)
+ fx_image=DestroyImage(fx_image);
+ return(fx_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m p l o d e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImplodeImage() creates a new image that is a copy of an existing
+% one with the image pixels "implode" by the specified percentage. It
+% allocates the memory necessary for the new Image structure and returns a
+% pointer to the new image.
+%
+% The format of the ImplodeImage method is:
+%
+% Image *ImplodeImage(const Image *image,const double amount,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o implode_image: Method ImplodeImage returns a pointer to the image
+% after it is implode. A null image is returned if there is a memory
+% shortage.
+%
+% o image: the image.
+%
+% o amount: Define the extent of the implosion.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ImplodeImage(const Image *image,const double amount,
+ ExceptionInfo *exception)
+{
+#define ImplodeImageTag "Implode/Image"
+
+ Image
+ *implode_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ radius;
+
+ PointInfo
+ center,
+ scale;
+
+ ResampleFilter
+ **resample_filter;
+
+ CacheView
+ *image_view,
+ *implode_view;
+
+ /*
+ Initialize implode image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ implode_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (implode_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&implode_image->exception);
+ implode_image=DestroyImage(implode_image);
+ return((Image *) NULL);
+ }
+ if (implode_image->background_color.opacity != OpaqueOpacity)
+ implode_image->matte=MagickTrue;
+ /*
+ Compute scaling factor.
+ */
+ scale.x=1.0;
+ scale.y=1.0;
+ center.x=0.5*image->columns;
+ center.y=0.5*image->rows;
+ radius=center.x;
+ if (image->columns > image->rows)
+ scale.y=(double) image->columns/(double) image->rows;
+ else
+ if (image->columns < image->rows)
+ {
+ scale.x=(double) image->rows/(double) image->columns;
+ radius=center.y;
+ }
+ /*
+ Implode image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(implode_image,&zero);
+ resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
+ image_view=AcquireCacheView(image);
+ implode_view=AcquireCacheView(implode_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ distance;
+
+ PointInfo
+ delta;
+
+ register IndexPacket
+ *__restrict implode_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
+ delta.y=scale.y*(double) (y-center.y);
+ pixel=zero;
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Determine if the pixel is within an ellipse.
+ */
+ delta.x=scale.x*(double) (x-center.x);
+ distance=delta.x*delta.x+delta.y*delta.y;
+ if (distance < (radius*radius))
+ {
+ double
+ factor;
+
+ /*
+ Implode the pixel.
+ */
+ factor=1.0;
+ if (distance > 0.0)
+ factor=pow(sin((double) (MagickPI*sqrt((double) distance)/
+ radius/2)),-amount);
+ (void) ResamplePixelColor(resample_filter[id],(double)
+ (factor*delta.x/scale.x+center.x),(double) (factor*delta.y/
+ scale.y+center.y),&pixel);
+ SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ImplodeImage)
+#endif
+ proceed=SetImageProgress(image,ImplodeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ implode_view=DestroyCacheView(implode_view);
+ image_view=DestroyCacheView(image_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ if (status == MagickFalse)
+ implode_image=DestroyImage(implode_image);
+ return(implode_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o r p h I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The MorphImages() method requires a minimum of two images. The first
+% image is transformed into the second by a number of intervening images
+% as specified by frames.
+%
+% The format of the MorphImage method is:
+%
+% Image *MorphImages(const Image *image,const unsigned long number_frames,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o number_frames: Define the number of in-between image to generate.
+% The more in-between frames, the smoother the morph.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MorphImages(const Image *image,
+ const unsigned long number_frames,ExceptionInfo *exception)
+{
+#define MorphImageTag "Morph/Image"
+
+ Image
+ *morph_image,
+ *morph_images;
+
+ long
+ y;
+
+ MagickOffsetType
+ scene;
+
+ MagickRealType
+ alpha,
+ beta;
+
+ register const Image
+ *next;
+
+ register long
+ i;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Clone first frame in sequence.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ morph_images=CloneImage(image,0,0,MagickTrue,exception);
+ if (morph_images == (Image *) NULL)
+ return((Image *) NULL);
+ if (GetNextImageInList(image) == (Image *) NULL)
+ {
+ /*
+ Morph single image.
+ */
+ for (i=1; i < (long) number_frames; i++)
+ {
+ morph_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (morph_image == (Image *) NULL)
+ {
+ morph_images=DestroyImageList(morph_images);
+ return((Image *) NULL);
+ }
+ AppendImageToList(&morph_images,morph_image);
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(i,number_frames) != MagickFalse))
+ {
+ status=image->progress_monitor(MorphImageTag,i,number_frames,
+ image->client_data);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ return(GetFirstImageInList(morph_images));
+ }
+ /*
+ Morph image sequence.
+ */
+ status=MagickTrue;
+ scene=0;
+ next=image;
+ for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ for (i=0; i < (long) number_frames; i++)
+ {
+ CacheView
+ *image_view,
+ *morph_view;
+
+ beta=(MagickRealType) (i+1.0)/(MagickRealType) (number_frames+1.0);
+ alpha=1.0-beta;
+ morph_image=ZoomImage(next,(unsigned long) (alpha*next->columns+beta*
+ GetNextImageInList(next)->columns+0.5),(unsigned long) (alpha*
+ next->rows+beta*GetNextImageInList(next)->rows+0.5),exception);
+ if (morph_image == (Image *) NULL)
+ {
+ morph_images=DestroyImageList(morph_images);
+ return((Image *) NULL);
+ }
+ if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&morph_image->exception);
+ morph_image=DestroyImage(morph_image);
+ return((Image *) NULL);
+ }
+ AppendImageToList(&morph_images,morph_image);
+ morph_images=GetLastImageInList(morph_images);
+ morph_image=ZoomImage(GetNextImageInList(next),morph_images->columns,
+ morph_images->rows,exception);
+ if (morph_image == (Image *) NULL)
+ {
+ morph_images=DestroyImageList(morph_images);
+ return((Image *) NULL);
+ }
+ image_view=AcquireCacheView(morph_image);
+ morph_view=AcquireCacheView(morph_images);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) morph_images->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
+ exception);
+ q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) morph_images->columns; x++)
+ {
+ q->red=RoundToQuantum(alpha*q->red+beta*p->red);
+ q->green=RoundToQuantum(alpha*q->green+beta*p->green);
+ q->blue=RoundToQuantum(alpha*q->blue+beta*p->blue);
+ q->opacity=RoundToQuantum(alpha*q->opacity+beta*p->opacity);
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(morph_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ morph_view=DestroyCacheView(morph_view);
+ image_view=DestroyCacheView(image_view);
+ morph_image=DestroyImage(morph_image);
+ }
+ if (i < (long) number_frames)
+ break;
+ /*
+ Clone last frame in sequence.
+ */
+ morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
+ if (morph_image == (Image *) NULL)
+ {
+ morph_images=DestroyImageList(morph_images);
+ return((Image *) NULL);
+ }
+ AppendImageToList(&morph_images,morph_image);
+ morph_images=GetLastImageInList(morph_images);
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_MorphImages)
+#endif
+ proceed=SetImageProgress(image,MorphImageTag,scene,
+ GetImageListLength(image));
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ scene++;
+ }
+ if (GetNextImageInList(next) != (Image *) NULL)
+ {
+ morph_images=DestroyImageList(morph_images);
+ return((Image *) NULL);
+ }
+ return(GetFirstImageInList(morph_images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P l a s m a I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PlasmaImage() initializes an image with plasma fractal values. The image
+% must be initialized with a base color and the random number generator
+% seeded before this method is called.
+%
+% The format of the PlasmaImage method is:
+%
+% MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
+% unsigned long attenuate,unsigned long depth)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o segment: Define the region to apply plasma fractals values.
+%
+% o attenuate: Define the plasmattenuation factor.
+%
+% o depth: Limit the plasma recursion depth.
+%
+*/
+
+static inline Quantum PlasmaPixel(RandomInfo *random_info,
+ const MagickRealType pixel,const MagickRealType noise)
+{
+ Quantum
+ plasma;
+
+ plasma=RoundToQuantum(pixel+noise*GetPseudoRandomValue(random_info)-
+ noise/2.0);
+ return(plasma);
+}
+
+MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
+ RandomInfo *random_info,const SegmentInfo *segment,unsigned long attenuate,
+ unsigned long depth)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ x,
+ x_mid,
+ y,
+ y_mid;
+
+ MagickRealType
+ plasma;
+
+ PixelPacket
+ u,
+ v;
+
+ if (((segment->x2-segment->x1) == 0.0) && ((segment->y2-segment->y1) == 0.0))
+ return(MagickTrue);
+ if (depth != 0)
+ {
+ SegmentInfo
+ local_info;
+
+ /*
+ Divide the area into quadrants and recurse.
+ */
+ depth--;
+ attenuate++;
+ x_mid=(long) (segment->x1+segment->x2+0.5)/2;
+ y_mid=(long) (segment->y1+segment->y2+0.5)/2;
+ local_info=(*segment);
+ local_info.x2=(double) x_mid;
+ local_info.y2=(double) y_mid;
+ (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
+ local_info=(*segment);
+ local_info.y1=(double) y_mid;
+ local_info.x2=(double) x_mid;
+ (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
+ local_info=(*segment);
+ local_info.x1=(double) x_mid;
+ local_info.y2=(double) y_mid;
+ (void) PlasmaImageProxy(image,random_info,&local_info,attenuate,depth);
+ local_info=(*segment);
+ local_info.x1=(double) x_mid;
+ local_info.y1=(double) y_mid;
+ return(PlasmaImageProxy(image,random_info,&local_info,attenuate,depth));
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ x_mid=(long) (segment->x1+segment->x2+0.5)/2;
+ y_mid=(long) (segment->y1+segment->y2+0.5)/2;
+ if ((segment->x1 == (double) x_mid) && (segment->x2 == (double) x_mid) &&
+ (segment->y1 == (double) y_mid) && (segment->y2 == (double) y_mid))
+ return(MagickFalse);
+ /*
+ Average pixels and apply plasma.
+ */
+ exception=(&image->exception);
+ plasma=(MagickRealType) QuantumRange/(2.0*attenuate);
+ if ((segment->x1 != (double) x_mid) || (segment->x2 != (double) x_mid))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Left pixel.
+ */
+ x=(long) (segment->x1+0.5);
+ (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,exception);
+ (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,exception);
+ q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickTrue);
+ q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
+ plasma);
+ q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
+ plasma);
+ q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
+ plasma);
+ (void) SyncAuthenticPixels(image,exception);
+ if (segment->x1 != segment->x2)
+ {
+ /*
+ Right pixel.
+ */
+ x=(long) (segment->x2+0.5);
+ (void) GetOneVirtualPixel(image,x,(long) (segment->y1+0.5),&u,
+ exception);
+ (void) GetOneVirtualPixel(image,x,(long) (segment->y2+0.5),&v,
+ exception);
+ q=QueueAuthenticPixels(image,x,y_mid,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickTrue);
+ q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
+ plasma);
+ q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
+ 2.0,plasma);
+ q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
+ plasma);
+ (void) SyncAuthenticPixels(image,exception);
+ }
+ }
+ if ((segment->y1 != (double) y_mid) || (segment->y2 != (double) y_mid))
+ {
+ if ((segment->x1 != (double) x_mid) || (segment->y2 != (double) y_mid))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Bottom pixel.
+ */
+ y=(long) (segment->y2+0.5);
+ (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
+ exception);
+ (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
+ exception);
+ q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickTrue);
+ q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
+ plasma);
+ q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
+ 2.0,plasma);
+ q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
+ plasma);
+ (void) SyncAuthenticPixels(image,exception);
+ }
+ if (segment->y1 != segment->y2)
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Top pixel.
+ */
+ y=(long) (segment->y1+0.5);
+ (void) GetOneVirtualPixel(image,(long) (segment->x1+0.5),y,&u,
+ exception);
+ (void) GetOneVirtualPixel(image,(long) (segment->x2+0.5),y,&v,
+ exception);
+ q=QueueAuthenticPixels(image,x_mid,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickTrue);
+ q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
+ plasma);
+ q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/
+ 2.0,plasma);
+ q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
+ plasma);
+ (void) SyncAuthenticPixels(image,exception);
+ }
+ }
+ if ((segment->x1 != segment->x2) || (segment->y1 != segment->y2))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Middle pixel.
+ */
+ x=(long) (segment->x1+0.5);
+ y=(long) (segment->y1+0.5);
+ (void) GetOneVirtualPixel(image,x,y,&u,exception);
+ x=(long) (segment->x2+0.5);
+ y=(long) (segment->y2+0.5);
+ (void) GetOneVirtualPixel(image,x,y,&v,exception);
+ q=QueueAuthenticPixels(image,x_mid,y_mid,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickTrue);
+ q->red=PlasmaPixel(random_info,(MagickRealType) (u.red+v.red)/2.0,
+ plasma);
+ q->green=PlasmaPixel(random_info,(MagickRealType) (u.green+v.green)/2.0,
+ plasma);
+ q->blue=PlasmaPixel(random_info,(MagickRealType) (u.blue+v.blue)/2.0,
+ plasma);
+ (void) SyncAuthenticPixels(image,exception);
+ }
+ if (((segment->x2-segment->x1) < 3.0) && ((segment->y2-segment->y1) < 3.0))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+MagickExport MagickBooleanType PlasmaImage(Image *image,
+ const SegmentInfo *segment,unsigned long attenuate,unsigned long depth)
+{
+ MagickBooleanType
+ status;
+
+ RandomInfo
+ *random_info;
+
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ random_info=AcquireRandomInfo();
+ status=PlasmaImageProxy(image,random_info,segment,attenuate,depth);
+ random_info=DestroyRandomInfo(random_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P o l a r o i d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PolaroidImage() simulates a Polaroid picture.
+%
+% The format of the AnnotateImage method is:
+%
+% Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
+% const double angle,ExceptionInfo exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o draw_info: the draw info.
+%
+% o angle: Apply the effect along this angle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
+ const double angle,ExceptionInfo *exception)
+{
+ const char
+ *value;
+
+ long
+ quantum;
+
+ Image
+ *bend_image,
+ *caption_image,
+ *flop_image,
+ *picture_image,
+ *polaroid_image,
+ *rotate_image,
+ *trim_image;
+
+ unsigned long
+ height;
+
+ /*
+ Simulate a Polaroid picture.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ quantum=(long) MagickMax(MagickMax((double) image->columns,(double)
+ image->rows)/25.0,10.0);
+ height=image->rows+2*quantum;
+ caption_image=(Image *) NULL;
+ value=GetImageProperty(image,"Caption");
+ if (value != (const char *) NULL)
+ {
+ char
+ *caption,
+ geometry[MaxTextExtent];
+
+ DrawInfo
+ *annotate_info;
+
+ long
+ count;
+
+ MagickBooleanType
+ status;
+
+ TypeMetric
+ metrics;
+
+ /*
+ Generate caption image.
+ */
+ caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
+ if (caption_image == (Image *) NULL)
+ return((Image *) NULL);
+ annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
+ caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
+ value);
+ (void) CloneString(&annotate_info->text,caption);
+ count=FormatMagickCaption(caption_image,annotate_info,&metrics,&caption);
+ status=SetImageExtent(caption_image,image->columns,(unsigned long)
+ ((count+1)*(metrics.ascent-metrics.descent)+0.5));
+ if (status == MagickFalse)
+ caption_image=DestroyImage(caption_image);
+ else
+ {
+ caption_image->background_color=image->border_color;
+ (void) SetImageBackgroundColor(caption_image);
+ (void) CloneString(&annotate_info->text,caption);
+ (void) FormatMagickString(geometry,MaxTextExtent,"+0+%g",
+ metrics.ascent);
+ if (annotate_info->gravity == UndefinedGravity)
+ (void) CloneString(&annotate_info->geometry,AcquireString(
+ geometry));
+ (void) AnnotateImage(caption_image,annotate_info);
+ height+=caption_image->rows;
+ }
+ annotate_info=DestroyDrawInfo(annotate_info);
+ caption=DestroyString(caption);
+ }
+ picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
+ exception);
+ if (picture_image == (Image *) NULL)
+ {
+ if (caption_image != (Image *) NULL)
+ caption_image=DestroyImage(caption_image);
+ return((Image *) NULL);
+ }
+ picture_image->background_color=image->border_color;
+ (void) SetImageBackgroundColor(picture_image);
+ (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
+ if (caption_image != (Image *) NULL)
+ {
+ (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
+ quantum,(long) (image->rows+3*quantum/2));
+ caption_image=DestroyImage(caption_image);
+ }
+ (void) QueryColorDatabase("none",&picture_image->background_color,exception);
+ (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
+ rotate_image=RotateImage(picture_image,90.0,exception);
+ picture_image=DestroyImage(picture_image);
+ if (rotate_image == (Image *) NULL)
+ return((Image *) NULL);
+ picture_image=rotate_image;
+ bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
+ picture_image->columns,exception);
+ picture_image=DestroyImage(picture_image);
+ if (bend_image == (Image *) NULL)
+ return((Image *) NULL);
+ InheritException(&bend_image->exception,exception);
+ picture_image=bend_image;
+ rotate_image=RotateImage(picture_image,-90.0,exception);
+ picture_image=DestroyImage(picture_image);
+ if (rotate_image == (Image *) NULL)
+ return((Image *) NULL);
+ picture_image=rotate_image;
+ picture_image->background_color=image->background_color;
+ polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
+ exception);
+ if (polaroid_image == (Image *) NULL)
+ {
+ picture_image=DestroyImage(picture_image);
+ return(picture_image);
+ }
+ flop_image=FlopImage(polaroid_image,exception);
+ polaroid_image=DestroyImage(polaroid_image);
+ if (flop_image == (Image *) NULL)
+ {
+ picture_image=DestroyImage(picture_image);
+ return(picture_image);
+ }
+ polaroid_image=flop_image;
+ (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
+ (long) (-0.01*picture_image->columns/2.0),0L);
+ picture_image=DestroyImage(picture_image);
+ (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
+ rotate_image=RotateImage(polaroid_image,angle,exception);
+ polaroid_image=DestroyImage(polaroid_image);
+ if (rotate_image == (Image *) NULL)
+ return((Image *) NULL);
+ polaroid_image=rotate_image;
+ trim_image=TrimImage(polaroid_image,exception);
+ polaroid_image=DestroyImage(polaroid_image);
+ if (trim_image == (Image *) NULL)
+ return((Image *) NULL);
+ polaroid_image=trim_image;
+ return(polaroid_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e c o l o r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RecolorImage() translate, scale, shear, or rotate image colors. Although
+% you can use variable sized matrices, typically you use a 5 x 5 for an RGBA
+% image and a 6x6 for CMYKA. Populate the last row with normalized values to
+% translate.
+%
+% The format of the RecolorImage method is:
+%
+% Image *RecolorImage(const Image *image,const unsigned long order,
+% const double *color_matrix,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o order: the number of columns and rows in the recolor matrix.
+%
+% o color_matrix: An array of double representing the recolor matrix.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *RecolorImage(const Image *image,const unsigned long order,
+ const double *color_matrix,ExceptionInfo *exception)
+{
+#define RecolorImageTag "Recolor/Image"
+
+ Image
+ *recolor_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ register const double
+ *k;
+
+ CacheView
+ *image_view,
+ *recolor_view;
+
+ /*
+ Initialize image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ recolor_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (recolor_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(recolor_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&recolor_image->exception);
+ recolor_image=DestroyImage(recolor_image);
+ return((Image *) NULL);
+ }
+ if (image->debug != MagickFalse)
+ {
+ char
+ format[MaxTextExtent],
+ *message;
+
+ long
+ u,
+ v;
+
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),
+ " Recolor image with %ldx%ld color matrix:",order,order);
+ message=AcquireString("");
+ k=color_matrix;
+ for (v=0; v < (long) order; v++)
+ {
+ *message='\0';
+ (void) FormatMagickString(format,MaxTextExtent,"%ld: ",v);
+ (void) ConcatenateString(&message,format);
+ for (u=0; u < (long) order; u++)
+ {
+ (void) FormatMagickString(format,MaxTextExtent,"%+f ",*k++);
+ (void) ConcatenateString(&message,format);
+ }
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
+ }
+ message=DestroyString(message);
+ }
+ /*
+ Recolor image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ k=color_matrix;
+ image_view=AcquireCacheView(image);
+ recolor_view=AcquireCacheView(recolor_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel,
+ recolor_pixel;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register IndexPacket
+ *__restrict recolor_indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(recolor_view,0,y,recolor_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ recolor_indexes=GetCacheViewAuthenticIndexQueue(recolor_view);
+ pixel=zero;
+ recolor_pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ SetMagickPixelPacket(recolor_image,p,indexes,&recolor_pixel);
+ switch (order)
+ {
+ case 0:
+ break;
+ case 1:
+ {
+ recolor_pixel.red=k[0]*pixel.red;
+ break;
+ }
+ case 2:
+ {
+ recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green;
+ recolor_pixel.green=k[2]*pixel.red+k[3]*pixel.green;
+ break;
+ }
+ case 3:
+ {
+ recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue;
+ recolor_pixel.green=k[3]*pixel.red+k[4]*pixel.green+k[5]*pixel.blue;
+ recolor_pixel.blue=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue;
+ break;
+ }
+ case 4:
+ {
+ recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
+ k[12]*QuantumRange;
+ recolor_pixel.green=k[4]*pixel.red+k[5]*pixel.green+k[6]*pixel.blue+
+ k[13]*QuantumRange;
+ recolor_pixel.blue=k[8]*pixel.red+k[9]*pixel.green+k[10]*pixel.blue+
+ k[14]*QuantumRange;
+ break;
+ }
+ case 5:
+ {
+ recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
+ k[3]*(QuantumRange-pixel.opacity)+k[20]*QuantumRange;
+ recolor_pixel.green=k[5]*pixel.red+k[6]*pixel.green+k[7]*pixel.blue+
+ k[8]*(QuantumRange-pixel.opacity)+k[21]*QuantumRange;
+ recolor_pixel.blue=k[10]*pixel.red+k[11]*pixel.green+k[12]*pixel.blue+
+ k[13]*(QuantumRange-pixel.opacity)+k[22]*QuantumRange;
+ recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[15]*pixel.red+
+ k[16]*pixel.green+k[17]*pixel.blue+k[18]*(QuantumRange-
+ pixel.opacity)+k[23]*QuantumRange);
+ break;
+ }
+ default:
+ {
+ recolor_pixel.red=k[0]*pixel.red+k[1]*pixel.green+k[2]*pixel.blue+
+ k[3]*pixel.index+k[4]*((Quantum) QuantumRange-pixel.opacity)+
+ k[30]*QuantumRange;
+ recolor_pixel.green=k[6]*pixel.red+k[7]*pixel.green+k[8]*pixel.blue+
+ k[9]*pixel.index+k[10]*((Quantum) QuantumRange-pixel.opacity)+
+ k[31]*QuantumRange;
+ recolor_pixel.blue=k[12]*pixel.red+k[13]*pixel.green+k[14]*pixel.blue+
+ k[15]*pixel.index+k[16]*((Quantum) QuantumRange-pixel.opacity)+
+ k[32]*QuantumRange;
+ if (image->colorspace == CMYKColorspace)
+ recolor_pixel.index=k[18]*pixel.red+k[19]*pixel.green+k[20]*
+ pixel.blue+k[21]*pixel.index+k[22]*((Quantum) QuantumRange-
+ pixel.opacity)+k[33]*QuantumRange;
+ recolor_pixel.opacity=(MagickRealType) QuantumRange-(k[24]*pixel.red+
+ k[25]*pixel.green+k[26]*pixel.blue+k[27]*pixel.index+k[28]*
+ (QuantumRange-pixel.opacity)+k[34]*QuantumRange);
+ break;
+ }
+ }
+ q->red=RoundToQuantum(recolor_pixel.red);
+ q->green=RoundToQuantum(recolor_pixel.green);
+ q->blue=RoundToQuantum(recolor_pixel.blue);
+ q->opacity=RoundToQuantum(recolor_pixel.opacity);
+ if (image->colorspace == CMYKColorspace)
+ recolor_indexes[x]=RoundToQuantum(recolor_pixel.index);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(recolor_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RecolorImage)
+#endif
+ proceed=SetImageProgress(image,RecolorImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ recolor_view=DestroyCacheView(recolor_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ recolor_image=DestroyImage(recolor_image);
+ return(recolor_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e p i a T o n e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickSepiaToneImage() applies a special effect to the image, similar to the
+% effect achieved in a photo darkroom by sepia toning. Threshold ranges from
+% 0 to QuantumRange and is a measure of the extent of the sepia toning. A
+% threshold of 80% is a good starting point for a reasonable tone.
+%
+% The format of the SepiaToneImage method is:
+%
+% Image *SepiaToneImage(const Image *image,const double threshold,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o threshold: the tone threshold.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
+ ExceptionInfo *exception)
+{
+#define SepiaToneImageTag "SepiaTone/Image"
+
+ Image
+ *sepia_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view,
+ *sepia_view;
+
+ /*
+ Initialize sepia-toned image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ sepia_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (sepia_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&sepia_image->exception);
+ sepia_image=DestroyImage(sepia_image);
+ return((Image *) NULL);
+ }
+ /*
+ Tone each row of the image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ sepia_view=AcquireCacheView(sepia_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickRealType
+ intensity,
+ tone;
+
+ intensity=(MagickRealType) PixelIntensityToQuantum(p);
+ tone=intensity > threshold ? (MagickRealType) QuantumRange : intensity+
+ (MagickRealType) QuantumRange-threshold;
+ q->red=RoundToQuantum(tone);
+ tone=intensity > (7.0*threshold/6.0) ? (MagickRealType) QuantumRange :
+ intensity+(MagickRealType) QuantumRange-7.0*threshold/6.0;
+ q->green=RoundToQuantum(tone);
+ tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
+ q->blue=RoundToQuantum(tone);
+ tone=threshold/7.0;
+ if ((MagickRealType) q->green < tone)
+ q->green=RoundToQuantum(tone);
+ if ((MagickRealType) q->blue < tone)
+ q->blue=RoundToQuantum(tone);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SepiaToneImage)
+#endif
+ proceed=SetImageProgress(image,SepiaToneImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ sepia_view=DestroyCacheView(sepia_view);
+ image_view=DestroyCacheView(image_view);
+ (void) NormalizeImage(sepia_image);
+ (void) ContrastImage(sepia_image,MagickTrue);
+ if (status == MagickFalse)
+ sepia_image=DestroyImage(sepia_image);
+ return(sepia_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h a d o w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ShadowImage() simulates a shadow from the specified image and returns it.
+%
+% The format of the ShadowImage method is:
+%
+% Image *ShadowImage(const Image *image,const double opacity,
+% const double sigma,const long x_offset,const long y_offset,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o opacity: percentage transparency.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o x_offset: the shadow x-offset.
+%
+% o y_offset: the shadow y-offset.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ShadowImage(const Image *image,const double opacity,
+ const double sigma,const long x_offset,const long y_offset,
+ ExceptionInfo *exception)
+{
+#define ShadowImageTag "Shadow/Image"
+
+ Image
+ *border_image,
+ *clone_image,
+ *shadow_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ border_info;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ clone_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (clone_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
+ clone_image->compose=OverCompositeOp;
+ border_info.width=(unsigned long) (2.0*sigma+0.5);
+ border_info.height=(unsigned long) (2.0*sigma+0.5);
+ border_info.x=0;
+ border_info.y=0;
+ (void) QueryColorDatabase("none",&clone_image->border_color,exception);
+ border_image=BorderImage(clone_image,&border_info,exception);
+ clone_image=DestroyImage(clone_image);
+ if (border_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (border_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
+ /*
+ Shadow image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(border_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) border_image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) border_image->columns; x++)
+ {
+ q->red=border_image->background_color.red;
+ q->green=border_image->background_color.green;
+ q->blue=border_image->background_color.blue;
+ if (border_image->matte == MagickFalse)
+ q->opacity=border_image->background_color.opacity;
+ else
+ q->opacity=RoundToQuantum((MagickRealType) (QuantumRange-(QuantumRange-
+ q->opacity)*opacity/100.0));
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ShadowImage)
+#endif
+ proceed=SetImageProgress(image,ShadowImageTag,progress++,
+ border_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
+ border_image=DestroyImage(border_image);
+ if (shadow_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (shadow_image->page.width == 0)
+ shadow_image->page.width=shadow_image->columns;
+ if (shadow_image->page.height == 0)
+ shadow_image->page.height=shadow_image->rows;
+ shadow_image->page.width+=x_offset-(long) border_info.width;
+ shadow_image->page.height+=y_offset-(long) border_info.height;
+ shadow_image->page.x+=x_offset-(long) border_info.width;
+ shadow_image->page.y+=y_offset-(long) border_info.height;
+ return(shadow_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S k e t c h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SketchImage() simulates a pencil sketch. We convolve the image with a
+% Gaussian operator of the given radius and standard deviation (sigma). For
+% reasonable results, radius should be larger than sigma. Use a radius of 0
+% and SketchImage() selects a suitable radius for you. Angle gives the angle
+% of the sketch.
+%
+% The format of the SketchImage method is:
+%
+% Image *SketchImage(const Image *image,const double radius,
+% const double sigma,const double angle,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting
+% the center pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o angle: Apply the effect along this angle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SketchImage(const Image *image,const double radius,
+ const double sigma,const double angle,ExceptionInfo *exception)
+{
+ Image
+ *blend_image,
+ *blur_image,
+ *dodge_image,
+ *random_image,
+ *sketch_image;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ RandomInfo
+ *random_info;
+
+ CacheView
+ *random_view;
+
+ /*
+ Sketch image.
+ */
+ random_image=CloneImage(image,image->columns << 1,image->rows << 1,
+ MagickTrue,exception);
+ if (random_image == (Image *) NULL)
+ return((Image *) NULL);
+ status=MagickTrue;
+ GetMagickPixelPacket(random_image,&zero);
+ random_info=AcquireRandomInfo();
+ random_view=AcquireCacheView(random_image);
+ for (y=0; y < (long) random_image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(random_view);
+ pixel=zero;
+ for (x=0; x < (long) random_image->columns; x++)
+ {
+ pixel.red=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info));
+ pixel.green=pixel.red;
+ pixel.blue=pixel.red;
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=pixel.red;
+ SetPixelPacket(random_image,&pixel,q,indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (status == MagickFalse)
+ break;
+ }
+ random_view=DestroyCacheView(random_view);
+ random_info=DestroyRandomInfo(random_info);
+ if (status == MagickFalse)
+ {
+ random_image=DestroyImage(random_image);
+ return(random_image);
+ }
+ blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
+ random_image=DestroyImage(random_image);
+ if (blur_image == (Image *) NULL)
+ return((Image *) NULL);
+ dodge_image=EdgeImage(blur_image,radius,exception);
+ blur_image=DestroyImage(blur_image);
+ if (dodge_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) NormalizeImage(dodge_image);
+ (void) NegateImage(dodge_image,MagickFalse);
+ (void) TransformImage(&dodge_image,(char *) NULL,"50%");
+ sketch_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (sketch_image == (Image *) NULL)
+ {
+ dodge_image=DestroyImage(dodge_image);
+ return((Image *) NULL);
+ }
+ (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
+ dodge_image=DestroyImage(dodge_image);
+ blend_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (blend_image == (Image *) NULL)
+ {
+ sketch_image=DestroyImage(sketch_image);
+ return((Image *) NULL);
+ }
+ (void) SetImageArtifact(blend_image,"compose:args","20x80");
+ (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
+ blend_image=DestroyImage(blend_image);
+ return(sketch_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S o l a r i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SolarizeImage() applies a special effect to the image, similar to the effect
+% achieved in a photo darkroom by selectively exposing areas of photo
+% sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
+% measure of the extent of the solarization.
+%
+% The format of the SolarizeImage method is:
+%
+% MagickBooleanType SolarizeImage(Image *image,const double threshold)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o threshold: Define the extent of the solarization.
+%
+*/
+MagickExport MagickBooleanType SolarizeImage(Image *image,
+ const double threshold)
+{
+#define SolarizeImageTag "Solarize/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->storage_class == PseudoClass)
+ {
+ register long
+ i;
+
+ /*
+ Solarize colormap.
+ */
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if ((MagickRealType) image->colormap[i].red > threshold)
+ image->colormap[i].red=(Quantum) QuantumRange-image->colormap[i].red;
+ if ((MagickRealType) image->colormap[i].green > threshold)
+ image->colormap[i].green=(Quantum) QuantumRange-
+ image->colormap[i].green;
+ if ((MagickRealType) image->colormap[i].blue > threshold)
+ image->colormap[i].blue=(Quantum) QuantumRange-
+ image->colormap[i].blue;
+ }
+ }
+ /*
+ Solarize image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((MagickRealType) q->red > threshold)
+ q->red=(Quantum) QuantumRange-q->red;
+ if ((MagickRealType) q->green > threshold)
+ q->green=(Quantum) QuantumRange-q->green;
+ if ((MagickRealType) q->blue > threshold)
+ q->blue=(Quantum) QuantumRange-q->blue;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SolarizeImage)
+#endif
+ proceed=SetImageProgress(image,SolarizeImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t e g a n o I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SteganoImage() hides a digital watermark within the image. Recover
+% the hidden watermark later to prove that the authenticity of an image.
+% Offset defines the start position within the image to hide the watermark.
+%
+% The format of the SteganoImage method is:
+%
+% Image *SteganoImage(const Image *image,Image *watermark,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o watermark: the watermark image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
+ ExceptionInfo *exception)
+{
+#define GetBit(alpha,i) ((((unsigned long) (alpha) >> (unsigned long) \
+ (i)) & 0x01) != 0)
+#define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) ? (unsigned long) (alpha) \
+ | (1UL << (unsigned long) (i)) : (unsigned long) (alpha) & \
+ ~(1UL << (unsigned long) (i)))
+#define SteganoImageTag "Stegano/Image"
+
+ Image
+ *stegano_image;
+
+ int
+ c;
+
+ long
+ i,
+ j,
+ k,
+ y;
+
+ MagickBooleanType
+ status;
+
+ PixelPacket
+ pixel;
+
+ register long
+ x;
+
+ register PixelPacket
+ *q;
+
+ unsigned long
+ depth;
+
+ /*
+ Initialize steganographic image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(watermark != (const Image *) NULL);
+ assert(watermark->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ stegano_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (stegano_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&stegano_image->exception);
+ stegano_image=DestroyImage(stegano_image);
+ return((Image *) NULL);
+ }
+ stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
+ /*
+ Hide watermark in low-order bits of image.
+ */
+ c=0;
+ i=0;
+ j=0;
+ depth=stegano_image->depth;
+ k=image->offset;
+ for (i=(long) depth-1; (i >= 0) && (j < (long) depth); i--)
+ {
+ for (y=0; (y < (long) watermark->rows) && (j < (long) depth); y++)
+ {
+ for (x=0; (x < (long) watermark->columns) && (j < (long) depth); x++)
+ {
+ (void) GetOneVirtualPixel(watermark,x,y,&pixel,exception);
+ if ((k/(long) stegano_image->columns) >= (long) stegano_image->rows)
+ break;
+ q=GetAuthenticPixels(stegano_image,k % (long) stegano_image->columns,
+ k/(long) stegano_image->columns,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ switch (c)
+ {
+ case 0:
+ {
+ SetBit(q->red,j,GetBit(PixelIntensityToQuantum(&pixel),i));
+ break;
+ }
+ case 1:
+ {
+ SetBit(q->green,j,GetBit(PixelIntensityToQuantum(&pixel),i));
+ break;
+ }
+ case 2:
+ {
+ SetBit(q->blue,j,GetBit(PixelIntensityToQuantum(&pixel),i));
+ break;
+ }
+ }
+ if (SyncAuthenticPixels(stegano_image,exception) == MagickFalse)
+ break;
+ c++;
+ if (c == 3)
+ c=0;
+ k++;
+ if (k == (long) (stegano_image->columns*stegano_image->columns))
+ k=0;
+ if (k == image->offset)
+ j++;
+ }
+ }
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick((MagickOffsetType) depth-i,depth) != MagickFalse))
+ {
+ status=image->progress_monitor(SteganoImageTag,(MagickOffsetType) depth-
+ i,depth,image->client_data);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ if (stegano_image->storage_class == PseudoClass)
+ (void) SyncImage(stegano_image);
+ return(stegano_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t e r e o A n a g l y p h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StereoAnaglyphImage() combines two images and produces a single image that
+% is the composite of a left and right image of a stereo pair. Special
+% red-green stereo glasses are required to view this effect.
+%
+% The format of the StereoAnaglyphImage method is:
+%
+% Image *StereoImage(const Image *left_image,const Image *right_image,
+% ExceptionInfo *exception)
+% Image *StereoAnaglyphImage(const Image *left_image,
+% const Image *right_image,const long x_offset,const long y_offset,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o left_image: the left image.
+%
+% o right_image: the right image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+% o x_offset: amount, in pixels, by which the left image is offset to the
+% right of the right image.
+%
+% o y_offset: amount, in pixels, by which the left image is offset to the
+% bottom of the right image.
+%
+%
+*/
+MagickExport Image *StereoImage(const Image *left_image,
+ const Image *right_image,ExceptionInfo *exception)
+{
+ return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
+}
+
+MagickExport Image *StereoAnaglyphImage(const Image *left_image,
+ const Image *right_image,const long x_offset,const long y_offset,
+ ExceptionInfo *exception)
+{
+#define StereoImageTag "Stereo/Image"
+
+ const Image
+ *image;
+
+ Image
+ *stereo_image;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ assert(left_image != (const Image *) NULL);
+ assert(left_image->signature == MagickSignature);
+ if (left_image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ left_image->filename);
+ assert(right_image != (const Image *) NULL);
+ assert(right_image->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert(right_image != (const Image *) NULL);
+ image=left_image;
+ if ((left_image->columns != right_image->columns) ||
+ (left_image->rows != right_image->rows))
+ ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
+ /*
+ Initialize stereo image attributes.
+ */
+ stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
+ MagickTrue,exception);
+ if (stereo_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&stereo_image->exception);
+ stereo_image=DestroyImage(stereo_image);
+ return((Image *) NULL);
+ }
+ /*
+ Copy left image to red channel and right image to blue channel.
+ */
+ for (y=0; y < (long) stereo_image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict r;
+
+ p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
+ exception);
+ q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
+ r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
+ (r == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) stereo_image->columns; x++)
+ {
+ r->red=p->red;
+ r->green=q->green;
+ r->blue=q->blue;
+ r->opacity=(Quantum) ((p->opacity+q->opacity)/2);
+ p++;
+ q++;
+ r++;
+ }
+ if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
+ break;
+ if ((image->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(y,image->rows) != MagickFalse))
+ {
+ status=image->progress_monitor(StereoImageTag,y,stereo_image->rows,
+ stereo_image->client_data);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ return(stereo_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S w i r l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SwirlImage() swirls the pixels about the center of the image, where
+% degrees indicates the sweep of the arc through which each pixel is moved.
+% You get a more dramatic effect as the degrees move from 1 to 360.
+%
+% The format of the SwirlImage method is:
+%
+% Image *SwirlImage(const Image *image,double degrees,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o degrees: Define the tightness of the swirling effect.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SwirlImage(const Image *image,double degrees,
+ ExceptionInfo *exception)
+{
+#define SwirlImageTag "Swirl/Image"
+
+ Image
+ *swirl_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ radius;
+
+ PointInfo
+ center,
+ scale;
+
+ ResampleFilter
+ **resample_filter;
+
+ CacheView
+ *image_view,
+ *swirl_view;
+
+ /*
+ Initialize swirl image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ swirl_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (swirl_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&swirl_image->exception);
+ swirl_image=DestroyImage(swirl_image);
+ return((Image *) NULL);
+ }
+ if (swirl_image->background_color.opacity != OpaqueOpacity)
+ swirl_image->matte=MagickTrue;
+ /*
+ Compute scaling factor.
+ */
+ center.x=(double) image->columns/2.0;
+ center.y=(double) image->rows/2.0;
+ radius=MagickMax(center.x,center.y);
+ scale.x=1.0;
+ scale.y=1.0;
+ if (image->columns > image->rows)
+ scale.y=(double) image->columns/(double) image->rows;
+ else
+ if (image->columns < image->rows)
+ scale.x=(double) image->rows/(double) image->columns;
+ degrees=(double) DegreesToRadians(degrees);
+ /*
+ Swirl image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(swirl_image,&zero);
+ resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
+ image_view=AcquireCacheView(image);
+ swirl_view=AcquireCacheView(swirl_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ distance;
+
+ PointInfo
+ delta;
+
+ register IndexPacket
+ *__restrict swirl_indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
+ delta.y=scale.y*(double) (y-center.y);
+ pixel=zero;
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ /*
+ Determine if the pixel is within an ellipse.
+ */
+ delta.x=scale.x*(double) (x-center.x);
+ distance=delta.x*delta.x+delta.y*delta.y;
+ if (distance < (radius*radius))
+ {
+ MagickRealType
+ cosine,
+ factor,
+ sine;
+
+ /*
+ Swirl the pixel.
+ */
+ factor=1.0-sqrt((double) distance)/radius;
+ sine=sin((double) (degrees*factor*factor));
+ cosine=cos((double) (degrees*factor*factor));
+ (void) ResamplePixelColor(resample_filter[id],(double) ((cosine*
+ delta.x-sine*delta.y)/scale.x+center.x),(double) ((sine*delta.x+
+ cosine*delta.y)/scale.y+center.y),&pixel);
+ SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SwirlImage)
+#endif
+ proceed=SetImageProgress(image,SwirlImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ swirl_view=DestroyCacheView(swirl_view);
+ image_view=DestroyCacheView(image_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ if (status == MagickFalse)
+ swirl_image=DestroyImage(swirl_image);
+ return(swirl_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TintImage() applies a color vector to each pixel in the image. The length
+% of the vector is 0 for black and white and at its maximum for the midtones.
+% The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
+%
+% The format of the TintImage method is:
+%
+% Image *TintImage(const Image *image,const char *opacity,
+% const PixelPacket tint,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o opacity: A color value used for tinting.
+%
+% o tint: A color value used for tinting.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *TintImage(const Image *image,const char *opacity,
+ const PixelPacket tint,ExceptionInfo *exception)
+{
+#define TintImageTag "Tint/Image"
+
+ GeometryInfo
+ geometry_info;
+
+ Image
+ *tint_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ MagickPixelPacket
+ color_vector,
+ pixel;
+
+ CacheView
+ *image_view,
+ *tint_view;
+
+ /*
+ Allocate tint image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ tint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (tint_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&tint_image->exception);
+ tint_image=DestroyImage(tint_image);
+ return((Image *) NULL);
+ }
+ if (opacity == (const char *) NULL)
+ return(tint_image);
+ /*
+ Determine RGB values of the color.
+ */
+ flags=ParseGeometry(opacity,&geometry_info);
+ pixel.red=geometry_info.rho;
+ if ((flags & SigmaValue) != 0)
+ pixel.green=geometry_info.sigma;
+ else
+ pixel.green=pixel.red;
+ if ((flags & XiValue) != 0)
+ pixel.blue=geometry_info.xi;
+ else
+ pixel.blue=pixel.red;
+ if ((flags & PsiValue) != 0)
+ pixel.opacity=geometry_info.psi;
+ else
+ pixel.opacity=(MagickRealType) OpaqueOpacity;
+ color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
+ PixelIntensity(&tint));
+ color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
+ PixelIntensity(&tint));
+ color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
+ PixelIntensity(&tint));
+ /*
+ Tint image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ tint_view=AcquireCacheView(tint_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ weight;
+
+ weight=QuantumScale*p->red-0.5;
+ pixel.red=(MagickRealType) p->red+color_vector.red*(1.0-(4.0*
+ (weight*weight)));
+ q->red=RoundToQuantum(pixel.red);
+ weight=QuantumScale*p->green-0.5;
+ pixel.green=(MagickRealType) p->green+color_vector.green*(1.0-(4.0*
+ (weight*weight)));
+ q->green=RoundToQuantum(pixel.green);
+ weight=QuantumScale*p->blue-0.5;
+ pixel.blue=(MagickRealType) p->blue+color_vector.blue*(1.0-(4.0*
+ (weight*weight)));
+ q->blue=RoundToQuantum(pixel.blue);
+ q->opacity=p->opacity;
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TintImage)
+#endif
+ proceed=SetImageProgress(image,TintImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ tint_view=DestroyCacheView(tint_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ tint_image=DestroyImage(tint_image);
+ return(tint_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% V i g n e t t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% VignetteImage() softens the edges of the image in vignette style.
+%
+% The format of the VignetteImage method is:
+%
+% Image *VignetteImage(const Image *image,const double radius,
+% const double sigma,const long x,const long y,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the pixel neighborhood.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+% o x, y: Define the x and y ellipse offset.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *VignetteImage(const Image *image,const double radius,
+ const double sigma,const long x,const long y,ExceptionInfo *exception)
+{
+ char
+ ellipse[MaxTextExtent];
+
+ DrawInfo
+ *draw_info;
+
+ Image
+ *canvas_image,
+ *blur_image,
+ *oval_image,
+ *vignette_image;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ canvas_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (canvas_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&canvas_image->exception);
+ canvas_image=DestroyImage(canvas_image);
+ return((Image *) NULL);
+ }
+ canvas_image->matte=MagickTrue;
+ oval_image=CloneImage(canvas_image,canvas_image->columns,
+ canvas_image->rows,MagickTrue,exception);
+ if (oval_image == (Image *) NULL)
+ {
+ canvas_image=DestroyImage(canvas_image);
+ return((Image *) NULL);
+ }
+ (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
+ (void) SetImageBackgroundColor(oval_image);
+ draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
+ (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
+ (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
+ (void) FormatMagickString(ellipse,MaxTextExtent,
+ "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,image->rows/2.0,
+ image->columns/2.0-x,image->rows/2.0-y);
+ draw_info->primitive=AcquireString(ellipse);
+ (void) DrawImage(oval_image,draw_info);
+ draw_info=DestroyDrawInfo(draw_info);
+ blur_image=BlurImage(oval_image,radius,sigma,exception);
+ oval_image=DestroyImage(oval_image);
+ if (blur_image == (Image *) NULL)
+ {
+ canvas_image=DestroyImage(canvas_image);
+ return((Image *) NULL);
+ }
+ blur_image->matte=MagickFalse;
+ (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
+ blur_image=DestroyImage(blur_image);
+ vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
+ canvas_image=DestroyImage(canvas_image);
+ return(vignette_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W a v e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WaveImage() creates a "ripple" effect in the image by shifting the pixels
+% vertically along a sine wave whose amplitude and wavelength is specified
+% by the given parameters.
+%
+% The format of the WaveImage method is:
+%
+% Image *WaveImage(const Image *image,const double amplitude,
+% const double wave_length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o amplitude, wave_length: Define the amplitude and wave length of the
+% sine wave.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *WaveImage(const Image *image,const double amplitude,
+ const double wave_length,ExceptionInfo *exception)
+{
+#define WaveImageTag "Wave/Image"
+
+ Image
+ *wave_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ *sine_map;
+
+ register long
+ i;
+
+ ResampleFilter
+ **resample_filter;
+
+ CacheView
+ *wave_view;
+
+ /*
+ Initialize wave image attributes.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ wave_image=CloneImage(image,image->columns,(unsigned long) (image->rows+2.0*
+ fabs(amplitude)),MagickTrue,exception);
+ if (wave_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&wave_image->exception);
+ wave_image=DestroyImage(wave_image);
+ return((Image *) NULL);
+ }
+ if (wave_image->background_color.opacity != OpaqueOpacity)
+ wave_image->matte=MagickTrue;
+ /*
+ Allocate sine map.
+ */
+ sine_map=(MagickRealType *) AcquireQuantumMemory((size_t) wave_image->columns,
+ sizeof(*sine_map));
+ if (sine_map == (MagickRealType *) NULL)
+ {
+ wave_image=DestroyImage(wave_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ for (i=0; i < (long) wave_image->columns; i++)
+ sine_map[i]=fabs(amplitude)+amplitude*sin((2*MagickPI*i)/wave_length);
+ /*
+ Wave image.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(wave_image,&zero);
+ resample_filter=AcquireResampleFilterThreadSet(image,MagickTrue,exception);
+ wave_view=AcquireCacheView(wave_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) wave_image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(wave_view);
+ pixel=zero;
+ id=GetOpenMPThreadId();
+ (void) SetResampleFilterVirtualPixelMethod(resample_filter[id],
+ BackgroundVirtualPixelMethod);
+ for (x=0; x < (long) wave_image->columns; x++)
+ {
+ (void) ResamplePixelColor(resample_filter[id],(double) x,(double) (y-
+ sine_map[x]),&pixel);
+ SetPixelPacket(wave_image,&pixel,q,indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_WaveImage)
+#endif
+ proceed=SetImageProgress(image,WaveImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ wave_view=DestroyCacheView(wave_view);
+ resample_filter=DestroyResampleFilterThreadSet(resample_filter);
+ sine_map=(MagickRealType *) RelinquishMagickMemory(sine_map);
+ if (status == MagickFalse)
+ wave_image=DestroyImage(wave_image);
+ return(wave_image);
+}
diff --git a/magick/fx.h b/magick/fx.h
new file mode 100644
index 0000000..c017ad5
--- /dev/null
+++ b/magick/fx.h
@@ -0,0 +1,128 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image f/x methods.
+*/
+#ifndef _MAGICKCORE_FX_H
+#define _MAGICKCORE_FX_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/draw.h"
+
+typedef enum
+{
+ UndefinedEvaluateOperator,
+ AddEvaluateOperator,
+ AndEvaluateOperator,
+ DivideEvaluateOperator,
+ LeftShiftEvaluateOperator,
+ MaxEvaluateOperator,
+ MinEvaluateOperator,
+ MultiplyEvaluateOperator,
+ OrEvaluateOperator,
+ RightShiftEvaluateOperator,
+ SetEvaluateOperator,
+ SubtractEvaluateOperator,
+ XorEvaluateOperator,
+ PowEvaluateOperator,
+ LogEvaluateOperator,
+ ThresholdEvaluateOperator,
+ ThresholdBlackEvaluateOperator,
+ ThresholdWhiteEvaluateOperator,
+ GaussianNoiseEvaluateOperator,
+ ImpulseNoiseEvaluateOperator,
+ LaplacianNoiseEvaluateOperator,
+ MultiplicativeNoiseEvaluateOperator,
+ PoissonNoiseEvaluateOperator,
+ UniformNoiseEvaluateOperator,
+ CosineEvaluateOperator,
+ SineEvaluateOperator,
+ AddModulusEvaluateOperator
+} MagickEvaluateOperator;
+
+typedef enum
+{
+ UndefinedFunction,
+ PolynomialFunction,
+ SinusoidFunction,
+ ArcsinFunction,
+ ArctanFunction
+} MagickFunction;
+
+typedef enum
+{
+ UndefinedNoise,
+ UniformNoise,
+ GaussianNoise,
+ MultiplicativeGaussianNoise,
+ ImpulseNoise,
+ LaplacianNoise,
+ PoissonNoise,
+ RandomNoise
+} NoiseType;
+
+extern MagickExport Image
+ *AddNoiseImage(const Image *,const NoiseType,ExceptionInfo *),
+ *AddNoiseImageChannel(const Image *,const ChannelType,const NoiseType,
+ ExceptionInfo *),
+ *BlueShiftImage(const Image *,const double,ExceptionInfo *),
+ *CharcoalImage(const Image *,const double,const double,ExceptionInfo *),
+ *ColorizeImage(const Image *,const char *,const PixelPacket,ExceptionInfo *),
+ *ConvolveImage(const Image *,const unsigned long,const double *,
+ ExceptionInfo *),
+ *ConvolveImageChannel(const Image *,const ChannelType,const unsigned long,
+ const double *,ExceptionInfo *),
+ *FxImage(const Image *,const char *,ExceptionInfo *),
+ *FxImageChannel(const Image *,const ChannelType,const char *,ExceptionInfo *),
+ *ImplodeImage(const Image *,const double,ExceptionInfo *),
+ *MorphImages(const Image *,const unsigned long,ExceptionInfo *),
+ *PolaroidImage(const Image *,const DrawInfo *,const double,ExceptionInfo *),
+ *RecolorImage(const Image *,const unsigned long,const double *,
+ ExceptionInfo *),
+ *SepiaToneImage(const Image *,const double,ExceptionInfo *),
+ *ShadowImage(const Image *,const double,const double,const long,const long,
+ ExceptionInfo *),
+ *SketchImage(const Image *,const double,const double,const double,
+ ExceptionInfo *),
+ *SteganoImage(const Image *,const Image *,ExceptionInfo *),
+ *StereoImage(const Image *,const Image *,ExceptionInfo *),
+ *StereoAnaglyphImage(const Image *,const Image *,const long,const long,
+ ExceptionInfo *),
+ *SwirlImage(const Image *,double,ExceptionInfo *),
+ *TintImage(const Image *,const char *,const PixelPacket,ExceptionInfo *),
+ *VignetteImage(const Image *,const double,const double,const long,
+ const long,ExceptionInfo *),
+ *WaveImage(const Image *,const double,const double,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ EvaluateImage(Image *,const MagickEvaluateOperator,const double,
+ ExceptionInfo *),
+ EvaluateImageChannel(Image *,const ChannelType,const MagickEvaluateOperator,
+ const double,ExceptionInfo *),
+ FunctionImage(Image *,const MagickFunction,const unsigned long,const double *,
+ ExceptionInfo *),
+ FunctionImageChannel(Image *,const ChannelType,const MagickFunction,
+ const unsigned long,const double *,ExceptionInfo *),
+ PlasmaImage(Image *,const SegmentInfo *,unsigned long,unsigned long),
+ SolarizeImage(Image *,const double);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/gem.c b/magick/gem.c
new file mode 100644
index 0000000..821a1e5
--- /dev/null
+++ b/magick/gem.c
@@ -0,0 +1,824 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% GGGG EEEEE M M %
+% G E MM MM %
+% G GG EEE M M M %
+% G G E M M %
+% GGGG EEEEE M M %
+% %
+% %
+% Graphic Gems - Graphic Support Methods %
+% %
+% Software Design %
+% John Cristy %
+% August 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/color-private.h"
+#include "magick/draw.h"
+#include "magick/gem.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/resize.h"
+#include "magick/transform.h"
+#include "magick/signature-private.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t H S B T o R G B %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertHSBToRGB() transforms a (hue, saturation, brightness) to a (red,
+% green, blue) triple.
+%
+% The format of the ConvertHSBToRGBImage method is:
+%
+% void ConvertHSBToRGB(const double hue,const double saturation,
+% const double brightness,Quantum *red,Quantum *green,Quantum *blue)
+%
+% A description of each parameter follows:
+%
+% o hue, saturation, brightness: A double value representing a
+% component of the HSB color space.
+%
+% o red, green, blue: A pointer to a pixel component of type Quantum.
+%
+*/
+MagickExport void ConvertHSBToRGB(const double hue,const double saturation,
+ const double brightness,Quantum *red,Quantum *green,Quantum *blue)
+{
+ MagickRealType
+ f,
+ h,
+ p,
+ q,
+ t;
+
+ /*
+ Convert HSB to RGB colorspace.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ if (saturation == 0.0)
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ *green=(*red);
+ *blue=(*red);
+ return;
+ }
+ h=6.0*(hue-floor(hue));
+ f=h-floor((double) h);
+ p=brightness*(1.0-saturation);
+ q=brightness*(1.0-saturation*f);
+ t=brightness*(1.0-(saturation*(1.0-f)));
+ switch ((int) h)
+ {
+ case 0:
+ default:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*t);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*p);
+ break;
+ }
+ case 1:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*q);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*p);
+ break;
+ }
+ case 2:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*p);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*t);
+ break;
+ }
+ case 3:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*p);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*q);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ break;
+ }
+ case 4:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*t);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*p);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ break;
+ }
+ case 5:
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*brightness);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*p);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*q);
+ break;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t H S L T o R G B %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertHSLToRGB() transforms a (hue, saturation, lightness) to a (red,
+% green, blue) triple.
+%
+% The format of the ConvertHSLToRGBImage method is:
+%
+% void ConvertHSLToRGB(const double hue,const double saturation,
+% const double lightness,Quantum *red,Quantum *green,Quantum *blue)
+%
+% A description of each parameter follows:
+%
+% o hue, saturation, lightness: A double value representing a
+% component of the HSL color space.
+%
+% o red, green, blue: A pointer to a pixel component of type Quantum.
+%
+*/
+
+static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
+ MagickRealType m2,MagickRealType hue)
+{
+ if (hue < 0.0)
+ hue+=1.0;
+ if (hue > 1.0)
+ hue-=1.0;
+ if ((6.0*hue) < 1.0)
+ return(m1+6.0*(m2-m1)*hue);
+ if ((2.0*hue) < 1.0)
+ return(m2);
+ if ((3.0*hue) < 2.0)
+ return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
+ return(m1);
+}
+
+MagickExport void ConvertHSLToRGB(const double hue,const double saturation,
+ const double lightness,Quantum *red,Quantum *green,Quantum *blue)
+{
+ MagickRealType
+ b,
+ g,
+ r,
+ m1,
+ m2;
+
+ /*
+ Convert HSL to RGB colorspace.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ if (saturation == 0)
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*lightness);
+ *green=(*red);
+ *blue=(*red);
+ return;
+ }
+ if (lightness <= 0.5)
+ m2=lightness*(saturation+1.0);
+ else
+ m2=(lightness+saturation)-(lightness*saturation);
+ m1=2.0*lightness-m2;
+ r=ConvertHueToRGB(m1,m2,hue+1.0/3.0);
+ g=ConvertHueToRGB(m1,m2,hue);
+ b=ConvertHueToRGB(m1,m2,hue-1.0/3.0);
+ *red=RoundToQuantum((MagickRealType) QuantumRange*r);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*g);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*b);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t H W B T o R G B %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertHWBToRGB() transforms a (hue, whiteness, blackness) to a (red, green,
+% blue) triple.
+%
+% The format of the ConvertHWBToRGBImage method is:
+%
+% void ConvertHWBToRGB(const double hue,const double whiteness,
+% const double blackness,Quantum *red,Quantum *green,Quantum *blue)
+%
+% A description of each parameter follows:
+%
+% o hue, whiteness, blackness: A double value representing a
+% component of the HWB color space.
+%
+% o red, green, blue: A pointer to a pixel component of type Quantum.
+%
+*/
+MagickExport void ConvertHWBToRGB(const double hue,const double whiteness,
+ const double blackness,Quantum *red,Quantum *green,Quantum *blue)
+{
+ MagickRealType
+ b,
+ f,
+ g,
+ n,
+ r,
+ v;
+
+ register long
+ i;
+
+ /*
+ Convert HWB to RGB colorspace.
+ */
+ assert(red != (Quantum *) NULL);
+ assert(green != (Quantum *) NULL);
+ assert(blue != (Quantum *) NULL);
+ v=1.0-blackness;
+ if (hue == 0.0)
+ {
+ *red=RoundToQuantum((MagickRealType) QuantumRange*v);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*v);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*v);
+ return;
+ }
+ i=(long) floor(6.0*hue);
+ f=6.0*hue-i;
+ if ((i & 0x01) != 0)
+ f=1.0-f;
+ n=whiteness+f*(v-whiteness); /* linear interpolation */
+ switch (i)
+ {
+ default:
+ case 6:
+ case 0: r=v; g=n; b=whiteness; break;
+ case 1: r=n; g=v; b=whiteness; break;
+ case 2: r=whiteness; g=v; b=n; break;
+ case 3: r=whiteness; g=n; b=v; break;
+ case 4: r=n; g=whiteness; b=v; break;
+ case 5: r=v; g=whiteness; b=n; break;
+ }
+ *red=RoundToQuantum((MagickRealType) QuantumRange*r);
+ *green=RoundToQuantum((MagickRealType) QuantumRange*g);
+ *blue=RoundToQuantum((MagickRealType) QuantumRange*b);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t R G B T o H S B %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertRGBToHSB() transforms a (red, green, blue) to a (hue, saturation,
+% brightness) triple.
+%
+% The format of the ConvertRGBToHSB method is:
+%
+% void ConvertRGBToHSB(const Quantum red,const Quantum green,
+% const Quantum blue,double *hue,double *saturation,double *brightness)
+%
+% A description of each parameter follows:
+%
+% o red, green, blue: A Quantum value representing the red, green, and
+% blue component of a pixel..
+%
+% o hue, saturation, brightness: A pointer to a double value representing a
+% component of the HSB color space.
+%
+*/
+MagickExport void ConvertRGBToHSB(const Quantum red,const Quantum green,
+ const Quantum blue,double *hue,double *saturation,double *brightness)
+{
+ MagickRealType
+ delta,
+ max,
+ min;
+
+ /*
+ Convert RGB to HSB colorspace.
+ */
+ assert(hue != (double *) NULL);
+ assert(saturation != (double *) NULL);
+ assert(brightness != (double *) NULL);
+ *hue=0.0;
+ *saturation=0.0;
+ *brightness=0.0;
+ min=(MagickRealType) (red < green ? red : green);
+ if ((MagickRealType) blue < min)
+ min=(MagickRealType) blue;
+ max=(MagickRealType) (red > green ? red : green);
+ if ((MagickRealType) blue > max)
+ max=(MagickRealType) blue;
+ if (max == 0.0)
+ return;
+ delta=max-min;
+ *saturation=(double) (delta/max);
+ *brightness=(double) (QuantumScale*max);
+ if (delta == 0.0)
+ return;
+ if ((MagickRealType) red == max)
+ *hue=(double) ((green-(MagickRealType) blue)/delta);
+ else
+ if ((MagickRealType) green == max)
+ *hue=(double) (2.0+(blue-(MagickRealType) red)/delta);
+ else
+ *hue=(double) (4.0+(red-(MagickRealType) green)/delta);
+ *hue/=6.0;
+ if (*hue < 0.0)
+ *hue+=1.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t R G B T o H S L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertRGBToHSL() transforms a (red, green, blue) to a (hue, saturation,
+% lightness) triple.
+%
+% The format of the ConvertRGBToHSL method is:
+%
+% void ConvertRGBToHSL(const Quantum red,const Quantum green,
+% const Quantum blue,double *hue,double *saturation,double *lightness)
+%
+% A description of each parameter follows:
+%
+% o red, green, blue: A Quantum value representing the red, green, and
+% blue component of a pixel..
+%
+% o hue, saturation, lightness: A pointer to a double value representing a
+% component of the HSL color space.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport void ConvertRGBToHSL(const Quantum red,const Quantum green,
+ const Quantum blue,double *hue,double *saturation,double *lightness)
+{
+ MagickRealType
+ b,
+ delta,
+ g,
+ max,
+ min,
+ r;
+
+ /*
+ Convert RGB to HSL colorspace.
+ */
+ assert(hue != (double *) NULL);
+ assert(saturation != (double *) NULL);
+ assert(lightness != (double *) NULL);
+ r=QuantumScale*red;
+ g=QuantumScale*green;
+ b=QuantumScale*blue;
+ max=MagickMax(r,MagickMax(g,b));
+ min=MagickMin(r,MagickMin(g,b));
+ *lightness=(double) ((min+max)/2.0);
+ delta=max-min;
+ if (delta == 0.0)
+ {
+ *hue=0.0;
+ *saturation=0.0;
+ return;
+ }
+ if (*lightness < 0.5)
+ *saturation=(double) (delta/(min+max));
+ else
+ *saturation=(double) (delta/(2.0-max-min));
+ if (r == max)
+ *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
+ else
+ if (g == max)
+ *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
+ delta;
+ else
+ if (b == max)
+ *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
+ (delta/2.0)))/delta;
+ if (*hue < 0.0)
+ *hue+=1.0;
+ if (*hue > 1.0)
+ *hue-=1.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n v e r t R G B T o H W B %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConvertRGBToHWB() transforms a (red, green, blue) to a (hue, whiteness,
+% blackness) triple.
+%
+% The format of the ConvertRGBToHWB method is:
+%
+% void ConvertRGBToHWB(const Quantum red,const Quantum green,
+% const Quantum blue,double *hue,double *whiteness,double *blackness)
+%
+% A description of each parameter follows:
+%
+% o red, green, blue: A Quantum value representing the red, green, and
+% blue component of a pixel.
+%
+% o hue, whiteness, blackness: A pointer to a double value representing a
+% component of the HWB color space.
+%
+*/
+MagickExport void ConvertRGBToHWB(const Quantum red,const Quantum green,
+ const Quantum blue,double *hue,double *whiteness,double *blackness)
+{
+ MagickRealType
+ f,
+ v,
+ w;
+
+ register long
+ i;
+
+ /*
+ Convert RGB to HWB colorspace.
+ */
+ assert(hue != (double *) NULL);
+ assert(whiteness != (double *) NULL);
+ assert(blackness != (double *) NULL);
+ w=(MagickRealType) MagickMin((double) red,MagickMin((double) green,(double)
+ blue));
+ v=(MagickRealType) MagickMax((double) red,MagickMax((double) green,(double)
+ blue));
+ *blackness=1.0-QuantumScale*v;
+ *whiteness=QuantumScale*w;
+ if (v == w)
+ {
+ *hue=0.0;
+ return;
+ }
+ f=((MagickRealType) red == w) ? green-(MagickRealType) blue :
+ (((MagickRealType) green == w) ? blue-(MagickRealType) red : red-
+ (MagickRealType) green);
+ i=((MagickRealType) red == w) ? 3 : (((MagickRealType) green == w) ? 5 : 1);
+ *hue=((double) i-f/(v-1.0*w))/6.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x p a n d A f f i n e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExpandAffine() computes the affine's expansion factor, i.e. the square root
+% of the factor by which the affine transform affects area. In an affine
+% transform composed of scaling, rotation, shearing, and translation, returns
+% the amount of scaling.
+%
+% The format of the ExpandAffine method is:
+%
+% double ExpandAffine(const AffineMatrix *affine)
+%
+% A description of each parameter follows:
+%
+% o expansion: Method ExpandAffine returns the affine's expansion factor.
+%
+% o affine: A pointer the affine transform of type AffineMatrix.
+%
+*/
+MagickExport double ExpandAffine(const AffineMatrix *affine)
+{
+ assert(affine != (const AffineMatrix *) NULL);
+ return(sqrt(fabs(affine->sx*affine->sy-affine->rx*affine->ry)));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e n e r a t e D i f f e r e n t i a l N o i s e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GenerateDifferentialNoise()
+%
+% The format of the GenerateDifferentialNoise method is:
+%
+% double GenerateDifferentialNoise(RandomInfo *random_info,
+% const Quantum pixel,const NoiseType noise_type,
+% const MagickRealType attenuate)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+% o pixel: noise is relative to this pixel value.
+%
+% o noise_type: the type of noise.
+%
+% o attenuate: attenuate the noise.
+%
+*/
+MagickExport double GenerateDifferentialNoise(RandomInfo *random_info,
+ const Quantum pixel,const NoiseType noise_type,const MagickRealType attenuate)
+{
+#define NoiseEpsilon (attenuate*1.0e-5)
+#define SigmaUniform ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
+#define SigmaGaussian ScaleCharToQuantum((unsigned char) (attenuate*4.0+0.5))
+#define SigmaImpulse (attenuate*0.10)
+#define SigmaLaplacian ScaleCharToQuantum((unsigned char) (attenuate*10.0+0.5))
+#define SigmaMultiplicativeGaussian \
+ ScaleCharToQuantum((unsigned char) (attenuate*1.0+0.5))
+#define SigmaPoisson (attenuate*0.05)
+#define TauGaussian ScaleCharToQuantum((unsigned char) (attenuate*20.0+0.5))
+
+ MagickRealType
+ alpha,
+ beta,
+ noise,
+ sigma;
+
+ alpha=GetPseudoRandomValue(random_info);
+ if (alpha == 0.0)
+ alpha=1.0;
+ switch (noise_type)
+ {
+ case UniformNoise:
+ default:
+ {
+ noise=(MagickRealType) pixel+SigmaUniform*(alpha-0.5);
+ break;
+ }
+ case GaussianNoise:
+ {
+ MagickRealType
+ tau;
+
+ beta=GetPseudoRandomValue(random_info);
+ sigma=sqrt(-2.0*log((double) alpha))*cos((double) (2.0*MagickPI*beta));
+ tau=sqrt(-2.0*log((double) alpha))*sin((double) (2.0*MagickPI*beta));
+ noise=(MagickRealType) pixel+sqrt((double) pixel)*SigmaGaussian*sigma+
+ TauGaussian*tau;
+ break;
+ }
+ case MultiplicativeGaussianNoise:
+ {
+ if (alpha <= NoiseEpsilon)
+ sigma=(MagickRealType) QuantumRange;
+ else
+ sigma=sqrt(-2.0*log((double) alpha));
+ beta=GetPseudoRandomValue(random_info);
+ noise=(MagickRealType) pixel+pixel*SigmaMultiplicativeGaussian*sigma/2.0*
+ cos((double) (2.0*MagickPI*beta));
+ break;
+ }
+ case ImpulseNoise:
+ {
+ if (alpha < (SigmaImpulse/2.0))
+ noise=0.0;
+ else
+ if (alpha >= (1.0-(SigmaImpulse/2.0)))
+ noise=(MagickRealType) QuantumRange;
+ else
+ noise=(MagickRealType) pixel;
+ break;
+ }
+ case LaplacianNoise:
+ {
+ if (alpha <= 0.5)
+ {
+ if (alpha <= NoiseEpsilon)
+ noise=(MagickRealType) pixel-(MagickRealType) QuantumRange;
+ else
+ noise=(MagickRealType) pixel+ScaleCharToQuantum((unsigned char)
+ (SigmaLaplacian*log((double) (2.0*alpha))+0.5));
+ break;
+ }
+ beta=1.0-alpha;
+ if (beta <= (0.5*NoiseEpsilon))
+ noise=(MagickRealType) (pixel+QuantumRange);
+ else
+ noise=(MagickRealType) pixel-ScaleCharToQuantum((unsigned char)
+ (SigmaLaplacian*log((double) (2.0*beta))+0.5));
+ break;
+ }
+ case PoissonNoise:
+ {
+ MagickRealType
+ poisson;
+
+ register long
+ i;
+
+ poisson=exp(-SigmaPoisson*(double) ScaleQuantumToChar(pixel));
+ for (i=0; alpha > poisson; i++)
+ {
+ beta=GetPseudoRandomValue(random_info);
+ alpha*=beta;
+ }
+ noise=(MagickRealType) ScaleCharToQuantum((unsigned char)
+ (i/SigmaPoisson));
+ break;
+ }
+ case RandomNoise:
+ {
+ noise=(MagickRealType) QuantumRange*GetPseudoRandomValue(random_info);
+ break;
+ }
+ }
+ return(noise);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t O p t i m a l K e r n e l W i d t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOptimalKernelWidth() computes the optimal kernel radius for a convolution
+% filter. Start with the minimum value of 3 pixels and walk out until we drop
+% below the threshold of one pixel numerical accuracy.
+%
+% The format of the GetOptimalKernelWidth method is:
+%
+% unsigned long GetOptimalKernelWidth(const double radius,
+% const double sigma)
+%
+% A description of each parameter follows:
+%
+% o width: Method GetOptimalKernelWidth returns the optimal width of
+% a convolution kernel.
+%
+% o radius: the radius of the Gaussian, in pixels, not counting the center
+% pixel.
+%
+% o sigma: the standard deviation of the Gaussian, in pixels.
+%
+*/
+MagickExport unsigned long GetOptimalKernelWidth1D(const double radius,
+ const double sigma)
+{
+ long
+ width;
+
+ MagickRealType
+ normalize,
+ value;
+
+ register long
+ u;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (radius > 0.0)
+ return((unsigned long) (2.0*ceil(radius)+1.0));
+ if (fabs(sigma) <= MagickEpsilon)
+ return(1);
+ for (width=5; ; )
+ {
+ normalize=0.0;
+ for (u=(-width/2); u <= (width/2); u++)
+ normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
+ u=width/2;
+ value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
+ if ((long) (QuantumRange*value) <= 0L)
+ break;
+ width+=2;
+ }
+ return((unsigned long) (width-2));
+}
+
+MagickExport unsigned long GetOptimalKernelWidth2D(const double radius,
+ const double sigma)
+{
+
+ long
+ width;
+
+ MagickRealType
+ alpha,
+ normalize,
+ value;
+
+ register long
+ u,
+ v;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (radius > 0.0)
+ return((unsigned long) (2.0*ceil(radius)+1.0));
+ if (fabs(sigma) <= MagickEpsilon)
+ return(1);
+ for (width=5; ; )
+ {
+ normalize=0.0;
+ for (v=(-width/2); v <= (width/2); v++)
+ {
+ for (u=(-width/2); u <= (width/2); u++)
+ {
+ alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
+ normalize+=alpha/(2.0*MagickPI*sigma*sigma);
+ }
+ }
+ v=width/2;
+ value=exp(-((double) v*v)/(2.0*sigma*sigma))/normalize;
+ if ((long) (QuantumRange*value) <= 0L)
+ break;
+ width+=2;
+ }
+ return((unsigned long) (width-2));
+}
+
+MagickExport unsigned long GetOptimalKernelWidth(const double radius,
+ const double sigma)
+{
+ return(GetOptimalKernelWidth1D(radius,sigma));
+}
diff --git a/magick/gem.h b/magick/gem.h
new file mode 100644
index 0000000..f16c4dc
--- /dev/null
+++ b/magick/gem.h
@@ -0,0 +1,56 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private graphic gems methods.
+*/
+#ifndef _MAGICKCORE_GEM_PRIVATE_H
+#define _MAGICKCORE_GEM_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/fx.h>
+#include <magick/random_.h>
+
+extern MagickExport double
+ ExpandAffine(const AffineMatrix *),
+ GenerateDifferentialNoise(RandomInfo *,const Quantum,const NoiseType,
+ const MagickRealType);
+
+extern MagickExport unsigned long
+ GetOptimalKernelWidth(const double,const double),
+ GetOptimalKernelWidth1D(const double,const double),
+ GetOptimalKernelWidth2D(const double,const double);
+
+extern MagickExport void
+ ConvertHSBToRGB(const double,const double,const double,Quantum *,Quantum *,
+ Quantum *),
+ ConvertHSLToRGB(const double,const double,const double,Quantum *,Quantum *,
+ Quantum *),
+ ConvertHWBToRGB(const double,const double,const double,Quantum *,Quantum *,
+ Quantum *),
+ ConvertRGBToHSB(const Quantum,const Quantum,const Quantum,double *,double *,
+ double *),
+ ConvertRGBToHSL(const Quantum,const Quantum,const Quantum,double *,double *,
+ double *),
+ ConvertRGBToHWB(const Quantum,const Quantum,const Quantum,double *,double *,
+ double *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/geometry.c b/magick/geometry.c
new file mode 100644
index 0000000..6ff1bba
--- /dev/null
+++ b/magick/geometry.c
@@ -0,0 +1,1390 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% GGGG EEEEE OOO M M EEEEE TTTTT RRRR Y Y %
+% G E O O MM MM E T R R Y Y %
+% G GG EEE O O M M M EEE T RRRR Y %
+% G G E O O M M E T R R Y %
+% GGGG EEEEE OOO M M EEEEE T R R Y %
+% %
+% %
+% MagickCore Geometry Methods %
+% %
+% Software Design %
+% John Cristy %
+% January 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/memory_.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetGeometry() parses a geometry specification and returns the width,
+% height, x, and y values. It also returns flags that indicates which
+% of the four values (width, height, x, y) were located in the string, and
+% whether the x or y values are negative. In addition, there are flags to
+% report any meta characters (%, !, <, or >).
+%
+% The format of the GetGeometry method is:
+%
+% MagickStatusType GetGeometry(const char *geometry,long *x,long *y,
+% unsigned long *width,unsigned long *height)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry.
+%
+% o x,y: The x and y offset as determined by the geometry specification.
+%
+% o width,height: The width and height as determined by the geometry
+% specification.
+%
+*/
+MagickExport MagickStatusType GetGeometry(const char *geometry,long *x,long *y,
+ unsigned long *width,unsigned long *height)
+{
+ char
+ *p,
+ pedantic_geometry[MaxTextExtent],
+ *q;
+
+ double
+ value;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Remove whitespace and meta characters from geometry specification.
+ */
+ flags=NoValue;
+ if ((geometry == (char *) NULL) || (*geometry == '\0'))
+ return(flags);
+ if (strlen(geometry) >= MaxTextExtent)
+ return(flags);
+ (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
+ for (p=pedantic_geometry; *p != '\0'; )
+ {
+ if (isspace((int) ((unsigned char) *p)) != 0)
+ {
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ continue;
+ }
+ switch (*p)
+ {
+ case '%':
+ {
+ flags|=PercentValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '!':
+ {
+ flags|=AspectValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '<':
+ {
+ flags|=LessValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '>':
+ {
+ flags|=GreaterValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '^':
+ {
+ flags|=MinimumValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '@':
+ {
+ flags|=AreaValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '(':
+ case ')':
+ {
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '-':
+ case '.':
+ case ',':
+ case '+':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case -41:
+ case 'x':
+ case 'X':
+ {
+ p++;
+ break;
+ }
+ default:
+ return(flags);
+ }
+ }
+ /*
+ Parse width, height, x, and y.
+ */
+ p=pedantic_geometry;
+ if (*p == '\0')
+ return(flags);
+ q=p;
+ value=strtod(p,&q);
+ if (LocaleNCompare(p,"0x",2) == 0)
+ value=(double) strtol(p,&q,10);
+ if (((int) *q == -41) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
+ {
+ /*
+ Parse width.
+ */
+ q=p;
+ if (LocaleNCompare(p,"0x",2) == 0)
+ *width=(unsigned long) strtol(p,&p,10);
+ else
+ *width=(unsigned long) floor(strtod(p,&p)+0.5);
+ if (p != q)
+ flags|=WidthValue;
+ }
+ if (((int) *p == -41) || (*p == 'x') || (*p == 'X'))
+ {
+ p++;
+ if ((*p != '+') && (*p != '-'))
+ {
+ /*
+ Parse height.
+ */
+ q=p;
+ *height=(unsigned long) floor(strtod(p,&p)+0.5);
+ if (p != q)
+ flags|=HeightValue;
+ }
+ }
+ if ((*p == '+') || (*p == '-'))
+ {
+ /*
+ Parse x value.
+ */
+ if (*p == '-')
+ flags|=XNegative;
+ q=p;
+ *x=(long) ceil(strtod(p,&p)-0.5);
+ if (p != q)
+ flags|=XValue;
+ if ((*p == '+') || (*p == '-'))
+ {
+ /*
+ Parse y value.
+ */
+ if (*p == '-')
+ flags|=YNegative;
+ q=p;
+ *y=(long) ceil(strtod(p,&p)-0.5);
+ if (p != q)
+ flags|=YValue;
+ }
+ }
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P a g e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPageGeometry() replaces any page mneumonic with the equivalent size in
+% picas.
+%
+% The format of the GetPageGeometry method is:
+%
+% char *GetPageGeometry(const char *page_geometry)
+%
+% A description of each parameter follows.
+%
+% o page_geometry: Specifies a pointer to an array of characters.
+% The string is either a Postscript page name (e.g. A4) or a postscript
+% page geometry (e.g. 612x792+36+36).
+%
+*/
+MagickExport char *GetPageGeometry(const char *page_geometry)
+{
+ static const char
+ *PageSizes[][2]=
+ {
+ { "4x6", "288x432" },
+ { "5x7", "360x504" },
+ { "7x9", "504x648" },
+ { "8x10", "576x720" },
+ { "9x11", "648x792" },
+ { "9x12", "648x864" },
+ { "10x13", "720x936" },
+ { "10x14", "720x1008" },
+ { "11x17", "792x1224" },
+ { "a0", "2384x3370" },
+ { "a1", "1684x2384" },
+ { "a10", "73x105" },
+ { "a2", "1191x1684" },
+ { "a3", "842x1191" },
+ { "a4", "595x842" },
+ { "a4smaLL", "595x842" },
+ { "a5", "420x595" },
+ { "a6", "297x420" },
+ { "a7", "210x297" },
+ { "a8", "148x210" },
+ { "a9", "105x148" },
+ { "archa", "648x864" },
+ { "archb", "864x1296" },
+ { "archC", "1296x1728" },
+ { "archd", "1728x2592" },
+ { "arche", "2592x3456" },
+ { "b0", "2920x4127" },
+ { "b1", "2064x2920" },
+ { "b10", "91x127" },
+ { "b2", "1460x2064" },
+ { "b3", "1032x1460" },
+ { "b4", "729x1032" },
+ { "b5", "516x729" },
+ { "b6", "363x516" },
+ { "b7", "258x363" },
+ { "b8", "181x258" },
+ { "b9", "127x181" },
+ { "c0", "2599x3676" },
+ { "c1", "1837x2599" },
+ { "c2", "1298x1837" },
+ { "c3", "918x1296" },
+ { "c4", "649x918" },
+ { "c5", "459x649" },
+ { "c6", "323x459" },
+ { "c7", "230x323" },
+ { "executive", "540x720" },
+ { "flsa", "612x936" },
+ { "flse", "612x936" },
+ { "folio", "612x936" },
+ { "halfletter", "396x612" },
+ { "isob0", "2835x4008" },
+ { "isob1", "2004x2835" },
+ { "isob10", "88x125" },
+ { "isob2", "1417x2004" },
+ { "isob3", "1001x1417" },
+ { "isob4", "709x1001" },
+ { "isob5", "499x709" },
+ { "isob6", "354x499" },
+ { "isob7", "249x354" },
+ { "isob8", "176x249" },
+ { "isob9", "125x176" },
+ { "jisb0", "1030x1456" },
+ { "jisb1", "728x1030" },
+ { "jisb2", "515x728" },
+ { "jisb3", "364x515" },
+ { "jisb4", "257x364" },
+ { "jisb5", "182x257" },
+ { "jisb6", "128x182" },
+ { "ledger", "1224x792" },
+ { "legal", "612x1008" },
+ { "letter", "612x792" },
+ { "lettersmaLL", "612x792" },
+ { "quarto", "610x780" },
+ { "statement", "396x612" },
+ { "tabloid", "792x1224" },
+ { (char *) NULL, (char *) NULL }
+ };
+
+ char
+ *page;
+
+ register long
+ i;
+
+ assert(page_geometry != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
+ page=AcquireString(page_geometry);
+ for (i=0; *PageSizes[i] != (char *) NULL; i++)
+ if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
+ {
+ RectangleInfo
+ geometry;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Replace mneumonic with the equivalent size in dots-per-inch.
+ */
+ (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
+ (void) ConcatenateMagickString(page,page_geometry+
+ strlen(PageSizes[i][0]),MaxTextExtent);
+ flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
+ &geometry.height);
+ if ((flags & GreaterValue) == 0)
+ (void) ConcatenateMagickString(page,">",MaxTextExtent);
+ break;
+ }
+ return(page);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G r a v i t y A d j u s t G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GravityAdjustGeometry() adjusts the offset of a region with regard to the
+% given: width, height and gravity; against which it is positioned.
+%
+% The region should also have an appropriate width and height to correctly
+% set the right offset of the top left corner of the region.
+%
+% The format of the GravityAdjustGeometry method is:
+%
+% void GravityAdjustGeometry(const unsigned long width,
+% const unsigned long height,const GravityType gravity,
+% RectangleInfo *region);
+%
+% A description of each parameter follows:
+%
+% o width, height: the larger area the region is relative to
+%
+% o gravity: the edge/corner the current offset is relative to
+%
+% o region: The region requiring a offset adjustment relative to gravity
+%
+*/
+MagickExport void GravityAdjustGeometry(const unsigned long width,
+ const unsigned long height,const GravityType gravity,RectangleInfo *region)
+{
+ if (region->height == 0)
+ region->height=height;
+ if (region->width == 0)
+ region->width=width;
+ switch (gravity)
+ {
+ case NorthEastGravity:
+ case EastGravity:
+ case SouthEastGravity:
+ {
+ region->x=(long) (width-region->width-region->x);
+ break;
+ }
+ case NorthGravity:
+ case SouthGravity:
+ case CenterGravity:
+ case StaticGravity:
+ {
+ region->x+=(long) (width/2-region->width/2);
+ break;
+ }
+ case ForgetGravity:
+ case NorthWestGravity:
+ case WestGravity:
+ case SouthWestGravity:
+ default:
+ break;
+ }
+ switch (gravity)
+ {
+ case SouthWestGravity:
+ case SouthGravity:
+ case SouthEastGravity:
+ {
+ region->y=(long) (height-region->height-region->y);
+ break;
+ }
+ case EastGravity:
+ case WestGravity:
+ case CenterGravity:
+ case StaticGravity:
+ {
+ region->y+=(long) (height/2-region->height/2);
+ break;
+ }
+ case ForgetGravity:
+ case NorthWestGravity:
+ case NorthGravity:
+ case NorthEastGravity:
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsGeometry() returns MagickTrue if the geometry specification is valid.
+% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
+%
+% The format of the IsGeometry method is:
+%
+% MagickBooleanType IsGeometry(const char *geometry)
+%
+% A description of each parameter follows:
+%
+% o geometry: This string is the geometry specification.
+%
+*/
+MagickExport MagickBooleanType IsGeometry(const char *geometry)
+{
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ if (geometry == (const char *) NULL)
+ return(MagickFalse);
+ flags=ParseGeometry(geometry,&geometry_info);
+ if (flags == NoValue)
+ flags=ParseGeometry(geometry+1,&geometry_info); /* i.e. +-4+-4 */
+ return(flags != NoValue ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s S c e n e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
+% specification (e.g. [1], [1-9], [1,7,4]).
+%
+% The format of the IsSceneGeometry method is:
+%
+% MagickBooleanType IsSceneGeometry(const char *geometry,
+% const MagickBooleanType pedantic)
+%
+% A description of each parameter follows:
+%
+% o geometry: This string is the geometry specification.
+%
+% o pedantic: A value other than 0 invokes a more restrictive set of
+% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
+%
+*/
+MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
+ const MagickBooleanType pedantic)
+{
+ char
+ *p;
+
+ double
+ value;
+
+ if (geometry == (const char *) NULL)
+ return(MagickFalse);
+ p=(char *) geometry;
+ value=strtod(geometry,&p);
+ if (p == geometry)
+ return(MagickFalse);
+ if (strspn(geometry,"0123456789-, ") != strlen(geometry))
+ return(MagickFalse);
+ if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e A b s o l u t e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseAbsoluteGeometry() returns a region as defined by the geometry string.
+%
+% The format of the ParseAbsoluteGeometry method is:
+%
+% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
+% RectangleInfo *region_info)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o region_info: the region as defined by the geometry string.
+%
+*/
+MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
+ RectangleInfo *region_info)
+{
+ MagickStatusType
+ flags;
+
+ flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
+ ®ion_info->width,®ion_info->height);
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e A f f i n e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseAffineGeometry() returns an affine matrix as defined by the geometry
+% string.
+%
+% The format of the ParseAffineGeometry method is:
+%
+% MagickStatusType ParseAffineGeometry(const char *geometry,
+% AffineMatrix *affine_matrix,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 1.0,0.0,0.0,1.0,3.2,1.2).
+%
+% o affine_matrix: the affine matrix as defined by the geometry string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
+ AffineMatrix *affine_matrix,ExceptionInfo *exception)
+{
+ char
+ token[MaxTextExtent];
+
+ const char
+ *p;
+
+ double
+ determinant;
+
+ MagickStatusType
+ flags;
+
+ register long
+ i;
+
+ GetAffineMatrix(affine_matrix);
+ flags=NoValue;
+ p=(char *) geometry;
+ for (i=0; (*p != '\0') && (i < 6); i++)
+ {
+ GetMagickToken(p,&p,token);
+ if (*token == ',')
+ GetMagickToken(p,&p,token);
+ switch (i)
+ {
+ case 0: affine_matrix->sx=atof(token); break;
+ case 1: affine_matrix->rx=atof(token); break;
+ case 2: affine_matrix->ry=atof(token); break;
+ case 3: affine_matrix->sy=atof(token); break;
+ case 4: affine_matrix->tx=atof(token); flags|=XValue; break;
+ case 5: affine_matrix->ty=atof(token); flags|=YValue; break;
+ }
+ }
+ determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
+ affine_matrix->ry);
+ if (fabs(determinant) < MagickEpsilon)
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidGeometry","`%s'",geometry);
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseGeometry() parses a geometry specification and returns the sigma,
+% rho, xi, and psi values. It also returns flags that indicates which
+% of the four values (sigma, rho, xi, psi) were located in the string, and
+% whether the xi or pi values are negative. In addition, there are flags to
+% report any meta characters (%, !, <, or >).
+%
+% The format of the ParseGeometry method is:
+%
+% MagickStatusType ParseGeometry(const char *geometry,
+% GeometryInfo *geometry_info)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry.
+%
+% o geometry_info: returns the parsed width/height/x/y in this structure.
+%
+*/
+MagickExport MagickStatusType ParseGeometry(const char *geometry,
+ GeometryInfo *geometry_info)
+{
+ char
+ *p,
+ pedantic_geometry[MaxTextExtent],
+ *q;
+
+ double
+ value;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Remove whitespaces meta characters from geometry specification.
+ */
+ assert(geometry_info != (GeometryInfo *) NULL);
+ flags=NoValue;
+ if ((geometry == (char *) NULL) || (*geometry == '\0'))
+ return(flags);
+ if (strlen(geometry) >= MaxTextExtent)
+ return(flags);
+ (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
+ for (p=pedantic_geometry; *p != '\0'; )
+ {
+ if (isspace((int) ((unsigned char) *p)) != 0)
+ {
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ continue;
+ }
+ switch (*p)
+ {
+ case '%':
+ {
+ flags|=PercentValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '!':
+ {
+ flags|=AspectValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '<':
+ {
+ flags|=LessValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '>':
+ {
+ flags|=GreaterValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '^':
+ {
+ flags|=MinimumValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '@':
+ {
+ flags|=AreaValue;
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '(':
+ case ')':
+ {
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ break;
+ }
+ case '-':
+ case '+':
+ case ',':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case -41:
+ case 'x':
+ case 'X':
+ case '/':
+ case ':':
+ {
+ p++;
+ break;
+ }
+ case '.':
+ {
+ p++;
+ flags|=DecimalValue;
+ break;
+ }
+ default:
+ return(flags);
+ }
+ }
+ /*
+ Parse rho, sigma, xi, psi, and optionally chi.
+ */
+ p=pedantic_geometry;
+ if (*p == '\0')
+ return(flags);
+ q=p;
+ value=strtod(p,&q);
+ if (LocaleNCompare(p,"0x",2) == 0)
+ value=(double) strtol(p,&q,10);
+ if (((int) *q == -41) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
+ (*q == '/') || (*q == ':') || (*q =='\0'))
+ {
+ /*
+ Parse rho.
+ */
+ q=p;
+ if (LocaleNCompare(p,"0x",2) == 0)
+ value=(double) strtol(p,&p,10);
+ else
+ value=strtod(p,&p);
+ if (p != q)
+ {
+ flags|=RhoValue;
+ geometry_info->rho=value;
+ }
+ }
+ q=p;
+ if (((int) *p == -41) || (*p == 'x') || (*p == 'X') || (*p == ',') ||
+ (*p == '/') || (*p == ':'))
+ {
+ /*
+ Parse sigma.
+ */
+ p++;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((((int) *q != -41) && (*q != 'x') && (*q != 'X')) ||
+ ((*p != '+') && (*p != '-')))
+ {
+ q=p;
+ value=strtod(p,&p);
+ if (p != q)
+ {
+ flags|=SigmaValue;
+ geometry_info->sigma=value;
+ }
+ }
+ }
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
+ {
+ /*
+ Parse xi value.
+ */
+ if ((*p == ',') || (*p == '/') || (*p == ':'))
+ p++;
+ q=p;
+ value=strtod(p,&p);
+ if (p != q)
+ {
+ flags|=XiValue;
+ if (*q == '-')
+ flags|=XiNegative;
+ geometry_info->xi=value;
+ }
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
+ (*p == ':'))
+ {
+ /*
+ Parse psi value.
+ */
+ if ((*p == ',') || (*p == '/') || (*p == ':'))
+ p++;
+ q=p;
+ value=strtod(p,&p);
+ if (p != q)
+ {
+ flags|=PsiValue;
+ if (*q == '-')
+ flags|=PsiNegative;
+ geometry_info->psi=value;
+ }
+ }
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
+ (*p == ':'))
+ {
+ /*
+ Parse chi value.
+ */
+ if ((*p == ',') || (*p == '/') || (*p == ':'))
+ p++;
+ q=p;
+ value=strtod(p,&p);
+ if (p != q)
+ {
+ flags|=ChiValue;
+ if (*q == '-')
+ flags|=ChiNegative;
+ geometry_info->chi=value;
+ }
+ }
+ }
+ if (strchr(pedantic_geometry,':') != (char *) NULL)
+ {
+ /*
+ Normalize sampling factor (e.g. 4:2:2 => 2x1).
+ */
+ geometry_info->rho/=geometry_info->sigma;
+ geometry_info->sigma=1.0;
+ if (geometry_info->xi == 0.0)
+ geometry_info->sigma=2.0;
+ }
+ if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
+ ((flags & PsiValue) == 0))
+ {
+ /*
+ Support negative height values (e.g. 30x-20).
+ */
+ geometry_info->sigma=geometry_info->xi;
+ geometry_info->xi=0.0;
+ flags|=SigmaValue;
+ flags&=(~XiValue);
+ }
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e G r a v i t y G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseGravityGeometry() returns a region as defined by the geometry string
+% with respect to the image dimensions and its gravity.
+%
+% The format of the ParseGravityGeometry method is:
+%
+% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
+% RectangeInfo *region_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o region_info: the region as defined by the geometry string with
+% respect to the image dimensions and its gravity.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
+ const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
+{
+ MagickStatusType
+ flags;
+
+ unsigned long
+ height,
+ width;
+
+ SetGeometry(image,region_info);
+ if (image->page.width != 0)
+ region_info->width=image->page.width;
+ if (image->page.height != 0)
+ region_info->height=image->page.height;
+ flags=ParseAbsoluteGeometry(geometry,region_info);
+ if (flags == NoValue)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidGeometry","`%s'",geometry);
+ return(flags);
+ }
+ if ((flags & PercentValue) != 0)
+ {
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ status;
+
+ PointInfo
+ scale;
+
+ /*
+ Geometry is a percentage of the image size.
+ */
+ if (image->gravity != UndefinedGravity)
+ flags|=XValue | YValue;
+ status=ParseGeometry(geometry,&geometry_info);
+ scale.x=geometry_info.rho;
+ if ((status & RhoValue) == 0)
+ scale.x=100.0;
+ scale.y=geometry_info.sigma;
+ if ((status & SigmaValue) == 0)
+ scale.y=scale.x;
+ region_info->width=(unsigned long) ((scale.x*image->columns/100.0)+0.5);
+ region_info->height=(unsigned long) ((scale.y*image->rows/100.0)+0.5);
+ }
+ /*
+ Adjust offset according to gravity setting.
+ */
+ width=region_info->width;
+ height=region_info->height;
+ if (width == 0)
+ region_info->width=image->page.width | image->columns;
+ if (height == 0)
+ region_info->height=image->page.height | image->rows;
+ GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
+ region_info->width=width;
+ region_info->height=height;
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P a r s e M e t a G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseMetaGeometry() is similar to GetGeometry() except the returned
+% geometry is modified as determined by the meta characters: %, !, <, >,
+% and ~.
+%
+% The format of the ParseMetaGeometry method is:
+%
+% MagickStatusType ParseMetaGeometry(const char *geometry,long *x,long *y,
+% unsigned long *width,unsigned long *height)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry.
+%
+% o x,y: The x and y offset as determined by the geometry specification.
+%
+% o width,height: The width and height as determined by the geometry
+% specification.
+%
+*/
+
+static inline unsigned long MagickMax(const unsigned long x,
+ const unsigned long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,long *x,
+ long *y,unsigned long *width,unsigned long *height)
+{
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ unsigned long
+ former_height,
+ former_width;
+
+ /*
+ Ensure the image geometry is valid.
+ */
+ assert(x != (long *) NULL);
+ assert(y != (long *) NULL);
+ assert(width != (unsigned long *) NULL);
+ assert(height != (unsigned long *) NULL);
+ if ((geometry == (char *) NULL) || (*geometry == '\0'))
+ return(NoValue);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
+ /*
+ Parse geometry using GetGeometry.
+ */
+ SetGeometryInfo(&geometry_info);
+ former_width=(*width);
+ former_height=(*height);
+ flags=GetGeometry(geometry,x,y,width,height);
+ if ((flags & PercentValue) != 0)
+ {
+ MagickStatusType
+ flags;
+
+ PointInfo
+ scale;
+
+ /*
+ Geometry is a percentage of the image size.
+ */
+ flags=ParseGeometry(geometry,&geometry_info);
+ scale.x=geometry_info.rho;
+ if ((flags & RhoValue) == 0)
+ scale.x=100.0;
+ scale.y=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ scale.y=scale.x;
+ *width=(unsigned long) (scale.x*former_width/100.0+0.5);
+ if (*width == 0)
+ *width=1;
+ *height=(unsigned long) (scale.y*former_height/100.0+0.5);
+ if (*height == 0)
+ *height=1;
+ former_width=(*width);
+ former_height=(*height);
+ }
+ if (((flags & AspectValue) == 0) && ((*width != former_width) ||
+ (*height != former_height)))
+ {
+ MagickRealType
+ scale_factor;
+
+ /*
+ Respect aspect ratio of the image.
+ */
+ if ((former_width == 0) || (former_height == 0))
+ scale_factor=1.0;
+ else
+ if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
+ {
+ scale_factor=(MagickRealType) *width/(MagickRealType) former_width;
+ if ((flags & MinimumValue) == 0)
+ {
+ if (scale_factor > ((MagickRealType) *height/
+ (MagickRealType) former_height))
+ scale_factor=(MagickRealType) *height/(MagickRealType)
+ former_height;
+ }
+ else
+ if (scale_factor < ((MagickRealType) *height/
+ (MagickRealType) former_height))
+ scale_factor=(MagickRealType) *height/(MagickRealType)
+ former_height;
+ }
+ else
+ if ((flags & RhoValue) != 0)
+ {
+ scale_factor=(MagickRealType) *width/(MagickRealType)
+ former_width;
+ if (((flags & MinimumValue) != 0) &&
+ (scale_factor < ((MagickRealType) *width/
+ (MagickRealType) former_height)))
+ scale_factor=(MagickRealType) *width/(MagickRealType)
+ former_height;
+ }
+ else
+ {
+ scale_factor=(MagickRealType) *height/(MagickRealType)
+ former_height;
+ if (((flags & MinimumValue) != 0) &&
+ (scale_factor < ((MagickRealType) *height/
+ (MagickRealType) former_width)))
+ scale_factor=(MagickRealType) *height/(MagickRealType)
+ former_width;
+ }
+ *width=MagickMax((unsigned long) (scale_factor*former_width+0.5),1UL);
+ *height=MagickMax((unsigned long) (scale_factor*former_height+0.5),1UL);
+ }
+ if ((flags & GreaterValue) != 0)
+ {
+ if (former_width < *width)
+ *width=former_width;
+ if (former_height < *height)
+ *height=former_height;
+ }
+ if ((flags & LessValue) != 0)
+ {
+ if (former_width > *width)
+ *width=former_width;
+ if (former_height > *height)
+ *height=former_height;
+ }
+ if ((flags & AreaValue) != 0)
+ {
+ MagickRealType
+ area,
+ distance;
+
+ PointInfo
+ scale;
+
+ /*
+ Geometry is a maximum area in pixels.
+ */
+ (void) ParseGeometry(geometry,&geometry_info);
+ area=geometry_info.rho;
+ distance=sqrt((double) former_width*former_height);
+ scale.x=(double) former_width/(double) (distance/sqrt((double) area));
+ scale.y=(double) former_height/(double) (distance/sqrt((double) area));
+ if ((scale.x < (double) *width) || (scale.y < (double) *height))
+ {
+ *width=(unsigned long) (former_width/(distance/sqrt((double) area)));
+ *height=(unsigned long) (former_height/(distance/
+ sqrt((double) area)));
+ }
+ former_width=(*width);
+ former_height=(*height);
+ }
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e P a g e G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParsePageGeometry() returns a region as defined by the geometry string with
+% respect to the image dimensions.
+%
+% The format of the ParsePageGeometry method is:
+%
+% MagickStatusType ParsePageGeometry(const Image *image,
+% const char *geometry,RectangeInfo *region_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o region_info: the region as defined by the geometry string with
+% respect to the image and its gravity.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickStatusType ParsePageGeometry(const Image *image,
+ const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
+{
+ MagickStatusType
+ flags;
+
+ SetGeometry(image,region_info);
+ if (image->page.width != 0)
+ region_info->width=image->page.width;
+ if (image->page.height != 0)
+ region_info->height=image->page.height;
+ flags=ParseAbsoluteGeometry(geometry,region_info);
+ if (flags == NoValue)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidGeometry","`%s'",geometry);
+ return(flags);
+ }
+ if ((flags & PercentValue) != 0)
+ {
+ region_info->width=image->columns;
+ region_info->height=image->rows;
+ }
+ flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
+ ®ion_info->width,®ion_info->height);
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e R e g i o n G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseRegionGeometry() returns a region as defined by the geometry string
+% with respect to the image dimensions and aspect ratio.
+%
+% The format of the ParseRegionGeometry method is:
+%
+% MagickStatusType ParseRegionGeometry(const Image *image,
+% const char *geometry,RectangeInfo *region_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o geometry: The geometry (e.g. 100x100+10+10).
+%
+% o region_info: the region as defined by the geometry string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
+ const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
+{
+ MagickStatusType
+ flags;
+
+ SetGeometry(image,region_info);
+ flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
+ ®ion_info->width,®ion_info->height);
+ if (flags == NoValue)
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidGeometry","`%s'",geometry);
+ return(flags);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t G e o m e t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetGeometry() sets the geometry to its default values.
+%
+% The format of the SetGeometry method is:
+%
+% SetGeometry(const Image *image,RectangleInfo *geometry)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: the geometry.
+%
+*/
+MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (RectangleInfo *) NULL);
+ (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
+ geometry->width=image->columns;
+ geometry->height=image->rows;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t G e o m e t r y I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetGeometryInfo sets the GeometryInfo structure to its default values.
+%
+% The format of the SetGeometryInfo method is:
+%
+% SetGeometryInfo(GeometryInfo *geometry_info)
+%
+% A description of each parameter follows:
+%
+% o geometry_info: the geometry info structure.
+%
+*/
+MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
+{
+ assert(geometry_info != (GeometryInfo *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
+}
diff --git a/magick/geometry.h b/magick/geometry.h
new file mode 100644
index 0000000..043b328
--- /dev/null
+++ b/magick/geometry.h
@@ -0,0 +1,151 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image geometry methods.
+*/
+#ifndef _MAGICKCORE_GEOMETRY_H
+#define _MAGICKCORE_GEOMETRY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+#undef NoValue
+ NoValue = 0x0000,
+#undef XValue
+ XValue = 0x0001,
+ XiValue = 0x0001,
+#undef YValue
+ YValue = 0x0002,
+ PsiValue = 0x0002,
+#undef WidthValue
+ WidthValue = 0x0004,
+ RhoValue = 0x0004,
+#undef HeightValue
+ HeightValue = 0x0008,
+ SigmaValue = 0x0008,
+ ChiValue = 0x0010,
+ XiNegative = 0x0020,
+#undef XNegative
+ XNegative = 0x0020,
+ PsiNegative = 0x0040,
+#undef YNegative
+ YNegative = 0x0040,
+ ChiNegative = 0x0080,
+ PercentValue = 0x1000,
+ AspectValue = 0x2000,
+ LessValue = 0x4000,
+ GreaterValue = 0x8000,
+ MinimumValue = 0x10000,
+ AreaValue = 0x20000,
+ DecimalValue = 0x40000,
+#undef AllValues
+ AllValues = 0x7fffffff
+} GeometryFlags;
+
+#if defined(ForgetGravity)
+#undef ForgetGravity
+#undef NorthWestGravity
+#undef NorthGravity
+#undef NorthEastGravity
+#undef WestGravity
+#undef CenterGravity
+#undef EastGravity
+#undef SouthWestGravity
+#undef SouthGravity
+#undef SouthEastGravity
+#undef StaticGravity
+#endif
+
+typedef enum
+{
+ UndefinedGravity,
+ ForgetGravity = 0,
+ NorthWestGravity = 1,
+ NorthGravity = 2,
+ NorthEastGravity = 3,
+ WestGravity = 4,
+ CenterGravity = 5,
+ EastGravity = 6,
+ SouthWestGravity = 7,
+ SouthGravity = 8,
+ SouthEastGravity = 9,
+ StaticGravity = 10
+} GravityType;
+
+typedef struct _AffineMatrix
+{
+ double
+ sx,
+ rx,
+ ry,
+ sy,
+ tx,
+ ty;
+} AffineMatrix;
+
+typedef struct _GeometryInfo
+{
+ double
+ rho,
+ sigma,
+ xi,
+ psi,
+ chi;
+} GeometryInfo;
+
+typedef struct _RectangleInfo
+{
+ unsigned long
+ width,
+ height;
+
+ long
+ x,
+ y;
+} RectangleInfo;
+
+extern MagickExport char
+ *GetPageGeometry(const char *);
+
+extern MagickExport MagickBooleanType
+ IsGeometry(const char *),
+ IsSceneGeometry(const char *,const MagickBooleanType);
+
+extern MagickExport MagickStatusType
+ GetGeometry(const char *,long *,long *,unsigned long *,unsigned long *),
+ ParseAbsoluteGeometry(const char *,RectangleInfo *),
+ ParseAffineGeometry(const char *,AffineMatrix *,ExceptionInfo *),
+ ParseGeometry(const char *,GeometryInfo *),
+ ParseGravityGeometry(const Image *,const char *,RectangleInfo *,
+ ExceptionInfo *),
+ ParseMetaGeometry(const char *,long *,long *,unsigned long *,unsigned long *),
+ ParsePageGeometry(const Image *,const char *,RectangleInfo *,ExceptionInfo *),
+ ParseRegionGeometry(const Image *,const char *,RectangleInfo *,
+ ExceptionInfo *);
+
+extern MagickExport void
+ GravityAdjustGeometry(const unsigned long,const unsigned long,
+ const GravityType,RectangleInfo *),
+ SetGeometry(const Image *,RectangleInfo *),
+ SetGeometryInfo(GeometryInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/hashmap.c b/magick/hashmap.c
new file mode 100644
index 0000000..33d3d7f
--- /dev/null
+++ b/magick/hashmap.c
@@ -0,0 +1,1982 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H H AAA SSSSS H H M M AAA PPPP %
+% H H A A SS H H MM MM A A P P %
+% HHHHH AAAAA SSS HHHHH M M M AAAAA PPPP %
+% H H A A SS H H M M A A P %
+% H H A A SSSSS H H M M A A P %
+% %
+% %
+% MagickCore Hash-map and Linked-list Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This module implements the standard handy hash and linked-list methods for
+% storing and retrieving large numbers of data elements. It is loosely based
+% on the Java implementation of these algorithms.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+
+/*
+ Typedef declarations.
+*/
+typedef struct _ElementInfo
+{
+ void
+ *value;
+
+ struct _ElementInfo
+ *next;
+} ElementInfo;
+
+typedef struct _EntryInfo
+{
+ size_t
+ hash;
+
+ void
+ *key,
+ *value;
+} EntryInfo;
+
+struct _LinkedListInfo
+{
+ unsigned long
+ capacity,
+ elements;
+
+ ElementInfo
+ *head,
+ *tail,
+ *next;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+struct _HashmapInfo
+{
+ size_t
+ (*hash)(const void *);
+
+ MagickBooleanType
+ (*compare)(const void *,const void *);
+
+ void
+ *(*relinquish_key)(void *),
+ *(*relinquish_value)(void *);
+
+ unsigned long
+ capacity,
+ entries,
+ next;
+
+ MagickBooleanType
+ head_of_list;
+
+ LinkedListInfo
+ **map;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A p p e n d V a l u e T o L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AppendValueToLinkedList() appends a value to the end of the linked-list.
+%
+% The format of the AppendValueToLinkedList method is:
+%
+% MagickBooleanType AppendValueToLinkedList(LinkedListInfo *list_info,
+% const void *value)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+% o value: the value.
+%
+*/
+MagickExport MagickBooleanType AppendValueToLinkedList(
+ LinkedListInfo *list_info,const void *value)
+{
+ register ElementInfo
+ *next;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ list_info->debug=IsEventLogging();
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (list_info->elements == list_info->capacity)
+ return(MagickFalse);
+ next=(ElementInfo *) AcquireMagickMemory(sizeof(*next));
+ if (next == (ElementInfo *) NULL)
+ return(MagickFalse);
+ next->value=(void *) value;
+ next->next=(ElementInfo *) NULL;
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (list_info->next == (ElementInfo *) NULL)
+ list_info->next=next;
+ if (list_info->elements == 0)
+ list_info->head=next;
+ else
+ list_info->tail->next=next;
+ list_info->tail=next;
+ list_info->elements++;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l e a r L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClearLinkedList() clears all the elements from the linked-list.
+%
+% The format of the ClearLinkedList method is:
+%
+% void ClearLinkedList(LinkedListInfo *list_info,
+% void *(*relinquish_value)(void *))
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+% o relinquish_value: the value deallocation method; typically
+% RelinquishMagickMemory().
+%
+*/
+MagickExport void ClearLinkedList(LinkedListInfo *list_info,
+ void *(*relinquish_value)(void *))
+{
+ ElementInfo
+ *element;
+
+ register ElementInfo
+ *next;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ next=list_info->head;
+ while (next != (ElementInfo *) NULL)
+ {
+ if (relinquish_value != (void *(*)(void *)) NULL)
+ next->value=relinquish_value(next->value);
+ element=next;
+ next=next->next;
+ element=(ElementInfo *) RelinquishMagickMemory(element);
+ }
+ list_info->head=(ElementInfo *) NULL;
+ list_info->tail=(ElementInfo *) NULL;
+ list_info->next=(ElementInfo *) NULL;
+ list_info->elements=0;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e H a s h m a p S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Specify the CompareHashmapString() method in NewHashmap() to find an entry
+% in a hash-map based on the contents of a string.
+%
+% The format of the CompareHashmapString method is:
+%
+% MagickBooleanType CompareHashmapString(const void *target,
+% const void *source)
+%
+% A description of each parameter follows:
+%
+% o target: the target string.
+%
+% o source: the source string.
+%
+*/
+MagickExport MagickBooleanType CompareHashmapString(const void *target,
+ const void *source)
+{
+ const char
+ *p,
+ *q;
+
+ p=(const char *) target;
+ q=(const char *) source;
+ return(LocaleCompare(p,q) == 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e H a s h m a p S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Specify the CompareHashmapStringInfo() method in NewHashmap() to find an
+% entry in a hash-map based on the contents of a string.
+%
+% The format of the CompareHashmapStringInfo method is:
+%
+% MagickBooleanType CompareHashmapStringInfo(const void *target,
+% const void *source)
+%
+% A description of each parameter follows:
+%
+% o target: the target string.
+%
+% o source: the source string.
+%
+*/
+MagickExport MagickBooleanType CompareHashmapStringInfo(const void *target,
+ const void *source)
+{
+ const StringInfo
+ *p,
+ *q;
+
+ p=(const StringInfo *) target;
+ q=(const StringInfo *) source;
+ return(CompareStringInfo(p,q) == 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyHashmap() frees the hash-map and all associated resources.
+%
+% The format of the DestroyHashmap method is:
+%
+% HashmapInfo *DestroyHashmap(HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport HashmapInfo *DestroyHashmap(HashmapInfo *hashmap_info)
+{
+ LinkedListInfo
+ *list_info;
+
+ register EntryInfo
+ *entry;
+
+ register long
+ i;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ for (i=0; i < (long) hashmap_info->capacity; i++)
+ {
+ list_info=hashmap_info->map[i];
+ if (list_info != (LinkedListInfo *) NULL)
+ {
+ list_info->next=list_info->head;
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ while (entry != (EntryInfo *) NULL)
+ {
+ if (hashmap_info->relinquish_key != (void *(*)(void *)) NULL)
+ entry->key=hashmap_info->relinquish_key(entry->key);
+ if (hashmap_info->relinquish_value != (void *(*)(void *)) NULL)
+ entry->value=hashmap_info->relinquish_value(entry->value);
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ }
+ }
+ if (list_info != (LinkedListInfo *) NULL)
+ list_info=DestroyLinkedList(list_info,RelinquishMagickMemory);
+ }
+ hashmap_info->map=(LinkedListInfo **) RelinquishMagickMemory(
+ hashmap_info->map);
+ hashmap_info->signature=(~MagickSignature);
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ DestroySemaphoreInfo(&hashmap_info->semaphore);
+ hashmap_info=(HashmapInfo *) RelinquishMagickMemory(hashmap_info);
+ return(hashmap_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyLinkedList() frees the linked-list and all associated resources.
+%
+% The format of the DestroyLinkedList method is:
+%
+% LinkedListInfo *DestroyLinkedList(LinkedListInfo *list_info,
+% void *(*relinquish_value)(void *))
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+% o relinquish_value: the value deallocation method; typically
+% RelinquishMagickMemory().
+%
+*/
+MagickExport LinkedListInfo *DestroyLinkedList(LinkedListInfo *list_info,
+ void *(*relinquish_value)(void *))
+{
+ ElementInfo
+ *entry;
+
+ register ElementInfo
+ *next;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ for (next=list_info->head; next != (ElementInfo *) NULL; )
+ {
+ if (relinquish_value != (void *(*)(void *)) NULL)
+ next->value=relinquish_value(next->value);
+ entry=next;
+ next=next->next;
+ entry=(ElementInfo *) RelinquishMagickMemory(entry);
+ }
+ list_info->signature=(~MagickSignature);
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ DestroySemaphoreInfo(&list_info->semaphore);
+ list_info=(LinkedListInfo *) RelinquishMagickMemory(list_info);
+ return(list_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L a s t V a l u e I n L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLastValueInLinkedList() gets the last value in the linked-list.
+%
+% The format of the GetLastValueInLinkedList method is:
+%
+% void *GetLastValueInLinkedList(LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked_list info.
+%
+*/
+MagickExport void *GetLastValueInLinkedList(LinkedListInfo *list_info)
+{
+ void
+ *value;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (list_info->elements == 0)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ value=list_info->tail->value;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t K e y I n H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextKeyInHashmap() gets the next key in the hash-map.
+%
+% The format of the GetNextKeyInHashmap method is:
+%
+% void *GetNextKeyInHashmap(HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport void *GetNextKeyInHashmap(HashmapInfo *hashmap_info)
+{
+ LinkedListInfo
+ *list_info;
+
+ register EntryInfo
+ *entry;
+
+ void
+ *key;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ while (hashmap_info->next < hashmap_info->capacity)
+ {
+ list_info=hashmap_info->map[hashmap_info->next];
+ if (list_info != (LinkedListInfo *) NULL)
+ {
+ if (hashmap_info->head_of_list == MagickFalse)
+ {
+ list_info->next=list_info->head;
+ hashmap_info->head_of_list=MagickTrue;
+ }
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ if (entry != (EntryInfo *) NULL)
+ {
+ key=entry->key;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(key);
+ }
+ hashmap_info->head_of_list=MagickFalse;
+ }
+ hashmap_info->next++;
+ }
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t V a l u e I n H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextValueInHashmap() gets the next value in the hash-map.
+%
+% The format of the GetNextValueInHashmap method is:
+%
+% void *GetNextValueInHashmap(HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport void *GetNextValueInHashmap(HashmapInfo *hashmap_info)
+{
+ LinkedListInfo
+ *list_info;
+
+ register EntryInfo
+ *entry;
+
+ void
+ *value;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ while (hashmap_info->next < hashmap_info->capacity)
+ {
+ list_info=hashmap_info->map[hashmap_info->next];
+ if (list_info != (LinkedListInfo *) NULL)
+ {
+ if (hashmap_info->head_of_list == MagickFalse)
+ {
+ list_info->next=list_info->head;
+ hashmap_info->head_of_list=MagickTrue;
+ }
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ if (entry != (EntryInfo *) NULL)
+ {
+ value=entry->value;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(value);
+ }
+ hashmap_info->head_of_list=MagickFalse;
+ }
+ hashmap_info->next++;
+ }
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t V a l u e I n L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextValueInLinkedList() gets the next value in the linked-list.
+%
+% The format of the GetNextValueInLinkedList method is:
+%
+% void *GetNextValueInLinkedList(LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+*/
+MagickExport void *GetNextValueInLinkedList(LinkedListInfo *list_info)
+{
+ void
+ *value;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (list_info->next == (ElementInfo *) NULL)
+ {
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return((void *) NULL);
+ }
+ value=list_info->next->value;
+ list_info->next=list_info->next->next;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N u m b e r O f E n t r i e s I n H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNumberOfEntriesInHashmap() returns the number of entries in the hash-map.
+%
+% The format of the GetNumberOfEntriesInHashmap method is:
+%
+% unsigned long GetNumberOfEntriesInHashmap(const HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport unsigned long GetNumberOfEntriesInHashmap(
+ const HashmapInfo *hashmap_info)
+{
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(hashmap_info->entries);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N u m b e r O f E l e m e n t s I n L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNumberOfElementsInLinkedList() returns the number of entries in the
+% linked-list.
+%
+% The format of the GetNumberOfElementsInLinkedList method is:
+%
+% unsigned long GetNumberOfElementsInLinkedList(
+% const LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+*/
+MagickExport unsigned long GetNumberOfElementsInLinkedList(
+ const LinkedListInfo *list_info)
+{
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(list_info->elements);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V a l u e F r o m H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetValueFromHashmap() gets an entry from the hash-map by its key.
+%
+% The format of the GetValueFromHashmap method is:
+%
+% void *GetValueFromHashmap(HashmapInfo *hashmap_info,const void *key)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+% o key: the key.
+%
+*/
+MagickExport void *GetValueFromHashmap(HashmapInfo *hashmap_info,
+ const void *key)
+{
+ LinkedListInfo
+ *list_info;
+
+ register EntryInfo
+ *entry;
+
+ size_t
+ hash;
+
+ void
+ *value;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (key == (const void *) NULL)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ hash=hashmap_info->hash(key);
+ list_info=hashmap_info->map[hash % hashmap_info->capacity];
+ if (list_info != (LinkedListInfo *) NULL)
+ {
+ list_info->next=list_info->head;
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ while (entry != (EntryInfo *) NULL)
+ {
+ if (entry->hash == hash)
+ {
+ MagickBooleanType
+ compare;
+
+ compare=MagickTrue;
+ if (hashmap_info->compare !=
+ (MagickBooleanType (*)(const void *,const void *)) NULL)
+ compare=hashmap_info->compare(key,entry->key);
+ if (compare == MagickTrue)
+ {
+ value=entry->value;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(value);
+ }
+ }
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ }
+ }
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V a l u e F r o m L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetValueFromLinkedList() gets a value from the linked-list at the specified
+% location.
+%
+% The format of the GetValueFromLinkedList method is:
+%
+% void *GetValueFromLinkedList(LinkedListInfo *list_info,
+% const unsigned long index)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked_list info.
+%
+% o index: the list index.
+%
+*/
+MagickExport void *GetValueFromLinkedList(LinkedListInfo *list_info,
+ const unsigned long index)
+{
+ register ElementInfo
+ *next;
+
+ register long
+ i;
+
+ void
+ *value;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (index >= list_info->elements)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (index == 0)
+ {
+ value=list_info->head->value;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+ }
+ if (index == (list_info->elements-1))
+ {
+ value=list_info->tail->value;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+ }
+ next=list_info->head;
+ for (i=0; i < (long) index; i++)
+ next=next->next;
+ value=next->value;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H a s h P o i n t e r T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Specify the HashPointerType() method in NewHashmap() to find an entry
+% in a hash-map based on the address of a pointer.
+%
+% The format of the HashPointerType method is:
+%
+% size_t HashPointerType(const void *pointer)
+%
+% A description of each parameter follows:
+%
+% o pointer: compute the hash entry location from this pointer address.
+%
+*/
+MagickExport size_t HashPointerType(const void *pointer)
+{
+ size_t
+ hash;
+
+ hash=(size_t) pointer;
+ hash+=(~(hash << 9));
+ hash^=(hash >> 14);
+ hash+=(hash << 4);
+ hash^=(hash >> 10);
+ return(hash);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H a s h S t r i n g T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Specify the HashStringType() method in NewHashmap() to find an entry
+% in a hash-map based on the contents of a string.
+%
+% The format of the HashStringType method is:
+%
+% size_t HashStringType(const void *string)
+%
+% A description of each parameter follows:
+%
+% o string: compute the hash entry location from this string.
+%
+*/
+MagickExport size_t HashStringType(const void *string)
+{
+ const unsigned char
+ *digest;
+
+ register long
+ i;
+
+ SignatureInfo
+ *signature_info;
+
+ size_t
+ hash;
+
+ StringInfo
+ *signature;
+
+ signature_info=AcquireSignatureInfo();
+ signature=StringToStringInfo((const char *) string);
+ UpdateSignature(signature_info,signature);
+ FinalizeSignature(signature_info);
+ digest=GetStringInfoDatum(GetSignatureDigest(signature_info));
+ hash=0;
+ for (i=0; i < 8; i++)
+ hash^=digest[i];
+ signature=DestroyStringInfo(signature);
+ signature_info=DestroySignatureInfo(signature_info);
+ return(hash);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H a s h S t r i n g I n f o T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Specify the HashStringInfoType() method in NewHashmap() to find an entry
+% in a hash-map based on the contents of a string.
+%
+% The format of the HashStringInfoType method is:
+%
+% size_t HashStringInfoType(const void *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: compute the hash entry location from this string.
+%
+*/
+MagickExport size_t HashStringInfoType(const void *string_info)
+{
+ const unsigned char
+ *digest;
+
+ register long
+ i;
+
+ SignatureInfo
+ *signature_info;
+
+ size_t
+ hash;
+
+ signature_info=AcquireSignatureInfo();
+ UpdateSignature(signature_info,(const StringInfo *) string_info);
+ FinalizeSignature(signature_info);
+ digest=GetStringInfoDatum(GetSignatureDigest(signature_info));
+ hash=0;
+ for (i=0; i < 8; i++)
+ hash^=digest[i];
+ signature_info=DestroySignatureInfo(signature_info);
+ return(hash);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n s e r t V a l u e I n L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InsertValueInLinkedList() inserts an element in the linked-list at the
+% specified location.
+%
+% The format of the InsertValueInLinkedList method is:
+%
+% MagickBooleanType InsertValueInLinkedList(ListInfo *list_info,
+% const unsigned long index,const void *value)
+%
+% A description of each parameter follows:
+%
+% o list_info: the hashmap info.
+%
+% o index: the index.
+%
+% o value: the value.
+%
+*/
+MagickExport MagickBooleanType InsertValueInLinkedList(
+ LinkedListInfo *list_info,const unsigned long index,const void *value)
+{
+ register ElementInfo
+ *next;
+
+ register long
+ i;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (value == (const void *) NULL)
+ return(MagickFalse);
+ if ((index > list_info->elements) ||
+ (list_info->elements == list_info->capacity))
+ return(MagickFalse);
+ next=(ElementInfo *) AcquireMagickMemory(sizeof(*next));
+ if (next == (ElementInfo *) NULL)
+ return(MagickFalse);
+ next->value=(void *) value;
+ next->next=(ElementInfo *) NULL;
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (list_info->elements == 0)
+ {
+ if (list_info->next == (ElementInfo *) NULL)
+ list_info->next=next;
+ list_info->head=next;
+ list_info->tail=next;
+ }
+ else
+ {
+ if (index == 0)
+ {
+ if (list_info->next == list_info->head)
+ list_info->next=next;
+ next->next=list_info->head;
+ list_info->head=next;
+ }
+ else
+ if (index == list_info->elements)
+ {
+ if (list_info->next == (ElementInfo *) NULL)
+ list_info->next=next;
+ list_info->tail->next=next;
+ list_info->tail=next;
+ }
+ else
+ {
+ ElementInfo
+ *element;
+
+ element=list_info->head;
+ next->next=element->next;
+ for (i=1; i < (long) index; i++)
+ {
+ element=element->next;
+ next->next=element->next;
+ }
+ next=next->next;
+ element->next=next;
+ if (list_info->next == next->next)
+ list_info->next=next;
+ }
+ }
+ list_info->elements++;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n s e r t V a l u e I n S o r t e d L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InsertValueInSortedLinkedList() inserts a value in the sorted linked-list.
+%
+% The format of the InsertValueInSortedLinkedList method is:
+%
+% MagickBooleanType InsertValueInSortedLinkedList(ListInfo *list_info,
+% int (*compare)(const void *,const void *),void **replace,
+% const void *value)
+%
+% A description of each parameter follows:
+%
+% o list_info: the hashmap info.
+%
+% o index: the index.
+%
+% o compare: the compare method.
+%
+% o replace: return previous element here.
+%
+% o value: the value.
+%
+*/
+MagickExport MagickBooleanType InsertValueInSortedLinkedList(
+ LinkedListInfo *list_info,int (*compare)(const void *,const void *),
+ void **replace,const void *value)
+{
+ ElementInfo
+ *element;
+
+ register ElementInfo
+ *next;
+
+ register long
+ i;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((compare == (int (*)(const void *,const void *)) NULL) ||
+ (value == (const void *) NULL))
+ return(MagickFalse);
+ if (list_info->elements == list_info->capacity)
+ return(MagickFalse);
+ next=(ElementInfo *) AcquireMagickMemory(sizeof(*next));
+ if (next == (ElementInfo *) NULL)
+ return(MagickFalse);
+ next->value=(void *) value;
+ element=(ElementInfo *) NULL;
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ next->next=list_info->head;
+ while (next->next != (ElementInfo *) NULL)
+ {
+ i=compare(value,next->next->value);
+ if ((i < 0) || ((replace != (void **) NULL) && (i == 0)))
+ {
+ if (i == 0)
+ {
+ *replace=next->next->value;
+ next->next=next->next->next;
+ if (element != (ElementInfo *) NULL)
+ element->next=(ElementInfo *) RelinquishMagickMemory(
+ element->next);
+ list_info->elements--;
+ }
+ if (element != (ElementInfo *) NULL)
+ element->next=next;
+ else
+ list_info->head=next;
+ break;
+ }
+ element=next->next;
+ next->next=next->next->next;
+ }
+ if (next->next == (ElementInfo *) NULL)
+ {
+ if (element != (ElementInfo *) NULL)
+ element->next=next;
+ else
+ list_info->head=next;
+ list_info->tail=next;
+ }
+ list_info->elements++;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s H a s h m a p E m p t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsHashmapEmpty() returns MagickTrue if the hash-map is empty.
+%
+% The format of the IsHashmapEmpty method is:
+%
+% MagickBooleanType IsHashmapEmpty(const HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport MagickBooleanType IsHashmapEmpty(const HashmapInfo *hashmap_info)
+{
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(hashmap_info->entries == 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s L i n k e d L i s t E m p t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsLinkedListEmpty() returns MagickTrue if the linked-list is empty.
+%
+% The format of the IsLinkedListEmpty method is:
+%
+% MagickBooleanType IsLinkedListEmpty(LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+*/
+MagickExport MagickBooleanType IsLinkedListEmpty(
+ const LinkedListInfo *list_info)
+{
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(list_info->elements == 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i n k e d L i s t T o A r r a y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LinkedListToArray() converts the linked-list to an array.
+%
+% The format of the LinkedListToArray method is:
+%
+% MagickBooleanType LinkedListToArray(LinkedListInfo *list_info,
+% void **array)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+% o array: the array.
+%
+*/
+MagickExport MagickBooleanType LinkedListToArray(LinkedListInfo *list_info,
+ void **array)
+{
+ register ElementInfo
+ *next;
+
+ register long
+ i;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (array == (void **) NULL)
+ return(MagickFalse);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ next=list_info->head;
+ for (i=0; next != (ElementInfo *) NULL; i++)
+ {
+ array[i]=next->value;
+ next=next->next;
+ }
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewHashmap() returns a pointer to a HashmapInfo structure initialized
+% to default values. The capacity is an initial estimate. The hashmap will
+% increase capacity dynamically as the demand requires.
+%
+% The format of the NewHashmap method is:
+%
+% HashmapInfo *NewHashmap(const unsigned long capacity,
+% size_t (*hash)(const void *),
+% MagickBooleanType (*compare)(const void *,const void *),
+% void *(*relinquish_key)(void *),void *(*relinquish_value)(void *))
+%
+% A description of each parameter follows:
+%
+% o capacity: the initial number entries in the hash-map: typically
+% SmallHashmapSize, MediumHashmapSize, or LargeHashmapSize. The
+% hashmap will dynamically increase its capacity on demand.
+%
+% o hash: the hash method, typically HashPointerType(), HashStringType(),
+% or HashStringInfoType().
+%
+% o compare: the compare method, typically NULL, CompareHashmapString(),
+% or CompareHashmapStringInfo().
+%
+% o relinquish_key: the key deallocation method, typically
+% RelinquishMagickMemory(), called whenever a key is removed from the
+% hash-map.
+%
+% o relinquish_value: the value deallocation method; typically
+% RelinquishMagickMemory(), called whenever a value object is removed from
+% the hash-map.
+%
+*/
+MagickExport HashmapInfo *NewHashmap(const unsigned long capacity,
+ size_t (*hash)(const void *),
+ MagickBooleanType (*compare)(const void *,const void *),
+ void *(*relinquish_key)(void *),void *(*relinquish_value)(void *))
+{
+ HashmapInfo
+ *hashmap_info;
+
+ hashmap_info=(HashmapInfo *) AcquireMagickMemory(sizeof(*hashmap_info));
+ if (hashmap_info == (HashmapInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(hashmap_info,0,sizeof(*hashmap_info));
+ hashmap_info->hash=HashPointerType;
+ if (hash != (size_t (*)(const void *)) NULL)
+ hashmap_info->hash=hash;
+ hashmap_info->compare=(MagickBooleanType (*)(const void *,const void *)) NULL;
+ if (compare != (MagickBooleanType (*)(const void *,const void *)) NULL)
+ hashmap_info->compare=compare;
+ hashmap_info->relinquish_key=relinquish_key;
+ hashmap_info->relinquish_value=relinquish_value;
+ hashmap_info->entries=0;
+ hashmap_info->capacity=capacity;
+ hashmap_info->map=(LinkedListInfo **) NULL;
+ if (~capacity >= 1UL)
+ hashmap_info->map=(LinkedListInfo **) AcquireQuantumMemory((size_t)
+ capacity+1UL,sizeof(*hashmap_info->map));
+ if (hashmap_info->map == (LinkedListInfo **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(hashmap_info->map,0,(size_t) capacity*
+ sizeof(*hashmap_info->map));
+ hashmap_info->debug=IsEventLogging();
+ hashmap_info->semaphore=AllocateSemaphoreInfo();
+ hashmap_info->signature=MagickSignature;
+ return(hashmap_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w L i n k e d L i s t I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewLinkedList() returns a pointer to a LinkedListInfo structure
+% initialized to default values.
+%
+% The format of the NewLinkedList method is:
+%
+% LinkedListInfo *NewLinkedList(const unsigned long capacity)
+%
+% A description of each parameter follows:
+%
+% o capacity: the maximum number of elements in the list.
+%
+*/
+MagickExport LinkedListInfo *NewLinkedList(const unsigned long capacity)
+{
+ LinkedListInfo
+ *list_info;
+
+ list_info=(LinkedListInfo *) AcquireMagickMemory(sizeof(*list_info));
+ if (list_info == (LinkedListInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(list_info,0,sizeof(*list_info));
+ list_info->capacity=capacity == 0 ? (unsigned long) (~0) : capacity;
+ list_info->elements=0;
+ list_info->head=(ElementInfo *) NULL;
+ list_info->tail=(ElementInfo *) NULL;
+ list_info->next=(ElementInfo *) NULL;
+ list_info->debug=MagickFalse;
+ list_info->semaphore=AllocateSemaphoreInfo();
+ list_info->signature=MagickSignature;
+ return(list_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P u t E n t r y I n H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PutEntryInHashmap() puts an entry in the hash-map. If the key already
+% exists in the map it is first removed.
+%
+% The format of the PutEntryInHashmap method is:
+%
+% MagickBooleanType PutEntryInHashmap(HashmapInfo *hashmap_info,
+% const void *key,const void *value)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+% o key: the key.
+%
+% o value: the value.
+%
+*/
+
+static MagickBooleanType IncreaseHashmapCapacity(HashmapInfo *hashmap_info)
+{
+#define MaxCapacities 20
+
+ const unsigned long
+ capacities[MaxCapacities] =
+ {
+ 17, 31, 61, 131, 257, 509, 1021, 2053, 4099, 8191, 16381, 32771,
+ 65537, 131071, 262147, 524287, 1048573, 2097143, 4194301, 8388617
+ };
+
+ ElementInfo
+ *element;
+
+ EntryInfo
+ *entry;
+
+ LinkedListInfo
+ *list_info,
+ *map_info,
+ **map;
+
+ register ElementInfo
+ *next;
+
+ register long
+ i;
+
+ unsigned long
+ capacity;
+
+ /*
+ Increase to the next prime capacity.
+ */
+ for (i=0; i < MaxCapacities; i++)
+ if (hashmap_info->capacity < capacities[i])
+ break;
+ if (i >= (MaxCapacities-1))
+ return(MagickFalse);
+ capacity=capacities[i+1];
+ map=(LinkedListInfo **) AcquireQuantumMemory((size_t) capacity+1UL,
+ sizeof(*map));
+ if (map == (LinkedListInfo **) NULL)
+ return(MagickFalse);
+ (void) ResetMagickMemory(map,0,(size_t) capacity*sizeof(*map));
+ /*
+ Copy entries to new hashmap with increased capacity.
+ */
+ for (i=0; i < (long) hashmap_info->capacity; i++)
+ {
+ list_info=hashmap_info->map[i];
+ if (list_info == (LinkedListInfo *) NULL)
+ continue;
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ for (next=list_info->head; next != (ElementInfo *) NULL; )
+ {
+ element=next;
+ next=next->next;
+ entry=(EntryInfo *) element->value;
+ map_info=map[entry->hash % capacity];
+ if (map_info == (LinkedListInfo *) NULL)
+ {
+ map_info=NewLinkedList(0);
+ map[entry->hash % capacity]=map_info;
+ }
+ map_info->next=element;
+ element->next=map_info->head;
+ map_info->head=element;
+ map_info->elements++;
+ }
+ list_info->signature=(~MagickSignature);
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ DestroySemaphoreInfo(&list_info->semaphore);
+ list_info=(LinkedListInfo *) RelinquishMagickMemory(list_info);
+ }
+ hashmap_info->map=(LinkedListInfo **) RelinquishMagickMemory(
+ hashmap_info->map);
+ hashmap_info->map=map;
+ hashmap_info->capacity=capacity;
+ return(MagickTrue);
+}
+
+MagickExport MagickBooleanType PutEntryInHashmap(HashmapInfo *hashmap_info,
+ const void *key,const void *value)
+{
+ EntryInfo
+ *entry,
+ *next;
+
+ LinkedListInfo
+ *list_info;
+
+ register unsigned long
+ i;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((key == (void *) NULL) || (value == (void *) NULL))
+ return(MagickFalse);
+ next=(EntryInfo *) AcquireMagickMemory(sizeof(*next));
+ if (next == (EntryInfo *) NULL)
+ return(MagickFalse);
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ next->hash=hashmap_info->hash(key);
+ next->key=(void *) key;
+ next->value=(void *) value;
+ list_info=hashmap_info->map[next->hash % hashmap_info->capacity];
+ if (list_info == (LinkedListInfo *) NULL)
+ {
+ list_info=NewLinkedList(0);
+ hashmap_info->map[next->hash % hashmap_info->capacity]=list_info;
+ }
+ else
+ {
+ list_info->next=list_info->head;
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ for (i=0; entry != (EntryInfo *) NULL; i++)
+ {
+ if (entry->hash == next->hash)
+ {
+ MagickBooleanType
+ compare;
+
+ compare=MagickTrue;
+ if (hashmap_info->compare !=
+ (MagickBooleanType (*)(const void *,const void *)) NULL)
+ compare=hashmap_info->compare(key,entry->key);
+ if (compare == MagickTrue)
+ {
+ (void) RemoveElementFromLinkedList(list_info,i);
+ if (hashmap_info->relinquish_key != (void *(*)(void *)) NULL)
+ entry->key=hashmap_info->relinquish_key(entry->key);
+ if (hashmap_info->relinquish_value != (void *(*)(void *)) NULL)
+ entry->value=hashmap_info->relinquish_value(entry->value);
+ entry=(EntryInfo *) RelinquishMagickMemory(entry);
+ break;
+ }
+ }
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ }
+ }
+ if (InsertValueInLinkedList(list_info,0,next) == MagickFalse)
+ {
+ next=(EntryInfo *) RelinquishMagickMemory(next);
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(MagickFalse);
+ }
+ if (list_info->elements >= (hashmap_info->capacity-1))
+ if (IncreaseHashmapCapacity(hashmap_info) == MagickFalse)
+ {
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(MagickFalse);
+ }
+ hashmap_info->entries++;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e E l e m e n t B y V a l u e F r o m L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveElementByValueFromLinkedList() removes an element from the linked-list
+% by value.
+%
+% The format of the RemoveElementByValueFromLinkedList method is:
+%
+% void *RemoveElementByValueFromLinkedList(LinkedListInfo *list_info,
+% const void *value)
+%
+% A description of each parameter follows:
+%
+% o list_info: the list info.
+%
+% o value: the value.
+%
+*/
+MagickExport void *RemoveElementByValueFromLinkedList(LinkedListInfo *list_info,
+ const void *value)
+{
+ ElementInfo
+ *next;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((list_info->elements == 0) || (value == (const void *) NULL))
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (value == list_info->head->value)
+ {
+ if (list_info->next == list_info->head)
+ list_info->next=list_info->head->next;
+ next=list_info->head;
+ list_info->head=list_info->head->next;
+ next=(ElementInfo *) RelinquishMagickMemory(next);
+ }
+ else
+ {
+ ElementInfo
+ *element;
+
+ next=list_info->head;
+ while ((next->next != (ElementInfo *) NULL) &&
+ (next->next->value != value))
+ next=next->next;
+ if (next->next == (ElementInfo *) NULL)
+ {
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return((void *) NULL);
+ }
+ element=next->next;
+ next->next=element->next;
+ if (element == list_info->tail)
+ list_info->tail=next;
+ if (list_info->next == element)
+ list_info->next=element->next;
+ element=(ElementInfo *) RelinquishMagickMemory(element);
+ }
+ list_info->elements--;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return((void *) value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e E l e m e n t F r o m L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveElementFromLinkedList() removes an element from the linked-list at the
+% specified location.
+%
+% The format of the RemoveElementFromLinkedList method is:
+%
+% void *RemoveElementFromLinkedList(LinkedListInfo *list_info,
+% const unsigned long index)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+% o index: the index.
+%
+*/
+MagickExport void *RemoveElementFromLinkedList(LinkedListInfo *list_info,
+ const unsigned long index)
+{
+ ElementInfo
+ *next;
+
+ register long
+ i;
+
+ void
+ *value;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (index >= list_info->elements)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (index == 0)
+ {
+ if (list_info->next == list_info->head)
+ list_info->next=list_info->head->next;
+ value=list_info->head->value;
+ next=list_info->head;
+ list_info->head=list_info->head->next;
+ next=(ElementInfo *) RelinquishMagickMemory(next);
+ }
+ else
+ {
+ ElementInfo
+ *element;
+
+ next=list_info->head;
+ for (i=1; i < (long) index; i++)
+ next=next->next;
+ element=next->next;
+ next->next=element->next;
+ if (element == list_info->tail)
+ list_info->tail=next;
+ if (list_info->next == element)
+ list_info->next=element->next;
+ value=element->value;
+ element=(ElementInfo *) RelinquishMagickMemory(element);
+ }
+ list_info->elements--;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e E n t r y F r o m H a s h m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveEntryFromHashmap() removes an entry from the hash-map by its key.
+%
+% The format of the RemoveEntryFromHashmap method is:
+%
+% void *RemoveEntryFromHashmap(HashmapInfo *hashmap_info,void *key)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+% o key: the key.
+%
+*/
+MagickExport void *RemoveEntryFromHashmap(HashmapInfo *hashmap_info,
+ const void *key)
+{
+ EntryInfo
+ *entry;
+
+ LinkedListInfo
+ *list_info;
+
+ register unsigned long
+ i;
+
+ size_t
+ hash;
+
+ void
+ *value;
+
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (key == (const void *) NULL)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ hash=hashmap_info->hash(key);
+ list_info=hashmap_info->map[hash % hashmap_info->capacity];
+ if (list_info != (LinkedListInfo *) NULL)
+ {
+ list_info->next=list_info->head;
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ for (i=0; entry != (EntryInfo *) NULL; i++)
+ {
+ if (entry->hash == hash)
+ {
+ MagickBooleanType
+ compare;
+
+ compare=MagickTrue;
+ if (hashmap_info->compare !=
+ (MagickBooleanType (*)(const void *,const void *)) NULL)
+ compare=hashmap_info->compare(key,entry->key);
+ if (compare == MagickTrue)
+ {
+ entry=(EntryInfo *) RemoveElementFromLinkedList(list_info,i);
+ if (entry == (EntryInfo *) NULL)
+ {
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return((void *) NULL);
+ }
+ if (hashmap_info->relinquish_key != (void *(*)(void *)) NULL)
+ entry->key=hashmap_info->relinquish_key(entry->key);
+ value=entry->value;
+ entry=(EntryInfo *) RelinquishMagickMemory(entry);
+ hashmap_info->entries--;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return(value);
+ }
+ }
+ entry=(EntryInfo *) GetNextValueInLinkedList(list_info);
+ }
+ }
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e L a s t E l e m e n t F r o m L i n k e d L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveLastElementFromLinkedList() removes the last element from the
+% linked-list.
+%
+% The format of the RemoveLastElementFromLinkedList method is:
+%
+% void *RemoveLastElementFromLinkedList(LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+*/
+MagickExport void *RemoveLastElementFromLinkedList(LinkedListInfo *list_info)
+{
+ void
+ *value;
+
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (list_info->elements == 0)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ if (list_info->next == list_info->tail)
+ list_info->next=(ElementInfo *) NULL;
+ if (list_info->elements == 1UL)
+ {
+ value=list_info->head->value;
+ list_info->head=(ElementInfo *) NULL;
+ list_info->tail=(ElementInfo *) RelinquishMagickMemory(list_info->tail);
+ }
+ else
+ {
+ ElementInfo
+ *next;
+
+ value=list_info->tail->value;
+ next=list_info->head;
+ while (next->next != list_info->tail)
+ next=next->next;
+ list_info->tail=(ElementInfo *) RelinquishMagickMemory(list_info->tail);
+ list_info->tail=next;
+ next->next=(ElementInfo *) NULL;
+ }
+ list_info->elements--;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t H a s h m a p I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetHashmapIterator() resets the hash-map iterator. Use it in conjunction
+% with GetNextKeyInHashmap() to iterate over all the keys in the hash-map.
+%
+% The format of the ResetHashmapIterator method is:
+%
+% ResetHashmapIterator(HashmapInfo *hashmap_info)
+%
+% A description of each parameter follows:
+%
+% o hashmap_info: the hashmap info.
+%
+*/
+MagickExport void ResetHashmapIterator(HashmapInfo *hashmap_info)
+{
+ assert(hashmap_info != (HashmapInfo *) NULL);
+ assert(hashmap_info->signature == MagickSignature);
+ if (hashmap_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(hashmap_info->semaphore);
+ hashmap_info->next=0;
+ hashmap_info->head_of_list=MagickFalse;
+ (void) UnlockSemaphoreInfo(hashmap_info->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t L i n k e d L i s t I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetLinkedListIterator() resets the lined-list iterator. Use it in
+% conjunction with GetNextValueInLinkedList() to iterate over all the values
+% in the linked-list.
+%
+% The format of the ResetLinkedListIterator method is:
+%
+% ResetLinkedListIterator(LinkedListInfo *list_info)
+%
+% A description of each parameter follows:
+%
+% o list_info: the linked-list info.
+%
+*/
+MagickExport void ResetLinkedListIterator(LinkedListInfo *list_info)
+{
+ assert(list_info != (LinkedListInfo *) NULL);
+ assert(list_info->signature == MagickSignature);
+ if (list_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(list_info->semaphore);
+ list_info->next=list_info->head;
+ (void) UnlockSemaphoreInfo(list_info->semaphore);
+}
diff --git a/magick/hashmap.h b/magick/hashmap.h
new file mode 100644
index 0000000..8154e69
--- /dev/null
+++ b/magick/hashmap.h
@@ -0,0 +1,86 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore hash methods.
+*/
+#ifndef _MAGICKCORE_HASHMAP_H
+#define _MAGICKCORE_HASHMAP_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define SmallHashmapSize 17
+#define MediumHashmapSize 509
+#define LargeHashmapSize 8191
+#define HugeHashmapSize 131071
+
+typedef struct _HashmapInfo
+ HashmapInfo;
+
+typedef struct _LinkedListInfo
+ LinkedListInfo;
+
+extern MagickExport HashmapInfo
+ *DestroyHashmap(HashmapInfo *),
+ *NewHashmap(const unsigned long,size_t (*)(const void *),
+ MagickBooleanType (*)(const void *,const void *),void *(*)(void *),
+ void *(*)(void *));
+
+extern MagickExport LinkedListInfo
+ *DestroyLinkedList(LinkedListInfo *,void *(*)(void *)),
+ *NewLinkedList(const unsigned long);
+
+extern MagickExport MagickBooleanType
+ AppendValueToLinkedList(LinkedListInfo *,const void *),
+ CompareHashmapString(const void *,const void *),
+ CompareHashmapStringInfo(const void *,const void *),
+ InsertValueInLinkedList(LinkedListInfo *,const unsigned long,const void *),
+ InsertValueInSortedLinkedList(LinkedListInfo *,
+ int (*)(const void *,const void *),void **,const void *),
+ IsHashmapEmpty(const HashmapInfo *),
+ IsLinkedListEmpty(const LinkedListInfo *),
+ LinkedListToArray(LinkedListInfo *,void **),
+ PutEntryInHashmap(HashmapInfo *,const void *,const void *);
+
+extern MagickExport size_t
+ HashPointerType(const void *),
+ HashStringType(const void *),
+ HashStringInfoType(const void *);
+
+extern MagickExport unsigned long
+ GetNumberOfElementsInLinkedList(const LinkedListInfo *),
+ GetNumberOfEntriesInHashmap(const HashmapInfo *);
+
+extern MagickExport void
+ ClearLinkedList(LinkedListInfo *,void *(*)(void *)),
+ *GetLastValueInLinkedList(LinkedListInfo *),
+ *GetNextKeyInHashmap(HashmapInfo *),
+ *GetNextValueInHashmap(HashmapInfo *),
+ *GetNextValueInLinkedList(LinkedListInfo *),
+ *GetValueFromHashmap(HashmapInfo *,const void *),
+ *GetValueFromLinkedList(LinkedListInfo *,const unsigned long),
+ *RemoveElementByValueFromLinkedList(LinkedListInfo *,const void *),
+ *RemoveElementFromLinkedList(LinkedListInfo *,const unsigned long),
+ *RemoveEntryFromHashmap(HashmapInfo *,const void *),
+ *RemoveLastElementFromLinkedList(LinkedListInfo *),
+ ResetHashmapIterator(HashmapInfo *),
+ ResetLinkedListIterator(LinkedListInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/histogram.c b/magick/histogram.c
new file mode 100644
index 0000000..408ce2e
--- /dev/null
+++ b/magick/histogram.c
@@ -0,0 +1,164 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% H H IIIII SSSSS TTTTT OOO GGGG RRRR AAA M M %
+% H H I SS T O O G R R A A MM MM %
+% HHHHH I SSS T O O G GG RRRR AAAAA M M M %
+% H H I SS T O O G G R R A A M M %
+% H H IIIII SSSSS T OOO GGG R R A A M M %
+% %
+% %
+% MagickCore Histogram Methods %
+% %
+% Software Design %
+% Anthony Thyssen %
+% Fred Weinhaus %
+% August 2009 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache-view.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/histogram.h"
+#include "magick/image.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/prepress.h"
+#include "magick/registry.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M i n M a x S t r e t c h I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MinMaxStretchImage() uses the exact minimum and maximum values found in
+% each of the channels given, as the BlackPoint and WhitePoint to linearly
+% stretch the colors (and histogram) of the image. The stretch points are
+% also moved further inward by the adjustment values given.
+%
+% If the adjustment values are both zero this function is equivelent to a
+% perfect normalization (or autolevel) of the image.
+%
+% Each channel is stretched independantally of each other (producing color
+% distortion) unless the special 'SyncChannels' flag is also provided in the
+% channels setting. If this flag is present the minimum and maximum point
+% will be extracted from all the given channels, and those channels will be
+% stretched by exactly the same amount (preventing color distortion).
+%
+% The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
+% default.
+%
+% The format of the MinMaxStretchImage method is:
+%
+% MagickBooleanType MinMaxStretchImage(Image *image,
+% const ChannelType channel, const double black_adjust,
+% const double white_adjust)
+%
+% A description of each parameter follows:
+%
+% o image: The image to auto-level
+%
+% o channel: The channels to auto-level. If the special 'SyncChannels'
+% flag is set, all the given channels are stretched by the same amount.
+%
+% o black_adjust, white_adjust: Move the Black/White Point inward
+% from the minimum and maximum points by this color value.
+%
+*/
+
+MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
+ const ChannelType channel, const double black_value,
+ const double white_value)
+{
+ double
+ min,max;
+
+ MagickStatusType
+ status;
+
+ if ((channel & SyncChannels) != 0 )
+ {
+ /*
+ autolevel all channels equally
+ */
+ GetImageChannelRange(image, channel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ return LevelImageChannel(image, channel, min, max, 1.0);
+ }
+
+ /*
+ autolevel each channel separateally
+ */
+ status = MagickTrue;
+ if ((channel & RedChannel) != 0)
+ {
+ GetImageChannelRange(image, RedChannel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ status = status && LevelImageChannel(image, RedChannel, min, max, 1.0);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ GetImageChannelRange(image, GreenChannel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ status = status && LevelImageChannel(image, GreenChannel, min, max, 1.0);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ GetImageChannelRange(image, BlueChannel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ status = status && LevelImageChannel(image, BlueChannel, min, max, 1.0);
+ }
+ if (((channel & OpacityChannel) != 0) &&
+ (image->matte == MagickTrue))
+ {
+ GetImageChannelRange(image, OpacityChannel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ status = status && LevelImageChannel(image, OpacityChannel, min, max, 1.0);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ GetImageChannelRange(image, IndexChannel, &min, &max, &image->exception);
+ min += black_value; max -= white_value;
+ status = status && LevelImageChannel(image, IndexChannel, min, max, 1.0);
+ }
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
diff --git a/magick/histogram.h b/magick/histogram.h
new file mode 100644
index 0000000..cbf6ff8
--- /dev/null
+++ b/magick/histogram.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore histogram methods.
+*/
+#ifndef _MAGICKCORE_HISTOGRAM_H
+#define _MAGICKCORE_HISTOGRAM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ MinMaxStretchImage(Image *,const ChannelType,const double,const double);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/identify.c b/magick/identify.c
new file mode 100644
index 0000000..9feddef
--- /dev/null
+++ b/magick/identify.c
@@ -0,0 +1,1002 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% IIIII DDDD EEEEE N N TTTTT IIIII FFFFF Y Y %
+% I D D E NN N T I F Y Y %
+% I D D EEE N N N T I FFF Y %
+% I D D E N NN T I F Y %
+% IIIII DDDD EEEEE N N T IIIII F Y %
+% %
+% %
+% Identify an Image Format and Characteristics. %
+% %
+% Software Design %
+% John Cristy %
+% September 1994 %
+% %
+% %
+% Copyright 1999-2008 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Identify describes the format and characteristics of one or more image
+% files. It will also report if an image is incomplete or corrupt.
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/annotate.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/client.h"
+#include "magick/coder.h"
+#include "magick/color.h"
+#include "magick/configure.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/delegate.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/identify.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/module.h"
+#include "magick/monitor.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/pixel-private.h"
+#include "magick/prepress.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/registry.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/signature.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/timer.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+#if defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
+#include <lcms/lcms.h>
+#else
+#include "lcms.h"
+#endif
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I d e n t i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IdentifyImage() identifies an image by printing its attributes to the file.
+% Attributes include the image width, height, size, and others.
+%
+% The format of the IdentifyImage method is:
+%
+% MagickBooleanType IdentifyImage(Image *image,FILE *file,
+% const MagickBooleanType verbose)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o file: the file, typically stdout.
+%
+% o verbose: A value other than zero prints more detailed information
+% about the image.
+%
+*/
+
+MagickExport MagickBooleanType IdentifyImage(Image *image,FILE *file,
+ const MagickBooleanType verbose)
+{
+#define IdentifyFormat " %s:\n min: " QuantumFormat \
+ " (%g)\n max: " QuantumFormat " (%g)\n" \
+ " mean: %g (%g)\n standard deviation: %g (%g)\n" \
+ " kurtosis: %g\n skewness: %g\n"
+
+ char
+ color[MaxTextExtent],
+ format[MaxTextExtent],
+ key[MaxTextExtent];
+
+ ColorspaceType
+ colorspace;
+
+ const char
+ *artifact,
+ *name,
+ *property,
+ *registry,
+ *value;
+
+ const MagickInfo
+ *magick_info;
+
+ const PixelPacket
+ *pixels;
+
+ double
+ elapsed_time,
+ user_time;
+
+ ExceptionInfo
+ *exception;
+
+ ImageType
+ type;
+
+ long
+ y;
+
+ MagickBooleanType
+ ping;
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ scale;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (file == (FILE *) NULL)
+ file=stdout;
+ *format='\0';
+ elapsed_time=GetElapsedTime(&image->timer);
+ user_time=GetUserTime(&image->timer);
+ GetTimerInfo(&image->timer);
+ if (verbose == MagickFalse)
+ {
+ /*
+ Display summary info about the image.
+ */
+ if (*image->magick_filename != '\0')
+ if (LocaleCompare(image->magick_filename,image->filename) != 0)
+ (void) fprintf(file,"%s=>",image->magick_filename);
+ if ((GetPreviousImageInList(image) == (Image *) NULL) &&
+ (GetNextImageInList(image) == (Image *) NULL) && (image->scene == 0))
+ (void) fprintf(file,"%s ",image->filename);
+ else
+ (void) fprintf(file,"%s[%lu] ",image->filename,image->scene);
+ (void) fprintf(file,"%s ",image->magick);
+ if ((image->magick_columns != 0) || (image->magick_rows != 0))
+ if ((image->magick_columns != image->columns) ||
+ (image->magick_rows != image->rows))
+ (void) fprintf(file,"%lux%lu=>",image->magick_columns,
+ image->magick_rows);
+ (void) fprintf(file,"%lux%lu ",image->columns,image->rows);
+ if ((image->page.width != 0) || (image->page.height != 0) ||
+ (image->page.x != 0) || (image->page.y != 0))
+ (void) fprintf(file,"%lux%lu%+ld%+ld ",image->page.width,
+ image->page.height,image->page.x,image->page.y);
+ (void) fprintf(file,"%lu-bit ",image->depth);
+ if (image->type != UndefinedType)
+ (void) fprintf(file,"%s ",MagickOptionToMnemonic(MagickTypeOptions,
+ (long) image->type));
+ if (image->storage_class == DirectClass)
+ {
+ (void) fprintf(file,"DirectClass ");
+ if (image->total_colors != 0)
+ {
+ (void) FormatMagickSize(image->total_colors,format);
+ (void) fprintf(file,"%s ",format);
+ }
+ }
+ else
+ if (image->total_colors <= image->colors)
+ (void) fprintf(file,"PseudoClass %luc ",image->colors);
+ else
+ (void) fprintf(file,"PseudoClass %lu=>%luc ",image->total_colors,
+ image->colors);
+ if (image->error.mean_error_per_pixel != 0.0)
+ (void) fprintf(file,"%ld/%f/%fdb ",(long)
+ (image->error.mean_error_per_pixel+0.5),
+ image->error.normalized_mean_error,
+ image->error.normalized_maximum_error);
+ if (GetBlobSize(image) != 0)
+ {
+ (void) FormatMagickSize(GetBlobSize(image),format);
+ (void) fprintf(file,"%s ",format);
+ }
+ if (elapsed_time > 0.06)
+ (void) fprintf(file,"%0.3fu %ld:%02ld",user_time,(long)
+ (elapsed_time/60.0+0.5),(long) ceil(fmod(elapsed_time,60.0)));
+ (void) fprintf(file,"\n");
+ (void) fflush(file);
+ return(ferror(file) != 0 ? MagickFalse : MagickTrue);
+ }
+ /*
+ Display verbose info about the image.
+ */
+ exception=AcquireExceptionInfo();
+ pixels=GetVirtualPixels(image,0,0,1,1,exception);
+ exception=DestroyExceptionInfo(exception);
+ ping=pixels == (const PixelPacket *) NULL ? MagickTrue : MagickFalse;
+ type=GetImageType(image,&image->exception);
+ (void) SignatureImage(image);
+ (void) fprintf(file,"Image: %s\n",image->filename);
+ if (*image->magick_filename != '\0')
+ if (LocaleCompare(image->magick_filename,image->filename) != 0)
+ {
+ char
+ filename[MaxTextExtent];
+
+ GetPathComponent(image->magick_filename,TailPath,filename);
+ (void) fprintf(file," Base filename: %s\n",filename);
+ }
+ magick_info=GetMagickInfo(image->magick,&image->exception);
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (*GetMagickDescription(magick_info) == '\0'))
+ (void) fprintf(file," Format: %s\n",image->magick);
+ else
+ (void) fprintf(file," Format: %s (%s)\n",image->magick,
+ GetMagickDescription(magick_info));
+ (void) fprintf(file," Class: %s\n",MagickOptionToMnemonic(MagickClassOptions,
+ (long) image->storage_class));
+ (void) fprintf(file," Geometry: %lux%lu%+ld%+ld\n",image->columns,
+ image->rows,image->tile_offset.x,image->tile_offset.y);
+ if ((image->magick_columns != 0) || (image->magick_rows != 0))
+ if ((image->magick_columns != image->columns) ||
+ (image->magick_rows != image->rows))
+ (void) fprintf(file," Base geometry: %lux%lu\n",image->magick_columns,
+ image->magick_rows);
+ if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0))
+ {
+ (void) fprintf(file," Resolution: %gx%g\n",image->x_resolution,
+ image->y_resolution);
+ (void) fprintf(file," Print size: %gx%g\n",(double) image->columns/
+ image->x_resolution,(double) image->rows/image->y_resolution);
+ }
+ (void) fprintf(file," Units: %s\n",MagickOptionToMnemonic(
+ MagickResolutionOptions,(long) image->units));
+ (void) fprintf(file," Type: %s\n",MagickOptionToMnemonic(MagickTypeOptions,
+ (long) type));
+ if (image->type != UndefinedType)
+ (void) fprintf(file," Base type: %s\n",MagickOptionToMnemonic(
+ MagickTypeOptions,(long) image->type));
+ (void) fprintf(file," Endianess: %s\n",MagickOptionToMnemonic(
+ MagickEndianOptions,(long) image->endian));
+ /*
+ Detail channel depth and extrema.
+ */
+ (void) fprintf(file," Colorspace: %s\n",MagickOptionToMnemonic(
+ MagickColorspaceOptions,(long) image->colorspace));
+ if (ping == MagickFalse)
+ {
+ ChannelStatistics
+ *channel_statistics;
+
+ unsigned long
+ depth;
+
+ depth=GetImageDepth(image,&image->exception);
+ if (image->depth == depth)
+ (void) fprintf(file," Depth: %lu-bit\n",image->depth);
+ else
+ (void) fprintf(file," Depth: %lu/%lu-bit\n",image->depth,depth);
+ channel_statistics=GetImageChannelStatistics(image,&image->exception);
+ (void) fprintf(file," Channel depth:\n");
+ colorspace=image->colorspace;
+ if (IsGrayImage(image,&image->exception) != MagickFalse)
+ colorspace=GRAYColorspace;
+ switch (colorspace)
+ {
+ case RGBColorspace:
+ default:
+ {
+ (void) fprintf(file," red: %lu-bit\n",
+ channel_statistics[RedChannel].depth);
+ (void) fprintf(file," green: %lu-bit\n",
+ channel_statistics[GreenChannel].depth);
+ (void) fprintf(file," blue: %lu-bit\n",
+ channel_statistics[BlueChannel].depth);
+ if (image->matte != MagickFalse)
+ (void) fprintf(file," alpha: %lu-bit\n",
+ channel_statistics[OpacityChannel].depth);
+ break;
+ }
+ case CMYKColorspace:
+ {
+ (void) fprintf(file," cyan: %lu-bit\n",
+ channel_statistics[CyanChannel].depth);
+ (void) fprintf(file," magenta: %lu-bit\n",
+ channel_statistics[MagentaChannel].depth);
+ (void) fprintf(file," yellow: %lu-bit\n",
+ channel_statistics[YellowChannel].depth);
+ (void) fprintf(file," black: %lu-bit\n",
+ channel_statistics[BlackChannel].depth);
+ if (image->matte != MagickFalse)
+ (void) fprintf(file," alpha: %lu-bit\n",
+ channel_statistics[OpacityChannel].depth);
+ break;
+ }
+ case GRAYColorspace:
+ {
+ (void) fprintf(file," gray: %lu-bit\n",
+ channel_statistics[GrayChannel].depth);
+ if (image->matte != MagickFalse)
+ (void) fprintf(file," alpha: %lu-bit\n",
+ channel_statistics[OpacityChannel].depth);
+ break;
+ }
+ }
+ scale=1;
+ if (image->depth <= MAGICKCORE_QUANTUM_DEPTH)
+ scale=QuantumRange/((unsigned long) QuantumRange >> ((unsigned long)
+ MAGICKCORE_QUANTUM_DEPTH-image->depth));
+ (void) fprintf(file," Channel statistics:\n");
+ switch (colorspace)
+ {
+ case RGBColorspace:
+ default:
+ {
+ (void) fprintf(file,IdentifyFormat,"red",(Quantum)
+ (channel_statistics[RedChannel].minima/scale),(double)
+ channel_statistics[RedChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[RedChannel].maxima/scale),(double)
+ channel_statistics[RedChannel].maxima/(double) QuantumRange,
+ channel_statistics[RedChannel].mean/(double) scale,
+ channel_statistics[RedChannel].mean/(double) QuantumRange,
+ channel_statistics[RedChannel].standard_deviation/(double) scale,
+ channel_statistics[RedChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[RedChannel].kurtosis,
+ channel_statistics[RedChannel].skewness);
+ (void) fprintf(file,IdentifyFormat,"green",(Quantum)
+ (channel_statistics[GreenChannel].minima/scale),(double)
+ channel_statistics[GreenChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[GreenChannel].maxima/scale),(double)
+ channel_statistics[GreenChannel].maxima/(double) QuantumRange,
+ channel_statistics[GreenChannel].mean/(double) scale,
+ channel_statistics[GreenChannel].mean/(double) QuantumRange,
+ channel_statistics[GreenChannel].standard_deviation/(double) scale,
+ channel_statistics[GreenChannel].standard_deviation/(double)
+ QuantumRange,
+ channel_statistics[GreenChannel].kurtosis,
+ channel_statistics[GreenChannel].skewness);
+ (void) fprintf(file,IdentifyFormat,"blue",(Quantum)
+ (channel_statistics[BlueChannel].minima/scale),(double)
+ channel_statistics[BlueChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[BlueChannel].maxima/scale),(double)
+ channel_statistics[BlueChannel].maxima/(double) QuantumRange,
+ channel_statistics[BlueChannel].mean/(double) scale,
+ channel_statistics[BlueChannel].mean/(double) QuantumRange,
+ channel_statistics[BlueChannel].standard_deviation/(double) scale,
+ channel_statistics[BlueChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[BlueChannel].kurtosis,
+ channel_statistics[BlueChannel].skewness);
+ break;
+ }
+ case CMYKColorspace:
+ {
+ (void) fprintf(file,IdentifyFormat,"cyan",(Quantum)
+ (channel_statistics[CyanChannel].minima/scale),(double)
+ channel_statistics[CyanChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[CyanChannel].maxima/scale),(double)
+ channel_statistics[CyanChannel].maxima/(double) QuantumRange,
+ channel_statistics[CyanChannel].mean/(double) scale,
+ channel_statistics[CyanChannel].mean/(double) QuantumRange,
+ channel_statistics[CyanChannel].standard_deviation/(double) scale,
+ channel_statistics[CyanChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[CyanChannel].kurtosis,
+ channel_statistics[CyanChannel].skewness);
+ (void) fprintf(file,IdentifyFormat,"magenta",(Quantum)
+ (channel_statistics[MagentaChannel].minima/scale),(double)
+ channel_statistics[MagentaChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[MagentaChannel].maxima/scale),(double)
+ channel_statistics[MagentaChannel].maxima/(double) QuantumRange,
+ channel_statistics[MagentaChannel].mean/(double) scale,
+ channel_statistics[MagentaChannel].mean/(double) QuantumRange,
+ channel_statistics[MagentaChannel].standard_deviation/(double)
+ scale,channel_statistics[MagentaChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[MagentaChannel].kurtosis,
+ channel_statistics[MagentaChannel].skewness);
+ (void) fprintf(file,IdentifyFormat,"yellow",(Quantum)
+ (channel_statistics[YellowChannel].minima/scale),(double)
+ channel_statistics[YellowChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[YellowChannel].maxima/scale),(double)
+ channel_statistics[YellowChannel].maxima/(double) QuantumRange,
+ channel_statistics[YellowChannel].mean/(double) scale,
+ channel_statistics[YellowChannel].mean/(double) QuantumRange,
+ channel_statistics[YellowChannel].standard_deviation/(double) scale,
+ channel_statistics[YellowChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[YellowChannel].kurtosis,
+ channel_statistics[YellowChannel].skewness);
+ (void) fprintf(file,IdentifyFormat,"black",(Quantum)
+ (channel_statistics[BlackChannel].minima/scale),(double)
+ channel_statistics[BlackChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[BlackChannel].maxima/scale),(double)
+ channel_statistics[BlackChannel].maxima/(double) QuantumRange,
+ channel_statistics[BlackChannel].mean/(double) scale,
+ channel_statistics[BlackChannel].mean/(double) QuantumRange,
+ channel_statistics[BlackChannel].standard_deviation/(double) scale,
+ channel_statistics[BlackChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[BlackChannel].kurtosis,
+ channel_statistics[BlackChannel].skewness);
+ break;
+ }
+ case GRAYColorspace:
+ {
+ (void) fprintf(file,IdentifyFormat,"gray",(Quantum)
+ (channel_statistics[GrayChannel].minima/scale),(double)
+ channel_statistics[GrayChannel].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[GrayChannel].maxima/scale),(double)
+ channel_statistics[GrayChannel].maxima/(double) QuantumRange,
+ channel_statistics[GrayChannel].mean/(double) scale,
+ channel_statistics[GrayChannel].mean/(double) QuantumRange,
+ channel_statistics[GrayChannel].standard_deviation/(double) scale,
+ channel_statistics[GrayChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[GrayChannel].kurtosis,
+ channel_statistics[GrayChannel].skewness);
+ break;
+ }
+ }
+ if (image->matte != MagickFalse)
+ (void) fprintf(file,IdentifyFormat,"alpha",(Quantum)
+ ((QuantumRange-channel_statistics[AlphaChannel].maxima)/scale),
+ (double) (QuantumRange-channel_statistics[AlphaChannel].maxima)/
+ (double) QuantumRange, (Quantum) ((QuantumRange-
+ channel_statistics[AlphaChannel].minima)/scale),(double)
+ (QuantumRange-channel_statistics[AlphaChannel].minima)/(double)
+ QuantumRange,(QuantumRange-channel_statistics[AlphaChannel].mean)/
+ (double) scale,(QuantumRange-channel_statistics[AlphaChannel].mean)/
+ (double) QuantumRange,
+ channel_statistics[AlphaChannel].standard_deviation/(double) scale,
+ channel_statistics[AlphaChannel].standard_deviation/(double)
+ QuantumRange,channel_statistics[AlphaChannel].kurtosis,
+ channel_statistics[AlphaChannel].skewness);
+ if (colorspace != GRAYColorspace)
+ {
+ (void) fprintf(file," Image statistics:\n");
+ (void) fprintf(file,IdentifyFormat,"Overall",(Quantum)
+ (channel_statistics[AllChannels].minima/scale),(double)
+ channel_statistics[AllChannels].minima/(double) QuantumRange,
+ (Quantum) (channel_statistics[AllChannels].maxima/scale),(double)
+ channel_statistics[AllChannels].maxima/(double) QuantumRange,
+ channel_statistics[AllChannels].mean/(double) scale,
+ channel_statistics[AllChannels].mean/(double) QuantumRange,
+ channel_statistics[AllChannels].standard_deviation/(double) scale,
+ channel_statistics[AllChannels].standard_deviation/(double)
+ QuantumRange,channel_statistics[AllChannels].kurtosis,
+ channel_statistics[AllChannels].skewness);
+ }
+ channel_statistics=(ChannelStatistics *) RelinquishMagickMemory(
+ channel_statistics);
+ if (image->colorspace == CMYKColorspace)
+ (void) fprintf(file," Total ink density: %.0f%%\n",100.0*
+ GetImageTotalInkDensity(image)/(double) QuantumRange);
+ x=0;
+ if (image->matte != MagickFalse)
+ {
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ p=(PixelPacket *) NULL;
+ indexes=(IndexPacket *) NULL;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (p->opacity == (Quantum) TransparentOpacity)
+ break;
+ p++;
+ }
+ if (x < (long) image->columns)
+ break;
+ }
+ if ((x < (long) image->columns) || (y < (long) image->rows))
+ {
+ char
+ tuple[MaxTextExtent];
+
+ MagickPixelPacket
+ pixel;
+
+ GetMagickPixelPacket(image,&pixel);
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ (void) QueryMagickColorname(image,&pixel,SVGCompliance,tuple,
+ &image->exception);
+ (void) fprintf(file," Alpha: %s ",tuple);
+ GetColorTuple(&pixel,MagickTrue,tuple);
+ (void) fprintf(file," %s\n",tuple);
+ }
+ }
+ if (ping == MagickFalse)
+ {
+ artifact=GetImageArtifact(image,"identify:unique");
+ if ((artifact != (const char *) NULL) &&
+ (IsMagickTrue(artifact) != MagickFalse))
+ (void) fprintf(file," Colors: %lu\n",GetNumberColors(image,
+ (FILE *) NULL,&image->exception));
+ if (IsHistogramImage(image,&image->exception) != MagickFalse)
+ {
+ (void) fprintf(file," Histogram:\n");
+ (void) GetNumberColors(image,file,&image->exception);
+ }
+ }
+ }
+ if (image->storage_class == PseudoClass)
+ {
+ (void) fprintf(file," Colormap: %lu\n",image->colors);
+ if (image->colors <= 1024)
+ {
+ char
+ color[MaxTextExtent],
+ hex[MaxTextExtent],
+ tuple[MaxTextExtent];
+
+ MagickPixelPacket
+ pixel;
+
+ register PixelPacket
+ *__restrict p;
+
+ GetMagickPixelPacket(image,&pixel);
+ p=image->colormap;
+ for (i=0; i < (long) image->colors; i++)
+ {
+ SetMagickPixelPacket(image,p,(IndexPacket *) NULL,&pixel);
+ (void) CopyMagickString(tuple,"(",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
+ if (pixel.colorspace == CMYKColorspace)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,
+ tuple);
+ }
+ if (pixel.matte != MagickFalse)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,
+ tuple);
+ }
+ (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
+ (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,
+ &image->exception);
+ GetColorTuple(&pixel,MagickTrue,hex);
+ (void) fprintf(file," %8ld: %s %s %s\n",i,tuple,hex,color);
+ p++;
+ }
+ }
+ }
+ if (image->error.mean_error_per_pixel != 0.0)
+ (void) fprintf(file," Mean error per pixel: %g\n",
+ image->error.mean_error_per_pixel);
+ if (image->error.normalized_mean_error != 0.0)
+ (void) fprintf(file," Normalized mean error: %g\n",
+ image->error.normalized_mean_error);
+ if (image->error.normalized_maximum_error != 0.0)
+ (void) fprintf(file," Normalized maximum error: %g\n",
+ image->error.normalized_maximum_error);
+ (void) fprintf(file," Rendering intent: %s\n",MagickOptionToMnemonic(
+ MagickIntentOptions,(long) image->rendering_intent));
+ if (image->gamma != 0.0)
+ (void) fprintf(file," Gamma: %g\n",image->gamma);
+ if ((image->chromaticity.red_primary.x != 0.0) ||
+ (image->chromaticity.green_primary.x != 0.0) ||
+ (image->chromaticity.blue_primary.x != 0.0) ||
+ (image->chromaticity.white_point.x != 0.0))
+ {
+ /*
+ Display image chromaticity.
+ */
+ (void) fprintf(file," Chromaticity:\n");
+ (void) fprintf(file," red primary: (%g,%g)\n",
+ image->chromaticity.red_primary.x,image->chromaticity.red_primary.y);
+ (void) fprintf(file," green primary: (%g,%g)\n",
+ image->chromaticity.green_primary.x,
+ image->chromaticity.green_primary.y);
+ (void) fprintf(file," blue primary: (%g,%g)\n",
+ image->chromaticity.blue_primary.x,image->chromaticity.blue_primary.y);
+ (void) fprintf(file," white point: (%g,%g)\n",
+ image->chromaticity.white_point.x,image->chromaticity.white_point.y);
+ }
+ if ((image->extract_info.width*image->extract_info.height) != 0)
+ (void) fprintf(file," Tile geometry: %lux%lu%+ld%+ld\n",
+ image->extract_info.width,image->extract_info.height,
+ image->extract_info.x,image->extract_info.y);
+ (void) fprintf(file," Interlace: %s\n",MagickOptionToMnemonic(
+ MagickInterlaceOptions,(long) image->interlace));
+ (void) QueryColorname(image,&image->background_color,SVGCompliance,color,
+ &image->exception);
+ (void) fprintf(file," Background color: %s\n",color);
+ (void) QueryColorname(image,&image->border_color,SVGCompliance,color,
+ &image->exception);
+ (void) fprintf(file," Border color: %s\n",color);
+ (void) QueryColorname(image,&image->matte_color,SVGCompliance,color,
+ &image->exception);
+ (void) fprintf(file," Matte color: %s\n",color);
+ (void) QueryColorname(image,&image->transparent_color,SVGCompliance,color,
+ &image->exception);
+ (void) fprintf(file," Transparent color: %s\n",color);
+ if ((image->page.width != 0) || (image->page.height != 0) ||
+ (image->page.x != 0) || (image->page.y != 0))
+ (void) fprintf(file," Page geometry: %lux%lu%+ld%+ld\n",image->page.width,
+ image->page.height,image->page.x,image->page.y);
+ if ((image->page.x != 0) || (image->page.y != 0))
+ (void) fprintf(file," Origin geometry: %+ld%+ld\n",image->page.x,
+ image->page.y);
+ (void) fprintf(file," Dispose: %s\n",MagickOptionToMnemonic(
+ MagickDisposeOptions,(long) image->dispose));
+ if (image->delay != 0)
+ (void) fprintf(file," Delay: %lux%ld\n",image->delay,
+ image->ticks_per_second);
+ if (image->iterations != 1)
+ (void) fprintf(file," Iterations: %lu\n",image->iterations);
+ if ((image->next != (Image *) NULL) || (image->previous != (Image *) NULL))
+ (void) fprintf(file," Scene: %lu of %lu\n",image->scene,
+ GetImageListLength(image));
+ else
+ if (image->scene != 0)
+ (void) fprintf(file," Scene: %lu\n",image->scene);
+ (void) fprintf(file," Compression: %s\n",MagickOptionToMnemonic(
+ MagickCompressOptions,(long) image->compression));
+ if (image->quality != UndefinedCompressionQuality)
+ (void) fprintf(file," Quality: %lu\n",image->quality);
+ (void) fprintf(file," Orientation: %s\n",MagickOptionToMnemonic(
+ MagickOrientationOptions,(long) image->orientation));
+ if (image->montage != (char *) NULL)
+ (void) fprintf(file," Montage: %s\n",image->montage);
+ if (image->directory != (char *) NULL)
+ {
+ Image
+ *tile;
+
+ ImageInfo
+ *image_info;
+
+ register char
+ *p,
+ *q;
+
+ WarningHandler
+ handler;
+
+ /*
+ Display visual image directory.
+ */
+ image_info=AcquireImageInfo();
+ (void) CloneString(&image_info->size,"64x64");
+ (void) fprintf(file," Directory:\n");
+ for (p=image->directory; *p != '\0'; p++)
+ {
+ q=p;
+ while ((*q != '\n') && (*q != '\0'))
+ q++;
+ (void) CopyMagickString(image_info->filename,p,(size_t) (q-p+1));
+ p=q;
+ (void) fprintf(file," %s",image_info->filename);
+ handler=SetWarningHandler((WarningHandler) NULL);
+ tile=ReadImage(image_info,&image->exception);
+ (void) SetWarningHandler(handler);
+ if (tile == (Image *) NULL)
+ {
+ (void) fprintf(file,"\n");
+ continue;
+ }
+ (void) fprintf(file," %lux%lu %s\n",tile->magick_columns,
+ tile->magick_rows,tile->magick);
+ (void) SignatureImage(tile);
+ ResetImagePropertyIterator(tile);
+ property=GetNextImageProperty(tile);
+ while (property != (const char *) NULL)
+ {
+ (void) fprintf(file," %s:\n",property);
+ value=GetImageProperty(tile,property);
+ if (value != (const char *) NULL)
+ (void) fprintf(file,"%s\n",value);
+ property=GetNextImageProperty(tile);
+ }
+ tile=DestroyImage(tile);
+ }
+ image_info=DestroyImageInfo(image_info);
+ }
+ (void) GetImageProperty(image,"exif:*");
+ ResetImagePropertyIterator(image);
+ property=GetNextImageProperty(image);
+ if (property != (const char *) NULL)
+ {
+ /*
+ Display image properties.
+ */
+ (void) fprintf(file," Properties:\n");
+ while (property != (const char *) NULL)
+ {
+ (void) fprintf(file," %c",*property);
+ if (strlen(property) > 1)
+ (void) fprintf(file,"%s: ",property+1);
+ if (strlen(property) > 80)
+ (void) fputc('\n',file);
+ value=GetImageProperty(image,property);
+ if (value != (const char *) NULL)
+ (void) fprintf(file,"%s\n",value);
+ property=GetNextImageProperty(image);
+ }
+ }
+ (void) FormatMagickString(key,MaxTextExtent,"8BIM:1999,2998:#1");
+ value=GetImageProperty(image,key);
+ if (value != (const char *) NULL)
+ {
+ /*
+ Display clipping path.
+ */
+ (void) fprintf(file," Clipping path: ");
+ if (strlen(value) > 80)
+ (void) fputc('\n',file);
+ (void) fprintf(file,"%s\n",value);
+ }
+ ResetImageProfileIterator(image);
+ name=GetNextImageProfile(image);
+ if (name != (char *) NULL)
+ {
+ const StringInfo
+ *profile;
+
+ /*
+ Identify image profiles.
+ */
+ (void) fprintf(file," Profiles:\n");
+ while (name != (char *) NULL)
+ {
+ profile=GetImageProfile(image,name);
+ if (profile == (StringInfo *) NULL)
+ continue;
+ (void) fprintf(file," Profile-%s: %lu bytes\n",name,(unsigned long)
+ GetStringInfoLength(profile));
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+ if ((LocaleCompare(name,"icc") == 0) ||
+ (LocaleCompare(name,"icm") == 0))
+ {
+ cmsHPROFILE
+ icc_profile;
+
+ icc_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
+ (DWORD) GetStringInfoLength(profile));
+ if (icc_profile != (cmsHPROFILE *) NULL)
+ {
+ const char
+ *name;
+
+ name=cmsTakeProductName(icc_profile);
+ if (name != (const char *) NULL)
+ (void) fprintf(file," %s\n",name);
+ (void) cmsCloseProfile(icc_profile);
+ }
+ }
+#endif
+ if (LocaleCompare(name,"iptc") == 0)
+ {
+ char
+ *attribute,
+ **attribute_list;
+
+ const char
+ *tag;
+
+ long
+ dataset,
+ record,
+ sentinel;
+
+ register long
+ j;
+
+ size_t
+ length,
+ profile_length;
+
+ profile_length=GetStringInfoLength(profile);
+ for (i=0; i < (long) profile_length; i+=(long) length)
+ {
+ length=1;
+ sentinel=GetStringInfoDatum(profile)[i++];
+ if (sentinel != 0x1c)
+ continue;
+ dataset=GetStringInfoDatum(profile)[i++];
+ record=GetStringInfoDatum(profile)[i++];
+ switch (record)
+ {
+ case 5: tag="Image Name"; break;
+ case 7: tag="Edit Status"; break;
+ case 10: tag="Priority"; break;
+ case 15: tag="Category"; break;
+ case 20: tag="Supplemental Category"; break;
+ case 22: tag="Fixture Identifier"; break;
+ case 25: tag="Keyword"; break;
+ case 30: tag="Release Date"; break;
+ case 35: tag="Release Time"; break;
+ case 40: tag="Special Instructions"; break;
+ case 45: tag="Reference Service"; break;
+ case 47: tag="Reference Date"; break;
+ case 50: tag="Reference Number"; break;
+ case 55: tag="Created Date"; break;
+ case 60: tag="Created Time"; break;
+ case 65: tag="Originating Program"; break;
+ case 70: tag="Program Version"; break;
+ case 75: tag="Object Cycle"; break;
+ case 80: tag="Byline"; break;
+ case 85: tag="Byline Title"; break;
+ case 90: tag="City"; break;
+ case 95: tag="Province State"; break;
+ case 100: tag="Country Code"; break;
+ case 101: tag="Country"; break;
+ case 103: tag="Original Transmission Reference"; break;
+ case 105: tag="Headline"; break;
+ case 110: tag="Credit"; break;
+ case 115: tag="Src"; break;
+ case 116: tag="Copyright String"; break;
+ case 120: tag="Caption"; break;
+ case 121: tag="Local Caption"; break;
+ case 122: tag="Caption Writer"; break;
+ case 200: tag="Custom Field 1"; break;
+ case 201: tag="Custom Field 2"; break;
+ case 202: tag="Custom Field 3"; break;
+ case 203: tag="Custom Field 4"; break;
+ case 204: tag="Custom Field 5"; break;
+ case 205: tag="Custom Field 6"; break;
+ case 206: tag="Custom Field 7"; break;
+ case 207: tag="Custom Field 8"; break;
+ case 208: tag="Custom Field 9"; break;
+ case 209: tag="Custom Field 10"; break;
+ case 210: tag="Custom Field 11"; break;
+ case 211: tag="Custom Field 12"; break;
+ case 212: tag="Custom Field 13"; break;
+ case 213: tag="Custom Field 14"; break;
+ case 214: tag="Custom Field 15"; break;
+ case 215: tag="Custom Field 16"; break;
+ case 216: tag="Custom Field 17"; break;
+ case 217: tag="Custom Field 18"; break;
+ case 218: tag="Custom Field 19"; break;
+ case 219: tag="Custom Field 20"; break;
+ default: tag="unknown"; break;
+ }
+ (void) fprintf(file," %s[%ld,%ld]: ",tag,dataset,record);
+ length=(size_t) (GetStringInfoDatum(profile)[i++] << 8);
+ length|=GetStringInfoDatum(profile)[i++];
+ attribute=(char *) NULL;
+ if (~length >= MaxTextExtent)
+ attribute=(char *) AcquireQuantumMemory(length+
+ MaxTextExtent,sizeof(*attribute));
+ if (attribute != (char *) NULL)
+ {
+ (void) CopyMagickString(attribute,(char *)
+ GetStringInfoDatum(profile)+i,length+1);
+ attribute_list=StringToList(attribute);
+ if (attribute_list != (char **) NULL)
+ {
+ for (j=0; attribute_list[j] != (char *) NULL; j++)
+ {
+ (void) fputs(attribute_list[j],file);
+ (void) fputs("\n",file);
+ attribute_list[j]=(char *) RelinquishMagickMemory(
+ attribute_list[j]);
+ }
+ attribute_list=(char **) RelinquishMagickMemory(
+ attribute_list);
+ }
+ attribute=DestroyString(attribute);
+ }
+ }
+ }
+ if (image->debug != MagickFalse)
+ PrintStringInfo(file,name,profile);
+ name=GetNextImageProfile(image);
+ }
+ }
+ ResetImageArtifactIterator(image);
+ artifact=GetNextImageArtifact(image);
+ if (artifact != (const char *) NULL)
+ {
+ /*
+ Display image artifacts.
+ */
+ (void) fprintf(file," Artifacts:\n");
+ while (artifact != (const char *) NULL)
+ {
+ (void) fprintf(file," %c",*artifact);
+ if (strlen(artifact) > 1)
+ (void) fprintf(file,"%s: ",artifact+1);
+ if (strlen(artifact) > 80)
+ (void) fputc('\n',file);
+ value=GetImageArtifact(image,artifact);
+ if (value != (const char *) NULL)
+ (void) fprintf(file,"%s\n",value);
+ artifact=GetNextImageArtifact(image);
+ }
+ }
+ ResetImageRegistryIterator();
+ registry=GetNextImageRegistry();
+ if (registry != (const char *) NULL)
+ {
+ /*
+ Display image registry.
+ */
+ (void) fprintf(file," Registry:\n");
+ while (registry != (const char *) NULL)
+ {
+ (void) fprintf(file," %c",*registry);
+ if (strlen(registry) > 1)
+ (void) fprintf(file,"%s: ",registry+1);
+ if (strlen(registry) > 80)
+ (void) fputc('\n',file);
+ value=(const char *) GetImageRegistry(StringRegistryType,registry,
+ &image->exception);
+ if (value != (const char *) NULL)
+ (void) fprintf(file,"%s\n",value);
+ registry=GetNextImageRegistry();
+ }
+ }
+ (void) fprintf(file," Tainted: %s\n",MagickOptionToMnemonic(
+ MagickBooleanOptions,(long) image->taint));
+ (void) FormatMagickSize(GetBlobSize(image),format);
+ (void) fprintf(file," Filesize: %s\n",format);
+ (void) FormatMagickSize((MagickSizeType) image->columns*image->rows,format);
+ (void) fprintf(file," Number pixels: %s\n",format);
+ if (elapsed_time > 0.06)
+ {
+ (void) FormatMagickSize((MagickSizeType) ((double) image->columns*
+ image->rows/elapsed_time+0.5),format);
+ (void) fprintf(file," Pixels per second: %s\n",format);
+ (void) fprintf(file," User time: %0.3fu\n",user_time);
+ (void) fprintf(file," Elapsed time: %ld:%02ld\n",(long) (elapsed_time/
+ 60.0+0.5),(long) ceil(fmod(elapsed_time,60.0)));
+ }
+ (void) fprintf(file," Version: %s\n",GetMagickVersion(
+ (unsigned long *) NULL));
+ (void) fflush(file);
+ return(ferror(file) != 0 ? MagickFalse : MagickTrue);
+}
diff --git a/magick/identify.h b/magick/identify.h
new file mode 100644
index 0000000..a6b4b4e
--- /dev/null
+++ b/magick/identify.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image identify method.
+*/
+#ifndef _MAGICKCORE_IDENTIFY_H
+#define _MAGICKCORE_IDENTIFY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ IdentifyImage(Image *,FILE *,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/image-private.h b/magick/image-private.h
new file mode 100644
index 0000000..0c0a8e0
--- /dev/null
+++ b/magick/image-private.h
@@ -0,0 +1,86 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image private methods.
+*/
+#ifndef _MAGICKCORE_IMAGE_PRIVATE_H
+#define _MAGICKCORE_IMAGE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define MagickPI 3.14159265358979323846264338327950288419716939937510
+#define Magick2PI 6.28318530717958647692528676655900576839433879875020
+#define MagickPI2 1.57079632679489661923132169163975144209858469968755
+#define MagickSQ1_2 0.7071067811865475244008443621048490
+#define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062
+#define QuantumScale ((double) 1.0/(double) QuantumRange)
+#define UndefinedTicksPerSecond 100L
+#define UndefinedCompressionQuality 0UL
+
+extern MagickExport const char
+ *BackgroundColor,
+ *BorderColor,
+ *DefaultTileFrame,
+ *DefaultTileGeometry,
+ *DefaultTileLabel,
+ *ForegroundColor,
+ *MatteColor,
+ *LoadImageTag,
+ *LoadImagesTag,
+ *PSDensityGeometry,
+ *PSPageGeometry,
+ *SaveImageTag,
+ *SaveImagesTag;
+
+extern MagickExport const double
+ DefaultResolution;
+
+static inline double DegreesToRadians(const double degrees)
+{
+ return(MagickPI*degrees/180.0);
+}
+
+static inline MagickRealType RadiansToDegrees(const MagickRealType radians)
+{
+ return(180.0*radians/MagickPI);
+}
+
+static inline unsigned char ScaleColor5to8(const unsigned long color)
+{
+ return((unsigned char) (((color) << 3) | ((color) >> 2)));
+}
+
+static inline unsigned char ScaleColor6to8(const unsigned long color)
+{
+ return((unsigned char) (((color) << 2) | ((color) >> 4)));
+}
+
+static inline unsigned long ScaleColor8to5(const unsigned char color)
+{
+ return((unsigned long) (((color) & ~0x07) >> 3));
+}
+
+static inline unsigned long ScaleColor8to6(const unsigned char color)
+{
+ return((unsigned long) (((color) & ~0x03) >> 2));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/image.c b/magick/image.c
new file mode 100644
index 0000000..9fa0eda
--- /dev/null
+++ b/magick/image.c
@@ -0,0 +1,4475 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% IIIII M M AAA GGGG EEEEE %
+% I MM MM A A G E %
+% I M M M AAAAA G GG EEE %
+% I M M A A G G E %
+% IIIII M M A A GGGG EEEEE %
+% %
+% %
+% MagickCore Image Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/animate.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/compress.h"
+#include "magick/constitute.h"
+#include "magick/deprecate.h"
+#include "magick/display.h"
+#include "magick/draw.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/image-private.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/module.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/paint.h"
+#include "magick/pixel-private.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantize.h"
+#include "magick/random_.h"
+#include "magick/segment.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/threshold.h"
+#include "magick/timer.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#include "magick/xwindow-private.h"
+
+/*
+ Constant declaration.
+*/
+const char
+ *BackgroundColor = "#ffffff", /* white */
+ *BorderColor = "#dfdfdf", /* gray */
+ *DefaultTileFrame = "15x15+3+3",
+ *DefaultTileGeometry = "120x120+4+3>",
+ *DefaultTileLabel = "%f\n%G\n%b",
+ *ForegroundColor = "#000", /* black */
+ *LoadImageTag = "Load/Image",
+ *LoadImagesTag = "Load/Images",
+ *MatteColor = "#bdbdbd", /* gray */
+ *PSDensityGeometry = "72.0x72.0",
+ *PSPageGeometry = "612x792",
+ *SaveImageTag = "Save/Image",
+ *SaveImagesTag = "Save/Images",
+ *TransparentColor = "#00000000"; /* transparent black */
+
+const double
+ DefaultResolution = 72.0;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireImage() returns a pointer to an image structure initialized to
+% default values.
+%
+% The format of the AcquireImage method is:
+%
+% Image *AcquireImage(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: Many of the image default values are set from this
+% structure. For example, filename, compression, depth, background color,
+% and others.
+%
+*/
+MagickExport Image *AcquireImage(const ImageInfo *image_info)
+{
+ Image
+ *image;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Allocate image structure.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ image=(Image *) AcquireMagickMemory(sizeof(*image));
+ if (image == (Image *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(image,0,sizeof(*image));
+ /*
+ Initialize Image structure.
+ */
+ (void) CopyMagickString(image->magick,"MIFF",MaxTextExtent);
+ image->storage_class=DirectClass;
+ image->depth=MAGICKCORE_QUANTUM_DEPTH;
+ image->colorspace=RGBColorspace;
+ image->interlace=NoInterlace;
+ image->ticks_per_second=UndefinedTicksPerSecond;
+ image->compose=OverCompositeOp;
+ image->blur=1.0;
+ GetExceptionInfo(&image->exception);
+ (void) QueryColorDatabase(BackgroundColor,&image->background_color,
+ &image->exception);
+ (void) QueryColorDatabase(BorderColor,&image->border_color,&image->exception);
+ (void) QueryColorDatabase(MatteColor,&image->matte_color,&image->exception);
+ (void) QueryColorDatabase(TransparentColor,&image->transparent_color,
+ &image->exception);
+ image->x_resolution=DefaultResolution;
+ image->y_resolution=DefaultResolution;
+ image->units=PixelsPerInchResolution;
+ GetTimerInfo(&image->timer);
+ image->cache=AcquirePixelCache(0);
+ image->blob=CloneBlobInfo((BlobInfo *) NULL);
+ image->debug=IsEventLogging();
+ image->reference_count=1;
+ image->semaphore=AllocateSemaphoreInfo();
+ image->signature=MagickSignature;
+ if (image_info == (ImageInfo *) NULL)
+ return(image);
+ /*
+ Transfer image info.
+ */
+ SetBlobExempt(image,image_info->file != (FILE *) NULL ? MagickTrue :
+ MagickFalse);
+ (void) CopyMagickString(image->filename,image_info->filename,MaxTextExtent);
+ (void) CopyMagickString(image->magick_filename,image_info->filename,
+ MaxTextExtent);
+ (void) CopyMagickString(image->magick,image_info->magick,MaxTextExtent);
+ if (image_info->size != (char *) NULL)
+ {
+ (void) ParseAbsoluteGeometry(image_info->size,&image->extract_info);
+ image->columns=image->extract_info.width;
+ image->rows=image->extract_info.height;
+ image->offset=image->extract_info.x;
+ image->extract_info.x=0;
+ image->extract_info.y=0;
+ }
+ if (image_info->extract != (char *) NULL)
+ {
+ RectangleInfo
+ geometry;
+
+ flags=ParseAbsoluteGeometry(image_info->extract,&geometry);
+ if (((flags & XValue) != 0) || ((flags & YValue) != 0))
+ {
+ image->extract_info=geometry;
+ Swap(image->columns,image->extract_info.width);
+ Swap(image->rows,image->extract_info.height);
+ }
+ }
+ image->compression=image_info->compression;
+ image->quality=image_info->quality;
+ image->endian=image_info->endian;
+ image->interlace=image_info->interlace;
+ image->units=image_info->units;
+ if (image_info->density != (char *) NULL)
+ {
+ GeometryInfo
+ geometry_info;
+
+ flags=ParseGeometry(image_info->density,&geometry_info);
+ image->x_resolution=geometry_info.rho;
+ image->y_resolution=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ image->y_resolution=image->x_resolution;
+ }
+ if (image_info->page != (char *) NULL)
+ {
+ char
+ *geometry;
+
+ image->page=image->extract_info;
+ geometry=GetPageGeometry(image_info->page);
+ (void) ParseAbsoluteGeometry(geometry,&image->page);
+ geometry=DestroyString(geometry);
+ }
+ if (image_info->depth != 0)
+ image->depth=image_info->depth;
+ image->dither=image_info->dither;
+ image->background_color=image_info->background_color;
+ image->border_color=image_info->border_color;
+ image->matte_color=image_info->matte_color;
+ image->transparent_color=image_info->transparent_color;
+ image->progress_monitor=image_info->progress_monitor;
+ image->client_data=image_info->client_data;
+ if (image_info->cache != (void *) NULL)
+ ClonePixelCacheMethods(image->cache,image_info->cache);
+ (void) SetImageVirtualPixelMethod(image,image_info->virtual_pixel_method);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% 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 unsigned long colors)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o colors: the number of colors in the image colormap.
+%
+*/
+
+static inline unsigned long MagickMax(const unsigned long x,
+ const unsigned long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline unsigned long MagickMin(const unsigned long x,
+ const unsigned long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType AcquireImageColormap(Image *image,
+ const unsigned long colors)
+{
+ register long
+ i;
+
+ size_t
+ length;
+
+ /*
+ Allocate image colormap.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ image->colors=MagickMin(colors,MaxColormapSize);
+ length=(size_t) colors;
+ if (image->colormap == (PixelPacket *) NULL)
+ image->colormap=(PixelPacket *) AcquireQuantumMemory(length,
+ sizeof(*image->colormap));
+ else
+ image->colormap=(PixelPacket *) ResizeQuantumMemory(image->colormap,length,
+ sizeof(*image->colormap));
+ if (image->colormap == (PixelPacket *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ unsigned long
+ pixel;
+
+ pixel=(unsigned long) (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));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireImageInfo() allocates the ImageInfo structure.
+%
+% The format of the AcquireImageInfo method is:
+%
+% ImageInfo *AcquireImageInfo(void)
+%
+*/
+MagickExport ImageInfo *AcquireImageInfo(void)
+{
+ ImageInfo
+ *image_info;
+
+ image_info=(ImageInfo *) AcquireMagickMemory(sizeof(*image_info));
+ if (image_info == (ImageInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetImageInfo(image_info);
+ return(image_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e N e x t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireNextImage() initializes the next image in a sequence to
+% default values. The next member of image points to the newly allocated
+% image. If there is a memory shortage, next is assigned NULL.
+%
+% The format of the AcquireNextImage method is:
+%
+% void AcquireNextImage(const ImageInfo *image_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o image_info: Many of the image default values are set from this
+% structure. For example, filename, compression, depth, background color,
+% and others.
+%
+% o image: the image.
+%
+*/
+MagickExport void AcquireNextImage(const ImageInfo *image_info,Image *image)
+{
+ /*
+ Allocate image structure.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ image->next=AcquireImage(image_info);
+ if (GetNextImageInList(image) == (Image *) NULL)
+ return;
+ (void) CopyMagickString(GetNextImageInList(image)->filename,image->filename,
+ MaxTextExtent);
+ if (image_info != (ImageInfo *) NULL)
+ (void) CopyMagickString(GetNextImageInList(image)->filename,
+ image_info->filename,MaxTextExtent);
+ DestroyBlob(GetNextImageInList(image));
+ image->next->blob=ReferenceBlob(image->blob);
+ image->next->endian=image->endian;
+ image->next->scene=image->scene+1;
+ image->next->previous=image;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A p p e n d I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AppendImages() takes all images from the current image pointer to the end
+% of the image list and appends them to each other top-to-bottom if the
+% stack parameter is true, otherwise left-to-right.
+%
+% The current gravity setting now effects how the image is justified in the
+% final image.
+%
+% The format of the AppendImages method is:
+%
+% Image *AppendImages(const Image *image,const MagickBooleanType stack,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o stack: A value other than 0 stacks the images top-to-bottom.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AppendImages(const Image *image,
+ const MagickBooleanType stack,ExceptionInfo *exception)
+{
+#define AppendImageTag "Append/Image"
+
+ CacheView
+ *append_view,
+ *image_view;
+
+ Image
+ *append_image;
+
+ long
+ n,
+ x_offset,
+ y,
+ y_offset;
+
+ MagickBooleanType
+ matte,
+ proceed,
+ status;
+
+ RectangleInfo
+ geometry;
+
+ register const Image
+ *next;
+
+ unsigned long
+ height,
+ number_images,
+ width;
+
+ /*
+ Ensure the image have the same column width.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ matte=image->matte;
+ number_images=1;
+ width=image->columns;
+ height=image->rows;
+ next=GetNextImageInList(image);
+ for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ if (next->matte != MagickFalse)
+ matte=MagickTrue;
+ number_images++;
+ if (stack != MagickFalse)
+ {
+ if (next->columns > width)
+ width=next->columns;
+ height+=next->rows;
+ continue;
+ }
+ width+=next->columns;
+ if (next->rows > height)
+ height=next->rows;
+ }
+ /*
+ Initialize append next attributes.
+ */
+ append_image=CloneImage(image,width,height,MagickTrue,exception);
+ if (append_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(append_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&append_image->exception);
+ append_image=DestroyImage(append_image);
+ return((Image *) NULL);
+ }
+ append_image->matte=matte;
+ (void) SetImageBackgroundColor(append_image);
+ status=MagickTrue;
+ x_offset=0;
+ y_offset=0;
+ append_view=AcquireCacheView(append_image);
+ for (n=0; n < (long) number_images; n++)
+ {
+ SetGeometry(append_image,&geometry);
+ GravityAdjustGeometry(image->columns,image->rows,image->gravity,&geometry);
+ if (stack != MagickFalse)
+ x_offset-=geometry.x;
+ else
+ y_offset-=geometry.y;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict append_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(append_view,x_offset,y+y_offset,
+ image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ append_indexes=GetCacheViewAuthenticIndexQueue(append_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=OpaqueOpacity;
+ if (image->matte != MagickFalse)
+ q->opacity=p->opacity;
+ if (image->colorspace == CMYKColorspace)
+ append_indexes[x]=indexes[x];
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(append_view,exception);
+ if (sync == MagickFalse)
+ continue;
+ }
+ image_view=DestroyCacheView(image_view);
+ proceed=SetImageProgress(image,AppendImageTag,n,number_images);
+ if (proceed == MagickFalse)
+ break;
+ if (stack == MagickFalse)
+ {
+ x_offset+=image->columns;
+ y_offset=0;
+ }
+ else
+ {
+ x_offset=0;
+ y_offset+=image->rows;
+ }
+ image=GetNextImageInList(image);
+ }
+ append_view=DestroyCacheView(append_view);
+ if (status == MagickFalse)
+ append_image=DestroyImage(append_image);
+ return(append_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A v e r a g e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AverageImages() takes a set of images and averages them together. Each
+% image in the set must have the same width and height. AverageImages()
+% returns a single image with each corresponding pixel component of each
+% image averaged. On failure, a NULL image is returned and exception
+% describes the reason for the failure.
+%
+% The format of the AverageImages method is:
+%
+% Image *AverageImages(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static MagickPixelPacket **DestroyPixelThreadSet(MagickPixelPacket **pixels)
+{
+ register long
+ i;
+
+ assert(pixels != (MagickPixelPacket **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (pixels[i] != (MagickPixelPacket *) NULL)
+ pixels[i]=(MagickPixelPacket *) RelinquishMagickMemory(pixels[i]);
+ pixels=(MagickPixelPacket **) RelinquishAlignedMemory(pixels);
+ return(pixels);
+}
+
+static MagickPixelPacket **AcquirePixelThreadSet(const Image *image)
+{
+ register long
+ i,
+ j;
+
+ MagickPixelPacket
+ **pixels;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ pixels=(MagickPixelPacket **) AcquireAlignedMemory(number_threads,
+ sizeof(*pixels));
+ if (pixels == (MagickPixelPacket **) NULL)
+ return((MagickPixelPacket **) NULL);
+ (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ pixels[i]=(MagickPixelPacket *) AcquireQuantumMemory(image->columns,
+ sizeof(**pixels));
+ if (pixels[i] == (MagickPixelPacket *) NULL)
+ return(DestroyPixelThreadSet(pixels));
+ for (j=0; j < (long) image->columns; j++)
+ GetMagickPixelPacket(image,&pixels[i][j]);
+ }
+ return(pixels);
+}
+
+MagickExport Image *AverageImages(const Image *image,ExceptionInfo *exception)
+{
+#define AverageImageTag "Average/Image"
+
+ CacheView
+ *average_view;
+
+ const Image
+ *next;
+
+ Image
+ *average_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ **average_pixels,
+ zero;
+
+ unsigned long
+ number_images;
+
+ /*
+ Ensure the image are the same size.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
+ if ((next->columns != image->columns) || (next->rows != image->rows))
+ ThrowImageException(OptionError,"ImageWidthsOrHeightsDiffer");
+ /*
+ Initialize average next attributes.
+ */
+ average_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (average_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(average_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&average_image->exception);
+ average_image=DestroyImage(average_image);
+ return((Image *) NULL);
+ }
+ average_pixels=AcquirePixelThreadSet(image);
+ if (average_pixels == (MagickPixelPacket **) NULL)
+ {
+ average_image=DestroyImage(average_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Average image pixels.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ number_images=GetImageListLength(image);
+ average_view=AcquireCacheView(average_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) average_image->rows; y++)
+ {
+ CacheView
+ *image_view;
+
+ const Image
+ *next;
+
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict average_indexes;
+
+ register long
+ i,
+ x;
+
+ register MagickPixelPacket
+ *average_pixel;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(average_view,0,y,average_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ average_indexes=GetCacheViewAuthenticIndexQueue(average_view);
+ pixel=zero;
+ average_pixel=average_pixels[GetOpenMPThreadId()];
+ for (x=0; x < (long) average_image->columns; x++)
+ average_pixel[x]=zero;
+ next=image;
+ for (i=0; i < (long) number_images; i++)
+ {
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ image_view=DestroyCacheView(image_view);
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) next->columns; x++)
+ {
+ SetMagickPixelPacket(next,p,indexes+x,&pixel);
+ average_pixel[x].red+=QuantumScale*pixel.red;
+ average_pixel[x].green+=QuantumScale*pixel.green;
+ average_pixel[x].blue+=QuantumScale*pixel.blue;
+ average_pixel[x].opacity+=QuantumScale*pixel.opacity;
+ if (average_image->colorspace == CMYKColorspace)
+ average_pixel[x].index+=QuantumScale*pixel.index;
+ p++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ for (x=0; x < (long) average_image->columns; x++)
+ {
+ average_pixel[x].red=(MagickRealType) (QuantumRange*
+ average_pixel[x].red/number_images);
+ average_pixel[x].green=(MagickRealType) (QuantumRange*
+ average_pixel[x].green/number_images);
+ average_pixel[x].blue=(MagickRealType) (QuantumRange*
+ average_pixel[x].blue/number_images);
+ average_pixel[x].opacity=(MagickRealType) (QuantumRange*
+ average_pixel[x].opacity/number_images);
+ if (average_image->colorspace == CMYKColorspace)
+ average_pixel[x].index=(MagickRealType) (QuantumRange*
+ average_pixel[x].index/number_images);
+ SetPixelPacket(average_image,&average_pixel[x],q,average_indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(average_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AverageImages)
+#endif
+ proceed=SetImageProgress(image,AverageImageTag,progress++,
+ average_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ average_view=DestroyCacheView(average_view);
+ average_pixels=DestroyPixelThreadSet(average_pixels);
+ if (status == MagickFalse)
+ average_image=DestroyImage(average_image);
+ return(average_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C a t c h I m a g e E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CatchImageException() returns if no exceptions are found in the image
+% sequence, otherwise it determines the most severe exception and reports
+% it as a warning or error depending on the severity.
+%
+% The format of the CatchImageException method is:
+%
+% ExceptionType CatchImageException(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: An image sequence.
+%
+*/
+MagickExport ExceptionType CatchImageException(Image *image)
+{
+ ExceptionInfo
+ *exception;
+
+ ExceptionType
+ severity;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ exception=AcquireExceptionInfo();
+ GetImageException(image,exception);
+ CatchException(exception);
+ severity=exception->severity;
+ exception=DestroyExceptionInfo(exception);
+ return(severity);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l i p I m a g e P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClipImagePath() sets the image clip mask based any clipping path information
+% if it exists.
+%
+% The format of the ClipImagePath method is:
+%
+% MagickBooleanType ClipImagePath(Image *image,const char *pathname,
+% const MagickBooleanType inside)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o pathname: name of clipping path resource. If name is preceded by #, use
+% clipping path numbered by name.
+%
+% o inside: if non-zero, later operations take effect inside clipping path.
+% Otherwise later operations take effect outside clipping path.
+%
+*/
+
+MagickExport MagickBooleanType ClipImage(Image *image)
+{
+ return(ClipImagePath(image,"#1",MagickTrue));
+}
+
+MagickExport MagickBooleanType ClipImagePath(Image *image,const char *pathname,
+ const MagickBooleanType inside)
+{
+#define ClipImagePathTag "ClipPath/Image"
+
+ char
+ *property;
+
+ const char
+ *value;
+
+ Image
+ *clip_mask;
+
+ ImageInfo
+ *image_info;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(pathname != NULL);
+ property=AcquireString(pathname);
+ (void) FormatMagickString(property,MaxTextExtent,"8BIM:1999,2998:%s",
+ pathname);
+ value=GetImageProperty(image,property);
+ property=DestroyString(property);
+ if (value == (const char *) NULL)
+ {
+ ThrowFileException(&image->exception,OptionError,"NoClipPathDefined",
+ image->filename);
+ return(MagickFalse);
+ }
+ image_info=AcquireImageInfo();
+ (void) CopyMagickString(image_info->filename,image->filename,MaxTextExtent);
+ (void) ConcatenateMagickString(image_info->filename,pathname,MaxTextExtent);
+ clip_mask=BlobToImage(image_info,value,strlen(value),&image->exception);
+ image_info=DestroyImageInfo(image_info);
+ if (clip_mask == (Image *) NULL)
+ return(MagickFalse);
+ if (clip_mask->storage_class == PseudoClass)
+ {
+ (void) SyncImage(clip_mask);
+ if (SetImageStorageClass(clip_mask,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ }
+ if (inside == MagickFalse)
+ (void) NegateImage(clip_mask,MagickFalse);
+ (void) FormatMagickString(clip_mask->magick_filename,MaxTextExtent,
+ "8BIM:1999,2998:%s\nPS",pathname);
+ (void) SetImageClipMask(image,clip_mask);
+ clip_mask=DestroyImage(clip_mask);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImage() copies an image and returns the copy as a new image object.
+% If the specified columns and rows is 0, an exact copy of the image is
+% returned, otherwise the pixel data is undefined and must be initialized
+% with the QueueAuthenticPixels() and SyncAuthenticPixels() methods. On
+% failure, a NULL image is returned and exception describes the reason for the
+% failure.
+%
+% The format of the CloneImage method is:
+%
+% Image *CloneImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,const MagickBooleanType orphan,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the cloned image.
+%
+% o rows: the number of rows in the cloned image.
+%
+% o detach: With a value other than 0, the cloned image is detached from
+% its parent I/O stream.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CloneImage(const Image *image,const unsigned long columns,
+ const unsigned long rows,const MagickBooleanType detach,
+ ExceptionInfo *exception)
+{
+ Image
+ *clone_image;
+
+ MagickRealType
+ scale;
+
+ size_t
+ length;
+
+ /*
+ Clone the image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ clone_image=(Image *) AcquireMagickMemory(sizeof(*clone_image));
+ if (clone_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(clone_image,0,sizeof(*clone_image));
+ clone_image->signature=MagickSignature;
+ clone_image->storage_class=image->storage_class;
+ clone_image->colorspace=image->colorspace;
+ clone_image->matte=image->matte;
+ clone_image->columns=image->columns;
+ clone_image->rows=image->rows;
+ clone_image->dither=image->dither;
+ if (image->colormap != (PixelPacket *) NULL)
+ {
+ /*
+ Allocate and copy the image colormap.
+ */
+ clone_image->colors=image->colors;
+ length=(size_t) image->colors;
+ clone_image->colormap=(PixelPacket *) AcquireQuantumMemory(length,
+ sizeof(*clone_image->colormap));
+ if (clone_image->colormap == (PixelPacket *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ (void) CopyMagickMemory(clone_image->colormap,image->colormap,length*
+ sizeof(*clone_image->colormap));
+ }
+ (void) CloneImageProfiles(clone_image,image);
+ (void) CloneImageProperties(clone_image,image);
+ (void) CloneImageArtifacts(clone_image,image);
+ GetTimerInfo(&clone_image->timer);
+ GetExceptionInfo(&clone_image->exception);
+ InheritException(&clone_image->exception,&image->exception);
+ if (image->ascii85 != (void *) NULL)
+ Ascii85Initialize(clone_image);
+ clone_image->magick_columns=image->magick_columns;
+ clone_image->magick_rows=image->magick_rows;
+ clone_image->type=image->type;
+ (void) CopyMagickString(clone_image->magick_filename,image->magick_filename,
+ MaxTextExtent);
+ (void) CopyMagickString(clone_image->magick,image->magick,MaxTextExtent);
+ (void) CopyMagickString(clone_image->filename,image->filename,MaxTextExtent);
+ clone_image->progress_monitor=image->progress_monitor;
+ clone_image->client_data=image->client_data;
+ clone_image->reference_count=1;
+ clone_image->next=NewImageList();
+ clone_image->previous=NewImageList();
+ clone_image->list=NewImageList();
+ clone_image->clip_mask=NewImageList();
+ clone_image->mask=NewImageList();
+ if (detach == MagickFalse)
+ clone_image->blob=ReferenceBlob(image->blob);
+ else
+ clone_image->blob=CloneBlobInfo((BlobInfo *) NULL);
+ clone_image->debug=IsEventLogging();
+ clone_image->semaphore=AllocateSemaphoreInfo();
+ if ((columns == 0) && (rows == 0))
+ {
+ if (image->montage != (char *) NULL)
+ (void) CloneString(&clone_image->montage,image->montage);
+ if (image->directory != (char *) NULL)
+ (void) CloneString(&clone_image->directory,image->directory);
+ if (image->clip_mask != (Image *) NULL)
+ clone_image->clip_mask=CloneImage(image->clip_mask,0,0,MagickTrue,
+ exception);
+ if (image->mask != (Image *) NULL)
+ clone_image->mask=CloneImage(image->mask,0,0,MagickTrue,exception);
+ clone_image->cache=ReferencePixelCache(image->cache);
+ return(clone_image);
+ }
+ scale=(MagickRealType) columns/(MagickRealType) image->columns;
+ clone_image->page.width=(unsigned long) (scale*image->page.width+0.5);
+ clone_image->page.x=(long) (scale*image->page.x+0.5);
+ clone_image->tile_offset.x=(long) (scale*image->tile_offset.x+0.5);
+ scale=(MagickRealType) rows/(MagickRealType) image->rows;
+ clone_image->page.height=(unsigned long) (scale*image->page.height+0.5);
+ clone_image->page.y=(long) (image->page.y*scale+0.5);
+ clone_image->tile_offset.y=(long) (scale*image->tile_offset.y+0.5);
+ clone_image->columns=columns;
+ clone_image->rows=rows;
+ clone_image->cache=ClonePixelCache(image->cache);
+ return(clone_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageInfo() makes a copy of the given image info structure. If
+% NULL is specified, a new image info structure is created initialized to
+% default values.
+%
+% The format of the CloneImageInfo method is:
+%
+% ImageInfo *CloneImageInfo(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport ImageInfo *CloneImageInfo(const ImageInfo *image_info)
+{
+ ImageInfo
+ *clone_info;
+
+ clone_info=AcquireImageInfo();
+ if (image_info == (ImageInfo *) NULL)
+ return(clone_info);
+ clone_info->compression=image_info->compression;
+ clone_info->temporary=image_info->temporary;
+ clone_info->adjoin=image_info->adjoin;
+ clone_info->antialias=image_info->antialias;
+ clone_info->scene=image_info->scene;
+ clone_info->number_scenes=image_info->number_scenes;
+ clone_info->depth=image_info->depth;
+ if (image_info->size != (char *) NULL)
+ (void) CloneString(&clone_info->size,image_info->size);
+ if (image_info->extract != (char *) NULL)
+ (void) CloneString(&clone_info->extract,image_info->extract);
+ if (image_info->scenes != (char *) NULL)
+ (void) CloneString(&clone_info->scenes,image_info->scenes);
+ if (image_info->page != (char *) NULL)
+ (void) CloneString(&clone_info->page,image_info->page);
+ clone_info->interlace=image_info->interlace;
+ clone_info->endian=image_info->endian;
+ clone_info->units=image_info->units;
+ clone_info->quality=image_info->quality;
+ if (image_info->sampling_factor != (char *) NULL)
+ (void) CloneString(&clone_info->sampling_factor,
+ image_info->sampling_factor);
+ if (image_info->server_name != (char *) NULL)
+ (void) CloneString(&clone_info->server_name,image_info->server_name);
+ if (image_info->font != (char *) NULL)
+ (void) CloneString(&clone_info->font,image_info->font);
+ if (image_info->texture != (char *) NULL)
+ (void) CloneString(&clone_info->texture,image_info->texture);
+ if (image_info->density != (char *) NULL)
+ (void) CloneString(&clone_info->density,image_info->density);
+ clone_info->pointsize=image_info->pointsize;
+ clone_info->fuzz=image_info->fuzz;
+ clone_info->pen=image_info->pen;
+ clone_info->background_color=image_info->background_color;
+ clone_info->border_color=image_info->border_color;
+ clone_info->matte_color=image_info->matte_color;
+ clone_info->transparent_color=image_info->transparent_color;
+ clone_info->dither=image_info->dither;
+ clone_info->monochrome=image_info->monochrome;
+ clone_info->colors=image_info->colors;
+ clone_info->colorspace=image_info->colorspace;
+ clone_info->type=image_info->type;
+ clone_info->orientation=image_info->orientation;
+ clone_info->preview_type=image_info->preview_type;
+ clone_info->group=image_info->group;
+ clone_info->ping=image_info->ping;
+ clone_info->verbose=image_info->verbose;
+ if (image_info->view != (char *) NULL)
+ (void) CloneString(&clone_info->view,image_info->view);
+ if (image_info->authenticate != (char *) NULL)
+ (void) CloneString(&clone_info->authenticate,image_info->authenticate);
+ (void) CloneImageOptions(clone_info,image_info);
+ clone_info->progress_monitor=image_info->progress_monitor;
+ clone_info->client_data=image_info->client_data;
+ clone_info->cache=image_info->cache;
+ if (image_info->cache != (void *) NULL)
+ clone_info->cache=ReferencePixelCache(image_info->cache);
+ if (image_info->profile != (void *) NULL)
+ clone_info->profile=(void *) CloneStringInfo((StringInfo *)
+ image_info->profile);
+ SetImageInfoFile(clone_info,image_info->file);
+ SetImageInfoBlob(clone_info,image_info->blob,image_info->length);
+ clone_info->stream=image_info->stream;
+ clone_info->virtual_pixel_method=image_info->virtual_pixel_method;
+ (void) CopyMagickString(clone_info->magick,image_info->magick,MaxTextExtent);
+ (void) CopyMagickString(clone_info->unique,image_info->unique,MaxTextExtent);
+ (void) CopyMagickString(clone_info->zero,image_info->zero,MaxTextExtent);
+ (void) CopyMagickString(clone_info->filename,image_info->filename,
+ MaxTextExtent);
+ clone_info->subimage=image_info->scene; /* deprecated */
+ clone_info->subrange=image_info->number_scenes; /* deprecated */
+ clone_info->channel=image_info->channel;
+ clone_info->debug=IsEventLogging();
+ clone_info->signature=image_info->signature;
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m b i n e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CombineImages() combines one or more images into a single image. The
+% grayscale value of the pixels of each image in the sequence is assigned in
+% order to the specified channels of the combined image. The typical
+% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
+%
+% The format of the CombineImages method is:
+%
+% Image *CombineImages(const Image *image,const ChannelType channel,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CombineImages(const Image *image,const ChannelType channel,
+ ExceptionInfo *exception)
+{
+#define CombineImageTag "Combine/Image"
+
+ CacheView
+ *combine_view;
+
+ const Image
+ *next;
+
+ Image
+ *combine_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Ensure the image are the same size.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ if ((next->columns != image->columns) || (next->rows != image->rows))
+ ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
+ }
+ combine_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (combine_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(combine_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&combine_image->exception);
+ combine_image=DestroyImage(combine_image);
+ return((Image *) NULL);
+ }
+ if ((channel & OpacityChannel) != 0)
+ combine_image->matte=MagickTrue;
+ (void) SetImageBackgroundColor(combine_image);
+ /*
+ Combine images.
+ */
+ status=MagickTrue;
+ progress=0;
+ combine_view=AcquireCacheView(combine_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) combine_image->rows; y++)
+ {
+ CacheView
+ *image_view;
+
+ const Image
+ *next;
+
+ PixelPacket
+ *pixels;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
+ 1,exception);
+ if (pixels == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ next=image;
+ if (((channel & RedChannel) != 0) && (next != (Image *) NULL))
+ {
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ q=pixels;
+ for (x=0; x < (long) combine_image->columns; x++)
+ {
+ q->red=PixelIntensityToQuantum(p);
+ p++;
+ q++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (((channel & GreenChannel) != 0) && (next != (Image *) NULL))
+ {
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ q=pixels;
+ for (x=0; x < (long) combine_image->columns; x++)
+ {
+ q->green=PixelIntensityToQuantum(p);
+ p++;
+ q++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (((channel & BlueChannel) != 0) && (next != (Image *) NULL))
+ {
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ q=pixels;
+ for (x=0; x < (long) combine_image->columns; x++)
+ {
+ q->blue=PixelIntensityToQuantum(p);
+ p++;
+ q++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (((channel & OpacityChannel) != 0) && (next != (Image *) NULL))
+ {
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ q=pixels;
+ for (x=0; x < (long) combine_image->columns; x++)
+ {
+ q->opacity=PixelIntensityToQuantum(p);
+ p++;
+ q++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) && (next != (Image *) NULL))
+ {
+ IndexPacket
+ *indexes;
+
+ image_view=AcquireCacheView(next);
+ p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ indexes=GetCacheViewAuthenticIndexQueue(combine_view);
+ for (x=0; x < (long) combine_image->columns; x++)
+ {
+ indexes[x]=PixelIntensityToQuantum(p);
+ p++;
+ }
+ image_view=DestroyCacheView(image_view);
+ next=GetNextImageInList(next);
+ }
+ if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_CombineImages)
+#endif
+ proceed=SetImageProgress(image,CombineImageTag,progress++,
+ combine_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ combine_view=DestroyCacheView(combine_view);
+ if (status == MagickFalse)
+ combine_image=DestroyImage(combine_image);
+ return(combine_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% 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 long displace)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o displace: displace the colormap this amount.
+%
+*/
+MagickExport MagickBooleanType CycleColormapImage(Image *image,
+ const long displace)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ 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=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ long
+ index;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ index=(long) (indexes[x]+displace) % image->colors;
+ if (index < 0)
+ index+=image->colors;
+ indexes[x]=(IndexPacket) index;
+ q->red=image->colormap[index].red;
+ q->green=image->colormap[index].green;
+ q->blue=image->colormap[index].blue;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImage() dereferences an image, deallocating memory associated with
+% the image if the reference count becomes zero.
+%
+% The format of the DestroyImage method is:
+%
+% Image *DestroyImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport Image *DestroyImage(Image *image)
+{
+ MagickBooleanType
+ destroy;
+
+ /*
+ Dereference image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ destroy=MagickFalse;
+ (void) LockSemaphoreInfo(image->semaphore);
+ image->reference_count--;
+ if (image->reference_count == 0)
+ destroy=MagickTrue;
+ (void) UnlockSemaphoreInfo(image->semaphore);
+ if (destroy == MagickFalse)
+ return((Image *) NULL);
+ /*
+ Destroy image.
+ */
+ DestroyImagePixels(image);
+ if (image->clip_mask != (Image *) NULL)
+ image->clip_mask=DestroyImage(image->clip_mask);
+ if (image->mask != (Image *) NULL)
+ image->mask=DestroyImage(image->mask);
+ if (image->montage != (char *) NULL)
+ image->montage=DestroyString(image->montage);
+ if (image->directory != (char *) NULL)
+ image->directory=DestroyString(image->directory);
+ if (image->colormap != (PixelPacket *) NULL)
+ image->colormap=(PixelPacket *) RelinquishMagickMemory(image->colormap);
+ if (image->geometry != (char *) NULL)
+ image->geometry=DestroyString(image->geometry);
+#if !defined(MAGICKCORE_EXCLUDE_DEPRECATED)
+ DestroyImageAttributes(image);
+#endif
+ DestroyImageProfiles(image);
+ DestroyImageProperties(image);
+ DestroyImageArtifacts(image);
+ if (image->ascii85 != (Ascii85Info*) NULL)
+ image->ascii85=(Ascii85Info *) RelinquishMagickMemory(image->ascii85);
+ DestroyBlob(image);
+ (void) DestroyExceptionInfo(&image->exception);
+ if (image->semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&image->semaphore);
+ image->signature=(~MagickSignature);
+ image=(Image *) RelinquishMagickMemory(image);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageInfo() deallocates memory associated with an ImageInfo
+% structure.
+%
+% The format of the DestroyImageInfo method is:
+%
+% ImageInfo *DestroyImageInfo(ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport ImageInfo *DestroyImageInfo(ImageInfo *image_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->size != (char *) NULL)
+ image_info->size=DestroyString(image_info->size);
+ if (image_info->extract != (char *) NULL)
+ image_info->extract=DestroyString(image_info->extract);
+ if (image_info->scenes != (char *) NULL)
+ image_info->scenes=DestroyString(image_info->scenes);
+ if (image_info->page != (char *) NULL)
+ image_info->page=DestroyString(image_info->page);
+ if (image_info->sampling_factor != (char *) NULL)
+ image_info->sampling_factor=DestroyString(
+ image_info->sampling_factor);
+ if (image_info->server_name != (char *) NULL)
+ image_info->server_name=DestroyString(
+ image_info->server_name);
+ if (image_info->font != (char *) NULL)
+ image_info->font=DestroyString(image_info->font);
+ if (image_info->texture != (char *) NULL)
+ image_info->texture=DestroyString(image_info->texture);
+ if (image_info->density != (char *) NULL)
+ image_info->density=DestroyString(image_info->density);
+ if (image_info->view != (char *) NULL)
+ image_info->view=DestroyString(image_info->view);
+ if (image_info->authenticate != (char *) NULL)
+ image_info->authenticate=DestroyString(
+ image_info->authenticate);
+ DestroyImageOptions(image_info);
+ if (image_info->cache != (void *) NULL)
+ image_info->cache=DestroyPixelCache(image_info->cache);
+ if (image_info->profile != (StringInfo *) NULL)
+ image_info->profile=(void *) DestroyStringInfo((StringInfo *)
+ image_info->profile);
+ image_info->signature=(~MagickSignature);
+ image_info=(ImageInfo *) RelinquishMagickMemory(image_info);
+ return(image_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D i s a s s o c i a t e I m a g e S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DisassociateImageStream() disassociates the image stream.
+%
+% The format of the DisassociateImageStream method is:
+%
+% MagickBooleanType DisassociateImageStream(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DisassociateImageStream(Image *image)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ (void) DetachBlob(image->blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e A l p h a C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
+% not activated. That is, the image is RGB rather than RGBA or CMYK rather
+% than CMYKA.
+%
+% The format of the GetImageAlphaChannel method is:
+%
+% MagickBooleanType GetImageAlphaChannel(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
+{
+ assert(image != (const Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ return(image->matte);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C l i p M a s k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageClipMask() returns the clip path associated with the image.
+%
+% The format of the GetImageClipMask method is:
+%
+% Image *GetImageClipMask(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport Image *GetImageClipMask(const Image *image,
+ ExceptionInfo *exception)
+{
+ assert(image != (const Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (image->clip_mask == (Image *) NULL)
+ return((Image *) NULL);
+ return(CloneImage(image->clip_mask,0,0,MagickTrue,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e E x c e p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageException() traverses an image sequence and returns any
+% error more severe than noted by the exception parameter.
+%
+% The format of the GetImageException method is:
+%
+% void GetImageException(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: Specifies a pointer to a list of one or more images.
+%
+% o exception: return the highest severity exception.
+%
+*/
+MagickExport void GetImageException(Image *image,ExceptionInfo *exception)
+{
+ register Image
+ *next;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ if (next->exception.severity == UndefinedException)
+ continue;
+ if (next->exception.severity > exception->severity)
+ InheritException(exception,&next->exception);
+ next->exception.severity=UndefinedException;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageInfo() initializes image_info to default values.
+%
+% The format of the GetImageInfo method is:
+%
+% void GetImageInfo(ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport void GetImageInfo(ImageInfo *image_info)
+{
+ ExceptionInfo
+ *exception;
+
+ /*
+ File and image dimension members.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image_info != (ImageInfo *) NULL);
+ (void) ResetMagickMemory(image_info,0,sizeof(*image_info));
+ image_info->adjoin=MagickTrue;
+ image_info->interlace=NoInterlace;
+ image_info->channel=DefaultChannels;
+ image_info->quality=UndefinedCompressionQuality;
+ image_info->antialias=MagickTrue;
+ image_info->dither=MagickTrue;
+ exception=AcquireExceptionInfo();
+ (void) QueryColorDatabase(BackgroundColor,&image_info->background_color,
+ exception);
+ (void) QueryColorDatabase(BorderColor,&image_info->border_color,exception);
+ (void) QueryColorDatabase(MatteColor,&image_info->matte_color,exception);
+ (void) QueryColorDatabase(TransparentColor,&image_info->transparent_color,
+ exception);
+ exception=DestroyExceptionInfo(exception);
+ image_info->debug=IsEventLogging();
+ image_info->signature=MagickSignature;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e M a s k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageMask() returns the mask associated with the image.
+%
+% The format of the GetImageMask method is:
+%
+% Image *GetImageMask(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport Image *GetImageMask(const Image *image,ExceptionInfo *exception)
+{
+ assert(image != (const Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (image->mask == (Image *) NULL)
+ return((Image *) NULL);
+ return(CloneImage(image->mask,0,0,MagickTrue,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e R e f e r e n c e C o u n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageReferenceCount() returns the image reference count.
+%
+% The format of the GetReferenceCount method is:
+%
+% long GetImageReferenceCount(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport long GetImageReferenceCount(Image *image)
+{
+ long
+ reference_count;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ AcquireSemaphoreInfo(&image->semaphore);
+ reference_count=image->reference_count;
+ RelinquishSemaphoreInfo(image->semaphore);
+ return(reference_count);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageType() returns the potential type of image:
+%
+% Bilevel Grayscale GrayscaleMatte
+% Palette PaletteMatte TrueColor
+% TrueColorMatte ColorSeparation ColorSeparationMatte
+%
+% To ensure the image type matches its potential, use SetImageType():
+%
+% (void) SetImageType(image,GetImageType(image));
+%
+% The format of the GetImageType method is:
+%
+% ImageType GetImageType(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->colorspace == CMYKColorspace)
+ {
+ if (image->matte == MagickFalse)
+ return(ColorSeparationType);
+ return(ColorSeparationMatteType);
+ }
+ if (IsMonochromeImage(image,exception) != MagickFalse)
+ return(BilevelType);
+ if (IsGrayImage(image,exception) != MagickFalse)
+ {
+ if (image->matte != MagickFalse)
+ return(GrayscaleMatteType);
+ return(GrayscaleType);
+ }
+ if (IsPaletteImage(image,exception) != MagickFalse)
+ {
+ if (image->matte != MagickFalse)
+ return(PaletteMatteType);
+ return(PaletteType);
+ }
+ if (image->matte != MagickFalse)
+ return(TrueColorMatteType);
+ return(TrueColorType);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e V i r t u a l P i x e l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageVirtualPixelMethod() gets the "virtual pixels" method for the
+% image. A virtual pixel is any pixel access that is outside the boundaries
+% of the image cache.
+%
+% The format of the GetImageVirtualPixelMethod() method is:
+%
+% VirtualPixelMethod GetImageVirtualPixelMethod(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport VirtualPixelMethod GetImageVirtualPixelMethod(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(GetPixelCacheVirtualMethod(image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n t e r p r e t I m a g e F i l e n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InterpretImageFilename() interprets embedded characters in an image filename.
+% The filename length is returned.
+%
+% The format of the InterpretImageFilename method is:
+%
+% size_t InterpretImageFilename(const ImageInfo *image_info,
+% Image *image,const char *format,int value,char *filename)
+%
+% A description of each parameter follows.
+%
+% o image_info: the image info..
+%
+% o image: the image.
+%
+% o format: A filename describing the format to use to write the numeric
+% argument. Only the first numeric format identifier is replaced.
+%
+% o value: Numeric value to substitute into format filename.
+%
+% o filename: return the formatted filename in this character buffer.
+%
+*/
+MagickExport size_t InterpretImageFilename(const ImageInfo *image_info,
+ Image *image,const char *format,int value,char *filename)
+{
+ char
+ *q;
+
+ int
+ c;
+
+ MagickBooleanType
+ canonical;
+
+ register const char
+ *p;
+
+ canonical=MagickFalse;
+ (void) CopyMagickString(filename,format,MaxTextExtent);
+ for (p=strchr(format,'%'); p != (char *) NULL; p=strchr(p+1,'%'))
+ {
+ q=(char *) p+1;
+ if (*q == '%')
+ {
+ p=q+1;
+ continue;
+ }
+ if (*q == '0')
+ {
+ long
+ value;
+
+ value=strtol(q,&q,10);
+ }
+ switch (*q)
+ {
+ case 'd':
+ case 'o':
+ case 'x':
+ {
+ q++;
+ c=(*q);
+ *q='\0';
+ (void) FormatMagickString(filename+(p-format),(size_t) (MaxTextExtent-
+ (p-format)),p,value);
+ *q=c;
+ (void) ConcatenateMagickString(filename,q,MaxTextExtent);
+ canonical=MagickTrue;
+ if (*(q-1) != '%')
+ break;
+ p++;
+ break;
+ }
+ case '[':
+ {
+ char
+ pattern[MaxTextExtent];
+
+ const char
+ *value;
+
+ long
+ depth;
+
+ register char
+ *r;
+
+ register long
+ i;
+
+ /*
+ Image option.
+ */
+ if (strchr(p,']') == (char *) NULL)
+ break;
+ depth=1;
+ r=q+1;
+ for (i=0; (i < (MaxTextExtent-1L)) && (*r != '\0'); i++)
+ {
+ if (*r == '[')
+ depth++;
+ if (*r == ']')
+ depth--;
+ if (depth <= 0)
+ break;
+ pattern[i]=(*r++);
+ }
+ pattern[i]='\0';
+ if (LocaleNCompare(pattern,"filename:",9) != 0)
+ break;
+ value=(const char *) NULL;
+ if ((image_info != (const ImageInfo *) NULL) &&
+ (image != (const Image *) NULL))
+ value=GetMagickProperty(image_info,image,pattern);
+ else
+ if (image != (Image *) NULL)
+ value=GetImageProperty(image,pattern);
+ else
+ if (image_info != (ImageInfo *) NULL)
+ value=GetImageOption(image_info,pattern);
+ if (value == (const char *) NULL)
+ break;
+ q--;
+ c=(*q);
+ *q='\0';
+ (void) CopyMagickString(filename+(p-format),value,(size_t)
+ (MaxTextExtent-(p-format)));
+ *q=c;
+ (void) ConcatenateMagickString(filename,r+1,MaxTextExtent);
+ canonical=MagickTrue;
+ if (*(q-1) != '%')
+ break;
+ p++;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ for (q=filename; *q != '\0'; q++)
+ if ((*q == '%') && (*(q+1) == '%'))
+ (void) CopyMagickString(q,q+1,(size_t) (MaxTextExtent-(q-filename)));
+ if (canonical == MagickFalse)
+ (void) CopyMagickString(filename,format,MaxTextExtent);
+ return(strlen(filename));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s H i g h D y n a m i c R a n g e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsHighDynamicRangeImage() returns MagickTrue if any pixel component is
+% non-integer or exceeds the bounds of the quantum depth (e.g. for Q16
+% 0..65535.
+%
+% The format of the IsHighDynamicRangeImage method is:
+%
+% MagickBooleanType IsHighDynamicRangeImage(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType IsHighDynamicRangeImage(const Image *image,
+ ExceptionInfo *exception)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ (void) image;
+ (void) exception;
+ return(MagickFalse);
+#else
+ CacheView
+ *image_view;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=MagickTrue;
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if ((pixel.red < 0.0) || (pixel.red > QuantumRange) ||
+ (pixel.red != (QuantumAny) pixel.red))
+ break;
+ if ((pixel.green < 0.0) || (pixel.green > QuantumRange) ||
+ (pixel.green != (QuantumAny) pixel.green))
+ break;
+ if ((pixel.blue < 0.0) || (pixel.blue > QuantumRange) ||
+ (pixel.blue != (QuantumAny) pixel.blue))
+ break;
+ if (pixel.matte != MagickFalse)
+ {
+ if ((pixel.opacity < 0.0) || (pixel.opacity > QuantumRange) ||
+ (pixel.opacity != (QuantumAny) pixel.opacity))
+ break;
+ }
+ if (pixel.colorspace == CMYKColorspace)
+ {
+ if ((pixel.index < 0.0) || (pixel.index > QuantumRange) ||
+ (pixel.index != (QuantumAny) pixel.index))
+ break;
+ }
+ p++;
+ }
+ if (x < (long) image->columns)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status != MagickFalse ? MagickFalse : MagickTrue);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s I m a g e O b j e c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsImageObject() returns MagickTrue if the image sequence contains a valid
+% set of image objects.
+%
+% The format of the IsImageObject method is:
+%
+% MagickBooleanType IsImageObject(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType IsImageObject(const Image *image)
+{
+ register const Image
+ *p;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
+ if (p->signature != MagickSignature)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s T a i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsTaintImage() returns MagickTrue any pixel in the image has been altered
+% since it was first constituted.
+%
+% The format of the IsTaintImage method is:
+%
+% MagickBooleanType IsTaintImage(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType IsTaintImage(const Image *image)
+{
+ char
+ magick[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ register const Image
+ *p;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ (void) CopyMagickString(magick,image->magick,MaxTextExtent);
+ (void) CopyMagickString(filename,image->filename,MaxTextExtent);
+ for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
+ {
+ if (p->taint != MagickFalse)
+ return(MagickTrue);
+ if (LocaleCompare(p->magick,magick) != 0)
+ return(MagickTrue);
+ if (LocaleCompare(p->filename,filename) != 0)
+ return(MagickTrue);
+ }
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o d i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ModifyImage() ensures that there is only a single reference to the image
+% to be modified, updating the provided image pointer to point to a clone of
+% the original image if necessary.
+%
+% The format of the ModifyImage method is:
+%
+% MagickBooleanType ModifyImage(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ModifyImage(Image **image,
+ ExceptionInfo *exception)
+{
+ Image
+ *clone_image;
+
+ assert(image != (Image **) NULL);
+ assert(*image != (Image *) NULL);
+ assert((*image)->signature == MagickSignature);
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
+ if (GetImageReferenceCount(*image) <= 1)
+ return(MagickTrue);
+ clone_image=CloneImage(*image,0,0,MagickTrue,exception);
+ AcquireSemaphoreInfo(&(*image)->semaphore);
+ (*image)->reference_count--;
+ RelinquishSemaphoreInfo((*image)->semaphore);
+ *image=clone_image;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w M a g i c k I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewMagickImage() creates a blank image canvas of the specified size and
+% background color.
+%
+% The format of the NewMagickImage method is:
+%
+% Image *NewMagickImage(const ImageInfo *image_info,
+% const unsigned long width,const unsigned long height,
+% const MagickPixelPacket *background)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o width: the image width.
+%
+% o height: the image height.
+%
+% o background: the image color.
+%
+*/
+MagickExport Image *NewMagickImage(const ImageInfo *image_info,
+ const unsigned long width,const unsigned long height,
+ const MagickPixelPacket *background)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *image;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image_info->signature == MagickSignature);
+ assert(background != (const MagickPixelPacket *) NULL);
+ image=AcquireImage(image_info);
+ image->columns=width;
+ image->rows=height;
+ image->colorspace=background->colorspace;
+ image->matte=background->matte;
+ image->fuzz=background->fuzz;
+ image->depth=background->depth;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetPixelPacket(image,background,q,indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (status == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ image=DestroyImage(image);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e f e r e n c e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReferenceImage() increments the reference count associated with an image
+% returning a pointer to the image.
+%
+% The format of the ReferenceImage method is:
+%
+% Image *ReferenceImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport Image *ReferenceImage(Image *image)
+{
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ (void) LockSemaphoreInfo(image->semaphore);
+ image->reference_count++;
+ (void) UnlockSemaphoreInfo(image->semaphore);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e P a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImagePage() resets the image page canvas and position.
+%
+% The format of the ResetImagePage method is:
+%
+% MagickBooleanType ResetImagePage(Image *image,const char *page)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o page: the relative page specification.
+%
+*/
+MagickExport MagickBooleanType ResetImagePage(Image *image,const char *page)
+{
+ MagickStatusType
+ flags;
+
+ RectangleInfo
+ geometry;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ flags=ParseAbsoluteGeometry(page,&geometry);
+ if ((flags & WidthValue) != 0)
+ {
+ if ((flags & HeightValue) == 0)
+ geometry.height=geometry.width;
+ image->page.width=geometry.width;
+ image->page.height=geometry.height;
+ }
+ if ((flags & AspectValue) != 0)
+ {
+ if ((flags & XValue) != 0)
+ image->page.x+=geometry.x;
+ if ((flags & YValue) != 0)
+ image->page.y+=geometry.y;
+ }
+ else
+ {
+ if ((flags & XValue) != 0)
+ {
+ image->page.x=geometry.x;
+ if ((image->page.width == 0) && (geometry.x > 0))
+ image->page.width=image->columns+geometry.x;
+ }
+ if ((flags & YValue) != 0)
+ {
+ image->page.y=geometry.y;
+ if ((image->page.height == 0) && (geometry.y > 0))
+ image->page.height=image->rows+geometry.y;
+ }
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e p a r a t e I m a g e C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeparateImageChannel() separates a channel from the image and returns it as
+% a grayscale image. A channel is a particular color component of each pixel
+% in the image.
+%
+% The format of the SeparateImageChannel method is:
+%
+% MagickBooleanType SeparateImageChannel(Image *image,
+% const ChannelType channel)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: Identify which channel to extract: RedChannel, GreenChannel,
+% BlueChannel, OpacityChannel, CyanChannel, MagentaChannel,
+% YellowChannel, or BlackChannel.
+%
+*/
+MagickExport MagickBooleanType SeparateImageChannel(Image *image,
+ const ChannelType channel)
+{
+#define SeparateImageTag "Separate/Image"
+
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Separate image channels.
+ */
+ status=MagickTrue;
+ if ( channel == GrayChannels )
+ image->matte=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ switch (channel)
+ {
+ case RedChannel:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ break;
+ }
+ case GreenChannel:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=q->green;
+ q->blue=q->green;
+ q++;
+ }
+ break;
+ }
+ case BlueChannel:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=q->blue;
+ q->green=q->blue;
+ q++;
+ }
+ break;
+ }
+ case OpacityChannel:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=q->opacity;
+ q->green=q->opacity;
+ q->blue=q->opacity;
+ q++;
+ }
+ break;
+ }
+ case BlackChannel:
+ {
+ if ((image->storage_class != PseudoClass) &&
+ (image->colorspace != CMYKColorspace))
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=indexes[x];
+ q->green=indexes[x];
+ q->blue=indexes[x];
+ q++;
+ }
+ break;
+ }
+ case TrueAlphaChannel:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=(Quantum) (QuantumRange-q->opacity);
+ q->green=(Quantum) (QuantumRange-q->opacity);
+ q->blue=(Quantum) (QuantumRange-q->opacity);
+ q++;
+ }
+ break;
+ }
+ case GrayChannels:
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->opacity=(Quantum) (QuantumRange-PixelIntensityToQuantum(q));
+ q++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SeparateImageChannel)
+#endif
+ proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if ( channel != GrayChannels )
+ image->matte=MagickFalse;
+ (void) SetImageColorspace(image,RGBColorspace);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e p a r a t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeparateImages() returns a separate grayscale image for each channel
+% specified.
+%
+% The format of the SeparateImages method is:
+%
+% MagickBooleanType SeparateImages(const Image *image,
+% const ChannelType channel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: Identify which channels to extract: RedChannel, GreenChannel,
+% BlueChannel, OpacityChannel, CyanChannel, MagentaChannel,
+% YellowChannel, or BlackChannel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SeparateImages(const Image *image,const ChannelType channel,
+ ExceptionInfo *exception)
+{
+ Image
+ *images,
+ *separate_image;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ images=NewImageList();
+ if ((channel & RedChannel) != 0)
+ {
+ separate_image=CloneImage(image,0,0,MagickTrue,exception);
+ (void) SeparateImageChannel(separate_image,RedChannel);
+ AppendImageToList(&images,separate_image);
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ separate_image=CloneImage(image,0,0,MagickTrue,exception);
+ (void) SeparateImageChannel(separate_image,GreenChannel);
+ AppendImageToList(&images,separate_image);
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ separate_image=CloneImage(image,0,0,MagickTrue,exception);
+ (void) SeparateImageChannel(separate_image,BlueChannel);
+ AppendImageToList(&images,separate_image);
+ }
+ if (((channel & BlackChannel) != 0) && (image->colorspace == CMYKColorspace))
+ {
+ separate_image=CloneImage(image,0,0,MagickTrue,exception);
+ (void) SeparateImageChannel(separate_image,BlackChannel);
+ AppendImageToList(&images,separate_image);
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ separate_image=CloneImage(image,0,0,MagickTrue,exception);
+ (void) SeparateImageChannel(separate_image,OpacityChannel);
+ AppendImageToList(&images,separate_image);
+ }
+ return(images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e A l p h a C h a n n e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
+% channel.
+%
+% The format of the SetImageAlphaChannel method is:
+%
+% MagickBooleanType SetImageAlphaChannel(Image *image,
+% const AlphaChannelType alpha_type)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o alpha_type: The alpha channel type: ActivateAlphaChannel,
+% CopyAlphaChannel, DeactivateAlphaChannel, ExtractAlphaChannel,
+% OpaqueAlphaChannel, ResetAlphaChannel, SetAlphaChannel,
+% ShapeAlphaChannel, and TransparentAlphaChannel.
+%
+*/
+MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
+ const AlphaChannelType alpha_type)
+{
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ status=MagickFalse;
+ switch (alpha_type)
+ {
+ case ActivateAlphaChannel:
+ {
+ image->matte=MagickTrue;
+ break;
+ }
+ case BackgroundAlphaChannel:
+ {
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ IndexPacket
+ index;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ background;
+
+ PixelPacket
+ pixel;
+
+ /*
+ Set transparent pixels to background color.
+ */
+ if (image->matte == MagickFalse)
+ break;
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ break;
+ GetMagickPixelPacket(image,&background);
+ SetMagickPixelPacket(image,&image->background_color,(const IndexPacket *)
+ NULL,&background);
+ if (image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&background);
+ index=0;
+ SetPixelPacket(image,&background,&pixel,&index);
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ #if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+ #endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (q->opacity == TransparentOpacity)
+ {
+ q->red=pixel.red;
+ q->green=pixel.green;
+ q->blue=pixel.blue;
+ }
+ q++;
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ indexes[x]=index;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+ }
+ case DeactivateAlphaChannel:
+ {
+ image->matte=MagickFalse;
+ break;
+ }
+ case ShapeAlphaChannel:
+ case CopyAlphaChannel:
+ {
+ /*
+ Special usage case for SeparateImageChannel(): copy grayscale color to
+ the alpha channel.
+ */
+ status=SeparateImageChannel(image,GrayChannels);
+ image->matte=MagickTrue; /* make sure transparency is now on! */
+ if (alpha_type == ShapeAlphaChannel)
+ {
+ MagickPixelPacket
+ background;
+
+ /*
+ Reset all color channels to background color.
+ */
+ GetMagickPixelPacket(image,&background);
+ SetMagickPixelPacket(image,&(image->background_color),(IndexPacket *)
+ NULL,&background);
+ (void) LevelImageColors(image,DefaultChannels,&background,&background,
+ MagickTrue);
+ }
+ break;
+ }
+ case ExtractAlphaChannel:
+ {
+ status=SeparateImageChannel(image,TrueAlphaChannel);
+ image->matte=MagickFalse;
+ break;
+ }
+ case ResetAlphaChannel:
+ case OpaqueAlphaChannel:
+ {
+ status=SetImageOpacity(image,OpaqueOpacity);
+ image->matte=MagickTrue;
+ break;
+ }
+ case TransparentAlphaChannel:
+ {
+ status=SetImageOpacity(image,TransparentOpacity);
+ image->matte=MagickTrue;
+ break;
+ }
+ case SetAlphaChannel:
+ {
+ if (image->matte == MagickFalse)
+ {
+ status=SetImageOpacity(image,OpaqueOpacity);
+ image->matte=MagickTrue;
+ }
+ break;
+ }
+ case UndefinedAlphaChannel:
+ break;
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e B a c k g r o u n d C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageBackgroundColor() initializes the image pixels to the image
+% background color. The background color is defined by the background_color
+% member of the image structure.
+%
+% The format of the SetImage method is:
+%
+% MagickBooleanType SetImageBackgroundColor(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType SetImageBackgroundColor(Image *image)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ IndexPacket
+ index;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ background;
+
+ PixelPacket
+ pixel;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->background_color.opacity != OpaqueOpacity)
+ image->matte=MagickTrue;
+ GetMagickPixelPacket(image,&background);
+ SetMagickPixelPacket(image,&image->background_color,(const IndexPacket *)
+ NULL,&background);
+ if (image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&background);
+ index=0;
+ SetPixelPacket(image,&background,&pixel,&index);
+ /*
+ Set image background color.
+ */
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ *q++=pixel;
+ if (image->colorspace == CMYKColorspace)
+ {
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ indexes[x]=index;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e S t o r a g e C l a s s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageStorageClass() sets the image class: DirectClass for true color
+% images or PseudoClass for colormapped images.
+%
+% The format of the SetImageStorageClass method is:
+%
+% MagickBooleanType SetImageStorageClass(Image *image,
+% const ClassType storage_class)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o storage_class: The image class.
+%
+*/
+MagickExport MagickBooleanType SetImageStorageClass(Image *image,
+ const ClassType storage_class)
+{
+ Cache
+ cache;
+
+ if (image->storage_class == storage_class)
+ return(MagickTrue);
+ image->storage_class=storage_class;
+ cache=GetImagePixelCache(image,MagickTrue,&image->exception);
+ return(cache == (Cache) NULL ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e C l i p M a s k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageClipMask() associates a clip path with the image. The clip path
+% must be the same dimensions as the image. Set any pixel component of
+% the clip path to TransparentOpacity to prevent that corresponding image
+% pixel component from being updated when SyncAuthenticPixels() is applied.
+%
+% The format of the SetImageClipMask method is:
+%
+% MagickBooleanType SetImageClipMask(Image *image,const Image *clip_mask)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clip_mask: the image clip path.
+%
+*/
+MagickExport MagickBooleanType SetImageClipMask(Image *image,
+ const Image *clip_mask)
+{
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (clip_mask != (const Image *) NULL)
+ if ((clip_mask->columns != image->columns) ||
+ (clip_mask->rows != image->rows))
+ ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
+ if (image->clip_mask != (Image *) NULL)
+ image->clip_mask=DestroyImage(image->clip_mask);
+ image->clip_mask=NewImageList();
+ if (clip_mask == (Image *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ image->clip_mask=CloneImage(clip_mask,0,0,MagickTrue,&image->exception);
+ if (image->clip_mask == (Image *) NULL)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageExtent() sets the image size (i.e. columns & rows).
+%
+% The format of the SetImageExtent method is:
+%
+% MagickBooleanType SetImageExtent(Image *image,
+% const unsigned long columns,const unsigned long rows)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: The image width in pixels.
+%
+% o rows: The image height in pixels.
+%
+*/
+MagickExport MagickBooleanType SetImageExtent(Image *image,
+ const unsigned long columns,const unsigned long rows)
+{
+ Cache
+ cache;
+
+ if ((columns != 0) && (rows != 0))
+ {
+ image->columns=columns;
+ image->rows=rows;
+ }
+ cache=GetImagePixelCache(image,MagickTrue,&image->exception);
+ return(cache == (Cache) NULL ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageInfo() initializes the `magick' field of the ImageInfo structure.
+% It is set to a type of image format based on the prefix or suffix of the
+% filename. For example, `ps:image' returns PS indicating a Postscript image.
+% JPEG is returned for this filename: `image.jpg'. The filename prefix has
+% precendence over the suffix. Use an optional index enclosed in brackets
+% after a file name to specify a desired scene of a multi-resolution image
+% format like Photo CD (e.g. img0001.pcd[4]). A True (non-zero) return value
+% indicates success.
+%
+% The format of the SetImageInfo method is:
+%
+% MagickBooleanType SetImageInfo(ImageInfo *image_info,
+% const MagickBooleanType rectify,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info..
+%
+% o rectify: an unsigned value other than zero rectifies the attribute for
+% multi-frame support (user may want multi-frame but image format may not
+% support it).
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType SetImageInfo(ImageInfo *image_info,
+ const MagickBooleanType rectify,ExceptionInfo *exception)
+{
+ char
+ extension[MaxTextExtent],
+ filename[MaxTextExtent],
+ magic[MaxTextExtent],
+ *q,
+ subimage[MaxTextExtent];
+
+ const MagicInfo
+ *magic_info;
+
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *sans_exception;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ register const char
+ *p;
+
+ ssize_t
+ count;
+
+ unsigned char
+ magick[2*MaxTextExtent];
+
+ /*
+ Look for 'image.format' in filename.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ *subimage='\0';
+ GetPathComponent(image_info->filename,SubimagePath,subimage);
+ if (*subimage != '\0')
+ {
+ /*
+ Look for scene specification (e.g. img0001.pcd[4]).
+ */
+ if (IsSceneGeometry(subimage,MagickFalse) == MagickFalse)
+ {
+ if (IsGeometry(subimage) != MagickFalse)
+ (void) CloneString(&image_info->extract,subimage);
+ }
+ else
+ {
+ unsigned long
+ first,
+ last;
+
+ (void) CloneString(&image_info->scenes,subimage);
+ image_info->scene=(unsigned long) atol(image_info->scenes);
+ image_info->number_scenes=image_info->scene;
+ p=image_info->scenes;
+ for (q=(char *) image_info->scenes; *q != '\0'; p++)
+ {
+ while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
+ p++;
+ first=(unsigned long) strtol(p,&q,10);
+ last=first;
+ while (isspace((int) ((unsigned char) *q)) != 0)
+ q++;
+ if (*q == '-')
+ last=(unsigned long) strtol(q+1,&q,10);
+ if (first > last)
+ Swap(first,last);
+ if (first < image_info->scene)
+ image_info->scene=first;
+ if (last > image_info->number_scenes)
+ image_info->number_scenes=last;
+ p=q;
+ }
+ image_info->number_scenes-=image_info->scene-1;
+ image_info->subimage=image_info->scene;
+ image_info->subrange=image_info->number_scenes;
+ }
+ }
+ *extension='\0';
+ GetPathComponent(image_info->filename,ExtensionPath,extension);
+#if defined(MAGICKCORE_ZLIB_DELEGATE)
+ if (*extension != '\0')
+ if ((LocaleCompare(extension,"gz") == 0) ||
+ (LocaleCompare(extension,"Z") == 0) ||
+ (LocaleCompare(extension,"wmz") == 0))
+ {
+ char
+ path[MaxTextExtent];
+
+ (void) CopyMagickString(path,image_info->filename,MaxTextExtent);
+ path[strlen(path)-strlen(extension)-1]='\0';
+ GetPathComponent(path,ExtensionPath,extension);
+ }
+#endif
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+ if (*extension != '\0')
+ if (LocaleCompare(extension,"bz2") == 0)
+ {
+ char
+ path[MaxTextExtent];
+
+ (void) CopyMagickString(path,image_info->filename,MaxTextExtent);
+ path[strlen(path)-strlen(extension)-1]='\0';
+ GetPathComponent(path,ExtensionPath,extension);
+ }
+#endif
+ image_info->affirm=MagickFalse;
+ sans_exception=AcquireExceptionInfo();
+ if (*extension != '\0')
+ {
+ MagickFormatType
+ format_type;
+
+ register long
+ i;
+
+ static const char
+ *format_type_formats[] =
+ {
+ "AUTOTRACE",
+ "BROWSE",
+ "DCRAW",
+ "EDIT",
+ "EPHEMERAL",
+ "LAUNCH",
+ "MPEG:DECODE",
+ "MPEG:ENCODE",
+ "PRINT",
+ "PS:ALPHA",
+ "PS:CMYK",
+ "PS:COLOR",
+ "PS:GRAY",
+ "PS:MONO",
+ "SCAN",
+ "SHOW",
+ "WIN",
+ (char *) NULL
+ };
+
+ /*
+ User specified image format.
+ */
+ (void) CopyMagickString(magic,extension,MaxTextExtent);
+ LocaleUpper(magic);
+ /*
+ Look for explicit image formats.
+ */
+ format_type=UndefinedFormatType;
+ i=0;
+ while ((format_type != UndefinedFormatType) &&
+ (format_type_formats[i] != (char *) NULL))
+ {
+ if ((*magic == *format_type_formats[i]) &&
+ (LocaleCompare(magic,format_type_formats[i]) == 0))
+ format_type=ExplicitFormatType;
+ i++;
+ }
+ magick_info=GetMagickInfo(magic,sans_exception);
+ if ((magick_info != (const MagickInfo *) NULL) &&
+ (magick_info->format_type != UndefinedFormatType))
+ format_type=magick_info->format_type;
+ if (format_type == UndefinedFormatType)
+ (void) CopyMagickString(image_info->magick,magic,MaxTextExtent);
+ else
+ if (format_type == ExplicitFormatType)
+ {
+ image_info->affirm=MagickTrue;
+ (void) CopyMagickString(image_info->magick,magic,MaxTextExtent);
+ }
+ if (LocaleCompare(magic,"RGB") == 0)
+ image_info->affirm=MagickFalse; /* maybe SGI disguised as RGB */
+ }
+ /*
+ Look for explicit 'format:image' in filename.
+ */
+ *magic='\0';
+ GetPathComponent(image_info->filename,MagickPath,magic);
+ if (*magic == '\0')
+ (void) CopyMagickString(magic,image_info->magick,MaxTextExtent);
+ else
+ {
+ /*
+ User specified image format.
+ */
+ LocaleUpper(magic);
+ if (IsMagickConflict(magic) == MagickFalse)
+ {
+ (void) CopyMagickString(image_info->magick,magic,MaxTextExtent);
+ if (LocaleCompare(magic,"EPHEMERAL") != 0)
+ image_info->affirm=MagickTrue;
+ else
+ image_info->temporary=MagickTrue;
+ }
+ }
+ magick_info=GetMagickInfo(magic,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (GetMagickEndianSupport(magick_info) == MagickFalse))
+ image_info->endian=UndefinedEndian;
+ GetPathComponent(image_info->filename,CanonicalPath,filename);
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ if (rectify != MagickFalse)
+ {
+ /*
+ Rectify multi-image file support.
+ */
+ (void) InterpretImageFilename(image_info,(Image *) NULL,
+ image_info->filename,(int) image_info->scene,filename);
+ if ((LocaleCompare(filename,image_info->filename) != 0) &&
+ (strchr(filename,'%') == (char *) NULL))
+ image_info->adjoin=MagickFalse;
+ magick_info=GetMagickInfo(magic,exception);
+ if (magick_info != (const MagickInfo *) NULL)
+ if (GetMagickAdjoin(magick_info) == MagickFalse)
+ image_info->adjoin=MagickFalse;
+ return(MagickTrue);
+ }
+ if (image_info->affirm != MagickFalse)
+ return(MagickTrue);
+ /*
+ Determine the image format from the first few bytes of the file.
+ */
+ image=AcquireImage(image_info);
+ (void) CopyMagickString(image->filename,image_info->filename,MaxTextExtent);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ {
+ image=DestroyImage(image);
+ return(MagickFalse);
+ }
+ if ((IsBlobSeekable(image) == MagickFalse) ||
+ (IsBlobExempt(image) != MagickFalse))
+ {
+ /*
+ Copy standard input or pipe to temporary file.
+ */
+ *filename='\0';
+ status=ImageToFile(image,filename,exception);
+ (void) CloseBlob(image);
+ if (status == MagickFalse)
+ {
+ image=DestroyImage(image);
+ return(MagickFalse);
+ }
+ SetImageInfoFile(image_info,(FILE *) NULL);
+ (void) CopyMagickString(image->filename,filename,MaxTextExtent);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ {
+ image=DestroyImage(image);
+ return(MagickFalse);
+ }
+ (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
+ image_info->temporary=MagickTrue;
+ }
+ (void) ResetMagickMemory(magick,0,sizeof(magick));
+ count=ReadBlob(image,2*MaxTextExtent,magick);
+ (void) CloseBlob(image);
+ image=DestroyImage(image);
+ /*
+ Check magic.xml configuration file.
+ */
+ sans_exception=AcquireExceptionInfo();
+ magic_info=GetMagicInfo(magick,(size_t) count,sans_exception);
+ if ((magic_info != (const MagicInfo *) NULL) &&
+ (GetMagicName(magic_info) != (char *) NULL))
+ {
+ (void) CopyMagickString(image_info->magick,GetMagicName(magic_info),
+ MaxTextExtent);
+ magick_info=GetMagickInfo(image_info->magick,sans_exception);
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (GetMagickEndianSupport(magick_info) == MagickFalse))
+ image_info->endian=UndefinedEndian;
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ return(MagickTrue);
+ }
+ magick_info=GetMagickInfo(image_info->magick,sans_exception);
+ if ((magick_info == (const MagickInfo *) NULL) ||
+ (GetMagickEndianSupport(magick_info) == MagickFalse))
+ image_info->endian=UndefinedEndian;
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e I n f o B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageInfoBlob() sets the image info blob member.
+%
+% The format of the SetImageInfoBlob method is:
+%
+% void SetImageInfoBlob(ImageInfo *image_info,const void *blob,
+% const size_t length)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o blob: the blob.
+%
+% o length: the blob length.
+%
+*/
+MagickExport void SetImageInfoBlob(ImageInfo *image_info,const void *blob,
+ const size_t length)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ image_info->blob=(void *) blob;
+ image_info->length=length;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e I n f o F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageInfoFile() sets the image info file member.
+%
+% The format of the SetImageInfoFile method is:
+%
+% void SetImageInfoFile(ImageInfo *image_info,FILE *file)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o file: the file.
+%
+*/
+MagickExport void SetImageInfoFile(ImageInfo *image_info,FILE *file)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ image_info->file=file;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e M a s k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageMask() associates a mask with the image. The mask must be the same
+% dimensions as the image.
+%
+% The format of the SetImageMask method is:
+%
+% MagickBooleanType SetImageMask(Image *image,const Image *mask)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o mask: the image mask.
+%
+*/
+MagickExport MagickBooleanType SetImageMask(Image *image,
+ const Image *mask)
+{
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (mask != (const Image *) NULL)
+ if ((mask->columns != image->columns) || (mask->rows != image->rows))
+ ThrowBinaryException(ImageError,"ImageSizeDiffers",image->filename);
+ if (image->mask != (Image *) NULL)
+ image->mask=DestroyImage(image->mask);
+ image->mask=NewImageList();
+ if (mask == (Image *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ image->mask=CloneImage(mask,0,0,MagickTrue,&image->exception);
+ if (image->mask == (Image *) NULL)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e O p a c i t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageOpacity() sets the opacity levels of the image.
+%
+% The format of the SetImageOpacity method is:
+%
+% MagickBooleanType SetImageOpacity(Image *image,const Quantum opacity)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o opacity: the level of transparency: 0 is fully opaque and QuantumRange is
+% fully transparent.
+%
+*/
+MagickExport MagickBooleanType SetImageOpacity(Image *image,
+ const Quantum opacity)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ image->matte=opacity != OpaqueOpacity ? MagickTrue : MagickFalse;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->opacity=opacity;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageType() sets the type of image. Choose from these types:
+%
+% Bilevel Grayscale GrayscaleMatte
+% Palette PaletteMatte TrueColor
+% TrueColorMatte ColorSeparation ColorSeparationMatte
+% OptimizeType
+%
+% The format of the SetImageType method is:
+%
+% MagickBooleanType SetImageType(Image *image,const ImageType type)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o type: Image type.
+%
+*/
+MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type)
+{
+ const char
+ *artifact;
+
+ ImageInfo
+ *image_info;
+
+ MagickBooleanType
+ status;
+
+ QuantizeInfo
+ *quantize_info;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ status=MagickTrue;
+ image_info=AcquireImageInfo();
+ image_info->dither=image->dither;
+ artifact=GetImageArtifact(image,"dither");
+ if (artifact != (const char *) NULL)
+ (void) SetImageOption(image_info,"dither",artifact);
+ switch (type)
+ {
+ case BilevelType:
+ {
+ if (IsGrayImage(image,&image->exception) == MagickFalse)
+ status=TransformImageColorspace(image,GRAYColorspace);
+ if (IsMonochromeImage(image,&image->exception) == MagickFalse)
+ {
+ quantize_info=AcquireQuantizeInfo(image_info);
+ quantize_info->number_colors=2;
+ quantize_info->colorspace=GRAYColorspace;
+ status=QuantizeImage(quantize_info,image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ }
+ image->matte=MagickFalse;
+ break;
+ }
+ case GrayscaleType:
+ {
+ if (IsGrayImage(image,&image->exception) == MagickFalse)
+ status=TransformImageColorspace(image,GRAYColorspace);
+ image->matte=MagickFalse;
+ break;
+ }
+ case GrayscaleMatteType:
+ {
+ if (IsGrayImage(image,&image->exception) == MagickFalse)
+ status=TransformImageColorspace(image,GRAYColorspace);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ break;
+ }
+ case PaletteType:
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ if ((image->storage_class == DirectClass) || (image->colors > 256))
+ {
+ quantize_info=AcquireQuantizeInfo(image_info);
+ quantize_info->number_colors=256;
+ status=QuantizeImage(quantize_info,image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ }
+ image->matte=MagickFalse;
+ break;
+ }
+ case PaletteBilevelMatteType:
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ (void) BilevelImageChannel(image,AlphaChannel,(double) QuantumRange/2.0);
+ quantize_info=AcquireQuantizeInfo(image_info);
+ status=QuantizeImage(quantize_info,image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ break;
+ }
+ case PaletteMatteType:
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ quantize_info=AcquireQuantizeInfo(image_info);
+ quantize_info->colorspace=TransparentColorspace;
+ status=QuantizeImage(quantize_info,image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ break;
+ }
+ case TrueColorType:
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ if (image->storage_class != DirectClass)
+ status=SetImageStorageClass(image,DirectClass);
+ image->matte=MagickFalse;
+ break;
+ }
+ case TrueColorMatteType:
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ if (image->storage_class != DirectClass)
+ status=SetImageStorageClass(image,DirectClass);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ break;
+ }
+ case ColorSeparationType:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ status=TransformImageColorspace(image,CMYKColorspace);
+ }
+ if (image->storage_class != DirectClass)
+ status=SetImageStorageClass(image,DirectClass);
+ image->matte=MagickFalse;
+ break;
+ }
+ case ColorSeparationMatteType:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ if (image->colorspace != RGBColorspace)
+ status=TransformImageColorspace(image,RGBColorspace);
+ status=TransformImageColorspace(image,CMYKColorspace);
+ }
+ if (image->storage_class != DirectClass)
+ status=SetImageStorageClass(image,DirectClass);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ break;
+ }
+ case OptimizeType:
+ case UndefinedType:
+ break;
+ }
+ image->type=type;
+ image_info=DestroyImageInfo(image_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e V i r t u a l P i x e l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageVirtualPixelMethod() sets the "virtual pixels" method for the
+% image and returns the previous setting. A virtual pixel is any pixel access
+% that is outside the boundaries of the image cache.
+%
+% The format of the SetImageVirtualPixelMethod() method is:
+%
+% VirtualPixelMethod SetImageVirtualPixelMethod(const Image *image,
+% const VirtualPixelMethod virtual_pixel_method)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: choose the type of virtual pixel.
+%
+*/
+MagickExport VirtualPixelMethod SetImageVirtualPixelMethod(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ return(SetPixelCacheVirtualMethod(image,virtual_pixel_method));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ 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=(int) PixelIntensityToQuantum(color_2)-
+ (int) PixelIntensityToQuantum(color_1);
+ return(intensity);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport MagickBooleanType SortColormapByIntensity(Image *image)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ unsigned short
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ 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.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (i=0; i < (long) 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.
+ */
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ pixels[(long) image->colormap[i].opacity]=(unsigned short) i;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ IndexPacket
+ index;
+
+ register long
+ x;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register PixelPacket
+ *__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 < (long) image->columns; x++)
+ {
+ index=(IndexPacket) pixels[(long) indexes[x]];
+ indexes[x]=index;
+ *q++=image->colormap[(long) index];
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (status == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ pixels=(unsigned short *) RelinquishMagickMemory(pixels);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StripImage() strips an image of all profiles and comments.
+%
+% The format of the StripImage method is:
+%
+% MagickBooleanType StripImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType StripImage(Image *image)
+{
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ DestroyImageProfiles(image);
+ (void) DeleteImageProperty(image,"comment");
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncImage() initializes the red, green, and blue intensities of each pixel
+% as defined by the colormap index.
+%
+% The format of the SyncImage method is:
+%
+% MagickBooleanType SyncImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static inline IndexPacket PushColormapIndex(Image *image,
+ const unsigned long index,MagickBooleanType *range_exception)
+{
+ if (index < image->colors)
+ return((IndexPacket) index);
+ *range_exception=MagickTrue;
+ return((IndexPacket) 0);
+}
+
+MagickExport MagickBooleanType SyncImage(Image *image)
+{
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ range_exception,
+ status;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (image->storage_class == DirectClass)
+ return(MagickFalse);
+ range_exception=MagickFalse;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ IndexPacket
+ index;
+
+ PixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__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 < (long) image->columns; x++)
+ {
+ index=PushColormapIndex(image,(unsigned long) indexes[x],
+ &range_exception);
+ pixel=image->colormap[(long) index];
+ q->red=pixel.red;
+ q->green=pixel.green;
+ q->blue=pixel.blue;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (status == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ if (range_exception != MagickFalse)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ CorruptImageError,"InvalidColormapIndex","`%s'",image->filename);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T e x t u r e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TextureImage() repeatedly tiles the texture image across and down the image
+% canvas.
+%
+% The format of the TextureImage method is:
+%
+% MagickBooleanType TextureImage(Image *image,const Image *texture)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o texture: This image is the texture to layer on the background.
+%
+*/
+MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture)
+{
+#define TextureImageTag "Texture/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickStatusType
+ status;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (texture == (const Image *) NULL)
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Tile texture onto the image background.
+ */
+ status=MagickTrue;
+ exception=(&image->exception);
+ for (y=0; y < (long) image->rows; y+=texture->rows)
+ {
+ register long
+ x;
+
+ for (x=0; x < (long) image->columns; x+=texture->columns)
+ status|=CompositeImage(image,image->compose,texture,x+
+ texture->tile_offset.x,y+texture->tile_offset.y);
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,TextureImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ (void) SetImageProgress(image,TextureImageTag,image->rows,image->rows);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
diff --git a/magick/image.h b/magick/image.h
new file mode 100644
index 0000000..9a3eaea
--- /dev/null
+++ b/magick/image.h
@@ -0,0 +1,555 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image methods.
+*/
+#ifndef _MAGICKCORE_IMAGE_H
+#define _MAGICKCORE_IMAGE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/color.h>
+
+#define OpaqueOpacity ((Quantum) 0UL)
+#define TransparentOpacity ((Quantum) QuantumRange)
+
+typedef enum
+{
+ UndefinedAlphaChannel,
+ ActivateAlphaChannel,
+ BackgroundAlphaChannel,
+ CopyAlphaChannel,
+ DeactivateAlphaChannel,
+ ExtractAlphaChannel,
+ OpaqueAlphaChannel,
+ ResetAlphaChannel, /* deprecated */
+ SetAlphaChannel,
+ ShapeAlphaChannel,
+ TransparentAlphaChannel
+} AlphaChannelType;
+
+typedef enum
+{
+ UndefinedType,
+ BilevelType,
+ GrayscaleType,
+ GrayscaleMatteType,
+ PaletteType,
+ PaletteMatteType,
+ TrueColorType,
+ TrueColorMatteType,
+ ColorSeparationType,
+ ColorSeparationMatteType,
+ OptimizeType,
+ PaletteBilevelMatteType
+} ImageType;
+
+typedef enum
+{
+ UndefinedInterlace,
+ NoInterlace,
+ LineInterlace,
+ PlaneInterlace,
+ PartitionInterlace,
+ GIFInterlace,
+ JPEGInterlace,
+ PNGInterlace
+} InterlaceType;
+
+typedef enum
+{
+ UndefinedOrientation,
+ TopLeftOrientation,
+ TopRightOrientation,
+ BottomRightOrientation,
+ BottomLeftOrientation,
+ LeftTopOrientation,
+ RightTopOrientation,
+ RightBottomOrientation,
+ LeftBottomOrientation
+} OrientationType;
+
+typedef enum
+{
+ UndefinedResolution,
+ PixelsPerInchResolution,
+ PixelsPerCentimeterResolution
+} ResolutionType;
+
+typedef struct _PrimaryInfo
+{
+ double
+ x,
+ y,
+ z;
+} PrimaryInfo;
+
+typedef struct _SegmentInfo
+{
+ double
+ x1,
+ y1,
+ x2,
+ y2;
+} SegmentInfo;
+
+typedef enum
+{
+ UndefinedTransmitType,
+ FileTransmitType,
+ BlobTransmitType,
+ StreamTransmitType,
+ ImageTransmitType
+} TransmitType;
+
+typedef struct _ChromaticityInfo
+{
+ PrimaryInfo
+ red_primary,
+ green_primary,
+ blue_primary,
+ white_point;
+} ChromaticityInfo;
+
+#include "magick/blob.h"
+#include "magick/colorspace.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/composite.h"
+#include "magick/compress.h"
+#include "magick/effect.h"
+#include "magick/geometry.h"
+#include "magick/layer.h"
+#include "magick/monitor.h"
+#include "magick/pixel.h"
+#include "magick/profile.h"
+#include "magick/quantum.h"
+#include "magick/resample.h"
+#include "magick/resize.h"
+#include "magick/semaphore.h"
+#include "magick/stream.h"
+#include "magick/timer.h"
+
+struct _Image
+{
+ ClassType
+ storage_class;
+
+ ColorspaceType
+ colorspace;
+
+ CompressionType
+ compression;
+
+ unsigned long
+ quality;
+
+ OrientationType
+ orientation;
+
+ MagickBooleanType
+ taint,
+ matte;
+
+ unsigned long
+ columns,
+ rows,
+ depth,
+ colors;
+
+ PixelPacket
+ *colormap,
+ background_color,
+ border_color,
+ matte_color;
+
+ double
+ gamma;
+
+ ChromaticityInfo
+ chromaticity;
+
+ RenderingIntent
+ rendering_intent;
+
+ void
+ *profiles;
+
+ ResolutionType
+ units;
+
+ char
+ *montage,
+ *directory,
+ *geometry;
+
+ long
+ offset;
+
+ double
+ x_resolution,
+ y_resolution;
+
+ RectangleInfo
+ page,
+ extract_info,
+ tile_info; /* deprecated */
+
+ double
+ bias,
+ blur, /* deprecated */
+ fuzz;
+
+ FilterTypes
+ filter;
+
+ InterlaceType
+ interlace;
+
+ EndianType
+ endian;
+
+ GravityType
+ gravity;
+
+ CompositeOperator
+ compose;
+
+ DisposeType
+ dispose;
+
+ struct _Image
+ *clip_mask;
+
+ unsigned long
+ scene,
+ delay;
+
+ long
+ ticks_per_second;
+
+ unsigned long
+ iterations,
+ total_colors;
+
+ long
+ start_loop;
+
+ ErrorInfo
+ error;
+
+ TimerInfo
+ timer;
+
+ MagickProgressMonitor
+ progress_monitor;
+
+ void
+ *client_data,
+ *cache,
+ *attributes; /* deprecated */
+
+ Ascii85Info
+ *ascii85;
+
+ BlobInfo
+ *blob;
+
+ char
+ filename[MaxTextExtent],
+ magick_filename[MaxTextExtent],
+ magick[MaxTextExtent];
+
+ unsigned long
+ magick_columns,
+ magick_rows;
+
+ ExceptionInfo
+ exception;
+
+ MagickBooleanType
+ debug;
+
+ volatile long
+ reference_count;
+
+ SemaphoreInfo
+ *semaphore;
+
+ ProfileInfo
+ color_profile,
+ iptc_profile,
+ *generic_profile;
+
+ unsigned long
+ generic_profiles; /* this & ProfileInfo is deprecated */
+
+ unsigned long
+ signature;
+
+ struct _Image
+ *previous,
+ *list,
+ *next;
+
+ InterpolatePixelMethod
+ interpolate;
+
+ MagickBooleanType
+ black_point_compensation;
+
+ PixelPacket
+ transparent_color;
+
+ struct _Image
+ *mask;
+
+ RectangleInfo
+ tile_offset;
+
+ void
+ *properties,
+ *artifacts;
+
+ ImageType
+ type;
+
+ MagickBooleanType
+ dither;
+};
+
+struct _ImageInfo
+{
+ CompressionType
+ compression;
+
+ OrientationType
+ orientation;
+
+ MagickBooleanType
+ temporary,
+ adjoin,
+ affirm,
+ antialias;
+
+ char
+ *size,
+ *extract,
+ *page,
+ *scenes;
+
+ unsigned long
+ scene,
+ number_scenes,
+ depth;
+
+ InterlaceType
+ interlace;
+
+ EndianType
+ endian;
+
+ ResolutionType
+ units;
+
+ unsigned long
+ quality;
+
+ char
+ *sampling_factor,
+ *server_name,
+ *font,
+ *texture,
+ *density;
+
+ double
+ pointsize,
+ fuzz;
+
+ PixelPacket
+ background_color,
+ border_color,
+ matte_color;
+
+ MagickBooleanType
+ dither,
+ monochrome;
+
+ unsigned long
+ colors;
+
+ ColorspaceType
+ colorspace;
+
+ ImageType
+ type;
+
+ PreviewType
+ preview_type;
+
+ long
+ group;
+
+ MagickBooleanType
+ ping,
+ verbose;
+
+ char
+ *view,
+ *authenticate;
+
+ ChannelType
+ channel;
+
+ Image
+ *attributes; /* deprecated */
+
+ void
+ *options;
+
+ MagickProgressMonitor
+ progress_monitor;
+
+ void
+ *client_data,
+ *cache;
+
+ StreamHandler
+ stream;
+
+ FILE
+ *file;
+
+ void
+ *blob;
+
+ size_t
+ length;
+
+ char
+ magick[MaxTextExtent],
+ unique[MaxTextExtent],
+ zero[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ MagickBooleanType
+ debug;
+
+ char
+ *tile; /* deprecated */
+
+ unsigned long
+ subimage, /* deprecated */
+ subrange; /* deprecated */
+
+ PixelPacket
+ pen; /* deprecated */
+
+ unsigned long
+ signature;
+
+ VirtualPixelMethod
+ virtual_pixel_method;
+
+ PixelPacket
+ transparent_color;
+
+ void
+ *profile;
+
+ MagickBooleanType
+ synchronize;
+};
+
+extern MagickExport ExceptionType
+ CatchImageException(Image *);
+
+extern MagickExport Image
+ *AcquireImage(const ImageInfo *),
+ *AppendImages(const Image *,const MagickBooleanType,ExceptionInfo *),
+ *AverageImages(const Image *,ExceptionInfo *),
+ *CloneImage(const Image *,const unsigned long,const unsigned long,
+ const MagickBooleanType,ExceptionInfo *),
+ *CombineImages(const Image *,const ChannelType,ExceptionInfo *),
+ *DestroyImage(Image *),
+ *GetImageClipMask(const Image *,ExceptionInfo *),
+ *GetImageMask(const Image *,ExceptionInfo *),
+ *NewMagickImage(const ImageInfo *,const unsigned long,const unsigned long,
+ const MagickPixelPacket *),
+ *ReferenceImage(Image *),
+ *SeparateImages(const Image *,const ChannelType,ExceptionInfo *);
+
+extern MagickExport ImageInfo
+ *AcquireImageInfo(void),
+ *CloneImageInfo(const ImageInfo *),
+ *DestroyImageInfo(ImageInfo *);
+
+extern MagickExport ImageType
+ GetImageType(const Image *,ExceptionInfo *);
+
+extern MagickExport long
+ GetImageReferenceCount(Image *);
+
+extern MagickExport MagickBooleanType
+ AcquireImageColormap(Image *,const unsigned long),
+ ClipImage(Image *),
+ ClipImagePath(Image *,const char *,const MagickBooleanType),
+ CycleColormapImage(Image *,const long),
+ GetImageAlphaChannel(const Image *),
+ IsTaintImage(const Image *),
+ IsMagickConflict(const char *),
+ IsHighDynamicRangeImage(const Image *,ExceptionInfo *),
+ IsImageObject(const Image *),
+ ListMagickInfo(FILE *,ExceptionInfo *),
+ ModifyImage(Image **,ExceptionInfo *),
+ ResetImagePage(Image *,const char *),
+ SeparateImageChannel(Image *,const ChannelType),
+ SetImageAlphaChannel(Image *,const AlphaChannelType),
+ SetImageBackgroundColor(Image *),
+ SetImageClipMask(Image *,const Image *),
+ SetImageExtent(Image *,const unsigned long,const unsigned long),
+ SetImageInfo(ImageInfo *,const MagickBooleanType,ExceptionInfo *),
+ SetImageMask(Image *,const Image *),
+ SetImageOpacity(Image *,const Quantum),
+ SetImageStorageClass(Image *,const ClassType),
+ SetImageType(Image *,const ImageType),
+ SortColormapByIntensity(Image *),
+ StripImage(Image *),
+ SyncImage(Image *),
+ TextureImage(Image *,const Image *);
+
+extern MagickExport size_t
+ InterpretImageFilename(const ImageInfo *,Image *,const char *,int,char *);
+
+extern MagickExport VirtualPixelMethod
+ GetImageVirtualPixelMethod(const Image *),
+ SetImageVirtualPixelMethod(const Image *,const VirtualPixelMethod);
+
+extern MagickExport void
+ AcquireNextImage(const ImageInfo *,Image *),
+ DestroyImagePixels(Image *),
+ DisassociateImageStream(Image *),
+ GetImageException(Image *,ExceptionInfo *),
+ GetImageInfo(ImageInfo *),
+ SetImageInfoBlob(ImageInfo *,const void *,const size_t),
+ SetImageInfoFile(ImageInfo *,FILE *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/layer.c b/magick/layer.c
new file mode 100644
index 0000000..b7c686b
--- /dev/null
+++ b/magick/layer.c
@@ -0,0 +1,1973 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% L AAA Y Y EEEEE RRRR %
+% L A A Y Y E R R %
+% L AAAAA Y EEE RRRR %
+% L A A Y E R R %
+% LLLLL A A Y EEEEE R R %
+% %
+% MagickCore Image Layering Methods %
+% %
+% Software Design %
+% John Cristy %
+% Anthony Thyssen %
+% January 2006 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/composite.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel-private.h"
+#include "magick/property.h"
+#include "magick/profile.h"
+#include "magick/resource_.h"
+#include "magick/resize.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l e a r B o u n d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClearBounds() Clear the area specified by the bounds in an image to
+% transparency. This typically used to handle Background Disposal
+% for the previous frame in an animation sequence.
+%
+% WARNING: no bounds checks are performed, except for the null or
+% missed image, for images that don't change. in all other cases
+% bound must fall within the image.
+%
+% The format is:
+%
+% void ClearBounds(Image *image,RectangleInfo *bounds)
+%
+% A description of each parameter follows:
+%
+% o image: the image to had the area cleared in
+%
+% o bounds: the area to be clear within the imag image
+%
+*/
+static void ClearBounds(Image *image,RectangleInfo *bounds)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ if (bounds->x < 0)
+ return;
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ exception=(&image->exception);
+ for (y=0; y < (long) bounds->height; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) bounds->width; x++)
+ {
+ q->opacity=(Quantum) TransparentOpacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s B o u n d s C l e a r e d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
+% when going from the first image to the second image. This typically used
+% to check if a proposed disposal method will work successfully to generate
+% the second frame image from the first disposed form of the previous frame.
+%
+% The format is:
+%
+% MagickBooleanType IsBoundsCleared(const Image *image1,
+% const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image1, image 2: the images to check for cleared pixels
+%
+% o bounds: the area to be clear within the imag image
+%
+% o exception: return any errors or warnings in this structure.
+%
+% WARNING: no bounds checks are performed, except for the null or
+% missed image, for images that don't change. in all other cases
+% bound must fall within the image.
+%
+*/
+static MagickBooleanType IsBoundsCleared(const Image *image1,
+ const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
+{
+ long
+ y;
+
+ register long
+ x;
+
+ register const PixelPacket
+ *p,
+ *q;
+
+ if ( bounds->x< 0 ) return(MagickFalse);
+
+ for (y=0; y < (long) bounds->height; y++)
+ {
+ p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,
+ exception);
+ q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) bounds->width; x++)
+ {
+ if ((p->opacity <= (long) (QuantumRange/2)) &&
+ (q->opacity > (long) (QuantumRange/2)))
+ break;
+ p++;
+ q++;
+ }
+ if (x < (long) bounds->width)
+ break;
+ }
+ return(y < (long) bounds->height ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o a l e s c e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CoalesceImages() composites a set of images while respecting any page
+% offsets and disposal methods. GIF, MIFF, and MNG animation sequences
+% typically start with an image background and each subsequent image
+% varies in size and offset. A new image sequence is returned with all
+% images the same size as the first images virtual canvas and composited
+% with the next image in the sequence.
+%
+% The format of the CoalesceImages method is:
+%
+% Image *CoalesceImages(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
+{
+ Image
+ *coalesce_image,
+ *dispose_image,
+ *previous;
+
+ register Image
+ *next;
+
+ RectangleInfo
+ bounds;
+
+ /*
+ Coalesce the image sequence.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ /* initialise first image */
+ next=GetFirstImageInList(image);
+ bounds=next->page;
+ if (bounds.width == 0)
+ {
+ bounds.width=next->columns;
+ if (bounds.x > 0)
+ bounds.width+=bounds.x;
+ }
+ if (bounds.height == 0)
+ {
+ bounds.height=next->rows;
+ if (bounds.y > 0)
+ bounds.height+=bounds.y;
+ }
+ bounds.x=0;
+ bounds.y=0;
+ coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
+ exception);
+ if (coalesce_image == (Image *) NULL)
+ return((Image *) NULL);
+ coalesce_image->page=bounds;
+ coalesce_image->dispose=NoneDispose;
+ coalesce_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(coalesce_image);
+ /*
+ Coalesce rest of the images.
+ */
+ dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
+ (void) CompositeImage(coalesce_image,CopyCompositeOp,next,next->page.x,
+ next->page.y);
+ next=GetNextImageInList(next);
+ for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ /*
+ Determine the bounds that was overlaid in the previous image.
+ */
+ previous=GetPreviousImageInList(next);
+ bounds=previous->page;
+ bounds.width=previous->columns;
+ bounds.height=previous->rows;
+ if (bounds.x < 0)
+ {
+ bounds.width+=bounds.x;
+ bounds.x=0;
+ }
+ if ((long) (bounds.x+bounds.width) > (long) coalesce_image->columns)
+ bounds.width=coalesce_image->columns-bounds.x;
+ if (bounds.y < 0)
+ {
+ bounds.height+=bounds.y;
+ bounds.y=0;
+ }
+ if ((long) (bounds.y+bounds.height) > (long) coalesce_image->rows)
+ bounds.height=coalesce_image->rows-bounds.y;
+ /*
+ Replace the dispose image with the new coalesced image.
+ */
+ if (GetPreviousImageInList(next)->dispose != PreviousDispose)
+ {
+ dispose_image=DestroyImage(dispose_image);
+ dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
+ if (dispose_image == (Image *) NULL)
+ {
+ coalesce_image=DestroyImageList(coalesce_image);
+ return((Image *) NULL);
+ }
+ }
+ /*
+ Clear the overlaid area of the coalesced bounds for background disposal
+ */
+ if (next->previous->dispose == BackgroundDispose)
+ ClearBounds(dispose_image, &bounds);
+ /*
+ Next image is the dispose image, overlaid with next frame in sequence.
+ */
+ coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
+ coalesce_image->next->previous=coalesce_image;
+ previous=coalesce_image;
+ coalesce_image=GetNextImageInList(coalesce_image);
+ coalesce_image->matte=MagickTrue;
+ (void) CompositeImage(coalesce_image,next->matte != MagickFalse ?
+ OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
+ (void) CloneImageProfiles(coalesce_image,next);
+ (void) CloneImageProperties(coalesce_image,next);
+ (void) CloneImageArtifacts(coalesce_image,next);
+ coalesce_image->page=previous->page;
+ /*
+ If a pixel goes opaque to transparent, use background dispose.
+ */
+ if (IsBoundsCleared(previous,coalesce_image,&bounds,exception))
+ coalesce_image->dispose=BackgroundDispose;
+ else
+ coalesce_image->dispose=NoneDispose;
+ previous->dispose=coalesce_image->dispose;
+ }
+ dispose_image=DestroyImage(dispose_image);
+ return(GetFirstImageInList(coalesce_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D i s p o s e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DisposeImages() returns the coalesced frames of a GIF animation as it would
+% appear after the GIF dispose method of that frame has been applied. That
+% is it returned the appearance of each frame before the next is overlaid.
+%
+% The format of the DisposeImages method is:
+%
+% Image *DisposeImages(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *DisposeImages(const Image *image,ExceptionInfo *exception)
+{
+ Image
+ *dispose_image,
+ *dispose_images;
+
+ register Image
+ *next;
+
+ /*
+ Run the image through the animation sequence
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ next=GetFirstImageInList(image);
+ dispose_image=CloneImage(next,next->page.width,next->page.height,MagickTrue,
+ exception);
+ if (dispose_image == (Image *) NULL)
+ return((Image *) NULL);
+ dispose_image->page=next->page;
+ dispose_image->page.x=0;
+ dispose_image->page.y=0;
+ dispose_image->dispose=NoneDispose;
+ dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(dispose_image);
+ dispose_images=NewImageList();
+ for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ Image
+ *current_image;
+
+ /*
+ Overlay this frame's image over the previous disposal image.
+ */
+ current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
+ if (current_image == (Image *) NULL)
+ {
+ dispose_images=DestroyImageList(dispose_images);
+ dispose_image=DestroyImage(dispose_image);
+ return((Image *) NULL);
+ }
+ (void) CompositeImage(current_image,next->matte != MagickFalse ?
+ OverCompositeOp : CopyCompositeOp,next,next->page.x,next->page.y);
+ /*
+ Handle Background dispose: image is displayed for the delay period.
+ */
+ if (next->dispose == BackgroundDispose)
+ {
+ RectangleInfo
+ bounds;
+
+ bounds=next->page;
+ bounds.width=next->columns;
+ bounds.height=next->rows;
+ if (bounds.x < 0)
+ {
+ bounds.width+=bounds.x;
+ bounds.x=0;
+ }
+ if ((long) (bounds.x+bounds.width) > (long) current_image->columns)
+ bounds.width=current_image->columns-bounds.x;
+ if (bounds.y < 0)
+ {
+ bounds.height+=bounds.y;
+ bounds.y=0;
+ }
+ if ((long) (bounds.y+bounds.height) > (long) current_image->rows)
+ bounds.height=current_image->rows-bounds.y;
+ ClearBounds(current_image,&bounds);
+ }
+ /*
+ Select the appropriate previous/disposed image.
+ */
+ if (next->dispose == PreviousDispose)
+ current_image=DestroyImage(current_image);
+ else
+ {
+ dispose_image=DestroyImage(dispose_image);
+ dispose_image=current_image;
+ }
+ {
+ Image
+ *dispose;
+
+ /*
+ Save the dispose image just calculated for return.
+ */
+ dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
+ if (dispose == (Image *) NULL)
+ {
+ dispose_images=DestroyImageList(dispose_images);
+ dispose_image=DestroyImage(dispose_image);
+ return((Image *) NULL);
+ }
+ (void) CloneImageProfiles(dispose,next);
+ (void) CloneImageProperties(dispose,next);
+ (void) CloneImageArtifacts(dispose,next);
+ dispose->page.x=0;
+ dispose->page.y=0;
+ dispose->dispose=next->dispose;
+ AppendImageToList(&dispose_images,dispose);
+ }
+ }
+ dispose_image=DestroyImage(dispose_image);
+ return(GetFirstImageInList(dispose_images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o m p a r e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ComparePixels() Compare the two pixels and return true if the pixels
+% differ according to the given LayerType comparision method.
+%
+% This currently only used internally by CompareImageBounds(). It is
+% doubtful that this sub-routine will be useful outside this module.
+%
+% The format of the ComparePixels method is:
+%
+% MagickBooleanType *ComparePixels(const ImageLayerMethod method,
+% const MagickPixelPacket *p,const MagickPixelPacket *q)
+%
+% A description of each parameter follows:
+%
+% o method: What differences to look for. Must be one of
+% CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
+%
+% o p, q: the pixels to test for appropriate differences.
+%
+*/
+
+static MagickBooleanType ComparePixels(const ImageLayerMethod method,
+ const MagickPixelPacket *p,const MagickPixelPacket *q)
+{
+ MagickRealType
+ o1,
+ o2;
+
+ /*
+ Any change in pixel values
+ */
+ if (method == CompareAnyLayer)
+ return(IsMagickColorSimilar(p,q) == MagickFalse ? MagickTrue : MagickFalse);
+
+ o1 = (p->matte != MagickFalse) ? p->opacity : OpaqueOpacity;
+ o2 = (q->matte != MagickFalse) ? q->opacity : OpaqueOpacity;
+
+ /*
+ Pixel goes from opaque to transprency
+ */
+ if (method == CompareClearLayer)
+ return((MagickBooleanType) ( (o1 <= ((MagickRealType) QuantumRange/2.0)) &&
+ (o2 > ((MagickRealType) QuantumRange/2.0)) ) );
+
+ /*
+ overlay would change first pixel by second
+ */
+ if (method == CompareOverlayLayer)
+ {
+ if (o2 > ((MagickRealType) QuantumRange/2.0))
+ return MagickFalse;
+ return((MagickBooleanType) (IsMagickColorSimilar(p,q) == MagickFalse));
+ }
+ return(MagickFalse);
+}
+
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o m p a r e I m a g e B o u n d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareImageBounds() Given two images return the smallest rectangular area
+% by which the two images differ, accourding to the given 'Compare...'
+% layer method.
+%
+% This currently only used internally in this module, but may eventually
+% be used by other modules.
+%
+% The format of the CompareImageBounds method is:
+%
+% RectangleInfo *CompareImageBounds(const ImageLayerMethod method,
+% const Image *image1, const Image *image2, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o method: What differences to look for. Must be one of
+% CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
+%
+% o image1, image2: the two images to compare.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static RectangleInfo CompareImageBounds(const Image *image1,const Image *image2,
+ const ImageLayerMethod method,ExceptionInfo *exception)
+{
+ RectangleInfo
+ bounds;
+
+ MagickPixelPacket
+ pixel1,
+ pixel2;
+
+ register const IndexPacket
+ *indexes1,
+ *indexes2;
+
+ register const PixelPacket
+ *p,
+ *q;
+
+ long
+ y;
+
+ register long
+ x;
+
+ /*
+ Set bounding box of the differences between images
+ */
+ GetMagickPixelPacket(image1,&pixel1);
+ GetMagickPixelPacket(image2,&pixel2);
+ for (x=0; x < (long) image1->columns; x++)
+ {
+ p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
+ q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (const PixelPacket *) NULL))
+ break;
+ indexes1=GetVirtualIndexQueue(image1);
+ indexes2=GetVirtualIndexQueue(image2);
+ for (y=0; y < (long) image1->rows; y++)
+ {
+ SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
+ SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
+ if (ComparePixels(method,&pixel1,&pixel2))
+ break;
+ p++;
+ q++;
+ }
+ if (y < (long) image1->rows)
+ break;
+ }
+ if (x >= (long) image1->columns)
+ {
+ /*
+ Images are identical, return a null image.
+ */
+ bounds.x=-1;
+ bounds.y=-1;
+ bounds.width=1;
+ bounds.height=1;
+ return(bounds);
+ }
+ bounds.x=x;
+ for (x=(long) image1->columns-1; x >= 0; x--)
+ {
+ p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
+ q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (const PixelPacket *) NULL))
+ break;
+ indexes1=GetVirtualIndexQueue(image1);
+ indexes2=GetVirtualIndexQueue(image2);
+ for (y=0; y < (long) image1->rows; y++)
+ {
+ SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
+ SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
+ if (ComparePixels(method,&pixel1,&pixel2))
+ break;
+ p++;
+ q++;
+ }
+ if (y < (long) image1->rows)
+ break;
+ }
+ bounds.width=(unsigned long) (x-bounds.x+1);
+ for (y=0; y < (long) image1->rows; y++)
+ {
+ p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
+ q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (const PixelPacket *) NULL))
+ break;
+ indexes1=GetVirtualIndexQueue(image1);
+ indexes2=GetVirtualIndexQueue(image2);
+ for (x=0; x < (long) image1->columns; x++)
+ {
+ SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
+ SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
+ if (ComparePixels(method,&pixel1,&pixel2))
+ break;
+ p++;
+ q++;
+ }
+ if (x < (long) image1->columns)
+ break;
+ }
+ bounds.y=y;
+ for (y=(long) image1->rows-1; y >= 0; y--)
+ {
+ p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
+ q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (const PixelPacket *) NULL))
+ break;
+ indexes1=GetVirtualIndexQueue(image1);
+ indexes2=GetVirtualIndexQueue(image2);
+ for (x=0; x < (long) image1->columns; x++)
+ {
+ SetMagickPixelPacket(image1,p,indexes1+x,&pixel1);
+ SetMagickPixelPacket(image2,q,indexes2+x,&pixel2);
+ if (ComparePixels(method,&pixel1,&pixel2))
+ break;
+ p++;
+ q++;
+ }
+ if (x < (long) image1->columns)
+ break;
+ }
+ bounds.height=(unsigned long) (y-bounds.y+1);
+ return(bounds);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e I m a g e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareImageLayers() compares each image with the next in a sequence and
+% returns the minimum bounding region of all the pixel differences (of the
+% ImageLayerMethod specified) it discovers.
+%
+% Images do NOT have to be the same size, though it is best that all the
+% images are 'coalesced' (images are all the same size, on a flattened
+% canvas, so as to represent exactly how an specific frame should look).
+%
+% No GIF dispose methods are applied, so GIF animations must be coalesced
+% before applying this image operator to find differences to them.
+%
+% The format of the CompareImageLayers method is:
+%
+% Image *CompareImageLayers(const Image *images,
+% const ImageLayerMethod method,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o method: the layers type to compare images with. Must be one of...
+% CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *CompareImageLayers(const Image *image,
+ const ImageLayerMethod method, ExceptionInfo *exception)
+{
+ Image
+ *image_a,
+ *image_b,
+ *layers;
+
+ RectangleInfo
+ *bounds;
+
+ register const Image
+ *next;
+
+ register long
+ i;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert((method == CompareAnyLayer) ||
+ (method == CompareClearLayer) ||
+ (method == CompareOverlayLayer));
+ /*
+ Allocate bounds memory.
+ */
+ next=GetFirstImageInList(image);
+ bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
+ GetImageListLength(next),sizeof(*bounds));
+ if (bounds == (RectangleInfo *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ /*
+ Set up first comparision images.
+ */
+ image_a=CloneImage(next,next->page.width,next->page.height,
+ MagickTrue,exception);
+ if (image_a == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ return((Image *) NULL);
+ }
+ image_a->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(image_a);
+ image_a->page=next->page;
+ image_a->page.x=0;
+ image_a->page.y=0;
+ (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,next->page.y);
+ /*
+ Compute the bounding box of changes for the later images
+ */
+ i=0;
+ next=GetNextImageInList(next);
+ for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
+ {
+ image_b=CloneImage(image_a,0,0,MagickTrue,exception);
+ if (image_b == (Image *) NULL)
+ {
+ image_a=DestroyImage(image_a);
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ return((Image *) NULL);
+ }
+ (void) CompositeImage(image_a,CopyCompositeOp,next,next->page.x,
+ next->page.y);
+ bounds[i]=CompareImageBounds(image_b,image_a,method,exception);
+
+ image_b=DestroyImage(image_b);
+ i++;
+ }
+ image_a=DestroyImage(image_a);
+ /*
+ Clone first image in sequence.
+ */
+ next=GetFirstImageInList(image);
+ layers=CloneImage(next,0,0,MagickTrue,exception);
+ if (layers == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ return((Image *) NULL);
+ }
+ /*
+ Deconstruct the image sequence.
+ */
+ i=0;
+ next=GetNextImageInList(next);
+ for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
+ {
+ image_a=CloneImage(next,0,0,MagickTrue,exception);
+ if (image_a == (Image *) NULL)
+ break;
+ image_b=CropImage(image_a,&bounds[i],exception);
+ image_a=DestroyImage(image_a);
+ if (image_b == (Image *) NULL)
+ break;
+ AppendImageToList(&layers,image_b);
+ i++;
+ }
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ if (next != (Image *) NULL)
+ {
+ layers=DestroyImageList(layers);
+ return((Image *) NULL);
+ }
+ return(GetFirstImageInList(layers));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e c o n s t r u c t I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeconstructImages() compares each image with the next in a sequence and
+% returns the minimum bounding region of all differences from the first image.
+%
+% This function is deprecated in favor of the more universal
+% CompareImageLayers() function.
+%
+% The format of the DeconstructImages method is:
+%
+% Image *DeconstructImages(const Image *images, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport Image *DeconstructImages(const Image *images,
+ ExceptionInfo *exception)
+{
+ return(CompareImageLayers(images,CompareAnyLayer,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p t i m i z e L a y e r F r a m e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OptimizeLayerFrames() compares each image the GIF disposed forms of the
+% previous image in the sequence. From this it attempts to select the
+% smallest cropped image to replace each frame, while preserving the results
+% of the animation.
+%
+% Note that this not easy, and may require the expandsion of the bounds
+% of previous frame, to clear pixels for the next animation frame,
+% using GIF Background Dispose method.
+%
+% Currently this only used internally, with external wrappers below.
+%
+% The format of the OptimizeLayerFrames method is:
+%
+% static Image *OptimizeLayerFrames(const Image *image,
+% const ImageLayerMethod method, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o method: the layers type to optimize with. Must be one of...
+% OptimizeImageLayer, or OptimizePlusLayer
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+/*
+ Define a 'fake' dispose method where the frame is duplicated, with a
+ extra zero time delay frame which does a BackgroundDisposal to clear the
+ pixels that need to be cleared.
+*/
+#define DupDispose ((DisposeType)9)
+/*
+ Another 'fake' dispose method used to removed frames that don't change.
+*/
+#define DelDispose ((DisposeType)8)
+
+static Image *OptimizeLayerFrames(const Image *image,
+ const ImageLayerMethod method, ExceptionInfo *exception)
+{
+ ExceptionInfo
+ *sans_exception;
+
+ Image
+ *prev_image,
+ *dup_image,
+ *bgnd_image,
+ *optimized_image;
+
+ RectangleInfo
+ try_bounds,
+ bgnd_bounds,
+ dup_bounds,
+ *bounds;
+
+ MagickBooleanType
+ add_frames,
+ try_cleared,
+ cleared;
+
+ DisposeType
+ *disposals;
+
+ register const Image
+ *next;
+
+ register long
+ i;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert(method == OptimizeLayer ||
+ method == OptimizeImageLayer ||
+ method == OptimizePlusLayer);
+
+ /*
+ Are we allowed to add/remove frames from animation
+ */
+ add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
+ /*
+ Ensure all the images are the same size
+ */
+ next=GetFirstImageInList(image);
+ for (; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ if ((next->columns != image->columns) || (next->rows != image->rows))
+ ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
+ /*
+ FUTURE: also check they are fully coalesced (full page settings)
+ */
+ }
+ /*
+ Allocate memory (times 2 if we allow frame additions)
+ */
+ next=GetFirstImageInList(image);
+ bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
+ GetImageListLength(next),(add_frames != MagickFalse ? 2UL : 1UL)*
+ sizeof(*bounds));
+ if (bounds == (RectangleInfo *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ disposals=(DisposeType *) AcquireQuantumMemory((size_t)
+ GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
+ sizeof(*disposals));
+ if (disposals == (DisposeType *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Initialise Previous Image as fully transparent
+ */
+ prev_image=CloneImage(next,next->page.width,next->page.height,
+ MagickTrue,exception);
+ if (prev_image == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ disposals=(DisposeType *) RelinquishMagickMemory(disposals);
+ return((Image *) NULL);
+ }
+ prev_image->page=next->page; /* ERROR: <-- should not be need, but is! */
+ prev_image->page.x=0;
+ prev_image->page.y=0;
+ prev_image->dispose=NoneDispose;
+
+ prev_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(prev_image);
+ /*
+ Figure out the area of overlay of the first frame
+ No pixel could be cleared as all pixels are already cleared.
+ */
+ disposals[0]=NoneDispose;
+ bounds[0]=CompareImageBounds(prev_image,next,CompareAnyLayer,exception);
+ /*
+ Compute the bounding box of changes for each pair of images.
+ */
+ i=1;
+ bgnd_image=(Image *)NULL;
+ dup_image=(Image *)NULL;
+ dup_bounds.width=0;
+ dup_bounds.height=0;
+ dup_bounds.x=0;
+ dup_bounds.y=0;
+ next=GetNextImageInList(next);
+ for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
+ {
+ /*
+ Assume none disposal is the best
+ */
+ bounds[i]=CompareImageBounds(next->previous,next,CompareAnyLayer,exception);
+ cleared=IsBoundsCleared(next->previous,next,&bounds[i],exception);
+ disposals[i-1]=NoneDispose;
+ if ( bounds[i].x < 0 ) {
+ /*
+ Image frame is exactly the same as the previous frame!
+ If not adding frames leave it to be cropped down to a null image.
+ Otherwise mark previous image for deleted, transfering its crop bounds
+ to the current image.
+ */
+ if ( add_frames && i>=2 ) {
+ disposals[i-1]=DelDispose;
+ disposals[i]=NoneDispose;
+ bounds[i]=bounds[i-1];
+ i++;
+ continue;
+ }
+ }
+ else
+ {
+ /*
+ Compare a none disposal against a previous disposal
+ */
+ try_bounds=CompareImageBounds(prev_image,next,CompareAnyLayer,exception);
+ try_cleared=IsBoundsCleared(prev_image,next,&try_bounds,exception);
+ if ( (!try_cleared && cleared ) ||
+ try_bounds.width * try_bounds.height
+ < bounds[i].width * bounds[i].height )
+ {
+ cleared=try_cleared;
+ bounds[i]=try_bounds;
+ disposals[i-1]=PreviousDispose;
+ }
+
+ /*
+ If we are allowed lets try a complex frame duplication.
+ It is useless if the previous image already clears pixels correctly.
+ This method will always clear all the pixels that need to be cleared.
+ */
+ dup_bounds.width=dup_bounds.height=0;
+ if ( add_frames )
+ {
+ dup_image=CloneImage(next->previous,next->previous->page.width,
+ next->previous->page.height,MagickTrue,exception);
+ if (dup_image == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ disposals=(DisposeType *) RelinquishMagickMemory(disposals);
+ prev_image=DestroyImage(prev_image);
+ return((Image *) NULL);
+ }
+ dup_bounds=CompareImageBounds(dup_image,next,CompareClearLayer,exception);
+ ClearBounds(dup_image,&dup_bounds);
+ try_bounds=CompareImageBounds(dup_image,next,CompareAnyLayer,exception);
+ if ( cleared ||
+ dup_bounds.width*dup_bounds.height
+ +try_bounds.width*try_bounds.height
+ < bounds[i].width * bounds[i].height )
+ {
+ cleared=MagickFalse;
+ bounds[i]=try_bounds;
+ disposals[i-1]=DupDispose;
+ /* to be finalised later, if found to be optimial */
+ }
+ else
+ dup_bounds.width=dup_bounds.height=0;
+ }
+
+ /*
+ Now compare against a simple background disposal
+ */
+ bgnd_image=CloneImage(next->previous,next->previous->page.width,
+ next->previous->page.height,MagickTrue,exception);
+ if (bgnd_image == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ disposals=(DisposeType *) RelinquishMagickMemory(disposals);
+ prev_image=DestroyImage(prev_image);
+ if ( disposals[i-1] == DupDispose )
+ bgnd_image=DestroyImage(bgnd_image);
+ return((Image *) NULL);
+ }
+ bgnd_bounds=bounds[i-1];
+ ClearBounds(bgnd_image,&bgnd_bounds);
+ try_bounds=CompareImageBounds(bgnd_image,next,CompareAnyLayer,exception);
+
+ try_cleared=IsBoundsCleared(bgnd_image,next,&try_bounds,exception);
+ if ( try_cleared )
+ {
+ /*
+ Straight background disposal failed to clear pixels needed!
+ Lets try expanding the disposal area of the previous frame, to
+ include the pixels that are cleared. This guaranteed
+ to work, though may not be the most optimized solution.
+ */
+ try_bounds=CompareImageBounds(prev_image,next,CompareClearLayer,exception);
+ if ( bgnd_bounds.x < 0 )
+ bgnd_bounds = try_bounds;
+ else
+ {
+ if ( try_bounds.x < bgnd_bounds.x )
+ {
+ bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
+ if ( bgnd_bounds.width < try_bounds.width )
+ bgnd_bounds.width = try_bounds.width;
+ bgnd_bounds.x = try_bounds.x;
+ }
+ else
+ {
+ try_bounds.width += try_bounds.x - bgnd_bounds.x;
+ if ( bgnd_bounds.width < try_bounds.width )
+ bgnd_bounds.width = try_bounds.width;
+ }
+ if ( try_bounds.y < bgnd_bounds.y )
+ {
+ bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
+ if ( bgnd_bounds.height < try_bounds.height )
+ bgnd_bounds.height = try_bounds.height;
+ bgnd_bounds.y = try_bounds.y;
+ }
+ else
+ {
+ try_bounds.height += try_bounds.y - bgnd_bounds.y;
+ if ( bgnd_bounds.height < try_bounds.height )
+ bgnd_bounds.height = try_bounds.height;
+ }
+ }
+ ClearBounds(bgnd_image,&bgnd_bounds);
+ try_bounds=CompareImageBounds(bgnd_image,next,CompareAnyLayer,exception);
+ }
+ /*
+ Test if this background dispose is smaller than any of the
+ other methods we tryed before this (including duplicated frame)
+ */
+ if ( cleared ||
+ bgnd_bounds.width*bgnd_bounds.height
+ +try_bounds.width*try_bounds.height
+ < bounds[i-1].width*bounds[i-1].height
+ +dup_bounds.width*dup_bounds.height
+ +bounds[i].width*bounds[i].height )
+ {
+ cleared=MagickFalse;
+ bounds[i-1]=bgnd_bounds;
+ bounds[i]=try_bounds;
+ if ( disposals[i-1] == DupDispose )
+ dup_image=DestroyImage(dup_image);
+ disposals[i-1]=BackgroundDispose;
+ }
+ }
+ /*
+ Finalise choice of dispose, set new prev_image,
+ and junk any extra images as appropriate,
+ */
+ if ( disposals[i-1] == DupDispose )
+ {
+ if (bgnd_image != (Image *) NULL)
+ bgnd_image=DestroyImage(bgnd_image);
+ prev_image=DestroyImage(prev_image);
+ prev_image=dup_image, dup_image=(Image *) NULL;
+ bounds[i+1]=bounds[i];
+ bounds[i]=dup_bounds;
+ disposals[i-1]=DupDispose;
+ disposals[i]=BackgroundDispose;
+ i++;
+ }
+ else
+ {
+ if ( disposals[i-1] != PreviousDispose )
+ prev_image=DestroyImage(prev_image);
+ if ( disposals[i-1] == BackgroundDispose )
+ prev_image=bgnd_image, bgnd_image=(Image *)NULL;
+ else if (bgnd_image != (Image *) NULL)
+ bgnd_image=DestroyImage(bgnd_image);
+ if ( dup_image != (Image *) NULL)
+ dup_image=DestroyImage(dup_image);
+ if ( disposals[i-1] == NoneDispose )
+ {
+ prev_image=CloneImage(next->previous,next->previous->page.width,
+ next->previous->page.height,MagickTrue,exception);
+ if (prev_image == (Image *) NULL)
+ {
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ disposals=(DisposeType *) RelinquishMagickMemory(disposals);
+ return((Image *) NULL);
+ }
+ }
+ }
+ disposals[i]=disposals[i-1];
+ i++;
+ }
+ prev_image=DestroyImage(prev_image);
+ /*
+ Optimize all images in sequence.
+ */
+ sans_exception=AcquireExceptionInfo();
+ i=0;
+ next=GetFirstImageInList(image);
+ optimized_image=NewImageList();
+ while ( next != (const Image *) NULL )
+ {
+#if 0 /* For debuging */
+ printf("image %ld :- %d %ldx%ld%+ld%+ld\n", i, disposals[i],
+ bounds[i].width, bounds[i].height, bounds[i].x, bounds[i].y );
+#endif
+ prev_image=CloneImage(next,0,0,MagickTrue,exception);
+ if (prev_image == (Image *) NULL)
+ break;
+ if ( disposals[i] == DelDispose ) {
+ unsigned long time = 0;
+ while ( disposals[i] == DelDispose ) {
+ time += next->delay*1000/next->ticks_per_second;
+ next=GetNextImageInList(next);
+ i++;
+ }
+ time += next->delay*1000/next->ticks_per_second;
+ prev_image->ticks_per_second = 100L;
+ prev_image->delay = time*prev_image->ticks_per_second/1000;
+ }
+ bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
+ prev_image=DestroyImage(prev_image);
+ if (bgnd_image == (Image *) NULL)
+ break;
+ bgnd_image->dispose=disposals[i];
+ if ( disposals[i] == DupDispose ) {
+ bgnd_image->delay=0;
+ bgnd_image->dispose=NoneDispose;
+ }
+ else
+ next=GetNextImageInList(next);
+ AppendImageToList(&optimized_image,bgnd_image);
+ i++;
+ }
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
+ disposals=(DisposeType *) RelinquishMagickMemory(disposals);
+ if (next != (Image *) NULL)
+ {
+ optimized_image=DestroyImageList(optimized_image);
+ return((Image *) NULL);
+ }
+ return(GetFirstImageInList(optimized_image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p t i m i z e I m a g e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OptimizeImageLayers() compares each image the GIF disposed forms of the
+% previous image in the sequence. From this it attempts to select the
+% smallest cropped image to replace each frame, while preserving the results
+% of the GIF animation.
+%
+% The format of the OptimizeImageLayers method is:
+%
+% Image *OptimizeImageLayers(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *OptimizeImageLayers(const Image *image,
+ ExceptionInfo *exception)
+{
+ return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p t i m i z e P l u s I m a g e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
+% also add or even remove extra frames in the animation, if it improves
+% the total number of pixels in the resulting GIF animation.
+%
+% The format of the OptimizePlusImageLayers method is:
+%
+% Image *OptimizePlusImageLayers(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *OptimizePlusImageLayers(const Image *image,
+ ExceptionInfo *exception)
+{
+ return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p t i m i z e I m a g e T r a n s p a r e n c y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OptimizeImageTransparency() takes a frame optimized GIF animation, and
+% compares the overlayed pixels against the disposal image resulting from all
+% the previous frames in the animation. Any pixel that does not change the
+% disposal image (and thus does not effect the outcome of an overlay) is made
+% transparent.
+%
+% WARNING: This modifies the current images directly, rather than generate
+% a new image sequence.
+%
+% The format of the OptimizeImageTransperency method is:
+%
+% void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void OptimizeImageTransparency(const Image *image,
+ ExceptionInfo *exception)
+{
+ Image
+ *dispose_image;
+
+ register Image
+ *next;
+
+ /*
+ Run the image through the animation sequence
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ next=GetFirstImageInList(image);
+ dispose_image=CloneImage(next,next->page.width,next->page.height,
+ MagickTrue,exception);
+ if (dispose_image == (Image *) NULL)
+ return;
+ dispose_image->page=next->page;
+ dispose_image->page.x=0;
+ dispose_image->page.y=0;
+ dispose_image->dispose=NoneDispose;
+ dispose_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(dispose_image);
+
+ while ( next != (Image *) NULL )
+ {
+ Image
+ *current_image;
+
+ /*
+ Overlay this frame's image over the previous disposal image
+ */
+ current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
+ if (current_image == (Image *) NULL)
+ {
+ dispose_image=DestroyImage(dispose_image);
+ return;
+ }
+ (void) CompositeImage(current_image,next->matte != MagickFalse ?
+ OverCompositeOp : CopyCompositeOp, next,next->page.x,next->page.y);
+ /*
+ At this point the image would be displayed, for the delay period
+ **
+ Work out the disposal of the previous image
+ */
+ if (next->dispose == BackgroundDispose)
+ {
+ RectangleInfo
+ bounds=next->page;
+
+ bounds.width=next->columns;
+ bounds.height=next->rows;
+ if (bounds.x < 0)
+ {
+ bounds.width+=bounds.x;
+ bounds.x=0;
+ }
+ if ((long) (bounds.x+bounds.width) > (long) current_image->columns)
+ bounds.width=current_image->columns-bounds.x;
+ if (bounds.y < 0)
+ {
+ bounds.height+=bounds.y;
+ bounds.y=0;
+ }
+ if ((long) (bounds.y+bounds.height) > (long) current_image->rows)
+ bounds.height=current_image->rows-bounds.y;
+ ClearBounds(current_image, &bounds);
+ }
+ if (next->dispose != PreviousDispose)
+ {
+ dispose_image=DestroyImage(dispose_image);
+ dispose_image=current_image;
+ }
+ else
+ current_image=DestroyImage(current_image);
+
+ /*
+ Optimize Transparency of the next frame (if present)
+ */
+ next=GetNextImageInList(next);
+ if ( next != (Image *) NULL ) {
+ (void) CompositeImage(next, ChangeMaskCompositeOp,
+ dispose_image, -(next->page.x), -(next->page.y) );
+ }
+ }
+ dispose_image=DestroyImage(dispose_image);
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e D u p l i c a t e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveDuplicateLayers() removes any image that is exactly the same as the
+% next image in the given image list. Image size and virtual canvas offset
+% must also match, though not the virtual canvas size itself.
+%
+% No check is made with regards to image disposal setting, though it is the
+% dispose setting of later image that is kept. Also any time delays are also
+% added together. As such coalesced image animations should still produce the
+% same result, though with duplicte frames merged into a single frame.
+%
+% The format of the RemoveDuplicateLayers method is:
+%
+% void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void RemoveDuplicateLayers(Image **images,
+ ExceptionInfo *exception)
+{
+ register Image
+ *curr,
+ *next;
+
+ RectangleInfo
+ bounds;
+
+ assert((*images) != (const Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ curr=GetFirstImageInList(*images);
+ for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
+ {
+ if ( curr->columns != next->columns || curr->rows != next->rows
+ || curr->page.x != next->page.x || curr->page.y != next->page.y )
+ continue;
+ bounds=CompareImageBounds(curr,next,CompareAnyLayer,exception);
+ if ( bounds.x < 0 ) {
+ /*
+ the two images are the same, merge time delays and delete one.
+ */
+ unsigned long time;
+ time = curr->delay*1000/curr->ticks_per_second;
+ time += next->delay*1000/next->ticks_per_second;
+ next->ticks_per_second = 100L;
+ next->delay = time*curr->ticks_per_second/1000;
+ next->iterations = curr->iterations;
+ *images = curr;
+ (void) DeleteImageFromList(images);
+ }
+ }
+ *images = GetFirstImageInList(*images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e Z e r o D e l a y L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
+% images generally represent intermediate or partial updates in GIF
+% animations used for file optimization. They are not ment to be displayed
+% to users of the animation. Viewable images in an animation should have a
+% time delay of 3 or more centi-seconds (hundredths of a second).
+%
+% However if all the frames have a zero time delay, then either the animation
+% is as yet incomplete, or it is not a GIF animation. This a non-sensible
+% situation, so no image will be removed and a 'Zero Time Animation' warning
+% (exception) given.
+%
+% No warning will be given if no image was removed because all images had an
+% appropriate non-zero time delay set.
+%
+% Due to the special requirements of GIF disposal handling, GIF animations
+% should be coalesced first, before calling this function, though that is not
+% a requirement.
+%
+% The format of the RemoveZeroDelayLayers method is:
+%
+% void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void RemoveZeroDelayLayers(Image **images,
+ ExceptionInfo *exception)
+{
+ Image
+ *i;
+
+ assert((*images) != (const Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ i=GetFirstImageInList(*images);
+ for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
+ if ( i->delay != 0L ) break;
+ if ( i == (Image *) NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
+ return;
+ }
+ i=GetFirstImageInList(*images);
+ while ( i != (Image *) NULL )
+ {
+ if ( i->delay == 0L ) {
+ (void) DeleteImageFromList(&i);
+ *images=i;
+ }
+ else
+ i=GetNextImageInList(i);
+ }
+ *images=GetFirstImageInList(*images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p o s i t e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompositeLayers() compose first image sequence (source) over the second
+% image sequence (destination), using the given compose method and offsets.
+%
+% The pointers to the image list does not have to be the start of that image
+% list, but may start somewhere in the middle. Each layer from the two image
+% lists are composted together until the end of one of the image lists is
+% reached. The offset of each composition is also adjusted to match the
+% virtual canvas offsets of each layer. As such the given offset is relative
+% to the virtual canvas, and not the actual image.
+%
+% No GIF disposal handling is performed, so GIF animations should be
+% coalesced before use. However this not a requirement, and individual
+% layer images may have any size or offset, for special compositions.
+%
+% Special case:- If one of the image sequences is just a single image that
+% image is repeatally composed with all the images in the other image list.
+% Either the source or destination lists may be the single image, for this
+% situation.
+%
+% The destination list will be expanded as needed to match number of source
+% image overlaid (from current position to end of list).
+%
+% The format of the CompositeLayers method is:
+%
+% void CompositeLayers(Image *destination,
+% const CompositeOperator compose, Image *source,
+% const long x_offset, const long y_offset,
+% ExceptionInfo *exception);
+%
+% A description of each parameter follows:
+%
+% o destination: the destination images and results
+%
+% o source: source image(s) for the layer composition
+%
+% o compose, x_offset, y_offset: arguments passed on to CompositeImages()
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static inline void CompositeCanvas(Image *destination,
+ const CompositeOperator compose, Image *source,
+ long x_offset, long y_offset )
+{
+ x_offset += source->page.x - destination->page.x;
+ y_offset += source->page.y - destination->page.y;
+ (void) CompositeImage(destination, compose, source, x_offset, y_offset);
+}
+
+MagickExport void CompositeLayers(Image *destination,
+ const CompositeOperator compose, Image *source,
+ const long x_offset, const long y_offset,
+ ExceptionInfo *exception)
+{
+ assert(destination != (Image *) NULL);
+ assert(destination->signature == MagickSignature);
+ assert(source != (Image *) NULL);
+ assert(source->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (source->debug != MagickFalse || destination->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
+ source->filename, destination->filename);
+
+ /*
+ Overlay single source image over destation image/list
+ */
+ if ( source->previous == (Image *) NULL && source->next == (Image *) NULL )
+ while ( destination != (Image *) NULL )
+ {
+ CompositeCanvas(destination, compose, source, x_offset, y_offset);
+ destination=GetNextImageInList(destination);
+ }
+
+ /*
+ Overlay source image list over single destination
+ Generating multiple clones of destination image to match source list.
+ Original Destination image becomes first image of generated list.
+ As such the image list pointer does not require any change in caller.
+ Some animation attributes however also needs coping in this case.
+ */
+ else if ( destination->previous == (Image *) NULL &&
+ destination->next == (Image *) NULL )
+ {
+ Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
+
+ CompositeCanvas(destination, compose, source, x_offset, y_offset);
+ /* copy source image attributes ? */
+ source=GetNextImageInList(source);
+
+ while ( source != (Image *) NULL )
+ {
+ AppendImageToList(&destination,
+ CloneImage(dest,0,0,MagickTrue,exception));
+ destination=GetLastImageInList(destination);
+
+ CompositeCanvas(destination, compose, source, x_offset, y_offset);
+ destination->delay = source->delay;
+ destination->iterations = source->iterations;
+ source=GetNextImageInList(source);
+ }
+ dest=DestroyImage(dest);
+ }
+
+ /*
+ Overlay a source image list over a destination image list
+ until either list runs out of images. (Does not repeat)
+ */
+ else
+ while ( source != (Image *) NULL && destination != (Image *) NULL )
+ {
+ CompositeCanvas(destination, compose, source, x_offset, y_offset);
+ source=GetNextImageInList(source);
+ destination=GetNextImageInList(destination);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M e r g e I m a g e L a y e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MergeImageLayers() composes all the image layers from the current given
+% image onward to produce a single image of the merged layers.
+%
+% The inital canvas's size depends on the given ImageLayerMethod, and is
+% initialized using the first images background color. The images
+% are then compositied onto that image in sequence using the given
+% composition that has been assigned to each individual image.
+%
+% The format of the MergeImageLayers is:
+%
+% Image *MergeImageLayers(const Image *image,
+% const ImageLayerMethod method, ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image list to be composited together
+%
+% o method: the method of selecting the size of the initial canvas.
+%
+% MergeLayer: Merge all layers onto a canvas just large enough
+% to hold all the actual images. The virtual canvas of the
+% first image is preserved but otherwise ignored.
+%
+% FlattenLayer: Use the virtual canvas size of first image.
+% Images which fall outside this canvas is clipped.
+% This can be used to 'fill out' a given virtual canvas.
+%
+% MosaicLayer: Start with the virtual canvas of the first image,
+% enlarging left and right edges to contain all images.
+% Images with negative offsets will be clipped.
+%
+% TrimBoundsLayer: Determine the overall bounds of all the image
+% layers just as in "MergeLayer", then adjust the the canvas
+% and offsets to be relative to those bounds, without overlaying
+% the images.
+%
+% WARNING: a new image is not returned, the original image
+% sequence page data is modified instead.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MergeImageLayers(Image *image,
+ const ImageLayerMethod method,ExceptionInfo *exception)
+{
+#define MergeLayersTag "Merge/Layers"
+
+ Image
+ *canvas;
+
+ MagickBooleanType
+ proceed;
+
+ MagickOffsetType
+ scene;
+
+ RectangleInfo
+ page;
+
+ unsigned long
+ width,
+ height;
+
+ register const Image
+ *next;
+
+ unsigned long
+ number_images;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ /*
+ Determine canvas image size, and its virtual canvas size and offset
+ */
+ page=image->page;
+ width=image->columns;
+ height=image->rows;
+ switch (method)
+ {
+ case TrimBoundsLayer:
+ case MergeLayer:
+ default:
+ {
+ next = GetNextImageInList(image);
+ for ( ; next != (Image *) NULL; next=GetNextImageInList(next)) {
+ if ( page.x > next->page.x ) {
+ width += page.x-next->page.x;
+ page.x = next->page.x;
+ }
+ if ( page.y > next->page.y ) {
+ height += page.y-next->page.y;
+ page.y = next->page.y;
+ }
+ if ( width < (next->page.x + next->columns - page.x) )
+ width = (unsigned long) next->page.x + next->columns - page.x;
+ if ( height < (next->page.y + next->rows - page.y) )
+ height = (unsigned long) next->page.y + next->rows - page.y;
+ }
+ break;
+ }
+ case FlattenLayer:
+ {
+ if ( page.width > 0 )
+ width=page.width;
+ if ( page.height > 0 )
+ height=page.height;
+ page.x=0;
+ page.y=0;
+ break;
+ }
+ case MosaicLayer:
+ {
+ if ( page.width > 0 )
+ width=page.width;
+ if ( page.height > 0 )
+ height=page.height;
+ for (next=image; next != (Image *) NULL; next=GetNextImageInList(next)) {
+ if (method == MosaicLayer) {
+ page.x=next->page.x;
+ page.y=next->page.y;
+ if ( width < (next->page.x + next->columns) )
+ width = (unsigned long) next->page.x + next->columns;
+ if ( height < (next->page.y + next->rows) )
+ height = (unsigned long) next->page.y + next->rows;
+ }
+ }
+ page.width=width;
+ page.height=height;
+ page.x=0;
+ page.y=0;
+ }
+ break;
+ }
+ /* set virtual canvas size if not defined */
+ if ( page.width == 0 )
+ page.width = (page.x < 0) ? width : width+page.x;
+ if ( page.height == 0 )
+ page.height = (page.y < 0) ? height : height+page.y;
+
+ /*
+ Handle "TrimBoundsLayer" method seperatally to normal 'layer merge'
+ */
+ if ( method == TrimBoundsLayer ) {
+ number_images=GetImageListLength(image);
+ for (scene=0; scene < (long) number_images; scene++)
+ {
+ image->page.x -= page.x;
+ image->page.y -= page.y;
+ image->page.width = width;
+ image->page.height = height;
+ proceed=SetImageProgress(image,MergeLayersTag,scene,number_images);
+ if (proceed == MagickFalse)
+ break;
+ image=GetNextImageInList(image);
+ }
+ return((Image *) NULL);
+ }
+
+ /*
+ Create canvas size of width and height, and background color.
+ */
+ canvas=CloneImage(image,width,height,MagickTrue,exception);
+ if (canvas == (Image *) NULL)
+ return((Image *) NULL);
+ (void) SetImageBackgroundColor(canvas);
+ canvas->page=page;
+ canvas->dispose=UndefinedDispose;
+
+ /*
+ Compose images onto canvas, with progress monitor
+ */
+ number_images=GetImageListLength(image);
+ for (scene=0; scene < (long) number_images; scene++)
+ {
+ (void) CompositeImage(canvas,image->compose,image,image->page.x-
+ canvas->page.x,image->page.y-canvas->page.y);
+ proceed=SetImageProgress(image,MergeLayersTag,scene,number_images);
+ if (proceed == MagickFalse)
+ break;
+ image=GetNextImageInList(image);
+ }
+ return(canvas);
+}
+
diff --git a/magick/layer.h b/magick/layer.h
new file mode 100644
index 0000000..39c8cd2
--- /dev/null
+++ b/magick/layer.h
@@ -0,0 +1,75 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image layer methods.
+*/
+#ifndef _MAGICKCORE_LAYER_H
+#define _MAGICKCORE_LAYER_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UnrecognizedDispose,
+ UndefinedDispose = 0,
+ NoneDispose = 1,
+ BackgroundDispose = 2,
+ PreviousDispose = 3
+} DisposeType;
+
+typedef enum
+{
+ UndefinedLayer,
+ CoalesceLayer,
+ CompareAnyLayer,
+ CompareClearLayer,
+ CompareOverlayLayer,
+ DisposeLayer,
+ OptimizeLayer,
+ OptimizeImageLayer,
+ OptimizePlusLayer,
+ OptimizeTransLayer,
+ RemoveDupsLayer,
+ RemoveZeroLayer,
+ CompositeLayer,
+ MergeLayer,
+ FlattenLayer,
+ MosaicLayer,
+ TrimBoundsLayer
+} ImageLayerMethod;
+
+extern MagickExport Image
+ *CoalesceImages(const Image *,ExceptionInfo *),
+ *DisposeImages(const Image *,ExceptionInfo *),
+ *CompareImageLayers(const Image *,const ImageLayerMethod,ExceptionInfo *),
+ *DeconstructImages(const Image *,ExceptionInfo *),
+ *MergeImageLayers(Image *,const ImageLayerMethod,ExceptionInfo *),
+ *OptimizeImageLayers(const Image *,ExceptionInfo *),
+ *OptimizePlusImageLayers(const Image *,ExceptionInfo *);
+
+extern MagickExport void
+ CompositeLayers(Image *,const CompositeOperator,Image *,const long,const long,
+ ExceptionInfo *),
+ OptimizeImageTransparency(const Image *,ExceptionInfo *),
+ RemoveDuplicateLayers(Image **,ExceptionInfo *),
+ RemoveZeroDelayLayers(Image **,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/list.c b/magick/list.c
new file mode 100644
index 0000000..f0b9898
--- /dev/null
+++ b/magick/list.c
@@ -0,0 +1,1295 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L IIIII SSSSS TTTTT %
+% L I SS T %
+% L I SSS T %
+% L I SS T %
+% LLLLL IIIII SSSSS T %
+% %
+% %
+% MagickCore Image List Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/string_.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A p p e n d I m a g e T o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AppendImageToList() appends the second image list to the end of the first
+% list. The given image list pointer is left unchanged, unless it was empty.
+%
+% The format of the AppendImageToList method is:
+%
+% AppendImageToList(Image *images,const Image *image)
+%
+% A description of each parameter follows:
+%
+% o images: the image list to be appended to.
+%
+% o image: the appended image or image list.
+%
+*/
+MagickExport void AppendImageToList(Image **images,const Image *image)
+{
+ register Image
+ *p,
+ *q;
+
+ assert(images != (Image **) NULL);
+ if (image == (Image *) NULL)
+ return;
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((*images) == (Image *) NULL)
+ {
+ *images=(Image *) image;
+ return;
+ }
+ assert((*images)->signature == MagickSignature);
+ p=GetLastImageInList(*images);
+ q=GetFirstImageInList(image);
+ p->next=q;
+ q->previous=p;
+ SyncImageList(*images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageList() returns a duplicate of the image list.
+%
+% The format of the CloneImageList method is:
+%
+% Image *CloneImageList(const Image *images,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CloneImageList(const Image *images,ExceptionInfo *exception)
+{
+ Image
+ *clone,
+ *image;
+
+ register Image
+ *p;
+
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ while (images->previous != (Image *) NULL)
+ images=images->previous;
+ image=(Image *) NULL;
+ for (p=(Image *) NULL; images != (Image *) NULL; images=images->next)
+ {
+ clone=CloneImage(images,0,0,MagickTrue,exception);
+ if (clone == (Image *) NULL)
+ {
+ if (image != (Image *) NULL)
+ image=DestroyImageList(image);
+ return((Image *) NULL);
+ }
+ if (image == (Image *) NULL)
+ {
+ image=clone;
+ p=image;
+ continue;
+ }
+ p->next=clone;
+ clone->previous=p;
+ p=p->next;
+ }
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImages() clones one or more images from an image sequence, using a
+% comma separated list of image numbers or ranges.
+%
+% The numbers start at 0 for the first image in the list, while negative
+% numbers refer to images starting counting from the end of the range. Images
+% may be refered to multiple times to clone them multiple times. Images
+% refered beyond the available number of images in list are ignored.
+%
+% Images referenced may be reversed, and results in a clone of those images
+% also being made with a reversed order.
+%
+% The format of the CloneImages method is:
+%
+% Image *CloneImages(const Image *images,const char *scenes,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image sequence.
+%
+% o scenes: This character string specifies which scenes to clone
+% (e.g. 1,3-5,7-3,2).
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CloneImages(const Image *images,const char *scenes,
+ ExceptionInfo *exception)
+{
+ char
+ *p;
+
+ const Image
+ *next;
+
+ Image
+ *clone_images,
+ *image;
+
+ long
+ first,
+ last,
+ step;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ assert(images != (const Image *) NULL);
+ assert(images->signature == MagickSignature);
+ assert(scenes != (char *) NULL);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ clone_images=NewImageList();
+ images=GetFirstImageInList(images);
+ length=GetImageListLength(images);
+ for (p=(char *) scenes; *p != '\0';)
+ {
+ while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ','))
+ p++;
+ first=strtol(p,&p,10);
+ if (first < 0)
+ first+=(long) length;
+ last=first;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if (*p == '-')
+ {
+ last=strtol(p+1,&p,10);
+ if (last < 0)
+ last+=(long) length;
+ }
+ for (step=first > last ? -1 : 1; first != (last+step); first+=step)
+ {
+ i=0;
+ for (next=images; next != (Image *) NULL; next=GetNextImageInList(next))
+ {
+ if (i == first)
+ {
+ image=CloneImage(next,0,0,MagickTrue,exception);
+ if (image == (Image *) NULL)
+ break;
+ AppendImageToList(&clone_images,image);
+ }
+ i++;
+ }
+ }
+ }
+ return(GetFirstImageInList(clone_images));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e F r o m L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageFromList() deletes an image from the list. List pointer
+% is moved to the next image, if one is present. See RemoveImageFromList().
+%
+% The format of the DeleteImageFromList method is:
+%
+% DeleteImageFromList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport void DeleteImageFromList(Image **images)
+{
+ Image
+ *image;
+
+ image=RemoveImageFromList(images);
+ if (image != (Image *) NULL)
+ (void) DestroyImage(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImages() deletes one or more images from an image sequence, using a
+% comma separated list of image numbers or ranges.
+%
+% The numbers start at 0 for the first image, while negative numbers refer to
+% images starting counting from the end of the range. Images may be refered to
+% multiple times without problems. Image refered beyond the available number
+% of images in list are ignored.
+%
+% If the referenced images are in the reverse order, that range will be
+% completely ignored. Unlike CloneImages().
+%
+% The format of the DeleteImages method is:
+%
+% DeleteImages(Image **images,const char *scenes,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o images: the image sequence.
+%
+% o scenes: This character string specifies which scenes to delete
+% (e.g. 1,3-5,-2-6,2).
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void DeleteImages(Image **images,const char *scenes,
+ ExceptionInfo *exception)
+{
+ char
+ *p;
+
+ Image
+ *image;
+
+ long
+ first,
+ last;
+
+ MagickBooleanType
+ *delete_list;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ assert(images != (Image **) NULL);
+ assert((*images)->signature == MagickSignature);
+ assert(scenes != (char *) NULL);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ *images=GetFirstImageInList(*images);
+ length=GetImageListLength(*images);
+ delete_list=(MagickBooleanType *) AcquireQuantumMemory(length,
+ sizeof(*delete_list));
+ if (delete_list == (MagickBooleanType *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",(*images)->filename);
+ return;
+ }
+ image=(*images);
+ for (i=0; i < (long) length; i++)
+ delete_list[i]=MagickFalse;
+ /*
+ Note which images will be deleted, avoid duplicate deleted
+ */
+ for (p=(char *) scenes; *p != '\0';)
+ {
+ while ((isspace((int)*p) != 0) || (*p == ','))
+ p++;
+ first=strtol(p,&p,10);
+ if (first < 0)
+ first+=(long) length;
+ last=first;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if (*p == '-')
+ {
+ last=strtol(p+1,&p,10);
+ if (last < 0)
+ last+=(long) length;
+ }
+ if (first > last)
+ continue;
+ for (i=first; i <= last; i++)
+ if ((i >= 0) && (i < (long) length))
+ delete_list[i]=MagickTrue;
+ }
+ /*
+ Delete images marked for deletion, once only
+ */
+ image=(*images);
+ for (i=0; i < (long) length; i++)
+ {
+ *images=image;
+ image=GetNextImageInList(image);
+ if (delete_list[i] != MagickFalse)
+ DeleteImageFromList(images);
+
+ }
+ (void) RelinquishMagickMemory(delete_list);
+ *images=GetFirstImageInList(*images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageList() destroys an image list.
+%
+% The format of the DestroyImageList method is:
+%
+% Image *DestroyImageList(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+*/
+MagickExport Image *DestroyImageList(Image *images)
+{
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ while (images != (Image *) NULL)
+ DeleteImageFromList(&images);
+ return((Image *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t F i r s t I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetFirstImageInList() returns a pointer to the first image in the list.
+%
+% The format of the GetFirstImageInList method is:
+%
+% Image *GetFirstImageInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetFirstImageInList(const Image *images)
+{
+ register const Image
+ *p;
+
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ for (p=images; p->previous != (Image *) NULL; p=p->previous) ;
+ return((Image *) p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e F r o m L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageFromList() returns an image at the specified offset from the list.
+%
+% The format of the GetImageFromList method is:
+%
+% Image *GetImageFromList(const Image *images,const long index)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o index: the position within the list.
+%
+*/
+MagickExport Image *GetImageFromList(const Image *images,const long index)
+{
+ long
+ offset;
+
+ register const Image
+ *p;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ for (p=images; p->previous != (Image *) NULL; p=p->previous) ;
+ length=GetImageListLength(images);
+ for (offset=index; offset < 0; offset+=(long) length) ;
+ for (i=0; p != (Image *) NULL; p=p->next)
+ if (i++ == (long) (offset % length))
+ break;
+ if (p == (Image *) NULL)
+ return((Image *) NULL);
+ return((Image *) p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e I n d e x I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageIndexInList() returns the offset in the list of the specified image.
+%
+% The format of the GetImageIndexInList method is:
+%
+% long GetImageIndexInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport long GetImageIndexInList(const Image *images)
+{
+ register long
+ i;
+
+ if (images == (const Image *) NULL)
+ return(-1);
+ assert(images->signature == MagickSignature);
+ for (i=0; images->previous != (Image *) NULL; i++)
+ images=images->previous;
+ return(i);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e L i s t L e n g t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageListLength() returns the length of the list (the number of images in
+% the list).
+%
+% The format of the GetImageListLength method is:
+%
+% unsigned long GetImageListLength(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport unsigned long GetImageListLength(const Image *images)
+{
+ register long
+ i;
+
+ if (images == (Image *) NULL)
+ return(0);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ while (images->previous != (Image *) NULL)
+ images=images->previous;
+ for (i=0; images != (Image *) NULL; images=images->next)
+ i++;
+ return((unsigned long) i);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L a s t I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLastImageInList() returns a pointer to the last image in the list.
+%
+% The format of the GetLastImageInList method is:
+%
+% Image *GetLastImageInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetLastImageInList(const Image *images)
+{
+ register const Image
+ *p;
+
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ for (p=images; p->next != (Image *) NULL; p=p->next) ;
+ return((Image *) p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageInList() returns the next image in the list.
+%
+% The format of the GetNextImageInList method is:
+%
+% Image *GetNextImageInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetNextImageInList(const Image *images)
+{
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ return(images->next);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P r e v i o u s I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPreviousImageInList() returns the previous image in the list.
+%
+% The format of the GetPreviousImageInList method is:
+%
+% Image *GetPreviousImageInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *GetPreviousImageInList(const Image *images)
+{
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ return(images->previous);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% I m a g e L i s t T o A r r a y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImageListToArray() is a convenience method that converts an image list to
+% a sequential array. For example,
+%
+% group = ImageListToArray(images, exception);
+% while (i = 0; group[i] != (Image *) NULL; i++)
+% printf("%s\n", group[i]->filename);
+% printf("%d images\n", i);
+% group = RelinquishMagickMemory(group);
+%
+% The format of the ImageListToArray method is:
+%
+% Image **ImageListToArray(const Image *images,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image **ImageListToArray(const Image *images,
+ ExceptionInfo *exception)
+{
+ Image
+ **group;
+
+ register long
+ i;
+
+ if (images == (Image *) NULL)
+ return((Image **) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ group=(Image **) AcquireQuantumMemory((size_t) GetImageListLength(images)+1UL,
+ sizeof(*group));
+ if (group == (Image **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ return((Image **) NULL);
+ }
+ images=GetFirstImageInList(images);
+ for (i=0; images != (Image *) NULL; images=images->next)
+ group[i++]=(Image *) images;
+ group[i]=(Image *) NULL;
+ return(group);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n s e r t I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InsertImageInList() inserts the second image or image list into the first
+% image list immediatally after the image pointed to. The given image list
+% pointer is unchanged unless previously empty.
+%
+% The format of the InsertImageInList method is:
+%
+% InsertImageInList(Image **images,Image *image)
+%
+% A description of each parameter follows:
+%
+% o images: the image list to insert into.
+%
+% o image: the image list to insert.
+%
+*/
+MagickExport void InsertImageInList(Image **images,Image *image)
+{
+ Image
+ *split;
+
+ assert(images != (Image **) NULL);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((*images) == (Image *) NULL)
+ return;
+ assert((*images)->signature == MagickSignature);
+ split=SplitImageList(*images);
+ AppendImageToList(images,image);
+ AppendImageToList(images,split);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewImageList() creates an empty image list.
+%
+% The format of the NewImageList method is:
+%
+% Image *NewImageList(void)
+%
+*/
+MagickExport Image *NewImageList(void)
+{
+ return((Image *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r e p e n d I m a g e T o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PrependImageToList() prepends the image to the beginning of the list.
+%
+% The format of the PrependImageToList method is:
+%
+% PrependImageToList(Image *images,Image *image)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o image: the image.
+%
+*/
+MagickExport void PrependImageToList(Image **images,Image *image)
+{
+ AppendImageToList(&image,*images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e F r o m L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageFromList() removes and returns the image pointed to.
+%
+% The given image list pointer is set to point to the next image in list
+% if it exists, otherwise it is set to the previous image, or NULL if list
+% was emptied.
+%
+% The format of the RemoveImageFromList method is:
+%
+% Image *RemoveImageFromList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *RemoveImageFromList(Image **images)
+{
+ register Image
+ *p;
+
+ assert(images != (Image **) NULL);
+ if ((*images) == (Image *) NULL)
+ return((Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ p=(*images);
+ if ((p->previous == (Image *) NULL) && (p->next == (Image *) NULL))
+ *images=(Image *) NULL;
+ else
+ {
+ if (p->previous != (Image *) NULL)
+ {
+ p->previous->next=p->next;
+ *images=p->previous;
+ }
+ if (p->next != (Image *) NULL)
+ {
+ p->next->previous=p->previous;
+ *images=p->next;
+ }
+ p->previous=(Image *) NULL;
+ p->next=(Image *) NULL;
+ }
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e F i r s t I m a g e F r o m L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveFirstImageFromList() removes and returns the first image in the list.
+%
+% If the given image list pointer pointed to the removed first image, it is
+% set to the new first image of list, or NULL if list was emptied, otherwise
+% it is left as is.
+%
+% The format of the RemoveFirstImageFromList method is:
+%
+% Image *RemoveFirstImageFromList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *RemoveFirstImageFromList(Image **images)
+{
+ Image
+ *image;
+
+ assert(images != (Image **) NULL);
+ if ((*images) == (Image *) NULL)
+ return((Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ image=(*images);
+ while (image->previous != (Image *) NULL)
+ image=image->previous;
+ if (image == *images)
+ *images=(*images)->next;
+ if (image->next != (Image *) NULL)
+ {
+ image->next->previous=(Image *) NULL;
+ image->next=(Image *) NULL;
+ }
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e L a s t I m a g e F r o m L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveLastImageFromList() removes and returns the last image from the list.
+%
+% If the given image list pointer pointed to the removed last image, it is
+% set to the new last image of list, or NULL if list was emptied, otherwise
+% it is left as is.
+%
+% The format of the RemoveLastImageFromList method is:
+%
+% Image *RemoveLastImageFromList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *RemoveLastImageFromList(Image **images)
+{
+ Image
+ *image;
+
+ assert(images != (Image **) NULL);
+ if ((*images) == (Image *) NULL)
+ return((Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ image=(*images);
+ while (image->next != (Image *) NULL)
+ image=image->next;
+ if (image == *images)
+ *images=(*images)->previous;
+ if (image->previous != (Image *) NULL)
+ {
+ image->previous->next=(Image *) NULL;
+ image->previous=(Image *) NULL;
+ }
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e p l a c e I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReplaceImageInList() replaces an image in the list. Old image is destroyed.
+% The given image list pointer is set to point to the just inserted image.
+%
+% The format of the ReplaceImageInList method is:
+%
+% ReplaceImageInList(Image **images,Image *image)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o image: the image.
+%
+*/
+MagickExport void ReplaceImageInList(Image **images,Image *image)
+{
+ assert(images != (Image **) NULL);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((*images) == (Image *) NULL)
+ return;
+ assert((*images)->signature == MagickSignature);
+ image->next=(*images)->next;
+ if (image->next != (Image *) NULL)
+ image->next->previous=image;
+ image->previous=(*images)->previous;
+ if (image->previous != (Image *) NULL)
+ image->previous->next=image;
+ (void) DestroyImage(*images);
+ (*images)=image;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e v e r s e I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReverseImageList() reverses the order of an image list.
+% The list pointer is reset to that start of the re-ordered list.
+%
+% The format of the ReverseImageList method is:
+%
+% void ReverseImageList(Image **images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport void ReverseImageList(Image **images)
+{
+ Image
+ *next;
+
+ register Image
+ *p;
+
+ assert(images != (Image **) NULL);
+ if ((*images) == (Image *) NULL)
+ return;
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ for (p=(*images); p->next != (Image *) NULL; p=p->next) ;
+ *images=p;
+ for ( ; p != (Image *) NULL; p=p->next)
+ {
+ next=p->next;
+ p->next=p->previous;
+ p->previous=next;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l i c e I m a g e I n t o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SpliceImageIntoList() removes 'length' images from the list and replaces
+% them with the specified splice. Removed images are returned.
+%
+% The format of the SpliceImageIntoList method is:
+%
+% SpliceImageIntoList(Image **images,const unsigned long,
+% const Image *splice)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+% o length: the length of the image list to remove.
+%
+% o splice: Replace the removed image list with this list.
+%
+*/
+MagickExport Image *SpliceImageIntoList(Image **images,
+ const unsigned long length,const Image *splice)
+{
+ Image
+ *image,
+ *split;
+
+ register unsigned long
+ i;
+
+ assert(images != (Image **) NULL);
+ assert(splice != (Image *) NULL);
+ assert(splice->signature == MagickSignature);
+ if ((*images) == (Image *) NULL)
+ return((Image *) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ split=SplitImageList(*images);
+ AppendImageToList(images,splice);
+ image=(Image *) NULL;
+ for (i=0; (i < length) && (split != (Image *) NULL); i++)
+ AppendImageToList(&image,RemoveImageFromList(&split));
+ AppendImageToList(images,split);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l i t I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SplitImageList() splits an image into two lists, after given image
+% The list that was split off is returned, which may be empty.
+%
+% The format of the SplitImageList method is:
+%
+% Image *SplitImageList(Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *SplitImageList(Image *images)
+{
+ if ((images == (Image *) NULL) || (images->next == (Image *) NULL))
+ return((Image *) NULL);
+ images=images->next;
+ images->previous->next=(Image *) NULL;
+ images->previous=(Image *) NULL;
+ return(images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncImageList() synchronizes the scene numbers in an image list.
+%
+% The format of the SyncImageList method is:
+%
+% void SyncImageList(Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport void SyncImageList(Image *images)
+{
+ register Image
+ *p,
+ *q;
+
+ if (images == (Image *) NULL)
+ return;
+ assert(images->signature == MagickSignature);
+ for (p=images; p != (Image *) NULL; p=p->next)
+ {
+ for (q=p->next; q != (Image *) NULL; q=q->next)
+ if (p->scene == q->scene)
+ break;
+ if (q != (Image *) NULL)
+ break;
+ }
+ if (p == (Image *) NULL)
+ return;
+ for (p=images->next; p != (Image *) NULL; p=p->next)
+ p->scene=p->previous->scene+1;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c N e x t I m a g e I n L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncNextImageInList() returns the next image in the list after the blob
+% referenced is synchronized with the current image.
+%
+% The format of the SyncNextImageInList method is:
+%
+% Image *SyncNextImageInList(const Image *images)
+%
+% A description of each parameter follows:
+%
+% o images: the image list.
+%
+*/
+MagickExport Image *SyncNextImageInList(const Image *images)
+{
+ if (images == (Image *) NULL)
+ return((Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->next == (Image *) NULL)
+ return((Image *) NULL);
+ if (images->blob != images->next->blob)
+ {
+ DestroyBlob(images->next);
+ images->next->blob=ReferenceBlob(images->blob);
+ }
+ images->next->endian=images->endian;
+ return(images->next);
+}
diff --git a/magick/list.h b/magick/list.h
new file mode 100644
index 0000000..69550d6
--- /dev/null
+++ b/magick/list.h
@@ -0,0 +1,63 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image list methods.
+*/
+#ifndef _MAGICKCORE_LIST_H
+#define _MAGICKCORE_LIST_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport Image
+ *CloneImageList(const Image *,ExceptionInfo *),
+ *CloneImages(const Image *,const char *,ExceptionInfo *),
+ *DestroyImageList(Image *),
+ *GetFirstImageInList(const Image *),
+ *GetImageFromList(const Image *,const long),
+ *GetLastImageInList(const Image *),
+ *GetNextImageInList(const Image *),
+ *GetPreviousImageInList(const Image *),
+ **ImageListToArray(const Image *,ExceptionInfo *),
+ *NewImageList(void),
+ *RemoveImageFromList(Image **),
+ *RemoveLastImageFromList(Image **),
+ *RemoveFirstImageFromList(Image **),
+ *SpliceImageIntoList(Image **,const unsigned long,const Image *),
+ *SplitImageList(Image *),
+ *SyncNextImageInList(const Image *);
+
+extern MagickExport long
+ GetImageIndexInList(const Image *);
+
+extern MagickExport unsigned long
+ GetImageListLength(const Image *);
+
+extern MagickExport void
+ AppendImageToList(Image **,const Image *),
+ DeleteImageFromList(Image **),
+ DeleteImages(Image **,const char *,ExceptionInfo *),
+ InsertImageInList(Image **,Image *),
+ PrependImageToList(Image **,Image *),
+ ReplaceImageInList(Image **,Image *),
+ ReverseImageList(Image **),
+ SyncImageList(Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/locale.c b/magick/locale.c
new file mode 100644
index 0000000..cacf4e7
--- /dev/null
+++ b/magick/locale.c
@@ -0,0 +1,1057 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L OOO CCCC AAA L EEEEE %
+% L O O C A A L E %
+% L O O C AAAAA L EEE %
+% L O O C A A L E %
+% LLLLL OOO CCCC A A LLLLL EEEEE %
+% %
+% %
+% MagickCore Image Locale Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define LocaleFilename "locale.xml"
+#define MaxRecursionDepth 200
+
+/*
+ Static declarations.
+*/
+static const char
+ *LocaleMap =
+ "<?xml version=\"1.0\"?>"
+ "<localemap>"
+ " <locale name=\"C\">"
+ " <Exception>"
+ " <Message name=\"\">"
+ " </Message>"
+ " </Exception>"
+ " </locale>"
+ "</localemap>";
+
+static SemaphoreInfo
+ *locale_semaphore = (SemaphoreInfo *) NULL;
+
+static SplayTreeInfo
+ *locale_list = (SplayTreeInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_locale = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeLocaleList(ExceptionInfo *),
+ LoadLocaleLists(const char *,const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y L o c a l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyLocaleList() deallocates memory associated with the locale list.
+%
+% The format of the DestroyLocaleList method is:
+%
+% DestroyLocaleList(void)
+%
+*/
+MagickExport void DestroyLocaleList(void)
+{
+ AcquireSemaphoreInfo(&locale_semaphore);
+ if (locale_list != (SplayTreeInfo *) NULL)
+ locale_list=DestroySplayTree(locale_list);
+ instantiate_locale=MagickFalse;
+ RelinquishSemaphoreInfo(locale_semaphore);
+ DestroySemaphoreInfo(&locale_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y L o c a l e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyLocaleOptions() releases memory associated with an locale
+% messages.
+%
+% The format of the DestroyProfiles method is:
+%
+% LinkedListInfo *DestroyLocaleOptions(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static void *DestroyOptions(void *message)
+{
+ return(DestroyStringInfo((StringInfo *) message));
+}
+
+MagickExport LinkedListInfo *DestroyLocaleOptions(LinkedListInfo *messages)
+{
+ assert(messages != (LinkedListInfo *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(DestroyLinkedList(messages,DestroyOptions));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t L o c a l e I n f o _ %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleInfo_() searches the locale list for the specified tag and if
+% found returns attributes for that element.
+%
+% The format of the GetLocaleInfo method is:
+%
+% const LocaleInfo *GetLocaleInfo_(const char *tag,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o tag: the locale tag.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const LocaleInfo *GetLocaleInfo_(const char *tag,
+ ExceptionInfo *exception)
+{
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((locale_list == (SplayTreeInfo *) NULL) ||
+ (instantiate_locale == MagickFalse))
+ if (InitializeLocaleList(exception) == MagickFalse)
+ return((const LocaleInfo *) NULL);
+ if ((locale_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(locale_list) == 0))
+ return((const LocaleInfo *) NULL);
+ if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
+ {
+ ResetSplayTreeIterator(locale_list);
+ return((const LocaleInfo *) GetNextValueInSplayTree(locale_list));
+ }
+ return((const LocaleInfo *) GetValueFromSplayTree(locale_list,tag));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleInfoList() returns any locale messages that match the
+% specified pattern.
+%
+% The format of the GetLocaleInfoList function is:
+%
+% const LocaleInfo **GetLocaleInfoList(const char *pattern,
+% unsigned long *number_messages,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_messages: This integer returns the number of locale messages in
+% the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int LocaleInfoCompare(const void *x,const void *y)
+{
+ const LocaleInfo
+ **p,
+ **q;
+
+ p=(const LocaleInfo **) x,
+ q=(const LocaleInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->tag,(*q)->tag));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const LocaleInfo **GetLocaleInfoList(const char *pattern,
+ unsigned long *number_messages,ExceptionInfo *exception)
+{
+ const LocaleInfo
+ **messages;
+
+ register const LocaleInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate locale list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_messages != (unsigned long *) NULL);
+ *number_messages=0;
+ p=GetLocaleInfo_("*",exception);
+ if (p == (const LocaleInfo *) NULL)
+ return((const LocaleInfo **) NULL);
+ messages=(const LocaleInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
+ if (messages == (const LocaleInfo **) NULL)
+ return((const LocaleInfo **) NULL);
+ /*
+ Generate locale list.
+ */
+ AcquireSemaphoreInfo(&locale_semaphore);
+ ResetSplayTreeIterator(locale_list);
+ p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
+ for (i=0; p != (const LocaleInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
+ messages[i++]=p;
+ p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
+ }
+ RelinquishSemaphoreInfo(locale_semaphore);
+ qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleInfoCompare);
+ messages[i]=(LocaleInfo *) NULL;
+ *number_messages=(unsigned long) i;
+ return(messages);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleList() returns any locale messages that match the specified
+% pattern.
+%
+% The format of the GetLocaleList function is:
+%
+% char **GetLocaleList(const char *pattern,unsigned long *number_messages,
+% Exceptioninfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_messages: This integer returns the number of messages in the
+% list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int LocaleTagCompare(const void *x,const void *y)
+{
+ register char
+ **p,
+ **q;
+
+ p=(char **) x;
+ q=(char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetLocaleList(const char *pattern,
+ unsigned long *number_messages,ExceptionInfo *exception)
+{
+ char
+ **messages;
+
+ register const LocaleInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate locale list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_messages != (unsigned long *) NULL);
+ *number_messages=0;
+ p=GetLocaleInfo_("*",exception);
+ if (p == (const LocaleInfo *) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&locale_semaphore);
+ RelinquishSemaphoreInfo(locale_semaphore);
+ messages=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(locale_list)+1UL,sizeof(*messages));
+ if (messages == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&locale_semaphore);
+ p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
+ for (i=0; p != (const LocaleInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->tag,pattern,MagickTrue) != MagickFalse))
+ messages[i++]=ConstantString(p->tag);
+ p=(const LocaleInfo *) GetNextValueInSplayTree(locale_list);
+ }
+ RelinquishSemaphoreInfo(locale_semaphore);
+ qsort((void *) messages,(size_t) i,sizeof(*messages),LocaleTagCompare);
+ messages[i]=(char *) NULL;
+ *number_messages=(unsigned long) i;
+ return(messages);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e M e s s a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleMessage() returns a message in the current locale that matches the
+% supplied tag.
+%
+% The format of the GetLocaleMessage method is:
+%
+% const char *GetLocaleMessage(const char *tag)
+%
+% A description of each parameter follows:
+%
+% o tag: Return a message that matches this tag in the current locale.
+%
+*/
+MagickExport const char *GetLocaleMessage(const char *tag)
+{
+ char
+ name[MaxTextExtent];
+
+ const LocaleInfo
+ *locale_info;
+
+ ExceptionInfo
+ *exception;
+
+ if ((tag == (const char *) NULL) || (*tag == '\0'))
+ return(tag);
+ exception=AcquireExceptionInfo();
+ (void) FormatMagickString(name,MaxTextExtent,"%s/",tag);
+ locale_info=GetLocaleInfo_(name,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (locale_info != (const LocaleInfo *) NULL)
+ return(locale_info->message);
+ return(tag);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleOptions() returns any Magick configuration messages associated
+% with the specified filename.
+%
+% The format of the GetLocaleOptions method is:
+%
+% LinkedListInfo *GetLocaleOptions(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the locale file tag.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport LinkedListInfo *GetLocaleOptions(const char *filename,
+ ExceptionInfo *exception)
+{
+ char
+ path[MaxTextExtent];
+
+ const char
+ *element;
+
+ LinkedListInfo
+ *messages,
+ *paths;
+
+ StringInfo
+ *xml;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) CopyMagickString(path,filename,MaxTextExtent);
+ /*
+ Load XML from configuration files to linked-list.
+ */
+ messages=NewLinkedList(0);
+ paths=GetConfigurePaths(filename,exception);
+ if (paths != (LinkedListInfo *) NULL)
+ {
+ ResetLinkedListIterator(paths);
+ element=(const char *) GetNextValueInLinkedList(paths);
+ while (element != (const char *) NULL)
+ {
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",element,filename);
+ (void) LogMagickEvent(LocaleEvent,GetMagickModule(),
+ "Searching for locale file: \"%s\"",path);
+ xml=ConfigureFileToStringInfo(path);
+ if (xml != (StringInfo *) NULL)
+ (void) AppendValueToLinkedList(messages,xml);
+ element=(const char *) GetNextValueInLinkedList(paths);
+ }
+ paths=DestroyLinkedList(paths,RelinquishMagickMemory);
+ }
+#if defined(__WINDOWS__)
+ {
+ char
+ *blob;
+
+ blob=(char *) NTResourceToBlob(filename);
+ if (blob != (char *) NULL)
+ {
+ xml=StringToStringInfo(blob);
+ (void) AppendValueToLinkedList(messages,xml);
+ blob=DestroyString(blob);
+ }
+ }
+#endif
+ ResetLinkedListIterator(messages);
+ return(messages);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o c a l e V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLocaleValue() returns the message associated with the locale info.
+%
+% The format of the GetLocaleValue method is:
+%
+% const char *GetLocaleValue(const LocaleInfo *locale_info)
+%
+% A description of each parameter follows:
+%
+% o locale_info: The locale info.
+%
+*/
+MagickExport const char *GetLocaleValue(const LocaleInfo *locale_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(locale_info != (LocaleInfo *) NULL);
+ assert(locale_info->signature == MagickSignature);
+ return(locale_info->message);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e L o c a l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeLocaleList() initializes the locale list.
+%
+% The format of the InitializeLocaleList method is:
+%
+% MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeLocaleList(ExceptionInfo *exception)
+{
+ if ((locale_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_locale == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&locale_semaphore);
+ if ((locale_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_locale == MagickFalse))
+ {
+ char
+ *locale;
+
+ register const char
+ *p;
+
+ locale=(char *) NULL;
+ p=setlocale(LC_CTYPE,(const char *) NULL);
+ if (p != (const char *) NULL)
+ locale=ConstantString(p);
+ if (locale == (char *) NULL)
+ locale=GetEnvironmentValue("LC_ALL");
+ if (locale == (char *) NULL)
+ locale=GetEnvironmentValue("LC_MESSAGES");
+ if (locale == (char *) NULL)
+ locale=GetEnvironmentValue("LC_CTYPE");
+ if (locale == (char *) NULL)
+ locale=GetEnvironmentValue("LANG");
+ if (locale == (char *) NULL)
+ locale=ConstantString("C");
+ (void) LoadLocaleLists(LocaleFilename,locale,exception);
+ locale=DestroyString(locale);
+ instantiate_locale=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(locale_semaphore);
+ }
+ return(locale_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t L o c a l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListLocaleInfo() lists the locale info to a file.
+%
+% The format of the ListLocaleInfo method is:
+%
+% MagickBooleanType ListLocaleInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListLocaleInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const char
+ *path;
+
+ const LocaleInfo
+ **locale_info;
+
+ register long
+ i;
+
+ unsigned long
+ number_messages;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ number_messages=0;
+ locale_info=GetLocaleInfoList("*",&number_messages,exception);
+ if (locale_info == (const LocaleInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_messages; i++)
+ {
+ if (locale_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,locale_info[i]->path) != 0))
+ {
+ if (locale_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",locale_info[i]->path);
+ (void) fprintf(file,"Tag/Message\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=locale_info[i]->path;
+ (void) fprintf(file,"%s\n",locale_info[i]->tag);
+ if (locale_info[i]->message != (char *) NULL)
+ (void) fprintf(file," %s",locale_info[i]->message);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ locale_info=(const LocaleInfo **)
+ RelinquishMagickMemory((void *) locale_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d L o c a l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadLocaleList() loads the locale configuration file which provides a mapping
+% between locale attributes and a locale name.
+%
+% The format of the LoadLocaleList method is:
+%
+% MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The locale list in XML format.
+%
+% o filename: The locale list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void ChopLocaleComponents(char *path,const unsigned long components)
+{
+ long
+ count;
+
+ register char
+ *p;
+
+ if (*path == '\0')
+ return;
+ p=path+strlen(path)-1;
+ if (*p == '/')
+ *p='\0';
+ for (count=0; (count < (long) components) && (p > path); p--)
+ if (*p == '/')
+ {
+ *p='\0';
+ count++;
+ }
+ if (count < (long) components)
+ *path='\0';
+}
+
+static void *DestroyLocaleNode(void *locale_info)
+{
+ register LocaleInfo
+ *p;
+
+ p=(LocaleInfo *) locale_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->tag != (char *) NULL)
+ p->tag=DestroyString(p->tag);
+ if (p->message != (char *) NULL)
+ p->message=DestroyString(p->message);
+ return(RelinquishMagickMemory(p));
+}
+
+static MagickBooleanType LoadLocaleList(const char *xml,const char *filename,
+ const char *locale,const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ message[MaxTextExtent],
+ tag[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ LocaleInfo
+ *locale_info;
+
+ MagickBooleanType
+ status;
+
+ register char
+ *p;
+
+ /*
+ Read the locale configure file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading locale configure file \"%s\" ...",filename);
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (locale_list == (SplayTreeInfo *) NULL)
+ {
+ locale_list=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
+ DestroyLocaleNode);
+ if (locale_list == (SplayTreeInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ locale_info=(LocaleInfo *) NULL;
+ *tag='\0';
+ *message='\0';
+ *keyword='\0';
+ token=AcquireString(xml);
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ {
+ GetMagickToken(q,&q,token);
+ while (isspace((int) ((unsigned char) *q)) != 0)
+ q++;
+ }
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ {
+ GetMagickToken(q,&q,token);
+ while (isspace((int) ((unsigned char) *q)) != 0)
+ q++;
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"locale") == 0)
+ {
+ if (LocaleCompare(locale,token) != 0)
+ break;
+ continue;
+ }
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ *path='\0';
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadLocaleList(xml,path,locale,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<locale") == 0)
+ {
+ /*
+ Locale element.
+ */
+ while ((*token != '>') && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"</locale>") == 0)
+ {
+ ChopLocaleComponents(tag,1);
+ (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<localemap>") == 0)
+ continue;
+ if (LocaleCompare(keyword,"</localemap>") == 0)
+ continue;
+ if (LocaleCompare(keyword,"<message") == 0)
+ {
+ /*
+ Message element.
+ */
+ while ((*token != '>') && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"name") == 0)
+ {
+ (void) ConcatenateMagickString(tag,token,MaxTextExtent);
+ (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
+ }
+ }
+ for (p=(char *) q; (*q != '<') && (*q != '\0'); q++) ;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ q--;
+ while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
+ q--;
+ (void) CopyMagickString(message,p,(size_t) (q-p+2));
+ locale_info=(LocaleInfo *) AcquireMagickMemory(sizeof(*locale_info));
+ if (locale_info == (LocaleInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(locale_info,0,sizeof(*locale_info));
+ locale_info->path=ConstantString(filename);
+ locale_info->tag=ConstantString(tag);
+ locale_info->message=ConstantString(message);
+ locale_info->signature=MagickSignature;
+ status=AddValueToSplayTree(locale_list,locale_info->tag,locale_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ locale_info->tag);
+ (void) ConcatenateMagickString(tag,message,MaxTextExtent);
+ (void) ConcatenateMagickString(tag,"\n",MaxTextExtent);
+ q++;
+ continue;
+ }
+ if (LocaleCompare(keyword,"</message>") == 0)
+ {
+ ChopLocaleComponents(tag,2);
+ (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
+ continue;
+ }
+ if (*keyword == '<')
+ {
+ /*
+ Subpath element.
+ */
+ if (*(keyword+1) == '?')
+ continue;
+ if (*(keyword+1) == '/')
+ {
+ ChopLocaleComponents(tag,1);
+ if (*tag != '\0')
+ (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
+ continue;
+ }
+ token[strlen(token)-1]='\0';
+ (void) CopyMagickString(token,token+1,MaxTextExtent);
+ (void) ConcatenateMagickString(tag,token,MaxTextExtent);
+ (void) ConcatenateMagickString(tag,"/",MaxTextExtent);
+ continue;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d L o c a l e L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadLocaleList() loads one or more locale configuration file which
+% provides a mapping between locale attributes and a locale tag.
+%
+% The format of the LoadLocaleLists method is:
+%
+% MagickBooleanType LoadLocaleLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file tag.
+%
+% o locale: the actual locale.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadLocaleLists(const char *filename,
+ const char *locale,ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadLocaleList(LocaleMap,"built-in",locale,0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetLocaleOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),locale,0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyLocaleOptions(options);
+ if ((locale_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(locale_list) == 0))
+ {
+ options=GetLocaleOptions("english.xml",exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadLocaleList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),locale,0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyLocaleOptions(options);
+ }
+ if ((locale_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(locale_list) == 0))
+ status|=LoadLocaleList(LocaleMap,"built-in",locale,0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/locale_.h b/magick/locale_.h
new file mode 100644
index 0000000..d1919cb
--- /dev/null
+++ b/magick/locale_.h
@@ -0,0 +1,69 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore locale methods.
+*/
+#ifndef _MAGICKCORE_LOCALE_H
+#define _MAGICKCORE_LOCALE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/hashmap.h"
+
+typedef struct _LocaleInfo
+{
+ char
+ *path,
+ *tag,
+ *message;
+
+ MagickBooleanType
+ stealth;
+
+ struct _LocaleInfo
+ *previous,
+ *next; /* deprecated, use GetLocaleInfoList() */
+
+ unsigned long
+ signature;
+} LocaleInfo;
+
+extern MagickExport char
+ **GetLocaleList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const char
+ *GetLocaleMessage(const char *);
+
+extern MagickExport const LocaleInfo
+ *GetLocaleInfo_(const char *,ExceptionInfo *),
+ **GetLocaleInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport LinkedListInfo
+ *DestroyLocaleOptions(LinkedListInfo *),
+ *GetLocaleOptions(const char *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ ListLocaleInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyLocaleList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/log.c b/magick/log.c
new file mode 100644
index 0000000..502c711
--- /dev/null
+++ b/magick/log.c
@@ -0,0 +1,1681 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L OOO GGGG %
+% L O O G %
+% L O O G GG %
+% L O O G G %
+% LLLLL OOO GGG %
+% %
+% %
+% MagickCore Log Events %
+% %
+% Software Design %
+% John Cristy %
+% September 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/semaphore.h"
+#include "magick/timer.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/thread_.h"
+#include "magick/thread-private.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define LogFilename "log.xml"
+
+/*
+ Typedef declarations.
+*/
+typedef enum
+{
+ UndefinedHandler = 0x0000,
+ NoHandler = 0x0000,
+ ConsoleHandler = 0x0001,
+ StdoutHandler = 0x0002,
+ StderrHandler = 0x0004,
+ FileHandler = 0x0008,
+ DebugHandler = 0x0010,
+ EventHandler = 0x0020
+} LogHandlerType;
+
+typedef struct _EventInfo
+{
+ char
+ *name;
+
+ LogEventType
+ event;
+} EventInfo;
+
+typedef struct _HandlerInfo
+{
+ const char
+ *name;
+
+ LogHandlerType
+ handler;
+} HandlerInfo;
+
+struct _LogInfo
+{
+ LogEventType
+ event_mask;
+
+ LogHandlerType
+ handler_mask;
+
+ char
+ *path,
+ *name,
+ *filename,
+ *format;
+
+ unsigned long
+ generations,
+ limit;
+
+ FILE
+ *file;
+
+ unsigned long
+ generation;
+
+ MagickBooleanType
+ append,
+ stealth;
+
+ TimerInfo
+ timer;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Declare log map.
+*/
+static const HandlerInfo
+ LogHandlers[] =
+ {
+ { "console", ConsoleHandler },
+ { "debug", DebugHandler },
+ { "event", EventHandler },
+ { "file", FileHandler },
+ { "none", NoHandler },
+ { "stderr", StderrHandler },
+ { "stdout", StdoutHandler },
+ { (char *) NULL, UndefinedHandler }
+ };
+
+static const char
+ *LogMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<logmap>"
+ " <log events=\"None\" />"
+ " <log output=\"console\" />"
+ " <log filename=\"Magick-%d.log\" />"
+ " <log format=\"%t %r %u %v %d %c[%p]: %m/%f/%l/%d\n %e\" />"
+ "</logmap>";
+
+/*
+ Static declarations.
+*/
+static char
+ log_name[MaxTextExtent] = "Magick";
+
+static LinkedListInfo
+ *log_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *log_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_log = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static LogHandlerType
+ ParseLogHandlers(const char *);
+
+static LogInfo
+ *GetLogInfo(const char *,ExceptionInfo *);
+
+static MagickBooleanType
+ InitializeLogList(ExceptionInfo *),
+ LoadLogLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o s e M a g i c k L o g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloseMagickLog() closes the Magick log.
+%
+% The format of the CloseMagickLog method is:
+%
+% CloseMagickLog(void)
+%
+*/
+MagickExport void CloseMagickLog(void)
+{
+ ExceptionInfo
+ *exception;
+
+ LogInfo
+ *log_info;
+
+ if (IsEventLogging() == MagickFalse)
+ return;
+ exception=AcquireExceptionInfo();
+ log_info=GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ AcquireSemaphoreInfo(&log_semaphore);
+ if (log_info->file != (FILE *) NULL)
+ {
+ if (log_info->append == MagickFalse)
+ (void) fprintf(log_info->file,"</log>\n");
+ (void) fclose(log_info->file);
+ log_info->file=(FILE *) NULL;
+ }
+ RelinquishSemaphoreInfo(log_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y L o g L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyLogList() deallocates memory associated with the log list.
+%
+% The format of the DestroyLogList method is:
+%
+% DestroyLogList(void)
+%
+*/
+
+static void *DestroyLogElement(void *log_info)
+{
+ register LogInfo
+ *p;
+
+ p=(LogInfo *) log_info;
+ if (p->file != (FILE *) NULL)
+ {
+ if (p->append == MagickFalse)
+ (void) fprintf(p->file,"</log>\n");
+ (void) fclose(p->file);
+ p->file=(FILE *) NULL;
+ }
+ if (p->filename != (char *) NULL)
+ p->filename=DestroyString(p->filename);
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->format != (char *) NULL)
+ p->format=DestroyString(p->format);
+ p=(LogInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyLogList(void)
+{
+ AcquireSemaphoreInfo(&log_semaphore);
+ if (log_list != (LinkedListInfo *) NULL)
+ log_list=DestroyLinkedList(log_list,DestroyLogElement);
+ instantiate_log=MagickFalse;
+ RelinquishSemaphoreInfo(log_semaphore);
+ DestroySemaphoreInfo(&log_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t L o g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLogInfo() searches the log list for the specified name and if found
+% returns attributes for that log.
+%
+% The format of the GetLogInfo method is:
+%
+% LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the log name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
+{
+ register LogInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((log_list == (LinkedListInfo *) NULL) || (instantiate_log == MagickFalse))
+ if (InitializeLogList(exception) == MagickFalse)
+ return((LogInfo *) NULL);
+ if ((log_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(log_list) != MagickFalse))
+ return((LogInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ return((LogInfo *) GetValueFromLinkedList(log_list,0));
+ /*
+ Search for log tag.
+ */
+ AcquireSemaphoreInfo(&log_semaphore);
+ ResetLinkedListIterator(log_list);
+ p=(LogInfo *) GetNextValueInLinkedList(log_list);
+ while (p != (LogInfo *) NULL)
+ {
+ if (LocaleCompare(name,p->name) == 0)
+ break;
+ p=(LogInfo *) GetNextValueInLinkedList(log_list);
+ }
+ if (p != (LogInfo *) NULL)
+ (void) InsertValueInLinkedList(log_list,0,
+ RemoveElementByValueFromLinkedList(log_list,p));
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o g I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLogInfoList() returns any logs that match the specified pattern.
+%
+% The format of the GetLogInfoList function is:
+%
+% const LogInfo **GetLogInfoList(const char *pattern,
+% unsigned long *number_preferences,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_preferences: This integer returns the number of logs in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int LogInfoCompare(const void *x,const void *y)
+{
+ const LogInfo
+ **p,
+ **q;
+
+ p=(const LogInfo **) x,
+ q=(const LogInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const LogInfo **GetLogInfoList(const char *pattern,
+ unsigned long *number_preferences,ExceptionInfo *exception)
+{
+ const LogInfo
+ **preferences;
+
+ register const LogInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate log list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_preferences != (unsigned long *) NULL);
+ *number_preferences=0;
+ p=GetLogInfo("*",exception);
+ if (p == (const LogInfo *) NULL)
+ return((const LogInfo **) NULL);
+ preferences=(const LogInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(log_list)+1UL,sizeof(*preferences));
+ if (preferences == (const LogInfo **) NULL)
+ return((const LogInfo **) NULL);
+ /*
+ Generate log list.
+ */
+ AcquireSemaphoreInfo(&log_semaphore);
+ ResetLinkedListIterator(log_list);
+ p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+ for (i=0; p != (const LogInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ preferences[i++]=p;
+ p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+ }
+ RelinquishSemaphoreInfo(log_semaphore);
+ qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogInfoCompare);
+ preferences[i]=(LogInfo *) NULL;
+ *number_preferences=(unsigned long) i;
+ return(preferences);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o g L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLogList() returns any logs that match the specified pattern.
+%
+% The format of the GetLogList function is:
+%
+% char **GetLogList(const char *pattern,unsigned long *number_preferences,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_preferences: This integer returns the number of logs in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int LogCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetLogList(const char *pattern,
+ unsigned long *number_preferences,ExceptionInfo *exception)
+{
+ char
+ **preferences;
+
+ register const LogInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate log list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_preferences != (unsigned long *) NULL);
+ *number_preferences=0;
+ p=GetLogInfo("*",exception);
+ if (p == (const LogInfo *) NULL)
+ return((char **) NULL);
+ preferences=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(log_list)+1UL,sizeof(*preferences));
+ if (preferences == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Generate log list.
+ */
+ AcquireSemaphoreInfo(&log_semaphore);
+ ResetLinkedListIterator(log_list);
+ p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+ for (i=0; p != (const LogInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ preferences[i++]=ConstantString(p->name);
+ p=(const LogInfo *) GetNextValueInLinkedList(log_list);
+ }
+ RelinquishSemaphoreInfo(log_semaphore);
+ qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogCompare);
+ preferences[i]=(char *) NULL;
+ *number_preferences=(unsigned long) i;
+ return(preferences);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t L o g N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetLogName() returns the current log name.
+%
+% The format of the GetLogName method is:
+%
+% const char *GetLogName(void)
+%
+*/
+MagickExport const char *GetLogName(void)
+{
+ return(log_name);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e L o g L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeLogList() initialize the log list.
+%
+% The format of the InitializeLogList method is:
+%
+% MagickBooleanType InitializeLogList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeLogList(ExceptionInfo *exception)
+{
+ if ((log_list == (LinkedListInfo *) NULL) && (instantiate_log == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&log_semaphore);
+ if ((log_list == (LinkedListInfo *) NULL) &&
+ (instantiate_log == MagickFalse))
+ {
+ (void) LoadLogLists(LogFilename,exception);
+ instantiate_log=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(log_semaphore);
+ }
+ return(log_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s E v e n t L o g g i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsEventLogging() returns MagickTrue if debug of events is enabled otherwise
+% MagickFalse.
+%
+% The format of the IsEventLogging method is:
+%
+% MagickBooleanType IsEventLogging(void)
+%
+*/
+MagickExport MagickBooleanType IsEventLogging(void)
+{
+ const LogInfo
+ *log_info;
+
+ ExceptionInfo
+ *exception;
+
+ if ((log_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(log_list) != MagickFalse))
+ return(MagickFalse);
+ exception=AcquireExceptionInfo();
+ log_info=GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ return(log_info->event_mask != NoEvents ? MagickTrue : MagickFalse);
+}
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t L o g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListLogInfo() lists the log info to a file.
+%
+% The format of the ListLogInfo method is:
+%
+% MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
+{
+#define MegabytesToBytes(value) ((MagickSizeType) (value)*1024*1024)
+
+ char
+ limit[MaxTextExtent];
+
+ const char
+ *path;
+
+ const LogInfo
+ **log_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_aliases;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ log_info=GetLogInfoList("*",&number_aliases,exception);
+ if (log_info == (const LogInfo **) NULL)
+ return(MagickFalse);
+ j=0;
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_aliases; i++)
+ {
+ if (log_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,log_info[i]->path) != 0))
+ {
+ if (log_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",log_info[i]->path);
+ (void) fprintf(file,"Filename Generations Limit Format\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=log_info[i]->path;
+ if (log_info[i]->filename != (char *) NULL)
+ {
+ (void) fprintf(file,"%s",log_info[i]->filename);
+ for (j=(long) strlen(log_info[i]->filename); j <= 16; j++)
+ (void) fprintf(file," ");
+ }
+ (void) fprintf(file,"%9lu ",log_info[i]->generations);
+ (void) FormatMagickSize(MegabytesToBytes(log_info[i]->limit),limit);
+ (void) fprintf(file,"%8s ",limit);
+ if (log_info[i]->format != (char *) NULL)
+ (void) fprintf(file,"%s",log_info[i]->format);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ log_info=(const LogInfo **) RelinquishMagickMemory((void *) log_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o g M a g i c k E v e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LogMagickEvent() logs an event as determined by the log configuration file.
+% If an error occurs, MagickFalse is returned otherwise MagickTrue.
+%
+% The format of the LogMagickEvent method is:
+%
+% MagickBooleanType LogMagickEvent(const LogEventType type,
+% const char *module,const char *function,const unsigned long line,
+% const char *format,...)
+%
+% A description of each parameter follows:
+%
+% o type: the event type.
+%
+% o filename: the source module filename.
+%
+% o function: the function name.
+%
+% o line: the line number of the source module.
+%
+% o format: the output format.
+%
+*/
+static char *TranslateEvent(const LogEventType magick_unused(type),
+ const char *module,const char *function,const unsigned long line,
+ const char *domain,const char *event)
+{
+ char
+ *text;
+
+ double
+ elapsed_time,
+ user_time;
+
+ ExceptionInfo
+ *exception;
+
+ LogInfo
+ *log_info;
+
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ size_t
+ extent;
+
+ time_t
+ seconds;
+
+ exception=AcquireExceptionInfo();
+ log_info=(LogInfo *) GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ seconds=time((time_t *) NULL);
+ elapsed_time=GetElapsedTime(&log_info->timer);
+ user_time=GetUserTime(&log_info->timer);
+ text=AcquireString(event);
+ if (log_info->format == (char *) NULL)
+ return(text);
+ extent=strlen(event)+MaxTextExtent;
+ if (LocaleCompare(log_info->format,"xml") == 0)
+ {
+ char
+ timestamp[MaxTextExtent];
+
+ /*
+ Translate event in "XML" format.
+ */
+ (void) FormatMagickTime(seconds,extent,timestamp);
+ (void) FormatMagickString(text,extent,
+ "<entry>\n"
+ " <timestamp>%s</timestamp>\n"
+ " <elapsed-time>%ld:%02ld</elapsed-time>\n"
+ " <user-time>%0.3f</user-time>\n"
+ " <process-id>%ld</process-id>\n"
+ " <thread-id>%lu</thread-id>\n"
+ " <module>%s</module>\n"
+ " <function>%s</function>\n"
+ " <line>%lu</line>\n"
+ " <domain>%s</domain>\n"
+ " <event>%s</event>\n"
+ "</entry>",timestamp,(long) (elapsed_time/60.0),
+ (long) ceil(fmod(elapsed_time,60.0)),user_time,(long) getpid(),
+ GetMagickThreadSignature(),module,function,line,domain,event);
+ return(text);
+ }
+ /*
+ Translate event in "human readable" format.
+ */
+ q=text;
+ for (p=log_info->format; *p != '\0'; p++)
+ {
+ *q='\0';
+ if ((size_t) (q-text+MaxTextExtent) >= extent)
+ {
+ extent+=MaxTextExtent;
+ text=(char *) ResizeQuantumMemory(text,extent+MaxTextExtent,
+ sizeof(*text));
+ if (text == (char *) NULL)
+ return((char *) NULL);
+ q=text+strlen(text);
+ }
+ /*
+ The format of the log is defined by embedding special format characters:
+
+ %c client name
+ %d domain
+ %e event
+ %f function
+ %g generation
+ %l line
+ %m module
+ %n log name
+ %p process id
+ %r real CPU time
+ %t wall clock time
+ %u user CPU time
+ %v version
+ %% percent sign
+ \n newline
+ \r carriage return
+ */
+ if ((*p == '\\') && (*(p+1) == 'r'))
+ {
+ *q++='\r';
+ p++;
+ continue;
+ }
+ if ((*p == '\\') && (*(p+1) == 'n'))
+ {
+ *q++='\n';
+ p++;
+ continue;
+ }
+ if (*p != '%')
+ {
+ *q++=(*p);
+ continue;
+ }
+ p++;
+ switch (*p)
+ {
+ case 'c':
+ {
+ q+=CopyMagickString(q,GetClientName(),extent);
+ break;
+ }
+ case 'd':
+ {
+ q+=CopyMagickString(q,domain,extent);
+ break;
+ }
+ case 'e':
+ {
+ q+=CopyMagickString(q,event,extent);
+ break;
+ }
+ case 'f':
+ {
+ q+=CopyMagickString(q,function,extent);
+ break;
+ }
+ case 'g':
+ {
+ if (log_info->generations == 0)
+ {
+ (void) CopyMagickString(q,"0",extent);
+ q++;
+ break;
+ }
+ q+=FormatMagickString(q,extent,"%lu",log_info->generation %
+ log_info->generations);
+ break;
+ }
+ case 'l':
+ {
+ q+=FormatMagickString(q,extent,"%lu",line);
+ break;
+ }
+ case 'm':
+ {
+ register const char
+ *p;
+
+ for (p=module+strlen(module)-1; p > module; p--)
+ if (*p == *DirectorySeparator)
+ {
+ p++;
+ break;
+ }
+ q+=CopyMagickString(q,p,extent);
+ break;
+ }
+ case 'n':
+ {
+ q+=CopyMagickString(q,GetLogName(),extent);
+ break;
+ }
+ case 'p':
+ {
+ q+=FormatMagickString(q,extent,"%ld",(long) getpid());
+ break;
+ }
+ case 'r':
+ {
+ q+=FormatMagickString(q,extent,"%ld:%02ld",(long)
+ (elapsed_time/60.0),(long) ceil(fmod(elapsed_time,60.0)));
+ break;
+ }
+ case 't':
+ {
+ q+=FormatMagickTime(seconds,extent,q);
+ break;
+ }
+ case 'u':
+ {
+ q+=FormatMagickString(q,extent,"%0.3fu",user_time);
+ break;
+ }
+ case 'v':
+ {
+ q+=CopyMagickString(q,MagickLibVersionText,extent);
+ break;
+ }
+ case '%':
+ {
+ *q++=(*p);
+ break;
+ }
+ default:
+ {
+ *q++='%';
+ *q++=(*p);
+ break;
+ }
+ }
+ }
+ *q='\0';
+ return(text);
+}
+
+static char *TranslateFilename(const LogInfo *log_info)
+{
+ char
+ *filename;
+
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ size_t
+ extent;
+
+ /*
+ Translate event in "human readable" format.
+ */
+ assert(log_info != (LogInfo *) NULL);
+ assert(log_info->filename != (char *) NULL);
+ filename=AcquireString((char *) NULL);
+ extent=MaxTextExtent;
+ q=filename;
+ for (p=log_info->filename; *p != '\0'; p++)
+ {
+ *q='\0';
+ if ((size_t) (q-filename+MaxTextExtent) >= extent)
+ {
+ extent+=MaxTextExtent;
+ filename=(char *) ResizeQuantumMemory(filename,extent+MaxTextExtent,
+ sizeof(*filename));
+ if (filename == (char *) NULL)
+ return((char *) NULL);
+ q=filename+strlen(filename);
+ }
+ /*
+ The format of the filename is defined by embedding special format
+ characters:
+
+ %c client name
+ %n log name
+ %p process id
+ %v version
+ %% percent sign
+ */
+ if (*p != '%')
+ {
+ *q++=(*p);
+ continue;
+ }
+ p++;
+ switch (*p)
+ {
+ case 'c':
+ {
+ q+=CopyMagickString(q,GetClientName(),extent);
+ break;
+ }
+ case 'g':
+ {
+ if (log_info->generations == 0)
+ {
+ (void) CopyMagickString(q,"0",extent);
+ q++;
+ break;
+ }
+ q+=FormatMagickString(q,extent,"%lu",log_info->generation %
+ log_info->generations);
+ break;
+ }
+ case 'n':
+ {
+ q+=CopyMagickString(q,GetLogName(),extent);
+ break;
+ }
+ case 'p':
+ {
+ q+=FormatMagickString(q,extent,"%ld",(long) getpid());
+ break;
+ }
+ case 'v':
+ {
+ q+=CopyMagickString(q,MagickLibVersionText,extent);
+ break;
+ }
+ case '%':
+ {
+ *q++=(*p);
+ break;
+ }
+ default:
+ {
+ *q++='%';
+ *q++=(*p);
+ break;
+ }
+ }
+ }
+ *q='\0';
+ return(filename);
+}
+
+MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
+ const char *function,const unsigned long line,const char *format,
+ va_list operands)
+{
+ char
+ event[MaxTextExtent],
+ *text;
+
+ const char
+ *domain;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ n;
+
+ LogInfo
+ *log_info;
+
+ if (IsEventLogging() == MagickFalse)
+ return(MagickFalse);
+ exception=AcquireExceptionInfo();
+ log_info=(LogInfo *) GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ AcquireSemaphoreInfo(&log_semaphore);
+ if ((log_info->event_mask & type) == 0)
+ {
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(MagickTrue);
+ }
+ domain=MagickOptionToMnemonic(MagickLogEventOptions,type);
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(event,MaxTextExtent,format,operands);
+#else
+ n=vsprintf(event,format,operands);
+#endif
+ if (n < 0)
+ event[MaxTextExtent-1]='\0';
+ text=TranslateEvent(type,module,function,line,domain,event);
+ if (text == (char *) NULL)
+ {
+ (void) ContinueTimer((TimerInfo *) &log_info->timer);
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(MagickFalse);
+ }
+ if ((log_info->handler_mask & ConsoleHandler) != 0)
+ {
+ (void) fprintf(stderr,"%s\n",text);
+ (void) fflush(stderr);
+ }
+ if ((log_info->handler_mask & DebugHandler) != 0)
+ {
+#if defined(__WINDOWS__)
+ OutputDebugString(text);
+#endif
+ }
+ if ((log_info->handler_mask & EventHandler) != 0)
+ {
+#if defined(__WINDOWS__)
+ (void) NTReportEvent(text,MagickFalse);
+#endif
+ }
+ if ((log_info->handler_mask & FileHandler) != 0)
+ {
+ struct stat
+ file_info;
+
+ file_info.st_size=0;
+ if (log_info->file != (FILE *) NULL)
+ (void) fstat(fileno(log_info->file),&file_info);
+ if (file_info.st_size > (off_t) (1024*1024*log_info->limit))
+ {
+ (void) fprintf(log_info->file,"</log>\n");
+ (void) fclose(log_info->file);
+ log_info->file=(FILE *) NULL;
+ }
+ if (log_info->file == (FILE *) NULL)
+ {
+ char
+ *filename;
+
+ filename=TranslateFilename(log_info);
+ if (filename == (char *) NULL)
+ {
+ (void) ContinueTimer((TimerInfo *) &log_info->timer);
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(MagickFalse);
+ }
+ log_info->append=IsPathAccessible(filename);
+ log_info->file=OpenMagickStream(filename,"ab");
+ filename=(char *) RelinquishMagickMemory(filename);
+ if (log_info->file == (FILE *) NULL)
+ {
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(MagickFalse);
+ }
+ log_info->generation++;
+ if (log_info->append == MagickFalse)
+ {
+ (void) fprintf(log_info->file,"<?xml version=\"1.0\" "
+ "encoding=\"UTF-8\" standalone=\"yes\"?>\n");
+ (void) fprintf(log_info->file,"<log>\n");
+ }
+ }
+ (void) fprintf(log_info->file,"%s\n",text);
+ (void) fflush(log_info->file);
+ }
+ if ((log_info->handler_mask & StdoutHandler) != 0)
+ {
+ (void) fprintf(stdout,"%s\n",text);
+ (void) fflush(stdout);
+ }
+ if ((log_info->handler_mask & StderrHandler) != 0)
+ {
+ (void) fprintf(stderr,"%s\n",text);
+ (void) fflush(stderr);
+ }
+ text=(char *) RelinquishMagickMemory(text);
+ (void) ContinueTimer((TimerInfo *) &log_info->timer);
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(MagickTrue);
+}
+
+MagickBooleanType LogMagickEvent(const LogEventType type,const char *module,
+ const char *function,const unsigned long line,const char *format,...)
+{
+ va_list
+ operands;
+
+ MagickBooleanType
+ status;
+
+ va_start(operands,format);
+ status=LogMagickEventList(type,module,function,line,format,operands);
+ va_end(operands);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d L o g L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadLogList() loads the log configuration file which provides a
+% mapping between log attributes and log name.
+%
+% The format of the LoadLogList method is:
+%
+% MagickBooleanType LoadLogList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The log list in XML format.
+%
+% o filename: The log list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadLogList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ LogInfo
+ *log_info = (LogInfo *) NULL;
+
+ MagickStatusType
+ status;
+
+ /*
+ Load the log map file.
+ */
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (log_list == (LinkedListInfo *) NULL)
+ {
+ log_list=NewLinkedList(0);
+ if (log_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ token=AcquireString((const char *) xml);
+ for (q=(const char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status|=LoadLogList(xml,path,depth+1,exception);
+ xml=DestroyString(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<logmap>") == 0)
+ {
+ /*
+ Allocate memory for the log list.
+ */
+ log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
+ if (log_info == (LogInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
+ log_info->path=ConstantString(filename);
+ GetTimerInfo((TimerInfo *) &log_info->timer);
+ log_info->signature=MagickSignature;
+ continue;
+ }
+ if (log_info == (LogInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"</logmap>") == 0)
+ {
+ status=AppendValueToLinkedList(log_list,log_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
+ log_info=(LogInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'E':
+ case 'e':
+ {
+ if (LocaleCompare((char *) keyword,"events") == 0)
+ {
+ log_info->event_mask=(LogEventType) (log_info->event_mask |
+ ParseMagickOption(MagickLogEventOptions,MagickTrue,token));
+ break;
+ }
+ break;
+ }
+ case 'F':
+ case 'f':
+ {
+ if (LocaleCompare((char *) keyword,"filename") == 0)
+ {
+ if (log_info->filename != (char *) NULL)
+ log_info->filename=(char *)
+ RelinquishMagickMemory(log_info->filename);
+ log_info->filename=ConstantString(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"format") == 0)
+ {
+ if (log_info->format != (char *) NULL)
+ log_info->format=(char *)
+ RelinquishMagickMemory(log_info->format);
+ log_info->format=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'G':
+ case 'g':
+ {
+ if (LocaleCompare((char *) keyword,"generations") == 0)
+ {
+ if (LocaleCompare(token,"unlimited") == 0)
+ {
+ log_info->generations=(~0UL);
+ break;
+ }
+ log_info->generations=(unsigned long) atol(token);
+ break;
+ }
+ break;
+ }
+ case 'L':
+ case 'l':
+ {
+ if (LocaleCompare((char *) keyword,"limit") == 0)
+ {
+ if (LocaleCompare(token,"unlimited") == 0)
+ {
+ log_info->limit=(~0UL);
+ break;
+ }
+ log_info->limit=(unsigned long) atol(token);
+ break;
+ }
+ break;
+ }
+ case 'O':
+ case 'o':
+ {
+ if (LocaleCompare((char *) keyword,"output") == 0)
+ {
+ log_info->handler_mask=(LogHandlerType)
+ (log_info->handler_mask | ParseLogHandlers(token));
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=DestroyString(token);
+ if (log_list == (LinkedListInfo *) NULL)
+ return(MagickFalse);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d L o g L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadLogLists() loads one or more log configuration file which provides a
+% mapping between log attributes and log name.
+%
+% The format of the LoadLogLists method is:
+%
+% MagickBooleanType LoadLogLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the log configuration filename.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadLogLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadLogList(LogMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadLogList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((log_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(log_list) != MagickFalse))
+ status|=LoadLogList(LogMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P a r s e L o g H a n d l e r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseLogHandlers() parses a string defining which handlers takes a log
+% message and exports them.
+%
+% The format of the ParseLogHandlers method is:
+%
+% LogHandlerType ParseLogHandlers(const char *handlers)
+%
+% A description of each parameter follows:
+%
+% o handlers: one or more handlers separated by commas.
+%
+*/
+static LogHandlerType ParseLogHandlers(const char *handlers)
+{
+ LogHandlerType
+ handler_mask;
+
+ register const char
+ *p;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ handler_mask=NoHandler;
+ for (p=handlers; p != (char *) NULL; p=strchr(p,','))
+ {
+ while ((*p != '\0') && ((isspace((int) ((unsigned char) *p)) != 0) ||
+ (*p == ',')))
+ p++;
+ for (i=0; LogHandlers[i].name != (char *) NULL; i++)
+ {
+ length=strlen(LogHandlers[i].name);
+ if (LocaleNCompare(p,LogHandlers[i].name,length) == 0)
+ {
+ handler_mask=(LogHandlerType) (handler_mask | LogHandlers[i].handler);
+ break;
+ }
+ }
+ if (LogHandlers[i].name == (char *) NULL)
+ return(UndefinedHandler);
+ }
+ return(handler_mask);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t L o g E v e n t M a s k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetLogEventMask() accepts a list that determines which events to log. All
+% other events are ignored. By default, no debug is enabled. This method
+% returns the previous log event mask.
+%
+% The format of the SetLogEventMask method is:
+%
+% LogEventType SetLogEventMask(const char *events)
+%
+% A description of each parameter follows:
+%
+% o events: log these events.
+%
+*/
+MagickExport LogEventType SetLogEventMask(const char *events)
+{
+ ExceptionInfo
+ *exception;
+
+ LogInfo
+ *log_info;
+
+ long
+ option;
+
+ exception=AcquireExceptionInfo();
+ log_info=(LogInfo *) GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ option=ParseMagickOption(MagickLogEventOptions,MagickTrue,events);
+ AcquireSemaphoreInfo(&log_semaphore);
+ log_info=(LogInfo *) GetValueFromLinkedList(log_list,0);
+ log_info->event_mask=(LogEventType) option;
+ if (option == -1)
+ log_info->event_mask=UndefinedEvents;
+ RelinquishSemaphoreInfo(log_semaphore);
+ return(log_info->event_mask);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t L o g F o r m a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetLogFormat() sets the format for the "human readable" log record.
+%
+% The format of the LogMagickFormat method is:
+%
+% SetLogFormat(const char *format)
+%
+% A description of each parameter follows:
+%
+% o format: the log record format.
+%
+*/
+MagickExport void SetLogFormat(const char *format)
+{
+ LogInfo
+ *log_info;
+
+ ExceptionInfo
+ *exception;
+
+ exception=AcquireExceptionInfo();
+ log_info=(LogInfo *) GetLogInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ AcquireSemaphoreInfo(&log_semaphore);
+ if (log_info->format != (char *) NULL)
+ log_info->format=DestroyString(log_info->format);
+ log_info->format=ConstantString(format);
+ RelinquishSemaphoreInfo(log_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t L o g N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetLogName() sets the log name and returns it.
+%
+% The format of the SetLogName method is:
+%
+% const char *SetLogName(const char *name)
+%
+% A description of each parameter follows:
+%
+% o log_name: SetLogName() returns the current client name.
+%
+% o name: Specifies the new client name.
+%
+*/
+MagickExport const char *SetLogName(const char *name)
+{
+ if ((name != (char *) NULL) && (*name != '\0'))
+ (void) CopyMagickString(log_name,name,MaxTextExtent);
+ return(log_name);
+}
diff --git a/magick/log.h b/magick/log.h
new file mode 100644
index 0000000..87705ee
--- /dev/null
+++ b/magick/log.h
@@ -0,0 +1,93 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore log methods.
+*/
+#ifndef _MAGICKCORE_LOG_H
+#define _MAGICKCORE_LOG_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include "magick/exception.h"
+
+#if !defined(GetMagickModule)
+# define GetMagickModule() __FILE__,__func__,(unsigned long) __LINE__
+#endif
+
+#define MagickLogFilename "log.xml"
+
+typedef enum
+{
+ UndefinedEvents,
+ NoEvents = 0x00000,
+ TraceEvent = 0x00001,
+ AnnotateEvent = 0x00002,
+ BlobEvent = 0x00004,
+ CacheEvent = 0x00008,
+ CoderEvent = 0x00010,
+ ConfigureEvent = 0x00020,
+ DeprecateEvent = 0x00040,
+ DrawEvent = 0x00080,
+ ExceptionEvent = 0x00100,
+ LocaleEvent = 0x00200,
+ ModuleEvent = 0x00400,
+ PolicyEvent = 0x00800,
+ ResourceEvent = 0x01000,
+ TransformEvent = 0x02000,
+ UserEvent = 0x04000,
+ WandEvent = 0x08000,
+ X11Event = 0x10000,
+ AllEvents = 0x7fffffff
+} LogEventType;
+
+typedef struct _LogInfo
+ LogInfo;
+
+extern MagickExport char
+ **GetLogList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const char
+ *GetLogName(void),
+ *SetLogName(const char *);
+
+extern MagickExport const LogInfo
+ **GetLogInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport LogEventType
+ SetLogEventMask(const char *);
+
+extern MagickExport MagickBooleanType
+ IsEventLogging(void),
+ ListLogInfo(FILE *,ExceptionInfo *),
+ LogMagickEvent(const LogEventType,const char *,const char *,
+ const unsigned long,const char *,...)
+ magick_attribute((format (printf,5,6))),
+ LogMagickEventList(const LogEventType,const char *,const char *,
+ const unsigned long,const char *,va_list)
+ magick_attribute((format (printf,5,0)));
+
+extern MagickExport void
+ CloseMagickLog(void),
+ DestroyLogList(void),
+ SetLogFormat(const char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/mac.c b/magick/mac.c
new file mode 100644
index 0000000..9849907
--- /dev/null
+++ b/magick/mac.c
@@ -0,0 +1,1607 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M AAA CCCC %
+% MM MM A A C %
+% M M M AAAAA C %
+% M M A A C %
+% M M A A CCCC %
+% %
+% %
+% Macintosh Utility Methods for MagickCore %
+% %
+% Software Design %
+% John Cristy %
+% September 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The directory methods are strongly based on similar methods written
+% by Steve Summit, [email protected]. The Ghostscript launch code is strongly
+% based on Dave Schooley's Mac Gnuplot and contributed by
+% [email protected]. Mac-centric improvements contributed by
+% [email protected].
+%
+%
+*/
+
+#if defined(macintosh)
+/*
+ Include declarations.
+*/
+#define _X_H
+#define _WIDGET_H
+#include <AppleEvents.h>
+#include <AERegistry.h>
+#include <AEObjects.h>
+#include <AEPackObject.h>
+#include <Processes.h>
+#include <QuickDraw.h>
+#include <QDOffscreen.h>
+#include <Palettes.h>
+#include <ImageCompression.h>
+#include <PictUtils.h>
+#include <Files.h>
+#include <Gestalt.h>
+#include <TextUtils.h>
+#define ColorInfo KolorInfo
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/quantum.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/mac.h"
+
+/*
+ Global declaractions.
+*/
+ImageDescriptionHandle
+ image_description = nil;
+
+/*
+ Forward declaractions.
+*/
+static Boolean
+ SearchForFile(OSType,OSType,FSSpec *,short);
+
+static pascal void
+ ArcMethod(GrafVerb,Rect *,short,short),
+ BitsMethod(BitMap *,Rect *,Rect *,short,RgnHandle),
+ FilenameToFSSpec(const char *filename,FSSpec *fsspec),
+ LineMethod(Point),
+ OvalMethod(GrafVerb,Rect *),
+ PolyMethod(GrafVerb,PolyHandle),
+ RRectMethod(GrafVerb,Rect *,short,short),
+ RectMethod(GrafVerb,Rect *),
+ RegionMethod(GrafVerb,RgnHandle),
+ StandardPixmap(PixMapPtr,Rect *,MatrixRecordPtr,short,RgnHandle,PixMapPtr,
+ Rect *,short),
+ TextMethod(short,Ptr,Point,Point);
+
+/*
+ Static declarations
+ */
+#if defined(DISABLE_SIOUX)
+static MACEventHookPtr
+ event_hook = nil;
+
+static MACErrorHookPtr
+ exception.hook = nil;
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B o t t l e n e c k T e s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BottleneckTest() intercepts any compressed images.
+%
+% The format of the BottleneckTest method is:
+%
+% int BottleneckTest(const char *magick)
+%
+% A description of each parameter follows:
+%
+% o picture: Specifies a pointer to a PicHandle structure.
+%
+% o codec: the code type is returned in this CodecType pointer structure.
+%
+% o depth: the image depth is returned as an integer pointer.
+%
+% o colormap_id: the colormap ID is returned in this short pointer.
+%
+%
+*/
+
+static pascal void ArcMethod(GrafVerb verb,Rect *r,short startAngle,
+ short arcAngle)
+{
+#pragma unused (verb,r,startAngle,arcAngle)
+}
+
+static pascal void BitsMethod(BitMap *bitPtr,Rect *source_rectangle,
+ Rect *dstRect,short mode,RgnHandle maskRgn)
+{
+#pragma unused (bitPtr,source_rectangle,dstRect,mode,maskRgn)
+}
+
+static pascal void LineMethod(Point newPt)
+{
+#pragma unused (newPt)
+}
+
+static pascal void OvalMethod(GrafVerb verb,Rect *r)
+{
+#pragma unused (verb,r)
+}
+
+static pascal void PolyMethod(GrafVerb verb,PolyHandle poly)
+{
+#pragma unused (verb,poly)
+}
+
+static pascal void RectMethod(GrafVerb verb,Rect *r)
+{
+#pragma unused (verb,r)
+}
+
+static pascal void RegionMethod(GrafVerb verb,RgnHandle rgn)
+{
+#pragma unused (verb,rgn)
+}
+
+static pascal void RRectMethod(GrafVerb verb,Rect *r,short ovalWidth,
+ short ovalHeight)
+{
+#pragma unused (verb,r,ovalWidth,ovalHeight)
+}
+
+static pascal void StandardPixmap(PixMapPtr source,Rect *source_rectangle,
+ MatrixRecordPtr matrix,short mode,RgnHandle mask,PixMapPtr matte,
+ Rect *matte_rectangle,short flags)
+{
+#pragma unused (source_rectangle,matrix,mode,mask,matte,matte_rectangle,flags)
+
+ long
+ size;
+
+ Ptr
+ data;
+
+ GetCompressedPixMapInfo(source,&image_description,&data,&size,nil,nil);
+}
+
+static pascal void TextMethod(short byteCount,Ptr textBuf,Point numer,
+ Point denom)
+{
+#pragma unused (byteCount,textBuf,numer,denom)
+}
+
+#if !defined(DISABLE_QUICKTIME)
+static short BottleneckTest(PicHandle picture,CodecType *codec,int *depth,
+ short *colormap_id)
+{
+ CQDProcs
+ bottlenecks;
+
+ int
+ status;
+
+ long
+ version;
+
+ Rect
+ rectangle;
+
+ status=Gestalt(gestaltQuickTime,&version);
+ if (status != noErr)
+ {
+ ParamText("\pQuickTime not installed. Please install, then try again.",
+ "\p","\p","\p");
+ Alert(128,nil);
+ return(-1);
+ }
+ /*
+ Define our own bottlenecks to do nothing.
+ */
+ SetStdCProcs(&bottlenecks);
+ bottlenecks.textProc=NewQDTextUPP(&TextMethod);
+ bottlenecks.lineProc=NewQDLineUPP(&LineMethod);
+ bottlenecks.rectProc=NewQDRectUPP(&RectMethod);
+ bottlenecks.rRectProc=NewQDRRectUPP(&RRectMethod);
+ bottlenecks.ovalProc=NewQDOvalUPP(&OvalMethod);
+ bottlenecks.arcProc=NewQDArcUPP(&ArcMethod);
+ bottlenecks.polyProc=NewQDPolyUPP(&PolyMethod);
+ bottlenecks.rgnProc=NewQDRgnUPP(&RegionMethod);
+ bottlenecks.bitsProc=NewQDBitsUPP(&BitsMethod);
+ bottlenecks.newProc1=(UniversalProcPtr) NewStdPixUPP(&StandardPixmap);
+ /*
+ Install our custom bottlenecks to intercept any compressed images.
+ */
+ (*(qd.thePort)).grafProcs=(QDProcs *) &bottlenecks;
+ DrawPicture(picture,&((**picture).picFrame));
+ PaintRect(&rectangle);
+ (*(qd.thePort)).grafProcs=0L;
+ /*
+ Initialize our return values.
+ */
+ *codec='unkn';
+ *depth=0;
+ *colormap_id=(-1);
+ if (image_description != nil)
+ {
+ *codec=(**image_description).cType;
+ *depth=(**image_description).depth;
+ *colormap_id=(**image_description).clutID;
+ }
+ DisposeQDTextUPP(bottlenecks.textProc);
+ DisposeQDLineUPP(bottlenecks.lineProc);
+ DisposeQDRectUPP(bottlenecks.rectProc);
+ DisposeQDRRectUPP(bottlenecks.rRectProc);
+ DisposeQDOvalUPP(bottlenecks.ovalProc);
+ DisposeQDArcUPP(bottlenecks.arcProc);
+ DisposeQDPolyUPP(bottlenecks.polyProc);
+ DisposeQDRgnUPP(bottlenecks.rgnProc);
+ DisposeQDBitsUPP(bottlenecks.bitsProc);
+ DisposeStdPixUPP(bottlenecks.newProc1);
+ return(0);
+}
+#endif
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% c l o s e d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% closedir() closes the named directory stream and frees the DIR structure.
+%
+% The format of the closedir method is:
+%
+% closedir(entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+MagickExport void closedir(DIR *entry)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(entry != (DIR *) NULL);
+ RelinquishMagickMemory(entry);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x i t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Exit() exits the process.
+%
+% The format of the exit method is:
+%
+% Exit(status)
+%
+% A description of each parameter follows:
+%
+% o status: an integer value representing the status of the terminating
+% process.
+%
+%
+*/
+MagickExport int Exit(int status)
+{
+#if !defined(DISABLE_SIOUX)
+ (void) fprintf(stdout,"Select File->Quit to exit.\n");
+#endif
+ exit(status);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F i l e n a m e T o F S S p e c %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FilenameToFSSpec() sets the file type of an image.
+%
+% The format of the FilenameToFSSpec method is:
+%
+% FilenameToFSSpec(filename,fsspec)
+%
+% A description of each parameter follows:
+%
+% o filename: Specifies the name of the file.
+%
+% o fsspec: A pointer to type FSSpec.
+%
+%
+*/
+MagickExport void pascal FilenameToFSSpec(const char *filename,FSSpec *fsspec)
+{
+ Str255
+ name;
+
+ assert(filename != (char *) NULL);
+ c2pstrcpy(name,filename);
+ FSMakeFSSpec(0,0,name,fsspec);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k C o n f l i c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACIsMagickConflict() returns true if the image format conflicts with a
+% logical drive (.e.g. X:).
+%
+% Contributed by Mark Gavin of Digital Applications, Inc.
+%
+% The format of the MACIsMagickConflict method is:
+%
+% status=MACIsMagickConflict(magick)
+%
+% A description of each parameter follows:
+%
+% o magick: Specifies the image format.
+%
+%
+*/
+
+static OSErr HGetVInfo(short volume_index,StringPtr volume_name,short *volume,
+ unsigned long *free_bytes,unsigned long *total_bytes)
+{
+ HParamBlockRec
+ pb;
+
+ OSErr
+ result;
+
+ unsigned long
+ blocksize;
+
+ unsigned short
+ allocation_blocks,
+ free_blocks;
+
+ /*
+ Use the File Manager to get the real vRefNum.
+ */
+ pb.volumeParam.ioVRefNum=0;
+ pb.volumeParam.ioNamePtr=volume_name;
+ pb.volumeParam.ioVolIndex=volume_index;
+ result=PBHGetVInfoSync(&pb);
+ if (result != noErr)
+ return(result);
+ *volume=pb.volumeParam.ioVRefNum;
+ blocksize=(unsigned long) pb.volumeParam.ioVAlBlkSiz;
+ allocation_blocks=(unsigned short) pb.volumeParam.ioVNmAlBlks;
+ free_blocks=(unsigned short) pb.volumeParam.ioVFrBlk;
+ *free_bytes=free_blocks*blocksize;
+ *total_bytes=allocation_blocks*blocksize;
+ return(result);
+}
+
+MagickExport MagickBooleanType MACIsMagickConflict(const char *magick)
+{
+ unsigned long
+ free_bytes,
+ number_bytes;
+
+ OSErr
+ status;
+
+ short
+ volume;
+
+ Str255
+ volume_name;
+
+ assert(magick != (char *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",magick);
+ (void) CopyMagickString((char *) volume_name,magick,MaxTextExtent);
+ c2pstr((char *) volume_name);
+ if (volume_name[volume_name[0]] != ':')
+ volume_name[++volume_name[0]]=':';
+ status=HGetVInfo(-1,volume_name,&volume,&free_bytes,&number_bytes);
+ return(status != 0 ? MagickFalse : MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M A C E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACErrorHandler() displays an error reason and then terminates the program.
+%
+% The format of the MACErrorHandler method is:
+%
+% void MACErrorHandler(const ExceptionType error,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o exception: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+%
+*/
+MagickExport void MACErrorHandler(const ExceptionType error,const char *reason,
+ const char *description)
+{
+ char
+ buffer[3*MaxTextExtent];
+
+ if (reason == (char *) NULL)
+ return;
+ if (description == (char *) NULL)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s.\n",GetClientName(),
+ reason);
+ else
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s).\n",
+ GetClientName(),reason,description);
+#if defined(DISABLE_SIOUX)
+ if(exception.hook != (MACErrorHookPtr) NULL)
+ exception.hook(error,buffer);
+ else
+ {
+ MagickCoreTerminus();
+ exit(error);
+ }
+#else
+ puts(buffer);
+ MagickCoreTerminus();
+ exit(error);
+#endif
+}
+
+#if defined(DISABLE_SIOUX)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M A C F a t a l E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACFatalErrorHandler() displays an error reason and then terminates the
+% program.
+%
+% The format of the MACFatalErrorHandler method is:
+%
+% void MACFatalErrorHandler(const ExceptionType severity,
+% const char *reason,const char *description)
+%
+% A description of each parameter follows:
+%
+% o severity: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+%
+*/
+static void MACFatalErrorHandler(const ExceptionType severity,
+ const char *reason,const char *description)
+{
+ char
+ buffer[3*MaxTextExtent];
+
+ if (reason == (char *) NULL)
+ return;
+ if (description == (char *) NULL)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s.\n",GetClientName(),
+ reason);
+ else
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s).\n",
+ GetClientName(),reason,description);
+ if(exception.hook != (MACErrorHookPtr) NULL)
+ exception.hook(severity, buffer);
+ else
+ {
+ MagickCoreTerminus();
+ exit(severity);
+ }
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S E x e c u t e C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSExecuteCommand() executes the Ghostscript command.
+%
+%
+*/
+static OSErr MacGSExecuteCommand(const char *command,long length)
+{
+ AEAddressDesc
+ event_descriptor;
+
+ AEDesc
+ reply = {typeNull, NULL};
+
+ AppleEvent
+ event = {typeNull, NULL};
+
+ DescType
+ descriptor_type;
+
+ int
+ error;
+
+ OSType
+ id = 'gsVR';
+
+ Size
+ actualSize;
+
+ /*
+ Send the Apple Event.
+ */
+ (void) AECreateDesc(typeApplSignature,&id,sizeof(id),&event_descriptor);
+ (void) AECreateAppleEvent(id,'exec',&event_descriptor,-1,kAnyTransactionID,
+ &event);
+ (void) AEPutParamPtr(&event,keyDirectObject,typeChar,command,length);
+ (void) AESend(&event,&reply,kAEWaitReply+kAENeverInteract,kAENormalPriority,
+ kNoTimeOut,NULL,NULL);
+ /*
+ Handle the reply and exit.
+ */
+ (void) AEGetParamPtr(&reply,keyDirectObject,typeInteger,&descriptor_type,
+ &error,sizeof(error),&actualSize);
+ (void) AEDisposeDesc(&event_descriptor);
+ (void) AEDisposeDesc(&event);
+ if (reply.descriptorType != NULL)
+ AEDisposeDesc(&reply);
+ return((OSErr) error);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S L a u n c h A p p l i c a t i o n C o r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSLaunchApplicationCore() launches the Ghostscript command.
+%
+%
+*/
+static OSErr MacGSLaunchApplicationCore(long flags)
+{
+ FSSpec
+ file_info;
+
+ LaunchParamBlockRec
+ launch_info;
+
+ OSErr
+ error;
+
+ if (!SearchForFile('gsVR','APPL',&file_info,1))
+ return(-43);
+ launch_info.launchBlockID=extendedBlock;
+ launch_info.launchEPBLength=extendedBlockLen;
+ launch_info.launchFileFlags=0;
+ launch_info.launchControlFlags=launchContinue+launchNoFileFlags+flags;
+ launch_info.launchAppSpec=(&file_info);
+ launch_info.launchAppParameters=nil;
+ error=LaunchApplication(&launch_info);
+ return(error);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S L a u n c h A p p l i c a t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSLaunchApplication() launches the Ghostscript command.
+%
+%
+*/
+static OSErr MacGSLaunchApplication(void)
+{
+ return(MacGSLaunchApplicationCore(launchDontSwitch));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S L a u n c h A p p l i c a t i o n T o F r o n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSLaunchApplicationToFront() moves the Ghostscript window to the front.
+%
+%
+*/
+static OSErr MacGSLaunchApplicationToFront(void)
+{
+ return(MacGSLaunchApplicationCore(0));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S Q u i t A p p l i c a t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSQuitApplication() quits the Ghostscript application.
+%
+%
+*/
+static void MacGSQuitApplication(void)
+{
+ AEAddressDesc
+ event_descriptor;
+
+ AEDesc
+ reply = {typeNull, NULL};
+
+ AppleEvent
+ event = {typeNull, NULL};
+
+ OSType
+ id = 'GPLT';
+
+ /*
+ Send the Apple Event.
+ */
+ (void) AECreateDesc(typeApplSignature,&id,sizeof(id),&event_descriptor);
+ (void) AECreateAppleEvent(typeAppleEvent,kAEQuitApplication,
+ &event_descriptor,-1,kAnyTransactionID,&event);
+ (void) AESend(&event,&reply,kAENoReply,kAENormalPriority,kNoTimeOut,NULL,
+ NULL);
+ /*
+ Clean up and exit.
+ */
+ (void) AEDisposeDesc(&event_descriptor);
+ (void) AEDisposeDesc(&event);
+ if (reply.descriptorType != NULL)
+ AEDisposeDesc(&reply);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a c G S S e t W o r k i n g F o l d e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MacGSSetWorkingFolder() set the Ghostscript working folder.
+%
+%
+*/
+static OSErr MacGSSetWorkingFolder(char *directory)
+{
+ AEDesc
+ application_descriptor,
+ event_descriptor,
+ object,
+ path_descriptor,
+ type_descriptor,
+ reply;
+
+ AppleEvent
+ event;
+
+ DescType
+ folder_type = 'wfdr';
+
+ OSErr
+ error;
+
+ OSType
+ id = 'GPLT';
+
+ /*
+ Send the Apple Event.
+ */
+ AECreateDesc(typeNull,NULL,0,&application_descriptor);
+ AECreateDesc(typeChar,directory,strlen(directory),&path_descriptor);
+ (void) AECreateDesc(typeType,&folder_type,sizeof(DescType),&type_descriptor);
+ CreateObjSpecifier(cProperty,&application_descriptor,formPropertyID,
+ &type_descriptor,0,&object);
+ (void) AECreateDesc(typeApplSignature,&id,sizeof(id),&event_descriptor);
+ (void) AECreateAppleEvent(kAECoreSuite,kAESetData,&event_descriptor,-1,
+ kAnyTransactionID,&event);
+ (void) AEPutParamDesc(&event,keyDirectObject,&object);
+ (void) AEPutParamDesc(&event,keyAEData,&path_descriptor);
+ error=AESend(&event,&reply,kAENoReply+kAENeverInteract,kAENormalPriority,
+ kNoTimeOut,NULL,NULL);
+ (void) AEDisposeDesc(&event);
+ (void) AEDisposeDesc(&event_descriptor);
+ (void) AEDisposeDesc(&object);
+ (void) AEDisposeDesc(&type_descriptor);
+ (void) AEDisposeDesc(&path_descriptor);
+ (void) AEDisposeDesc(&application_descriptor);
+ return(error);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M A C S e t E r r o r H o o k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACSetErrorHook sets a callback function which is called if any error
+% occurs within ImageMagick.
+%
+% The format of the MACSetErrorHook method is:
+%
+% int MACSetErrorHook(MACErrorHookPtr hook)
+%
+% A description of each parameter follows:
+%
+% o hook: This function pointer is the callback function.
+%
+%
+*/
+MagickExport void MACSetErrorHook(MACErrorHookPtr hook)
+{
+ /*
+ We forget any previously set exception.hook.
+ */
+ exception.hook=hook;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M A C S e t E v e n t H o o k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACSetEventHook sets a callback function which is called every time
+% ImageMagick likes to release the processor.
+%
+% The format of the MACSetEventHook method is:
+%
+% int MACSetEventHook(MACEventHookPtr hook)
+%
+% A description of each parameter follows:
+%
+% o hook: This function pointer is the callback function.
+%
+%
+*/
+MagickExport void MACSetEventHook(MACEventHookPtr hook)
+{
+ /*
+ We forget any previously set event hook.
+ */
+ event_hook=hook;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M A C S y s t e m C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Method MACSystemCommand executes the specified command and waits until it
+% terminates. The returned value is the exit status of the command.
+%
+% The format of the MACSystemCommand method is:
+%
+% int MACSystemCommand(const char * command)
+%
+% A description of each parameter follows:
+%
+% o command: This string is the command to execute.
+%
+%
+*/
+MagickExport int MACSystemCommand(const char * command)
+{
+ /*
+ We only know how to launch Ghostscript.
+ */
+ if (MacGSLaunchApplicationToFront())
+ return(-1);
+ return(MacGSExecuteCommand(command,strlen(command)));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M A C W a r n i n g H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MACWarningHandler() displays a warning reason.
+%
+% The format of the MACWarningHandler method is:
+%
++ void MACWarningHandler(const ExceptionType warning,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o warning: Specifies the numeric warning category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+%
+*/
+MagickExport void MACWarningHandler(const ExceptionType warning,
+ const char *reason,const char *description)
+{
+ char
+ buffer[1664];
+
+ if (reason == (char *) NULL)
+ return;
+ if (description == (char *) NULL)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s.\n",GetClientName(),
+ reason);
+ else
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s).\n",
+ GetClientName(),reason,description);
+#if defined(DISABLE_SIOUX)
+ if(exception.hook != (MACErrorHookPtr) NULL)
+ exception.hook(warning, buffer);
+#else
+ (void)warning;
+ puts(buffer);
+#endif
+}
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% o p e n d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% opendir() opens the directory named by filename and associates a directory
+% stream with it.
+%
+% The format of the opendir method is:
+%
+% MagickExport DIR *opendir(char *path)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+MagickExport DIR *opendir(const char *path)
+{
+ Str255 pathname;
+
+ CInfoPBRec
+ search_info;
+
+ DIR
+ *entry;
+
+ int
+ error;
+
+ search_info.hFileInfo.ioNamePtr=0;
+ if ((path != (char *) NULL) || (*path != '\0'))
+ if ((path[0] != '.') || (path[1] != '\0'))
+ {
+ c2pstrcpy(pathname,path);
+ search_info.hFileInfo.ioNamePtr=pathname;
+ }
+ search_info.hFileInfo.ioCompletion=0;
+ search_info.hFileInfo.ioVRefNum=0;
+ search_info.hFileInfo.ioFDirIndex=0;
+ search_info.hFileInfo.ioDirID=0;
+ error=PBGetCatInfoSync(&search_info);
+ if (error != noErr)
+ {
+ errno=error;
+ return((DIR *) NULL);
+ }
+ entry=(DIR *) AcquireMagickMemory(sizeof(DIR));
+ if (entry == (DIR *) NULL)
+ return((DIR *) NULL);
+ entry->d_VRefNum=search_info.hFileInfo.ioVRefNum;
+ entry->d_DirID=search_info.hFileInfo.ioDirID;
+ entry->d_index=1;
+ return(entry);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r o c e s s P e n d i n g E v e n t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ProcessPendingEvents() processes any pending events. This prevents
+% ImageMagick from monopolizing the processor.
+%
+% The format of the ProcessPendingEvents method is:
+%
+% ProcessPendingEvents(text)
+%
+% A description of each parameter follows:
+%
+% o text: A character string representing the current process.
+%
+%
+*/
+MagickExport void ProcessPendingEvents(const char *text)
+{
+#if defined(DISABLE_SIOUX)
+ if (event_hook != (MACEventHookPtr) NULL)
+ event_hook(text);
+#else
+ static const char
+ *mark = (char *) NULL;
+
+ EventRecord
+ event;
+
+ while (WaitNextEvent(everyEvent,&event,0L,nil))
+ SIOUXHandleOneEvent(&event);
+ if (isatty(STDIN_FILENO) && (text != mark))
+ {
+ (void) puts(text);
+ mark=text;
+ }
+#endif
+}
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% r e a d d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% readdir() returns a pointer to a structure representing the directory entry
+% at the current position in the directory stream to which entry refers.
+%
+% The format of the readdir
+%
+% struct dirent *readdir(DIR *entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+MagickExport struct dirent *readdir(DIR *entry)
+{
+ CInfoPBRec
+ search_info;
+
+ int
+ error;
+
+ static struct dirent
+ dir_entry;
+
+ static unsigned char
+ pathname[MaxTextExtent];
+
+ if (entry == (DIR *) NULL)
+ return((struct dirent *) NULL);
+ search_info.hFileInfo.ioCompletion=0;
+ search_info.hFileInfo.ioNamePtr=pathname;
+ search_info.hFileInfo.ioVRefNum=0;
+ search_info.hFileInfo.ioFDirIndex=entry->d_index;
+ search_info.hFileInfo.ioDirID=entry->d_DirID;
+ error=PBGetCatInfoSync(&search_info);
+ if (error != noErr)
+ {
+ errno=error;
+ return((struct dirent *) NULL);
+ }
+ entry->d_index++;
+ p2cstrcpy(dir_entry.d_name,search_info.hFileInfo.ioNamePtr);
+ dir_entry.d_namlen=strlen(dir_entry.d_name);
+ return(&dir_entry);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d P I C T I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadPICTImage() reads an Apple Macintosh QuickDraw/PICT image file using
+% MacOS QuickDraw methods and returns it. It allocates the memory necessary
+% for the new Image structure and returns a pointer to the new image.
+%
+% This method was written and contributed by [email protected]
+% (feel free to copy and use it as you want. No warranty).
+%
+% The format of the ReadPICTImage method is:
+%
+% Image *ReadPICTImage(const ImageInfo *image_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: Method ReadPICTImage returns a pointer to the image after
+% reading. A null image is returned if there is a memory shortage or
+% if the image cannot be read.
+%
+% o image_info: the image info..
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline size_t MagickMax(const size_t x,const size_t y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+MagickExport Image *ReadPICTImage(const ImageInfo *image_info,
+ ExceptionInfo *exception)
+{
+#define PICTHeaderSize 512
+
+ CodecType
+ codec;
+
+ GDHandle
+ device;
+
+ GWorldPtr
+ graphic_world,
+ port;
+
+ Image
+ *image;
+
+ int
+ depth,
+ status;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed,
+ status;
+
+ PicHandle
+ picture_handle;
+
+ PictInfo
+ picture_info;
+
+ QDErr
+ theErr = noErr;
+
+ Rect
+ rectangle;
+
+ RGBColor
+ Pixel;
+
+ short
+ colormap_id;
+
+ /*
+ Open image file.
+ */
+ image=AcquireImage(image_info);
+ status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
+ if (status == MagickFalse)
+ return(NULL);
+ picture_handle=(PicHandle) NewHandle(MagickMax(GetBlobSize(image)-
+ PICTHeaderSize,PICTHeaderSize));
+ if (picture_handle == nil)
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ HLock((Handle) picture_handle);
+ (void) ReadBlob(image,PICTHeaderSize,*(unsigned char **) picture_handle);
+ status=ReadBlob(image,GetBlobSize(image)-PICTHeaderSize,*(unsigned char **)
+ picture_handle);
+ if (status == MagickFalse)
+ {
+ DisposeHandle((Handle) picture_handle);
+ ThrowReaderException(CorruptImageError,"UnableToReadImageData");
+ }
+ GetGWorld(&port,&device);
+ theErr=NewGWorld(&graphic_world,0,&(**picture_handle).picFrame,nil,nil,
+ useTempMem | keepLocal);
+ if ((theErr != noErr) && (graphic_world == nil))
+ {
+ DisposeHandle((Handle) picture_handle);
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ HUnlock((Handle) picture_handle);
+ SetGWorld(graphic_world,nil);
+ theErr=GetPictInfo(picture_handle,&picture_info,0,1,systemMethod,0);
+ if (theErr != noErr)
+ {
+ DisposeGWorld(graphic_world);
+ DisposeHandle((Handle) picture_handle);
+ ThrowReaderException(CorruptImageError,"UnableToReadImageData");
+ }
+#if defined(DISABLE_QUICKTIME)
+ codec='unkn';
+ colormap_id=(-1);
+ depth=picture_info.depth;
+#else
+ BottleneckTest(picture_handle,&codec,&depth,&colormap_id);
+#endif
+ switch (codec)
+ {
+ case 'rpza':
+ case 'jpeg':
+ case 'rle ':
+ case 'raw ':
+ case 'smc ':
+ {
+ if (depth > 200)
+ {
+ depth-=32;
+ picture_info.theColorTable=GetCTable(colormap_id);
+ }
+ break;
+ }
+ default:
+ {
+ depth=picture_info.depth;
+ if (depth <= 8)
+ (void) GetPictInfo(picture_handle,&picture_info,returnColorTable,
+ (short) (1 << picture_info.depth),systemMethod,0);
+ break;
+ }
+ }
+ image->x_resolution=(picture_info.hRes) >> 16;
+ image->y_resolution=(picture_info.vRes) >> 16;
+ image->units=PixelsPerInchResolution;
+ image->columns=picture_info.sourceRect.right-picture_info.sourceRect.left;
+ image->rows=picture_info.sourceRect.bottom-picture_info.sourceRect.top;
+ if ((depth <= 8) && ((*(picture_info.theColorTable))->ctSize != 0))
+ {
+ unsigned long
+ number_colors;
+
+ /*
+ Colormapped PICT image.
+ */
+ number_colors=(*(picture_info.theColorTable))->ctSize;
+ if (!AcquireImageColormap(image,number_colors))
+ {
+ if (picture_info.theColorTable != nil)
+ DisposeHandle((Handle) picture_info.theColorTable);
+ DisposeGWorld(graphic_world);
+ DisposeHandle((Handle) picture_handle);
+ ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ for (x=0; x < image->colors; x++)
+ {
+ image->colormap[x].red=
+ (*(picture_info.theColorTable))->ctTable[x].rgb.red;
+ image->colormap[x].green=
+ (*(picture_info.theColorTable))->ctTable[x].rgb.green;
+ image->colormap[x].blue=
+ (*(picture_info.theColorTable))->ctTable[x].rgb.blue;
+ }
+ }
+ SetRect(&rectangle,0,0,image->columns,image->rows);
+ (void) UpdateGWorld(&graphic_world,depth,&rectangle,
+ picture_info.theColorTable,nil,0);
+ LockPixels(GetGWorldPixMap(graphic_world)); /*->portPixMap); */
+ EraseRect(&rectangle);
+ DrawPicture(picture_handle,&rectangle);
+ if ((depth <= 8) && (colormap_id == -1))
+ {
+ DisposeHandle((Handle) picture_info.theColorTable);
+ picture_info.theColorTable=nil;
+ }
+ DisposeHandle((Handle) picture_handle);
+ /*
+ Convert PICT pixels to pixel packets.
+ */
+ for (y=0; y < image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < image->columns; x++)
+ {
+ GetCPixel(x,y,&Pixel);
+ q->red=ScaleCharToQuantum(Pixel.red & 0xff);
+ q->green=ScaleCharToQuantum(Pixel.green & 0xff);
+ q->blue=ScaleCharToQuantum(Pixel.blue & 0xff);
+ if (image->storage_class == PseudoClass)
+ indexes[x]=Color2Index(&Pixel);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,LoadImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ UnlockPixels(GetGWorldPixMap(graphic_world));
+ SetGWorld(port,device);
+ if (picture_info.theColorTable != nil)
+ DisposeHandle((Handle) picture_info.theColorTable);
+ DisposeGWorld(graphic_world);
+ (void) CloseBlob(image);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e a r c h F o r F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SearchForFile() searches for a file.
+%
+%
+*/
+static Boolean SearchForFile(OSType creator_type,OSType file_type,FSSpec *file,
+ short count)
+{
+ char
+ *buffer;
+
+ CInfoPBRec
+ search1_info,
+ search2_info;
+
+ FSSpec
+ application;
+
+ HParamBlockRec
+ parameter_info;
+
+ long
+ buffer_size = 16384;
+
+ OSErr
+ error;
+
+ ProcessInfoRec
+ application_info;
+
+ ProcessSerialNumber
+ serial_number;
+
+ serial_number.lowLongOfPSN=kCurrentProcess;
+ serial_number.highLongOfPSN=0;
+ application_info.processInfoLength=sizeof(ProcessInfoRec);
+ application_info.processName=NULL;
+ application_info.processAppSpec=(&application);
+ GetProcessInformation(&serial_number,&application_info);
+ buffer=NewPtr(buffer_size);
+ if (buffer == (char *) NULL)
+ return(false);
+ parameter_info.csParam.ioCompletion=NULL;
+ parameter_info.csParam.ioNamePtr=NULL;
+ parameter_info.csParam.ioVRefNum=application.vRefNum;
+ parameter_info.csParam.ioMatchPtr=file;
+ parameter_info.csParam.ioReqMatchCount=count;
+ parameter_info.csParam.ioSearchBits=fsSBFlFndrInfo;
+ parameter_info.csParam.ioSearchInfo1=&search1_info;
+ parameter_info.csParam.ioSearchInfo2=&search2_info;
+ parameter_info.csParam.ioSearchTime=0;
+ parameter_info.csParam.ioCatPosition.initialize=0;
+ parameter_info.csParam.ioOptBuffer=buffer;
+ parameter_info.csParam.ioOptBufSize=buffer_size;
+ search1_info.hFileInfo.ioNamePtr=NULL;
+ search1_info.hFileInfo.ioFlFndrInfo.fdType=file_type;
+ search1_info.hFileInfo.ioFlFndrInfo.fdCreator=creator_type;
+ search1_info.hFileInfo.ioFlAttrib=0;
+ search1_info.hFileInfo.ioFlParID=0;
+ search2_info=search1_info;
+ search2_info.hFileInfo.ioFlAttrib=0x10;
+ search2_info.hFileInfo.ioFlFndrInfo.fdCreator=creator_type;
+ search2_info.hFileInfo.ioFlFndrInfo.fdType=(-1);
+ search2_info.hFileInfo.ioFlFndrInfo.fdFlags=0;
+ search2_info.hFileInfo.ioFlFndrInfo.fdLocation.h=0;
+ search2_info.hFileInfo.ioFlFndrInfo.fdLocation.v=0;
+ search2_info.hFileInfo.ioFlFndrInfo.fdFldr=0;
+ search2_info.hFileInfo.ioFlParID=0;
+ error=PBCatSearchSync((CSParamPtr) ¶meter_info);
+ DisposePtr(buffer);
+ if (parameter_info.csParam.ioReqMatchCount ==
+ parameter_info.csParam.ioActMatchCount)
+ error=eofErr;
+ if (parameter_info.csParam.ioActMatchCount == 0)
+ error=0;
+ return(error == eofErr);
+}
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% s e e k d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% seekdir() sets the position of the next readdir() operation on the directory
+% stream.
+%
+% The format of the seekdir method is:
+%
+% void seekdir(DIR *entry,long position)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+% o position: specifies the position associated with the directory
+% stream.
+%
+%
+%
+*/
+MagickExport void seekdir(DIR *entry,long position)
+{
+ assert(entry != (DIR *) NULL);
+ entry->d_index=position;
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t A p p l i c a t i o n T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetApplicationType() sets the file type of an image.
+%
+% The format of the SetApplicationType method is:
+%
+% void SetApplicationType(const char *filename,const char *magick,
+% OSType application)
+%
+% A description of each parameter follows:
+%
+% o filename: Specifies the name of the file.
+%
+% o filename: Specifies the file type.
+%
+% o application: Specifies the type of the application.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport void SetApplicationType(const char *filename,const char *magick,
+ OSType application)
+{
+ FSSpec
+ file_specification;
+
+ OSType
+ filetype;
+
+ Str255
+ name;
+
+ assert(filename != (char *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(magick != (const char *) NULL);
+ filetype=' ';
+ (void) CopyMagickString((char *) &filetype,magick,MagickMin(strlen(magick),
+ 4));
+ if (LocaleCompare(magick,"JPG") == 0)
+ (void) CopyMagickString((char *) &filetype,"JPEG",MaxTextExtent);
+ c2pstrcpy(name,filename);
+ FSMakeFSSpec(0,0,name,&file_specification);
+ FSpCreate(&file_specification,application,filetype,smSystemScript);
+}
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% t e l l d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Method telldir returns the current location associated with the
+% named directory stream.
+%
+% The format of the telldir method is:
+%
+% telldir(DIR *entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+MagickExport long telldir(DIR *entry)
+{
+ return(entry->d_index);
+}
+#endif
+
+#endif
diff --git a/magick/mac.h b/magick/mac.h
new file mode 100644
index 0000000..16ac282
--- /dev/null
+++ b/magick/mac.h
@@ -0,0 +1,111 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore utility methods.
+*/
+#ifndef _MAGICKCORE_MAC_H
+#define _MAGICKCORE_MAC_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <locale.h>
+#include <Errors.h>
+#include <Files.h>
+#include <errno.h>
+
+#if defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+# include <dirent.h>
+# if !defined(DISABLE_SIOUX)
+# include <SIOUX.h>
+# endif
+#else
+# include <stat.h>
+
+#define S_IREAD 00400
+#define S_IWRITE 00200
+
+typedef struct _DIR
+{
+ int
+ d_VRefNum;
+
+ long int
+ d_DirID;
+
+ int
+ d_index;
+} DIR;
+
+struct dirent
+{
+ char
+ d_name[255];
+
+ int
+ d_namlen;
+};
+#endif
+
+MagickExport Image
+ *ReadPICTImage(const ImageInfo *,ExceptionInfo *);
+
+extern MagickExport int
+ Exit(int),
+ MACSystemCommand(const char *);
+
+extern MagickExport MagickBooleanType
+ MACIsMagickConflict(const char *);
+
+extern MagickExport void
+ MACErrorHandler(const ExceptionType,const char *,const char *),
+ MACWarningHandler(const ExceptionType,const char *,const char *),
+ ProcessPendingEvents(const char *),
+ SetApplicationType(const char *,const char *,OSType);
+
+#if defined(DISABLE_SIOUX)
+typedef void
+ (*MACEventHookPtr)(const char *);
+
+typedef void
+ (*MACErrorHookPtr)(const short,const char *text);
+
+extern MagickExport void
+ MACSetErrorHook(MACErrorHookPtr),
+ MACSetEventHook(MACEventHookPtr),
+ MACFatalErrorHandler(const ExceptionType,const char *,const char *);
+#endif
+
+#if !defined(_MAGICKCORE_POSIX_SUPPORT_VERSION)
+extern MagickExport DIR
+ *opendir(const char *);
+
+extern MagickExport long
+ telldir(DIR *);
+
+extern MagickExport struct dirent
+ *readdir(DIR *);
+
+extern MagickExport void
+ seekdir(DIR *,long),
+ closedir(DIR *);
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/magic.c b/magick/magic.c
new file mode 100644
index 0000000..271a055
--- /dev/null
+++ b/magick/magic.c
@@ -0,0 +1,954 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% M M AAA GGGG IIIII CCCC %
+% MM MM A A G I C %
+% M M M AAAAA G GGG I C %
+% M M A A G G I C %
+% M M A A GGGG IIIII CCCC %
+% %
+% %
+% MagickCore Image Magic Methods %
+% %
+% Software Design %
+% Bob Friesenhahn %
+% July 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/magic.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define MagicFilename "magic.xml"
+
+/*
+ Static declarations.
+*/
+static const char
+ *MagicMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<magicmap>"
+ " <magic name=\"AVI\" offset=\"0\" target=\"RIFF\" />"
+ " <magic name=\"8BIMWTEXT\" offset=\"0\" target=\"8\\000B\\000I\\000M\\000#\" />"
+ " <magic name=\"8BIMTEXT\" offset=\"0\" target=\"8BIM#\" />"
+ " <magic name=\"8BIM\" offset=\"0\" target=\"8BIM\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"BA\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"BM\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"CI\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"CP\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"IC\" />"
+ " <magic name=\"BMP\" offset=\"0\" target=\"PI\" />"
+ " <magic name=\"CIN\" offset=\"0\" target=\"\\200\\052\\137\\327\" />"
+ " <magic name=\"CGM\" offset=\"0\" target=\"BEGMF\" />"
+ " <magic name=\"DCM\" offset=\"128\" target=\"DICM\" />"
+ " <magic name=\"DCX\" offset=\"0\" target=\"\\261\\150\\336\\72\" />"
+ " <magic name=\"DDS\" offset=\"0\" target=\"DDS \" />"
+ " <magic name=\"DIB\" offset=\"0\" target=\"\\050\\000\" />"
+ " <magic name=\"DJVU\" offset=\"0\" target=\"AT&TFORM\" />"
+ " <magic name=\"DOT\" offset=\"0\" target=\"digraph\" />"
+ " <magic name=\"DPX\" offset=\"0\" target=\"SDPX\" />"
+ " <magic name=\"DPX\" offset=\"0\" target=\"XPDS\" />"
+ " <magic name=\"EMF\" offset=\"40\" target=\"\\040\\105\\115\\106\\000\\000\\001\\000\" />"
+ " <magic name=\"EPT\" offset=\"0\" target=\"\\305\\320\\323\\306\" />"
+ " <magic name=\"EXR\" offset=\"0\" target=\"\\166\\057\\061\\001\" />"
+ " <magic name=\"FAX\" offset=\"0\" target=\"DFAX\" />"
+ " <magic name=\"FIG\" offset=\"0\" target=\"#FIG\" />"
+ " <magic name=\"FITS\" offset=\"0\" target=\"IT0\" />"
+ " <magic name=\"FITS\" offset=\"0\" target=\"SIMPLE\" />"
+ " <magic name=\"FPX\" offset=\"0\" target=\"\\320\\317\\021\\340\" />"
+ " <magic name=\"GIF\" offset=\"0\" target=\"GIF8\" />"
+ " <magic name=\"GPLT\" offset=\"0\" target=\"#!/usr/local/bin/gnuplot\" />"
+ " <magic name=\"HDF\" offset=\"1\" target=\"HDF\" />"
+ " <magic name=\"HPGL\" offset=\"0\" target=\"IN;\" />"
+ " <magic name=\"HTML\" offset=\"1\" target=\"HTML\" />"
+ " <magic name=\"HTML\" offset=\"1\" target=\"html\" />"
+ " <magic name=\"ILBM\" offset=\"8\" target=\"ILBM\" />"
+ " <magic name=\"IPTCWTEXT\" offset=\"0\" target=\"\\062\\000#\\000\\060\\000=\\000\\042\\000&\\000#\\000\\060\\000;\\000&\\000#\\000\\062\\000;\\000\\042\\000\" />"
+ " <magic name=\"IPTCTEXT\" offset=\"0\" target=\"2#0=\\042�\\042\" />"
+ " <magic name=\"IPTC\" offset=\"0\" target=\"\\034\\002\" />"
+ " <magic name=\"JNG\" offset=\"0\" target=\"\\213JNG\\r\\n\\032\\n\" />"
+ " <magic name=\"JPEG\" offset=\"0\" target=\"\\377\\330\\377\" />"
+ " <magic name=\"JPC\" offset=\"0\" target=\"\\377\\117\" />"
+ " <magic name=\"JP2\" offset=\"4\" target=\"\\152\\120\\040\\040\\015\" />"
+ " <magic name=\"MIFF\" offset=\"0\" target=\"Id=ImageMagick\" />"
+ " <magic name=\"MIFF\" offset=\"0\" target=\"id=ImageMagick\" />"
+ " <magic name=\"MNG\" offset=\"0\" target=\"\\212MNG\\r\\n\\032\\n\" />"
+ " <magic name=\"MPC\" offset=\"0\" target=\"id=MagickCache\" />"
+ " <magic name=\"MPEG\" offset=\"0\" target=\"\\000\\000\\001\\263\" />"
+ " <magic name=\"MVG\" offset=\"0\" target=\"push graphic-context\" />"
+ " <magic name=\"PCD\" offset=\"2048\" target=\"PCD_\" />"
+ " <magic name=\"PCL\" offset=\"0\" target=\"\\033E\\033\" />"
+ " <magic name=\"PCX\" offset=\"0\" target=\"\\012\\002\" />"
+ " <magic name=\"PCX\" offset=\"0\" target=\"\\012\\005\" />"
+ " <magic name=\"PDB\" offset=\"60\" target=\"vIMGView\" />"
+ " <magic name=\"PDF\" offset=\"0\" target=\"%PDF-\" />"
+ " <magic name=\"PFA\" offset=\"0\" target=\"%!PS-AdobeFont-1.0\" />"
+ " <magic name=\"PFB\" offset=\"6\" target=\"%!PS-AdobeFont-1.0\" />"
+ " <magic name=\"PGX\" offset=\"0\" target=\"\\050\\107\\020\\115\\046\" />"
+ " <magic name=\"PICT\" offset=\"522\" target=\"\\000\\021\\002\\377\\014\\000\" />"
+ " <magic name=\"PNG\" offset=\"0\" target=\"\\211PNG\\r\\n\\032\\n\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P1\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P2\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P3\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P4\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P5\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P6\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"P7\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"PF\" />"
+ " <magic name=\"PNM\" offset=\"0\" target=\"Pf\" />"
+ " <magic name=\"PS\" offset=\"0\" target=\"%!\" />"
+ " <magic name=\"PS\" offset=\"0\" target=\"\\004%!\" />"
+ " <magic name=\"PS\" offset=\"0\" target=\"\\305\\320\\323\\306\" />"
+ " <magic name=\"PSD\" offset=\"0\" target=\"8BPS\" />"
+ " <magic name=\"PWP\" offset=\"0\" target=\"SFW95\" />"
+ " <magic name=\"RAD\" offset=\"0\" target=\"#?RADIANCE\" />"
+ " <magic name=\"RAD\" offset=\"0\" target=\"VIEW= \" />"
+ " <magic name=\"RLE\" offset=\"0\" target=\"\\122\\314\" />"
+ " <magic name=\"SCT\" offset=\"0\" target=\"CT\" />"
+ " <magic name=\"SFW\" offset=\"0\" target=\"SFW94\" />"
+ " <magic name=\"SGI\" offset=\"0\" target=\"\\001\\332\" />"
+ " <magic name=\"SUN\" offset=\"0\" target=\"\\131\\246\\152\\225\" />"
+ " <magic name=\"SVG\" offset=\"1\" target=\"?XML\" />"
+ " <magic name=\"SVG\" offset=\"1\" target=\"?xml\" />"
+ " <magic name=\"TXT\" offset=\"0\" target=\"# ImageMagick pixel enumeration:\" />"
+ " <magic name=\"TIFF\" offset=\"0\" target=\"\\115\\115\\000\\052\" />"
+ " <magic name=\"TIFF\" offset=\"0\" target=\"\\111\\111\\052\\000\" />"
+ " <magic name=\"TIFF64\" offset=\"0\" target=\"\\115\\115\\000\\053\\000\\010\\000\\000\" />"
+ " <magic name=\"TIFF64\" offset=\"0\" target=\"\\115\\115\\000\\053\\000\\010\\000\\000\" />"
+ " <magic name=\"VICAR\" offset=\"0\" target=\"LBLSIZE\" />"
+ " <magic name=\"VICAR\" offset=\"0\" target=\"NJPL1I\" />"
+ " <magic name=\"VIFF\" offset=\"0\" target=\"\\253\\001\" />"
+ " <magic name=\"WMF\" offset=\"0\" target=\"\\327\\315\\306\\232\" />"
+ " <magic name=\"WMF\" offset=\"0\" target=\"\\001\\000\\011\\000\" />"
+ " <magic name=\"WPG\" offset=\"0\" target=\"\\377WPC\" />"
+ " <magic name=\"XBM\" offset=\"0\" target=\"#define\" />"
+ " <magic name=\"XCF\" offset=\"0\" target=\"gimp xcf\" />"
+ " <magic name=\"XPM\" offset=\"1\" target=\"* XPM *\" />"
+ " <magic name=\"XWD\" offset=\"4\" target=\"\\007\\000\\000\" />"
+ " <magic name=\"XWD\" offset=\"5\" target=\"\\000\\000\\007\" />"
+ "</magicmap>";
+
+static LinkedListInfo
+ *magic_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *magic_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_magic = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeMagicList(ExceptionInfo *),
+ LoadMagicLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M a g i c L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagicList() deallocates memory associated with the magic list.
+%
+% The format of the DestroyMagicList method is:
+%
+% DestroyMagicList(void)
+%
+*/
+
+static void *DestroyMagicElement(void *magic_info)
+{
+ register MagicInfo
+ *p;
+
+ p=(MagicInfo *) magic_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->target != (char *) NULL)
+ p->target=DestroyString(p->target);
+ if (p->magic != (unsigned char *) NULL)
+ p->magic=(unsigned char *) RelinquishMagickMemory(p->magic);
+ p=(MagicInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyMagicList(void)
+{
+ AcquireSemaphoreInfo(&magic_semaphore);
+ if (magic_list != (LinkedListInfo *) NULL)
+ magic_list=DestroyLinkedList(magic_list,DestroyMagicElement);
+ instantiate_magic=MagickFalse;
+ RelinquishSemaphoreInfo(magic_semaphore);
+ DestroySemaphoreInfo(&magic_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagicInfo() searches the magic list for the specified name and if found
+% returns attributes for that magic.
+%
+% The format of the GetMagicInfo method is:
+%
+% const MagicInfo *GetMagicInfo(const unsigned char *magic,
+% const size_t length,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o magic: A binary string generally representing the first few characters
+% of the image file or blob.
+%
+% o length: the length of the binary signature.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const MagicInfo *GetMagicInfo(const unsigned char *magic,
+ const size_t length,ExceptionInfo *exception)
+{
+ register const MagicInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((magic_list == (LinkedListInfo *) NULL) ||
+ (instantiate_magic == MagickFalse))
+ if (InitializeMagicList(exception) == MagickFalse)
+ return((const MagicInfo *) NULL);
+ if ((magic_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(magic_list) != MagickFalse))
+ return((const MagicInfo *) NULL);
+ if (magic == (const unsigned char *) NULL)
+ return((const MagicInfo *) GetValueFromLinkedList(magic_list,0));
+ if (length == 0)
+ return((const MagicInfo *) NULL);
+ /*
+ Search for magic tag.
+ */
+ AcquireSemaphoreInfo(&magic_semaphore);
+ ResetLinkedListIterator(magic_list);
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ while (p != (const MagicInfo *) NULL)
+ {
+ assert(p->offset >= 0);
+ if (((size_t) (p->offset+p->length) <= length) &&
+ (memcmp(magic+p->offset,p->magic,p->length) == 0))
+ break;
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ }
+ if (p != (const MagicInfo *) NULL)
+ (void) InsertValueInLinkedList(magic_list,0,
+ RemoveElementByValueFromLinkedList(magic_list,p));
+ RelinquishSemaphoreInfo(magic_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagicInfoList() returns any image aliases that match the specified
+% pattern.
+%
+% The magic of the GetMagicInfoList function is:
+%
+% const MagicInfo **GetMagicInfoList(const char *pattern,
+% unsigned long *number_aliases,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_aliases: This integer returns the number of aliases in the
+% list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MagicInfoCompare(const void *x,const void *y)
+{
+ const MagicInfo
+ **p,
+ **q;
+
+ p=(const MagicInfo **) x,
+ q=(const MagicInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const MagicInfo **GetMagicInfoList(const char *pattern,
+ unsigned long *number_aliases,ExceptionInfo *exception)
+{
+ const MagicInfo
+ **aliases;
+
+ register const MagicInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate magic list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_aliases != (unsigned long *) NULL);
+ *number_aliases=0;
+ p=GetMagicInfo((const unsigned char *) "*",0,exception);
+ if (p == (const MagicInfo *) NULL)
+ return((const MagicInfo **) NULL);
+ aliases=(const MagicInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(magic_list)+1UL,sizeof(*aliases));
+ if (aliases == (const MagicInfo **) NULL)
+ return((const MagicInfo **) NULL);
+ /*
+ Generate magic list.
+ */
+ AcquireSemaphoreInfo(&magic_semaphore);
+ ResetLinkedListIterator(magic_list);
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ for (i=0; p != (const MagicInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ aliases[i++]=p;
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ }
+ RelinquishSemaphoreInfo(magic_semaphore);
+ qsort((void *) aliases,(size_t) i,sizeof(*aliases),MagicInfoCompare);
+ aliases[i]=(MagicInfo *) NULL;
+ *number_aliases=(unsigned long) i;
+ return(aliases);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagicList() returns any image format aliases that match the specified
+% pattern.
+%
+% The format of the GetMagicList function is:
+%
+% char **GetMagicList(const char *pattern,unsigned long *number_aliases,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_aliases: This integer returns the number of image format aliases
+% in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MagicCompare(const void *x,const void *y)
+{
+ register const char
+ *p,
+ *q;
+
+ p=(const char *) x;
+ q=(const char *) y;
+ return(LocaleCompare(p,q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetMagicList(const char *pattern,
+ unsigned long *number_aliases,ExceptionInfo *exception)
+{
+ char
+ **aliases;
+
+ register const MagicInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate configure list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_aliases != (unsigned long *) NULL);
+ *number_aliases=0;
+ p=GetMagicInfo((const unsigned char *) "*",0,exception);
+ if (p == (const MagicInfo *) NULL)
+ return((char **) NULL);
+ aliases=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(magic_list)+1UL,sizeof(*aliases));
+ if (aliases == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&magic_semaphore);
+ ResetLinkedListIterator(magic_list);
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ for (i=0; p != (const MagicInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ aliases[i++]=ConstantString(p->name);
+ p=(const MagicInfo *) GetNextValueInLinkedList(magic_list);
+ }
+ RelinquishSemaphoreInfo(magic_semaphore);
+ qsort((void *) aliases,(size_t) i,sizeof(*aliases),MagicCompare);
+ aliases[i]=(char *) NULL;
+ *number_aliases=(unsigned long) i;
+ return(aliases);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagicName() returns the name associated with the magic.
+%
+% The format of the GetMagicName method is:
+%
+% const char *GetMagicName(const MagicInfo *magic_info)
+%
+% A description of each parameter follows:
+%
+% o magic_info: The magic info.
+%
+*/
+MagickExport const char *GetMagicName(const MagicInfo *magic_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magic_info != (MagicInfo *) NULL);
+ assert(magic_info->signature == MagickSignature);
+ return(magic_info->name);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e M a g i c L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeMagicList() initializes the magic list.
+%
+% The format of the InitializeMagicList method is:
+%
+% MagickBooleanType InitializeMagicList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeMagicList(ExceptionInfo *exception)
+{
+ if ((magic_list == (LinkedListInfo *) NULL) &&
+ (instantiate_magic == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&magic_semaphore);
+ if ((magic_list == (LinkedListInfo *) NULL) &&
+ (instantiate_magic == MagickFalse))
+ {
+ (void) LoadMagicLists(MagicFilename,exception);
+ instantiate_magic=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(magic_semaphore);
+ }
+ return(magic_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t M a g i c I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListMagicInfo() lists the magic info to a file.
+%
+% The format of the ListMagicInfo method is:
+%
+% MagickBooleanType ListMagicInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListMagicInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const char
+ *path;
+
+ const MagicInfo
+ **magic_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_aliases;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ magic_info=GetMagicInfoList("*",&number_aliases,exception);
+ if (magic_info == (const MagicInfo **) NULL)
+ return(MagickFalse);
+ j=0;
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_aliases; i++)
+ {
+ if (magic_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (LocaleCompare(path,magic_info[i]->path) != 0))
+ {
+ if (magic_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",magic_info[i]->path);
+ (void) fprintf(file,"Name Offset Target\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=magic_info[i]->path;
+ (void) fprintf(file,"%s",magic_info[i]->name);
+ for (j=(long) strlen(magic_info[i]->name); j <= 9; j++)
+ (void) fprintf(file," ");
+ (void) fprintf(file,"%6ld ",(long) magic_info[i]->offset);
+ if (magic_info[i]->target != (char *) NULL)
+ (void) fprintf(file,"%s",magic_info[i]->target);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ magic_info=(const MagicInfo **) RelinquishMagickMemory((void *) magic_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d M a g i c L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadMagicList() loads the magic configuration file which provides a mapping
+% between magic attributes and a magic name.
+%
+% The format of the LoadMagicList method is:
+%
+% MagickBooleanType LoadMagicList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The magic list in XML format.
+%
+% o filename: The magic list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadMagicList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ MagickBooleanType
+ status;
+
+ MagicInfo
+ *magic_info;
+
+ /*
+ Load the magic map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading magic configure file \"%s\" ...",filename);
+ if (xml == (char *) NULL)
+ return(MagickFalse);
+ if (magic_list == (LinkedListInfo *) NULL)
+ magic_list=NewLinkedList(0);
+ status=MagickTrue;
+ magic_info=(MagicInfo *) NULL;
+ token=AcquireString(xml);
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadMagicList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<magic") == 0)
+ {
+ /*
+ Magic element.
+ */
+ magic_info=(MagicInfo *) AcquireMagickMemory(sizeof(*magic_info));
+ if (magic_info == (MagicInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(magic_info,0,sizeof(*magic_info));
+ magic_info->path=ConstantString(filename);
+ magic_info->signature=MagickSignature;
+ continue;
+ }
+ if (magic_info == (MagicInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AppendValueToLinkedList(magic_list,magic_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ magic_info->name);
+ magic_info=(MagicInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ magic_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'O':
+ case 'o':
+ {
+ if (LocaleCompare((char *) keyword,"offset") == 0)
+ {
+ magic_info->offset=(MagickOffsetType) atol(token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ magic_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if (LocaleCompare((char *) keyword,"target") == 0)
+ {
+ char
+ *p;
+
+ register unsigned char
+ *q;
+
+ size_t
+ length;
+
+ length=strlen(token);
+ magic_info->target=ConstantString(token);
+ magic_info->magic=(unsigned char *) ConstantString(token);
+ q=magic_info->magic;
+ for (p=magic_info->target; *p != '\0'; )
+ {
+ if (*p == '\\')
+ {
+ p++;
+ if (isdigit((int) ((unsigned char) *p)) != 0)
+ {
+ char
+ *end;
+
+ *q++=(unsigned char) strtol(p,&end,8);
+ p+=(end-p);
+ magic_info->length++;
+ continue;
+ }
+ switch (*p)
+ {
+ case 'b': *q='\b'; break;
+ case 'f': *q='\f'; break;
+ case 'n': *q='\n'; break;
+ case 'r': *q='\r'; break;
+ case 't': *q='\t'; break;
+ case 'v': *q='\v'; break;
+ case 'a': *q='a'; break;
+ case '?': *q='\?'; break;
+ default: *q=(unsigned char) (*p); break;
+ }
+ p++;
+ q++;
+ magic_info->length++;
+ continue;
+ }
+ else
+ if (LocaleNCompare(p,"&",5) == 0)
+ (void) CopyMagickString(p+1,p+5,length-magic_info->length);
+ *q++=(unsigned char) (*p++);
+ magic_info->length++;
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d M a g i c L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadMagicLists() loads one or more magic configuration file which provides a
+% mapping between magic attributes and a magic name.
+%
+% The format of the LoadMagicLists method is:
+%
+% MagickBooleanType LoadMagicLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadMagicLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadMagicList(MagicMap,"built-in",0,exception));
+#else
+ char
+ path[MaxTextExtent];
+
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ *path='\0';
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ (void) CopyMagickString(path,GetStringInfoPath(option),MaxTextExtent);
+ status|=LoadMagicList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((magic_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(magic_list) != MagickFalse))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ConfigureWarning,
+ "UnableToOpenConfigureFile","`%s'",path);
+ status|=LoadMagicList(MagicMap,"built-in",0,exception);
+ }
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/magic.h b/magick/magic.h
new file mode 100644
index 0000000..6c94021
--- /dev/null
+++ b/magick/magic.h
@@ -0,0 +1,72 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore magic methods.
+*/
+#ifndef _MAGICKCORE_MAGIC_H
+#define _MAGICKCORE_MAGIC_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _MagicInfo
+{
+ char
+ *path,
+ *name,
+ *target;
+
+ unsigned char
+ *magic;
+
+ size_t
+ length;
+
+ MagickOffsetType
+ offset;
+
+ MagickBooleanType
+ stealth;
+
+ struct _MagicInfo
+ *previous,
+ *next; /* deprecated, use GetMagicInfoList() */
+
+ unsigned long
+ signature;
+} MagicInfo;
+
+extern MagickExport char
+ **GetMagicList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const char
+ *GetMagicName(const MagicInfo *);
+
+extern MagickExport MagickBooleanType
+ ListMagicInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport const MagicInfo
+ *GetMagicInfo(const unsigned char *,const size_t,ExceptionInfo *),
+ **GetMagicInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyMagicList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/magick-config.h b/magick/magick-config.h
new file mode 100644
index 0000000..4e53c87
--- /dev/null
+++ b/magick/magick-config.h
@@ -0,0 +1,1340 @@
+#ifndef _MAGICK_MAGICK_CONFIG_H
+#define _MAGICK_MAGICK_CONFIG_H 1
+
+/* magick/magick-config.h. Generated automatically at end of configure. */
+/* config/config.h. Generated from config.h.in by configure. */
+/* config/config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define if you have AUTOTRACE library */
+/* #undef AUTOTRACE_DELEGATE */
+
+/* Define if coders and filters are to be built as modules. */
+#ifndef MAGICKCORE_BUILD_MODULES
+#define MAGICKCORE_BUILD_MODULES 1
+#endif
+
+/* Define if you have the bzip2 library */
+#ifndef MAGICKCORE_BZLIB_DELEGATE
+#define MAGICKCORE_BZLIB_DELEGATE 1
+#endif
+
+/* Define if you have CAIRO library */
+#ifndef MAGICKCORE_CAIRO_DELEGATE
+#define MAGICKCORE_CAIRO_DELEGATE 1
+#endif
+
+/* permit enciphering and deciphering image pixels */
+#ifndef MAGICKCORE_CIPHER_SUPPORT
+#define MAGICKCORE_CIPHER_SUPPORT 1
+#endif
+
+/* Define to 1 if the `closedir' function returns void instead of `int'. */
+/* #undef CLOSEDIR_VOID */
+
+/* Location of coder modules */
+#ifndef MAGICKCORE_CODER_PATH
+#define MAGICKCORE_CODER_PATH "/usr/local/lib/ImageMagick-6.5.5/modules-Q16/coders/"
+#endif
+
+/* Subdirectory of lib where coder modules are installed */
+#ifndef MAGICKCORE_CODER_RELATIVE_PATH
+#define MAGICKCORE_CODER_RELATIVE_PATH "ImageMagick-6.5.5/modules-Q16/coders"
+#endif
+
+/* Directory where architecture-dependent configuration files live. */
+#ifndef MAGICKCORE_CONFIGURE_PATH
+#define MAGICKCORE_CONFIGURE_PATH "/usr/local/lib/ImageMagick-6.5.5/config/"
+#endif
+
+/* Subdirectory of lib where architecture-dependent configuration files live.
+ */
+#ifndef MAGICKCORE_CONFIGURE_RELATIVE_PATH
+#define MAGICKCORE_CONFIGURE_RELATIVE_PATH "ImageMagick-6.5.5/config"
+#endif
+
+/* Define if you have DJVU library */
+#ifndef MAGICKCORE_DJVU_DELEGATE
+#define MAGICKCORE_DJVU_DELEGATE 1
+#endif
+
+/* Directory where ImageMagick documents live. */
+#ifndef MAGICKCORE_DOCUMENTATION_PATH
+#define MAGICKCORE_DOCUMENTATION_PATH "/usr/local/share/doc/ImageMagick-6.5.5/"
+#endif
+
+/* Define if you have Display Postscript */
+/* #undef DPS_DELEGATE */
+
+/* Build self-contained, embeddable, zero-configuration ImageMagick
+ (experimental) */
+/* #undef EMBEDDABLE_SUPPORT */
+
+/* exclude deprecated methods in MagickCore API */
+/* #undef EXCLUDE_DEPRECATED */
+
+/* Directory where executables are installed. */
+#ifndef MAGICKCORE_EXECUTABLE_PATH
+#define MAGICKCORE_EXECUTABLE_PATH "/usr/local/bin/"
+#endif
+
+/* Define if you have FFTW library */
+#ifndef MAGICKCORE_FFTW_DELEGATE
+#define MAGICKCORE_FFTW_DELEGATE 1
+#endif
+
+/* Location of filter modules */
+#ifndef MAGICKCORE_FILTER_PATH
+#define MAGICKCORE_FILTER_PATH "/usr/local/lib/ImageMagick-6.5.5/modules-Q16/filters/"
+#endif
+
+/* Subdirectory of lib where filter modules are installed */
+#ifndef MAGICKCORE_FILTER_RELATIVE_PATH
+#define MAGICKCORE_FILTER_RELATIVE_PATH "ImageMagick-6.5.5/modules-Q16/filters"
+#endif
+
+/* Define if you have FONTCONFIG library */
+#ifndef MAGICKCORE_FONTCONFIG_DELEGATE
+#define MAGICKCORE_FONTCONFIG_DELEGATE 1
+#endif
+
+/* Define if you have FlashPIX library */
+/* #undef FPX_DELEGATE */
+
+/* Define if you have FreeType (TrueType font) library */
+#ifndef MAGICKCORE_FREETYPE_DELEGATE
+#define MAGICKCORE_FREETYPE_DELEGATE 1
+#endif
+
+/* Define if you have Ghostscript library or framework */
+/* #undef GS_DELEGATE */
+
+/* Define if you have GVC library */
+/* #undef GVC_DELEGATE */
+
+/* Define to 1 if you have the `argz_add' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_ADD
+#define MAGICKCORE_HAVE_ARGZ_ADD 1
+#endif
+
+/* Define to 1 if you have the `argz_append' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_APPEND
+#define MAGICKCORE_HAVE_ARGZ_APPEND 1
+#endif
+
+/* Define to 1 if you have the `argz_count' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_COUNT
+#define MAGICKCORE_HAVE_ARGZ_COUNT 1
+#endif
+
+/* Define to 1 if you have the `argz_create_sep' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_CREATE_SEP
+#define MAGICKCORE_HAVE_ARGZ_CREATE_SEP 1
+#endif
+
+/* Define to 1 if you have the <argz.h> header file. */
+#ifndef MAGICKCORE_HAVE_ARGZ_H
+#define MAGICKCORE_HAVE_ARGZ_H 1
+#endif
+
+/* Define to 1 if you have the `argz_insert' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_INSERT
+#define MAGICKCORE_HAVE_ARGZ_INSERT 1
+#endif
+
+/* Define to 1 if you have the `argz_next' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_NEXT
+#define MAGICKCORE_HAVE_ARGZ_NEXT 1
+#endif
+
+/* Define to 1 if you have the `argz_stringify' function. */
+#ifndef MAGICKCORE_HAVE_ARGZ_STRINGIFY
+#define MAGICKCORE_HAVE_ARGZ_STRINGIFY 1
+#endif
+
+/* Define to 1 if you have the <arm/limits.h> header file. */
+/* #undef HAVE_ARM_LIMITS_H */
+
+/* Define to 1 if you have the `atexit' function. */
+#ifndef MAGICKCORE_HAVE_ATEXIT
+#define MAGICKCORE_HAVE_ATEXIT 1
+#endif
+
+/* define if bool is a built-in type */
+#ifndef MAGICKCORE_HAVE_BOOL
+#define MAGICKCORE_HAVE_BOOL /**/
+#endif
+
+/* Define to 1 if you have the `clock' function. */
+#ifndef MAGICKCORE_HAVE_CLOCK
+#define MAGICKCORE_HAVE_CLOCK 1
+#endif
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#ifndef MAGICKCORE_HAVE_CLOCK_GETTIME
+#define MAGICKCORE_HAVE_CLOCK_GETTIME 1
+#endif
+
+/* Define to 1 if you have the `closedir' function. */
+#ifndef MAGICKCORE_HAVE_CLOSEDIR
+#define MAGICKCORE_HAVE_CLOSEDIR 1
+#endif
+
+/* Define to 1 if you have the <complex.h> header file. */
+#ifndef MAGICKCORE_HAVE_COMPLEX_H
+#define MAGICKCORE_HAVE_COMPLEX_H 1
+#endif
+
+/* Define to 1 if you have the declaration of `cygwin_conv_path', and to 0 if
+ you don't. */
+/* #undef HAVE_DECL_CYGWIN_CONV_PATH */
+
+/* Define to 1 if you have the declaration of `pread', and to 0 if you don't.
+ */
+#ifndef MAGICKCORE_HAVE_DECL_PREAD
+#define MAGICKCORE_HAVE_DECL_PREAD 1
+#endif
+
+/* Define to 1 if you have the declaration of `pwrite', and to 0 if you don't.
+ */
+#ifndef MAGICKCORE_HAVE_DECL_PWRITE
+#define MAGICKCORE_HAVE_DECL_PWRITE 1
+#endif
+
+/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you
+ don't. */
+#ifndef MAGICKCORE_HAVE_DECL_STRLCPY
+#define MAGICKCORE_HAVE_DECL_STRLCPY 0
+#endif
+
+/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you
+ don't. */
+#ifndef MAGICKCORE_HAVE_DECL_VSNPRINTF
+#define MAGICKCORE_HAVE_DECL_VSNPRINTF 1
+#endif
+
+/* Define to 1 if you have the `directio' function. */
+/* #undef HAVE_DIRECTIO */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#ifndef MAGICKCORE_HAVE_DIRENT_H
+#define MAGICKCORE_HAVE_DIRENT_H 1
+#endif
+
+/* Define if you have the GNU dld library. */
+/* #undef HAVE_DLD */
+
+/* Define to 1 if you have the <dld.h> header file. */
+/* #undef HAVE_DLD_H */
+
+/* Define to 1 if you have the `dlerror' function. */
+#ifndef MAGICKCORE_HAVE_DLERROR
+#define MAGICKCORE_HAVE_DLERROR 1
+#endif
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#ifndef MAGICKCORE_HAVE_DLFCN_H
+#define MAGICKCORE_HAVE_DLFCN_H 1
+#endif
+
+/* Define to 1 if you have the <dl.h> header file. */
+/* #undef HAVE_DL_H */
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Define if you have the _dyld_func_lookup function. */
+/* #undef HAVE_DYLD */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#ifndef MAGICKCORE_HAVE_ERRNO_H
+#define MAGICKCORE_HAVE_ERRNO_H 1
+#endif
+
+/* Define to 1 if the system has the type `error_t'. */
+#ifndef MAGICKCORE_HAVE_ERROR_T
+#define MAGICKCORE_HAVE_ERROR_T 1
+#endif
+
+/* Define to 1 if you have the `execvp' function. */
+#ifndef MAGICKCORE_HAVE_EXECVP
+#define MAGICKCORE_HAVE_EXECVP 1
+#endif
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#ifndef MAGICKCORE_HAVE_FCNTL_H
+#define MAGICKCORE_HAVE_FCNTL_H 1
+#endif
+
+/* Define to 1 if you have the `floor' function. */
+/* #undef HAVE_FLOOR */
+
+/* Define to 1 if you have the `fork' function. */
+#ifndef MAGICKCORE_HAVE_FORK
+#define MAGICKCORE_HAVE_FORK 1
+#endif
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#ifndef MAGICKCORE_HAVE_FSEEKO
+#define MAGICKCORE_HAVE_FSEEKO 1
+#endif
+
+/* Define to 1 if you have the <ft2build.h> header file. */
+#ifndef MAGICKCORE_HAVE_FT2BUILD_H
+#define MAGICKCORE_HAVE_FT2BUILD_H 1
+#endif
+
+/* Define to 1 if you have the `ftime' function. */
+#ifndef MAGICKCORE_HAVE_FTIME
+#define MAGICKCORE_HAVE_FTIME 1
+#endif
+
+/* Define to 1 if you have the `ftruncate' function. */
+#ifndef MAGICKCORE_HAVE_FTRUNCATE
+#define MAGICKCORE_HAVE_FTRUNCATE 1
+#endif
+
+/* Define to 1 if you have the `getcwd' function. */
+#ifndef MAGICKCORE_HAVE_GETCWD
+#define MAGICKCORE_HAVE_GETCWD 1
+#endif
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#ifndef MAGICKCORE_HAVE_GETDTABLESIZE
+#define MAGICKCORE_HAVE_GETDTABLESIZE 1
+#endif
+
+/* Define to 1 if you have the `getexecname' function. */
+/* #undef HAVE_GETEXECNAME */
+
+/* Define to 1 if you have the `getpagesize' function. */
+#ifndef MAGICKCORE_HAVE_GETPAGESIZE
+#define MAGICKCORE_HAVE_GETPAGESIZE 1
+#endif
+
+/* Define to 1 if you have the `getpid' function. */
+#ifndef MAGICKCORE_HAVE_GETPID
+#define MAGICKCORE_HAVE_GETPID 1
+#endif
+
+/* Define to 1 if you have the `getrusage' function. */
+#ifndef MAGICKCORE_HAVE_GETRUSAGE
+#define MAGICKCORE_HAVE_GETRUSAGE 1
+#endif
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#ifndef MAGICKCORE_HAVE_GETTIMEOFDAY
+#define MAGICKCORE_HAVE_GETTIMEOFDAY 1
+#endif
+
+/* Define to 1 if you have the `gmtime_r' function. */
+#ifndef MAGICKCORE_HAVE_GMTIME_R
+#define MAGICKCORE_HAVE_GMTIME_R 1
+#endif
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#ifndef MAGICKCORE_HAVE_INTTYPES_H
+#define MAGICKCORE_HAVE_INTTYPES_H 1
+#endif
+
+/* Define if you have the <lcms.h> header file. */
+#ifndef MAGICKCORE_HAVE_LCMS_H
+#define MAGICKCORE_HAVE_LCMS_H 1
+#endif
+
+/* Define if you have the <lcms/lcms.h> header file. */
+/* #undef HAVE_LCMS_LCMS_H */
+
+/* Define if you have the libdl library or equivalent. */
+#ifndef MAGICKCORE_HAVE_LIBDL
+#define MAGICKCORE_HAVE_LIBDL 1
+#endif
+
+/* Define if libdlloader will be built on this platform */
+#ifndef MAGICKCORE_HAVE_LIBDLLOADER
+#define MAGICKCORE_HAVE_LIBDLLOADER 1
+#endif
+
+/* Define to 1 if you have the `gcov' library (-lgcov). */
+/* #undef HAVE_LIBGCOV */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#ifndef MAGICKCORE_HAVE_LIMITS_H
+#define MAGICKCORE_HAVE_LIMITS_H 1
+#endif
+
+/* Define to 1 if you have the <linux/unistd.h> header file. */
+#ifndef MAGICKCORE_HAVE_LINUX_UNISTD_H
+#define MAGICKCORE_HAVE_LINUX_UNISTD_H 1
+#endif
+
+/* Define to 1 if you have the <locale.h> header file. */
+#ifndef MAGICKCORE_HAVE_LOCALE_H
+#define MAGICKCORE_HAVE_LOCALE_H 1
+#endif
+
+/* Define to 1 if you have the `localtime_r' function. */
+#ifndef MAGICKCORE_HAVE_LOCALTIME_R
+#define MAGICKCORE_HAVE_LOCALTIME_R 1
+#endif
+
+/* Define to 1 if the type `long double' works and has more range or precision
+ than `double'. */
+#ifndef MAGICKCORE_HAVE_LONG_DOUBLE_WIDER
+#define MAGICKCORE_HAVE_LONG_DOUBLE_WIDER 1
+#endif
+
+/* Define to 1 if you have the `lstat' function. */
+#ifndef MAGICKCORE_HAVE_LSTAT
+#define MAGICKCORE_HAVE_LSTAT 1
+#endif
+
+/* define if the compiler implements L"widestring" */
+#ifndef MAGICKCORE_HAVE_LSTRING
+#define MAGICKCORE_HAVE_LSTRING /**/
+#endif
+
+/* Define this if a modern libltdl is already installed */
+#ifndef MAGICKCORE_HAVE_LTDL
+#define MAGICKCORE_HAVE_LTDL 1
+#endif
+
+/* Define to 1 if you have the <machine/param.h> header file. */
+/* #undef HAVE_MACHINE_PARAM_H */
+
+/* Define to 1 if you have the <mach-o/dyld.h> header file. */
+/* #undef HAVE_MACH_O_DYLD_H */
+
+/* Define to 1 if you have the `memmove' function. */
+#ifndef MAGICKCORE_HAVE_MEMMOVE
+#define MAGICKCORE_HAVE_MEMMOVE 1
+#endif
+
+/* Define to 1 if you have the <memory.h> header file. */
+#ifndef MAGICKCORE_HAVE_MEMORY_H
+#define MAGICKCORE_HAVE_MEMORY_H 1
+#endif
+
+/* Define to 1 if you have the `memset' function. */
+#ifndef MAGICKCORE_HAVE_MEMSET
+#define MAGICKCORE_HAVE_MEMSET 1
+#endif
+
+/* Define to 1 if you have the `mkstemp' function. */
+#ifndef MAGICKCORE_HAVE_MKSTEMP
+#define MAGICKCORE_HAVE_MKSTEMP 1
+#endif
+
+/* Define to 1 if you have a working `mmap' system call. */
+#ifndef MAGICKCORE_HAVE_MMAP
+#define MAGICKCORE_HAVE_MMAP 1
+#endif
+
+/* Define to 1 if you have a working `mmap' system call. */
+#ifndef MAGICKCORE_HAVE_MMAP_FILEIO
+#define MAGICKCORE_HAVE_MMAP_FILEIO 1
+#endif
+
+/* Define to 1 if you have the `munmap' function. */
+#ifndef MAGICKCORE_HAVE_MUNMAP
+#define MAGICKCORE_HAVE_MUNMAP 1
+#endif
+
+/* define if the compiler implements namespaces */
+#ifndef MAGICKCORE_HAVE_NAMESPACES
+#define MAGICKCORE_HAVE_NAMESPACES /**/
+#endif
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the `opendir' function. */
+#ifndef MAGICKCORE_HAVE_OPENDIR
+#define MAGICKCORE_HAVE_OPENDIR 1
+#endif
+
+/* Define to 1 if you have the <OS.h> header file. */
+/* #undef HAVE_OS_H */
+
+/* Define to 1 if you have the `pclose' function. */
+#ifndef MAGICKCORE_HAVE_PCLOSE
+#define MAGICKCORE_HAVE_PCLOSE 1
+#endif
+
+/* Define to 1 if you have the `poll' function. */
+#ifndef MAGICKCORE_HAVE_POLL
+#define MAGICKCORE_HAVE_POLL 1
+#endif
+
+/* Define to 1 if you have the `popen' function. */
+#ifndef MAGICKCORE_HAVE_POPEN
+#define MAGICKCORE_HAVE_POPEN 1
+#endif
+
+/* Define to 1 if you have the `posix_fadvise' function. */
+#ifndef MAGICKCORE_HAVE_POSIX_FADVISE
+#define MAGICKCORE_HAVE_POSIX_FADVISE 1
+#endif
+
+/* Define to 1 if you have the `posix_fallocate' function. */
+#ifndef MAGICKCORE_HAVE_POSIX_FALLOCATE
+#define MAGICKCORE_HAVE_POSIX_FALLOCATE 1
+#endif
+
+/* Define to 1 if you have the `posix_madvise' function. */
+#ifndef MAGICKCORE_HAVE_POSIX_MADVISE
+#define MAGICKCORE_HAVE_POSIX_MADVISE 1
+#endif
+
+/* Define to 1 if you have the `posix_memalign' function. */
+#ifndef MAGICKCORE_HAVE_POSIX_MEMALIGN
+#define MAGICKCORE_HAVE_POSIX_MEMALIGN 1
+#endif
+
+/* Define to 1 if you have the `pow' function. */
+/* #undef HAVE_POW */
+
+/* Define to 1 if you have the `pread' function. */
+#ifndef MAGICKCORE_HAVE_PREAD
+#define MAGICKCORE_HAVE_PREAD 1
+#endif
+
+/* Define if libtool can extract symbol lists from object files. */
+#ifndef MAGICKCORE_HAVE_PRELOADED_SYMBOLS
+#define MAGICKCORE_HAVE_PRELOADED_SYMBOLS 1
+#endif
+
+/* Define if you have POSIX threads libraries and header files. */
+#ifndef MAGICKCORE_HAVE_PTHREAD
+#define MAGICKCORE_HAVE_PTHREAD 1
+#endif
+
+/* Define to 1 if you have the `pwrite' function. */
+#ifndef MAGICKCORE_HAVE_PWRITE
+#define MAGICKCORE_HAVE_PWRITE 1
+#endif
+
+/* Define to 1 if you have the `raise' function. */
+#ifndef MAGICKCORE_HAVE_RAISE
+#define MAGICKCORE_HAVE_RAISE 1
+#endif
+
+/* Define to 1 if you have the `rand_r' function. */
+#ifndef MAGICKCORE_HAVE_RAND_R
+#define MAGICKCORE_HAVE_RAND_R 1
+#endif
+
+/* Define to 1 if you have the `readdir' function. */
+#ifndef MAGICKCORE_HAVE_READDIR
+#define MAGICKCORE_HAVE_READDIR 1
+#endif
+
+/* Define to 1 if you have the `readdir_r' function. */
+#ifndef MAGICKCORE_HAVE_READDIR_R
+#define MAGICKCORE_HAVE_READDIR_R 1
+#endif
+
+/* Define to 1 if you have the `readlink' function. */
+#ifndef MAGICKCORE_HAVE_READLINK
+#define MAGICKCORE_HAVE_READLINK 1
+#endif
+
+/* Define to 1 if you have the `realpath' function. */
+#ifndef MAGICKCORE_HAVE_REALPATH
+#define MAGICKCORE_HAVE_REALPATH 1
+#endif
+
+/* Define to 1 if you have the `seekdir' function. */
+#ifndef MAGICKCORE_HAVE_SEEKDIR
+#define MAGICKCORE_HAVE_SEEKDIR 1
+#endif
+
+/* Define to 1 if you have the `select' function. */
+#ifndef MAGICKCORE_HAVE_SELECT
+#define MAGICKCORE_HAVE_SELECT 1
+#endif
+
+/* Define to 1 if you have the `setlocale' function. */
+#ifndef MAGICKCORE_HAVE_SETLOCALE
+#define MAGICKCORE_HAVE_SETLOCALE 1
+#endif
+
+/* Define to 1 if you have the `setvbuf' function. */
+#ifndef MAGICKCORE_HAVE_SETVBUF
+#define MAGICKCORE_HAVE_SETVBUF 1
+#endif
+
+/* X11 server supports shape extension */
+#ifndef MAGICKCORE_HAVE_SHAPE
+#define MAGICKCORE_HAVE_SHAPE 1
+#endif
+
+/* X11 server supports shared memory extension */
+#ifndef MAGICKCORE_HAVE_SHARED_MEMORY
+#define MAGICKCORE_HAVE_SHARED_MEMORY 1
+#endif
+
+/* Define if you have the shl_load function. */
+/* #undef HAVE_SHL_LOAD */
+
+/* Define to 1 if you have the `sigaction' function. */
+#ifndef MAGICKCORE_HAVE_SIGACTION
+#define MAGICKCORE_HAVE_SIGACTION 1
+#endif
+
+/* Define to 1 if you have the `sigemptyset' function. */
+#ifndef MAGICKCORE_HAVE_SIGEMPTYSET
+#define MAGICKCORE_HAVE_SIGEMPTYSET 1
+#endif
+
+/* Define to 1 if you have the `sqrt' function. */
+/* #undef HAVE_SQRT */
+
+/* Define to 1 if you have the `stat' function. */
+#ifndef MAGICKCORE_HAVE_STAT
+#define MAGICKCORE_HAVE_STAT 1
+#endif
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#ifndef MAGICKCORE_HAVE_STDARG_H
+#define MAGICKCORE_HAVE_STDARG_H 1
+#endif
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#ifndef MAGICKCORE_HAVE_STDBOOL_H
+#define MAGICKCORE_HAVE_STDBOOL_H 1
+#endif
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#ifndef MAGICKCORE_HAVE_STDDEF_H
+#define MAGICKCORE_HAVE_STDDEF_H 1
+#endif
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#ifndef MAGICKCORE_HAVE_STDINT_H
+#define MAGICKCORE_HAVE_STDINT_H 1
+#endif
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#ifndef MAGICKCORE_HAVE_STDLIB_H
+#define MAGICKCORE_HAVE_STDLIB_H 1
+#endif
+
+/* define if the compiler supports ISO C++ standard library */
+#ifndef MAGICKCORE_HAVE_STD_LIBS
+#define MAGICKCORE_HAVE_STD_LIBS /**/
+#endif
+
+/* define if the compiler supports the std namespace */
+#ifndef MAGICKCORE_HAVE_STD_NAMESPACE
+#define MAGICKCORE_HAVE_STD_NAMESPACE /**/
+#endif
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#ifndef MAGICKCORE_HAVE_STRCASECMP
+#define MAGICKCORE_HAVE_STRCASECMP 1
+#endif
+
+/* Define to 1 if you have the `strchr' function. */
+#ifndef MAGICKCORE_HAVE_STRCHR
+#define MAGICKCORE_HAVE_STRCHR 1
+#endif
+
+/* Define to 1 if you have the `strcspn' function. */
+#ifndef MAGICKCORE_HAVE_STRCSPN
+#define MAGICKCORE_HAVE_STRCSPN 1
+#endif
+
+/* Define to 1 if you have the `strdup' function. */
+#ifndef MAGICKCORE_HAVE_STRDUP
+#define MAGICKCORE_HAVE_STRDUP 1
+#endif
+
+/* Define to 1 if you have the `strerror' function. */
+#ifndef MAGICKCORE_HAVE_STRERROR
+#define MAGICKCORE_HAVE_STRERROR 1
+#endif
+
+/* Define to 1 if you have the `strerror_r' function. */
+#ifndef MAGICKCORE_HAVE_STRERROR_R
+#define MAGICKCORE_HAVE_STRERROR_R 1
+#endif
+
+/* Define to 1 if cpp supports the ANSI # stringizing operator. */
+#ifndef MAGICKCORE_HAVE_STRINGIZE
+#define MAGICKCORE_HAVE_STRINGIZE 1
+#endif
+
+/* Define to 1 if you have the <strings.h> header file. */
+#ifndef MAGICKCORE_HAVE_STRINGS_H
+#define MAGICKCORE_HAVE_STRINGS_H 1
+#endif
+
+/* Define to 1 if you have the <string.h> header file. */
+#ifndef MAGICKCORE_HAVE_STRING_H
+#define MAGICKCORE_HAVE_STRING_H 1
+#endif
+
+/* Define to 1 if you have the `strlcat' function. */
+/* #undef HAVE_STRLCAT */
+
+/* Define to 1 if you have the `strlcpy' function. */
+/* #undef HAVE_STRLCPY */
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#ifndef MAGICKCORE_HAVE_STRNCASECMP
+#define MAGICKCORE_HAVE_STRNCASECMP 1
+#endif
+
+/* Define to 1 if you have the `strpbrk' function. */
+#ifndef MAGICKCORE_HAVE_STRPBRK
+#define MAGICKCORE_HAVE_STRPBRK 1
+#endif
+
+/* Define to 1 if you have the `strrchr' function. */
+#ifndef MAGICKCORE_HAVE_STRRCHR
+#define MAGICKCORE_HAVE_STRRCHR 1
+#endif
+
+/* Define to 1 if you have the `strspn' function. */
+#ifndef MAGICKCORE_HAVE_STRSPN
+#define MAGICKCORE_HAVE_STRSPN 1
+#endif
+
+/* Define to 1 if you have the `strstr' function. */
+#ifndef MAGICKCORE_HAVE_STRSTR
+#define MAGICKCORE_HAVE_STRSTR 1
+#endif
+
+/* Define to 1 if you have the `strtol' function. */
+#ifndef MAGICKCORE_HAVE_STRTOL
+#define MAGICKCORE_HAVE_STRTOL 1
+#endif
+
+/* Define to 1 if you have the `symlink' function. */
+#ifndef MAGICKCORE_HAVE_SYMLINK
+#define MAGICKCORE_HAVE_SYMLINK 1
+#endif
+
+/* Define to 1 if you have the `sysconf' function. */
+#ifndef MAGICKCORE_HAVE_SYSCONF
+#define MAGICKCORE_HAVE_SYSCONF 1
+#endif
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/dl.h> header file. */
+/* #undef HAVE_SYS_DL_H */
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_IPC_H
+#define MAGICKCORE_HAVE_SYS_IPC_H 1
+#endif
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_RESOURCE_H
+#define MAGICKCORE_HAVE_SYS_RESOURCE_H 1
+#endif
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_SELECT_H
+#define MAGICKCORE_HAVE_SYS_SELECT_H 1
+#endif
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_SOCKET_H
+#define MAGICKCORE_HAVE_SYS_SOCKET_H 1
+#endif
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_STAT_H
+#define MAGICKCORE_HAVE_SYS_STAT_H 1
+#endif
+
+/* Define to 1 if you have the <sys/syslimits.h> header file. */
+/* #undef HAVE_SYS_SYSLIMITS_H */
+
+/* Define to 1 if you have the <sys/timeb.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_TIMEB_H
+#define MAGICKCORE_HAVE_SYS_TIMEB_H 1
+#endif
+
+/* Define to 1 if you have the <sys/times.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_TIMES_H
+#define MAGICKCORE_HAVE_SYS_TIMES_H 1
+#endif
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_TIME_H
+#define MAGICKCORE_HAVE_SYS_TIME_H 1
+#endif
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_TYPES_H
+#define MAGICKCORE_HAVE_SYS_TYPES_H 1
+#endif
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#ifndef MAGICKCORE_HAVE_SYS_WAIT_H
+#define MAGICKCORE_HAVE_SYS_WAIT_H 1
+#endif
+
+/* Define to 1 if you have the `telldir' function. */
+#ifndef MAGICKCORE_HAVE_TELLDIR
+#define MAGICKCORE_HAVE_TELLDIR 1
+#endif
+
+/* Define to 1 if you have the `tempnam' function. */
+#ifndef MAGICKCORE_HAVE_TEMPNAM
+#define MAGICKCORE_HAVE_TEMPNAM 1
+#endif
+
+/* Define to 1 if you have the <tiffconf.h> header file. */
+#ifndef MAGICKCORE_HAVE_TIFFCONF_H
+#define MAGICKCORE_HAVE_TIFFCONF_H 1
+#endif
+
+/* Define to 1 if you have the `TIFFIsCODECConfigured' function. */
+#ifndef MAGICKCORE_HAVE_TIFFISCODECCONFIGURED
+#define MAGICKCORE_HAVE_TIFFISCODECCONFIGURED 1
+#endif
+
+/* Define to 1 if you have the `TIFFMergeFieldInfo' function. */
+#ifndef MAGICKCORE_HAVE_TIFFMERGEFIELDINFO
+#define MAGICKCORE_HAVE_TIFFMERGEFIELDINFO 1
+#endif
+
+/* Define to 1 if you have the `TIFFReadEXIFDirectory' function. */
+#ifndef MAGICKCORE_HAVE_TIFFREADEXIFDIRECTORY
+#define MAGICKCORE_HAVE_TIFFREADEXIFDIRECTORY 1
+#endif
+
+/* Define to 1 if you have the `TIFFSetErrorHandlerExt' function. */
+#ifndef MAGICKCORE_HAVE_TIFFSETERRORHANDLEREXT
+#define MAGICKCORE_HAVE_TIFFSETERRORHANDLEREXT 1
+#endif
+
+/* Define to 1 if you have the `TIFFSetTagExtender' function. */
+#ifndef MAGICKCORE_HAVE_TIFFSETTAGEXTENDER
+#define MAGICKCORE_HAVE_TIFFSETTAGEXTENDER 1
+#endif
+
+/* Define to 1 if you have the `TIFFSetWarningHandlerExt' function. */
+#ifndef MAGICKCORE_HAVE_TIFFSETWARNINGHANDLEREXT
+#define MAGICKCORE_HAVE_TIFFSETWARNINGHANDLEREXT 1
+#endif
+
+/* Define to 1 if you have the `TIFFSwabArrayOfTriples' function. */
+#ifndef MAGICKCORE_HAVE_TIFFSWABARRAYOFTRIPLES
+#define MAGICKCORE_HAVE_TIFFSWABARRAYOFTRIPLES 1
+#endif
+
+/* Define to 1 if you have the `times' function. */
+#ifndef MAGICKCORE_HAVE_TIMES
+#define MAGICKCORE_HAVE_TIMES 1
+#endif
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#ifndef MAGICKCORE_HAVE_UNISTD_H
+#define MAGICKCORE_HAVE_UNISTD_H 1
+#endif
+
+/* Define to 1 if you have the `usleep' function. */
+#ifndef MAGICKCORE_HAVE_USLEEP
+#define MAGICKCORE_HAVE_USLEEP 1
+#endif
+
+/* Define to 1 if you have the `vfork' function. */
+#ifndef MAGICKCORE_HAVE_VFORK
+#define MAGICKCORE_HAVE_VFORK 1
+#endif
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the `vprintf' function. */
+#ifndef MAGICKCORE_HAVE_VPRINTF
+#define MAGICKCORE_HAVE_VPRINTF 1
+#endif
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#ifndef MAGICKCORE_HAVE_VSNPRINTF
+#define MAGICKCORE_HAVE_VSNPRINTF 1
+#endif
+
+/* Define to 1 if you have the `vsprintf' function. */
+#ifndef MAGICKCORE_HAVE_VSPRINTF
+#define MAGICKCORE_HAVE_VSPRINTF 1
+#endif
+
+/* Define to 1 if you have the `waitpid' function. */
+#ifndef MAGICKCORE_HAVE_WAITPID
+#define MAGICKCORE_HAVE_WAITPID 1
+#endif
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#ifndef MAGICKCORE_HAVE_WCHAR_H
+#define MAGICKCORE_HAVE_WCHAR_H 1
+#endif
+
+/* This value is set to 1 to indicate that the system argz facility works */
+#ifndef MAGICKCORE_HAVE_WORKING_ARGZ
+#define MAGICKCORE_HAVE_WORKING_ARGZ 1
+#endif
+
+/* Define to 1 if `fork' works. */
+#ifndef MAGICKCORE_HAVE_WORKING_FORK
+#define MAGICKCORE_HAVE_WORKING_FORK 1
+#endif
+
+/* Define to 1 if `vfork' works. */
+#ifndef MAGICKCORE_HAVE_WORKING_VFORK
+#define MAGICKCORE_HAVE_WORKING_VFORK 1
+#endif
+
+/* Define to 1 if the system has the type `_Bool'. */
+#ifndef MAGICKCORE_HAVE__BOOL
+#define MAGICKCORE_HAVE__BOOL 1
+#endif
+
+/* Define to 1 if you have the `_exit' function. */
+#ifndef MAGICKCORE_HAVE__EXIT
+#define MAGICKCORE_HAVE__EXIT 1
+#endif
+
+/* Define to 1 if you have the `_NSGetExecutablePath' function. */
+/* #undef HAVE__NSGETEXECUTABLEPATH */
+
+/* Define to 1 if you have the `_pclose' function. */
+/* #undef HAVE__PCLOSE */
+
+/* Define to 1 if you have the `_popen' function. */
+/* #undef HAVE__POPEN */
+
+/* Define to 1 if you have the `_wfopen' function. */
+/* #undef HAVE__WFOPEN */
+
+/* Define to 1 if you have the `_wstat' function. */
+/* #undef HAVE__WSTAT */
+
+/* accurately represent the wide range of intensity levels in real scenes */
+/* #undef HDRI_SUPPORT */
+
+/* Define if you have umem memory allocation library */
+/* #undef HasUMEM */
+
+/* ImageMagick is formally installed under prefix */
+#ifndef MAGICKCORE_INSTALLED_SUPPORT
+#define MAGICKCORE_INSTALLED_SUPPORT 1
+#endif
+
+/* Define if you have JBIG library */
+/* #undef JBIG_DELEGATE */
+
+/* Define if you have JPEG version 2 "Jasper" library */
+#ifndef MAGICKCORE_JP2_DELEGATE
+#define MAGICKCORE_JP2_DELEGATE 1
+#endif
+
+/* Define if you have JPEG library */
+#ifndef MAGICKCORE_JPEG_DELEGATE
+#define MAGICKCORE_JPEG_DELEGATE 1
+#endif
+
+/* Define if you have LCMS library */
+#ifndef MAGICKCORE_LCMS_DELEGATE
+#define MAGICKCORE_LCMS_DELEGATE 1
+#endif
+
+/* Directory where architecture-dependent files live. */
+#ifndef MAGICKCORE_LIBRARY_PATH
+#define MAGICKCORE_LIBRARY_PATH "/usr/local/lib/ImageMagick-6.5.5/"
+#endif
+
+/* Subdirectory of lib where ImageMagick architecture dependent files are
+ installed */
+#ifndef MAGICKCORE_LIBRARY_RELATIVE_PATH
+#define MAGICKCORE_LIBRARY_RELATIVE_PATH "ImageMagick-6.5.5"
+#endif
+
+/* Define if you have LQR library */
+/* #undef LQR_DELEGATE */
+
+/* Define if using libltdl to support dynamically loadable modules */
+#ifndef MAGICKCORE_LTDL_DELEGATE
+#define MAGICKCORE_LTDL_DELEGATE 1
+#endif
+
+/* Define if the OS needs help to load dependent libraries for dlopen(). */
+/* #undef LTDL_DLOPEN_DEPLIBS */
+
+/* Define to the system default library search path. */
+#ifndef MAGICKCORE_LT_DLSEARCH_PATH
+#define MAGICKCORE_LT_DLSEARCH_PATH "/lib64:/usr/lib64:/lib:/usr/lib:/usr/lib64/atlas:/usr/lib64/mysql:/usr/lib64/qt-3.3/lib:/usr/lib64/xulrunner-1.9.1"
+#endif
+
+/* The archive extension */
+#ifndef MAGICKCORE_LT_LIBEXT
+#define MAGICKCORE_LT_LIBEXT "a"
+#endif
+
+/* Define to the extension used for runtime loadable modules, say, ".so". */
+#ifndef MAGICKCORE_LT_MODULE_EXT
+#define MAGICKCORE_LT_MODULE_EXT ".so"
+#endif
+
+/* Define to the name of the environment variable that determines the run-time
+ module search path. */
+#ifndef MAGICKCORE_LT_MODULE_PATH_VAR
+#define MAGICKCORE_LT_MODULE_PATH_VAR "LD_LIBRARY_PATH"
+#endif
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#ifndef MAGICKCORE_LT_OBJDIR
+#define MAGICKCORE_LT_OBJDIR ".libs/"
+#endif
+
+/* Define to prepend to default font search path. */
+/* #undef MAGICK_FONT_PATH */
+
+/* Magick API method prefix */
+/* #undef NAMESPACE_PREFIX */
+
+/* Turn off assert statements */
+/* #undef NDEBUG */
+
+/* Define if dlsym() requires a leading underscore in symbol names. */
+/* #undef NEED_USCORE */
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define if you have OPENEXR library */
+/* #undef OPENEXR_DELEGATE */
+
+/* Define to the address where bug reports for this package should be sent. */
+#ifndef MAGICKCORE_PACKAGE_BUGREPORT
+#define MAGICKCORE_PACKAGE_BUGREPORT "http://www.imagemagick.org"
+#endif
+
+/* Define to the full name of this package. */
+#ifndef MAGICKCORE_PACKAGE_NAME
+#define MAGICKCORE_PACKAGE_NAME "ImageMagick"
+#endif
+
+/* Define to the full name and version of this package. */
+#ifndef MAGICKCORE_PACKAGE_STRING
+#define MAGICKCORE_PACKAGE_STRING "ImageMagick 6.5.5"
+#endif
+
+/* Define to the one symbol short name of this package. */
+#ifndef MAGICKCORE_PACKAGE_TARNAME
+#define MAGICKCORE_PACKAGE_TARNAME "ImageMagick"
+#endif
+
+/* Define to the version of this package. */
+#ifndef MAGICKCORE_PACKAGE_VERSION
+#define MAGICKCORE_PACKAGE_VERSION "6.5.5"
+#endif
+
+/* Define if you have PNG library */
+#ifndef MAGICKCORE_PNG_DELEGATE
+#define MAGICKCORE_PNG_DELEGATE 1
+#endif
+
+/* Define to necessary symbol if this constant uses a non-standard name on
+ your system. */
+/* #undef PTHREAD_CREATE_JOINABLE */
+
+/* Pixel cache threshold in MB (defaults to available memory) */
+/* #undef PixelCacheThreshold */
+
+/* Number of bits in a pixel Quantum (8/16/32/64) */
+#ifndef MAGICKCORE_QUANTUM_DEPTH
+#define MAGICKCORE_QUANTUM_DEPTH 16
+#endif
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#ifndef MAGICKCORE_RETSIGTYPE
+#define MAGICKCORE_RETSIGTYPE void
+#endif
+
+/* Define if you have RSVG library */
+#ifndef MAGICKCORE_RSVG_DELEGATE
+#define MAGICKCORE_RSVG_DELEGATE 1
+#endif
+
+/* Define to the type of arg 1 for `select'. */
+#ifndef MAGICKCORE_SELECT_TYPE_ARG1
+#define MAGICKCORE_SELECT_TYPE_ARG1 int
+#endif
+
+/* Define to the type of args 2, 3 and 4 for `select'. */
+#ifndef MAGICKCORE_SELECT_TYPE_ARG234
+#define MAGICKCORE_SELECT_TYPE_ARG234 (fd_set *)
+#endif
+
+/* Define to the type of arg 5 for `select'. */
+#ifndef MAGICKCORE_SELECT_TYPE_ARG5
+#define MAGICKCORE_SELECT_TYPE_ARG5 (struct timeval *)
+#endif
+
+/* Directory where architecture-independent configuration files live. */
+#ifndef MAGICKCORE_SHARE_CONFIGURE_PATH
+#define MAGICKCORE_SHARE_CONFIGURE_PATH "/usr/local/share/ImageMagick-6.5.5/config/"
+#endif
+
+/* Subdirectory of lib where architecture-independent configuration files
+ live. */
+#ifndef MAGICKCORE_SHARE_CONFIGURE_RELATIVE_PATH
+#define MAGICKCORE_SHARE_CONFIGURE_RELATIVE_PATH "ImageMagick-6.5.5/config"
+#endif
+
+/* Directory where architecture-independent files live. */
+#ifndef MAGICKCORE_SHARE_PATH
+#define MAGICKCORE_SHARE_PATH "/usr/local/share/ImageMagick-6.5.5/"
+#endif
+
+/* The size of `off_t', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_OFF_T
+#define MAGICKCORE_SIZEOF_OFF_T 8
+#endif
+
+/* The size of `signed int', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_SIGNED_INT
+#define MAGICKCORE_SIZEOF_SIGNED_INT 4
+#endif
+
+/* The size of `signed long', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_SIGNED_LONG
+#define MAGICKCORE_SIZEOF_SIGNED_LONG 8
+#endif
+
+/* The size of `signed long long', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_SIGNED_LONG_LONG
+#define MAGICKCORE_SIZEOF_SIGNED_LONG_LONG 8
+#endif
+
+/* The size of `signed short', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_SIGNED_SHORT
+#define MAGICKCORE_SIZEOF_SIGNED_SHORT 2
+#endif
+
+/* The size of `size_t', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_SIZE_T
+#define MAGICKCORE_SIZEOF_SIZE_T 8
+#endif
+
+/* The size of `unsigned int', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_UNSIGNED_INT
+#define MAGICKCORE_SIZEOF_UNSIGNED_INT 4
+#endif
+
+/* The size of `unsigned int*', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_UNSIGNED_INTP
+#define MAGICKCORE_SIZEOF_UNSIGNED_INTP 8
+#endif
+
+/* The size of `unsigned long', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_UNSIGNED_LONG
+#define MAGICKCORE_SIZEOF_UNSIGNED_LONG 8
+#endif
+
+/* The size of `unsigned long long', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_UNSIGNED_LONG_LONG
+#define MAGICKCORE_SIZEOF_UNSIGNED_LONG_LONG 8
+#endif
+
+/* The size of `unsigned short', as computed by sizeof. */
+#ifndef MAGICKCORE_SIZEOF_UNSIGNED_SHORT
+#define MAGICKCORE_SIZEOF_UNSIGNED_SHORT 2
+#endif
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define to 1 if you have the ANSI C header files. */
+#ifndef MAGICKCORE_STDC_HEADERS
+#define MAGICKCORE_STDC_HEADERS 1
+#endif
+
+/* Define if you have TIFF library */
+#ifndef MAGICKCORE_TIFF_DELEGATE
+#define MAGICKCORE_TIFF_DELEGATE 1
+#endif
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#ifndef MAGICKCORE_TIME_WITH_SYS_TIME
+#define MAGICKCORE_TIME_WITH_SYS_TIME 1
+#endif
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# define _ALL_SOURCE 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# define _TANDEM_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__ 1
+#endif
+
+
+/* Define to use the Windows GDI32 library */
+/* #undef WINGDI32_DELEGATE */
+
+/* Define if using the dmalloc debugging malloc package */
+/* #undef WITH_DMALLOC */
+
+/* Define if you have wmflite library */
+#ifndef MAGICKCORE_WMFLITE_DELEGATE
+#define MAGICKCORE_WMFLITE_DELEGATE 1
+#endif
+
+/* Define if you have wmf library */
+/* #undef WMF_DELEGATE */
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+# endif
+#endif
+
+/* Location of X11 configure files */
+#ifndef MAGICKCORE_X11_CONFIGURE_PATH
+#define MAGICKCORE_X11_CONFIGURE_PATH ""
+#endif
+
+/* Define if you have X11 library */
+#ifndef MAGICKCORE_X11_DELEGATE
+#define MAGICKCORE_X11_DELEGATE 1
+#endif
+
+/* Define if you have XML library */
+#ifndef MAGICKCORE_XML_DELEGATE
+#define MAGICKCORE_XML_DELEGATE 1
+#endif
+
+/* Define to 1 if the X Window System is missing or not being used. */
+/* #undef X_DISPLAY_MISSING */
+
+/* Define if you have zlib compression library */
+#ifndef MAGICKCORE_ZLIB_DELEGATE
+#define MAGICKCORE_ZLIB_DELEGATE 1
+#endif
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#ifndef MAGICKCORE__FILE_OFFSET_BITS
+#define MAGICKCORE__FILE_OFFSET_BITS 64
+#endif
+
+/* enable run-time bounds-checking */
+/* #undef _FORTIFY_SOURCE */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to 1 if type `char' is unsigned and you are not using gcc. */
+#ifndef __CHAR_UNSIGNED__
+/* # undef __CHAR_UNSIGNED__ */
+#endif
+
+/* Define so that glibc/gnulib argp.h does not typedef error_t. */
+/* #undef __error_t_defined */
+
+/* Define to appropriate substitue if compiler does not have __func__ */
+/* #undef __func__ */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to a type to use for `error_t' if it is not otherwise available. */
+/* #undef error_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long int' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+ nothing if this is not supported. Do not define if restrict is
+ supported directly. */
+#ifndef _magickcore_restrict
+#define _magickcore_restrict __restrict
+#endif
+/* Work around a bug in Sun C++: it does not support _Restrict, even
+ though the corresponding Sun C compiler does, which causes
+ "#define restrict _Restrict" in the previous line. Perhaps some future
+ version of Sun C++ will work with _Restrict; if so, it'll probably
+ define __RESTRICT, just as Sun C does. */
+#if defined __SUNPRO_CC && !defined __RESTRICT
+# define _Restrict
+#endif
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef ssize_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
+
+/* once: _MAGICK_MAGICK_CONFIG_H */
+#endif
diff --git a/magick/magick-type.h b/magick/magick-type.h
new file mode 100644
index 0000000..5bdee39
--- /dev/null
+++ b/magick/magick-type.h
@@ -0,0 +1,189 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore types.
+*/
+#ifndef _MAGICKCORE_MAGICK_TYPE_H
+#define _MAGICKCORE_MAGICK_TYPE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/magick-config.h"
+
+#if !defined(MAGICKCORE_QUANTUM_DEPTH)
+#define MAGICKCORE_QUANTUM_DEPTH 16
+#endif
+
+#if defined(__WINDOWS__) && !defined(__MINGW32__)
+# define MagickLLConstant(c) (MagickOffsetType) (c ## i64)
+# define MagickULLConstant(c) (MagickSizeType) (c ## ui64)
+#else
+# define MagickLLConstant(c) (MagickOffsetType) (c ## LL)
+# define MagickULLConstant(c) (MagickSizeType) (c ## ULL)
+#endif
+
+#if (MAGICKCORE_QUANTUM_DEPTH == 8)
+#define MagickEpsilon 1.0e-6
+#define MagickHuge 1.0e6
+#define MaxColormapSize 256UL
+#define MaxMap 255UL
+
+typedef double MagickRealType;
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+typedef float Quantum;
+#define QuantumRange 255.0
+#define QuantumFormat "%g"
+#else
+typedef unsigned char Quantum;
+#define QuantumRange 255UL
+#define QuantumFormat "%u"
+#endif
+#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
+#define MagickEpsilon 1.0e-10
+#define MagickHuge 1.0e12
+#define MaxColormapSize 65536UL
+#define MaxMap 65535UL
+
+typedef double MagickRealType;
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+typedef float Quantum;
+#define QuantumRange 65535.0
+#define QuantumFormat "%g"
+#else
+typedef unsigned short Quantum;
+#define QuantumRange 65535UL
+#define QuantumFormat "%u"
+#endif
+#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
+#define MagickEpsilon 1.0e-10
+#define MagickHuge 1.0e12
+#define MaxColormapSize 65536UL
+#define MaxMap 65535UL
+
+typedef double MagickRealType;
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+typedef float Quantum;
+#define QuantumRange 4294967295.0
+#define QuantumFormat "%g"
+#else
+typedef unsigned int Quantum;
+#define QuantumRange 4294967295UL
+#define QuantumFormat "%u"
+#endif
+#elif (MAGICKCORE_QUANTUM_DEPTH == 64) && defined(MAGICKCORE_HAVE_LONG_DOUBLE_WIDER)
+#define MagickEpsilon 1.0e-10
+#define MagickHuge 1.0e12
+#define MaxColormapSize 65536UL
+#define MaxMap 65535UL
+
+typedef long double MagickRealType;
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+typedef double Quantum;
+#define QuantumRange 18446744073709551615.0
+#define QuantumFormat "%g"
+#else
+typedef unsigned long long Quantum;
+#define QuantumRange MagickULLConstant(18446744073709551615)
+#define QuantumFormat "%llu"
+#endif
+#else
+#if !defined(_CH_)
+# error "Specified value of MAGICKCORE_QUANTUM_DEPTH is not supported"
+#endif
+#endif
+#define MaxRGB QuantumRange /* deprecated */
+
+/*
+ Typedef declarations.
+*/
+typedef unsigned int MagickStatusType;
+#if !defined(__WINDOWS__)
+#if (MAGICKCORE_SIZEOF_UNSIGNED_LONG_LONG == 8)
+typedef long long MagickOffsetType;
+typedef unsigned long long MagickSizeType;
+#define MagickSizeFormat "%10llu"
+#else
+typedef long MagickOffsetType;
+typedef unsigned long MagickSizeType;
+#define MagickSizeFormat "%10lu"
+#endif
+#else
+typedef __int64 MagickOffsetType;
+typedef unsigned __int64 MagickSizeType;
+#define MagickSizeFormat "%10llu"
+#endif
+
+#if defined(_MSC_VER) && (_MSC_VER == 1200)
+typedef MagickOffsetType QuantumAny;
+#else
+typedef MagickSizeType QuantumAny;
+#endif
+
+#if defined(macintosh)
+#define ExceptionInfo MagickExceptionInfo
+#endif
+
+typedef enum
+{
+ UndefinedChannel,
+ RedChannel = 0x0001,
+ GrayChannel = 0x0001,
+ CyanChannel = 0x0001,
+ GreenChannel = 0x0002,
+ MagentaChannel = 0x0002,
+ BlueChannel = 0x0004,
+ YellowChannel = 0x0004,
+ AlphaChannel = 0x0008,
+ OpacityChannel = 0x0008,
+ MatteChannel = 0x0008, /* deprecated */
+ BlackChannel = 0x0020,
+ IndexChannel = 0x0020,
+ AllChannels = 0x002F,
+ /* special channel types */
+ TrueAlphaChannel = 0x0040, /* extract actual alpha channel from opacity */
+ RGBChannels = 0x0080, /* set alpha from grayscale mask in RGB */
+ GrayChannels = 0x0080,
+ SyncChannels = 0x0100, /* channels should be modified equally */
+ DefaultChannels = ( (AllChannels | SyncChannels) &~ OpacityChannel)
+} ChannelType;
+
+typedef enum
+{
+ UndefinedClass,
+ DirectClass,
+ PseudoClass
+} ClassType;
+
+typedef enum
+{
+ MagickFalse = 0,
+ MagickTrue = 1
+} MagickBooleanType;
+
+typedef struct _BlobInfo BlobInfo;
+
+typedef struct _ExceptionInfo ExceptionInfo;
+
+typedef struct _Image Image;
+
+typedef struct _ImageInfo ImageInfo;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/magick.c b/magick/magick.c
new file mode 100644
index 0000000..61b4ae7
--- /dev/null
+++ b/magick/magick.c
@@ -0,0 +1,1440 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M AAA GGGG IIIII CCCC K K %
+% MM MM A A G I C K K %
+% M M M AAAAA G GGG I C KKK %
+% M M A A G G I C K K %
+% M M A A GGGG IIIII CCCC K K %
+% %
+% %
+% Methods to Read or List ImageMagick Image formats %
+% %
+% Software Design %
+% Bob Friesenhahn %
+% John Cristy %
+% November 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/coder.h"
+#include "magick/client.h"
+#include "magick/coder.h"
+#include "magick/configure.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/mime.h"
+#include "magick/module.h"
+#if defined(__WINDOWS__)
+# include "magick/nt-feature.h"
+#endif
+#include "magick/random_.h"
+#include "magick/registry.h"
+#include "magick/resource_.h"
+#include "magick/policy.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/thread_.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xwindow-private.h"
+
+/*
+ Define declarations.
+*/
+#if !defined(MAGICKCORE_RETSIGTYPE)
+# define MAGICKCORE_RETSIGTYPE void
+#endif
+#if !defined(SIG_DFL)
+# define SIG_DFL ((SignalHandler *) 0)
+#endif
+#if !defined(SIG_ERR)
+# define SIG_ERR ((SignalHandler *) -1)
+#endif
+#if !defined(SIGMAX)
+#define SIGMAX 64
+#endif
+
+/*
+ Typedef declarations.
+*/
+typedef MAGICKCORE_RETSIGTYPE
+ SignalHandler(int);
+
+/*
+ Global declarations.
+*/
+static SemaphoreInfo
+ *magick_semaphore = (SemaphoreInfo *) NULL;
+
+static SignalHandler
+ *signal_handlers[SIGMAX] = { (SignalHandler *) NULL };
+
+static SplayTreeInfo
+ *magick_list = (SplayTreeInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_magick = MagickFalse; /* double-checked locking pattern */
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeMagickList(ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M a g i c k L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagickList() deallocates memory associated with the MagickInfo list.
+%
+% The format of the DestroyMagickList method is:
+%
+% void DestroyMagickList(void)
+%
+*/
+MagickExport void DestroyMagickList(void)
+{
+ AcquireSemaphoreInfo(&magick_semaphore);
+ if (magick_list != (SplayTreeInfo *) NULL)
+ magick_list=DestroySplayTree(magick_list);
+ instantiate_magick=MagickFalse;
+ RelinquishSemaphoreInfo(magick_semaphore);
+ DestroySemaphoreInfo(&magick_semaphore);
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ UnregisterStaticModules();
+#endif
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ DestroyModuleList();
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e D e c o d e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageDecoder() returns the image decoder.
+%
+% The format of the GetImageDecoder method is:
+%
+% DecodeImageHandler *GetImageDecoder(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport DecodeImageHandler *GetImageDecoder(const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->decoder);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e E n c o d e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageEncoder() returns the image encoder.
+%
+% The format of the GetImageEncoder method is:
+%
+% EncodeImageHandler *GetImageEncoder(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport EncodeImageHandler *GetImageEncoder(const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->encoder);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e M a g i c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageMagick() searches for an image format that matches the specified
+% magick string. If one is found, MagickTrue is returned otherwise
+% MagickFalse.
+%
+% The format of the GetImageMagick method is:
+%
+% MagickBooleanType GetImageMagick(const unsigned char *magick,
+% const size_t length,char *format)
+%
+% A description of each parameter follows:
+%
+% o magick: the image format we are searching for.
+%
+% o length: the length of the binary string.
+%
+% o format: the image format as determined by the magick bytes.
+%
+*/
+MagickExport MagickBooleanType GetImageMagick(const unsigned char *magick,
+ const size_t length,char *format)
+{
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ register const MagickInfo
+ *p;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick != (const unsigned char *) NULL);
+ exception=AcquireExceptionInfo();
+ p=GetMagickInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ if (p == (const MagickInfo *) NULL)
+ return(MagickFalse);
+ status=MagickFalse;
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ while (p != (const MagickInfo *) NULL)
+ {
+ if ((p->magick != (IsImageFormatHandler *) NULL) &&
+ (p->magick(magick,length) != 0))
+ {
+ status=MagickTrue;
+ (void) CopyMagickString(format,p->name,MaxTextExtent);
+ break;
+ }
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+ RelinquishSemaphoreInfo(magick_semaphore);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k A d j o i n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickAdjoin() returns MagickTrue if the magick adjoin is MagickTrue.
+%
+% The format of the GetMagickAdjoin method is:
+%
+% MagickBooleanType GetMagickAdjoin(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickBooleanType GetMagickAdjoin(const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->adjoin);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k B l o b S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickBlobSupport() returns MagickTrue if the magick supports blobs.
+%
+% The format of the GetMagickBlobSupport method is:
+%
+% MagickBooleanType GetMagickBlobSupport(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickBooleanType GetMagickBlobSupport(
+ const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->blob_support);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k D e s c r i p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickDescription() returns the magick description.
+%
+% The format of the GetMagickDescription method is:
+%
+% const char *GetMagickDescription(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport const char *GetMagickDescription(const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k E n d i a n S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickEndianSupport() returns the MagickTrue if the coder respects
+% endianness other than MSBEndian.
+%
+% The format of the GetMagickEndianSupport method is:
+%
+% MagickBooleanType GetMagickEndianSupport(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickBooleanType GetMagickEndianSupport(
+ const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->endian_support);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickInfo() returns a pointer MagickInfo structure that matches
+% the specified name. If name is NULL, the head of the image format list
+% is returned.
+%
+% The format of the GetMagickInfo method is:
+%
+% const MagickInfo *GetMagickInfo(const char *name,Exception *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the image format we are looking for.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const MagickInfo *GetMagickInfo(const char *name,
+ ExceptionInfo *exception)
+{
+ register const MagickInfo
+ *p;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((magick_list == (SplayTreeInfo *) NULL) ||
+ (instantiate_magick == MagickFalse))
+ if (InitializeMagickList(exception) == MagickFalse)
+ return((const MagickInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ {
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ if (LocaleCompare(name,"*") == 0)
+ (void) OpenModules(exception);
+#endif
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ RelinquishSemaphoreInfo(magick_semaphore);
+ return(p);
+ }
+ /*
+ Find name in list.
+ */
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ while (p != (const MagickInfo *) NULL)
+ {
+ if (LocaleCompare(p->name,name) == 0)
+ break;
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ if (p == (const MagickInfo *) NULL)
+ {
+ if (*name != '\0')
+ (void) OpenModule(name,exception);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ while (p != (const MagickInfo *) NULL)
+ {
+ if (LocaleCompare(p->name,name) == 0)
+ break;
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+ }
+#endif
+ RelinquishSemaphoreInfo(magick_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickInfoList() returns any image formats that match the specified
+% pattern.
+%
+% The format of the GetMagickInfoList function is:
+%
+% const MagickInfo **GetMagickInfoList(const char *pattern,
+% unsigned long *number_formats,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_formats: This integer returns the number of formats in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MagickInfoCompare(const void *x,const void *y)
+{
+ const MagickInfo
+ **p,
+ **q;
+
+ p=(const MagickInfo **) x,
+ q=(const MagickInfo **) y;
+ return(LocaleCompare((*p)->name,(*q)->name));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const MagickInfo **GetMagickInfoList(const char *pattern,
+ unsigned long *number_formats,ExceptionInfo *exception)
+{
+ const MagickInfo
+ **formats;
+
+ register const MagickInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate magick list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_formats != (unsigned long *) NULL);
+ *number_formats=0;
+ p=GetMagickInfo("*",exception);
+ if (p == (const MagickInfo *) NULL)
+ return((const MagickInfo **) NULL);
+ formats=(const MagickInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(magick_list)+1UL,sizeof(*formats));
+ if (formats == (const MagickInfo **) NULL)
+ return((const MagickInfo **) NULL);
+ /*
+ Generate magick list.
+ */
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ for (i=0; p != (const MagickInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ formats[i++]=p;
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+ RelinquishSemaphoreInfo(magick_semaphore);
+ qsort((void *) formats,(size_t) i,sizeof(*formats),MagickInfoCompare);
+ formats[i]=(MagickInfo *) NULL;
+ *number_formats=(unsigned long) i;
+ return(formats);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickList() returns any image formats that match the specified pattern.
+%
+% The format of the GetMagickList function is:
+%
+% char **GetMagickList(const char *pattern,unsigned long *number_formats,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_formats: This integer returns the number of formats in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MagickCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetMagickList(const char *pattern,
+ unsigned long *number_formats,ExceptionInfo *exception)
+{
+ char
+ **formats;
+
+ register const MagickInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate magick list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_formats != (unsigned long *) NULL);
+ *number_formats=0;
+ p=GetMagickInfo("*",exception);
+ if (p == (const MagickInfo *) NULL)
+ return((char **) NULL);
+ formats=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(magick_list)+1UL,sizeof(*formats));
+ if (formats == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ for (i=0; p != (const MagickInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ formats[i++]=ConstantString(p->name);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+ RelinquishSemaphoreInfo(magick_semaphore);
+ qsort((void *) formats,(size_t) i,sizeof(*formats),MagickCompare);
+ formats[i]=(char *) NULL;
+ *number_formats=(unsigned long) i;
+ return(formats);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k E n d i a n S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickRawSupport() returns the MagickTrue if the coder is a raw format.
+%
+% The format of the GetMagickRawSupport method is:
+%
+% MagickBooleanType GetMagickRawSupport(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickBooleanType GetMagickRawSupport(
+ const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->raw);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k S e e k a b l e S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickSeekableStream() returns MagickTrue if the magick supports a
+% seekable stream.
+%
+% The format of the GetMagickSeekableStream method is:
+%
+% MagickBooleanType GetMagickSeekableStream(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickBooleanType GetMagickSeekableStream(
+ const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->seekable_stream);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k T h r e a d S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickThreadSupport() returns MagickTrue if the magick supports threads.
+%
+% The format of the GetMagickThreadSupport method is:
+%
+% MagickStatusType GetMagickThreadSupport(const MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: The magick info.
+%
+*/
+MagickExport MagickStatusType GetMagickThreadSupport(
+ const MagickInfo *magick_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ return(magick_info->thread_support);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e M a g i c k L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeMagickList() initializes the magick list.
+%
+% The format of the InitializeMagickList() method is:
+%
+% InitializeMagickList(Exceptioninfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void *DestroyMagickNode(void *magick_info)
+{
+ register MagickInfo
+ *p;
+
+ p=(MagickInfo *) magick_info;
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->description != (char *) NULL)
+ p->description=DestroyString(p->description);
+ if (p->version != (char *) NULL)
+ p->version=DestroyString(p->version);
+ if (p->note != (char *) NULL)
+ p->note=DestroyString(p->note);
+ if (p->module != (char *) NULL)
+ p->module=DestroyString(p->module);
+ return(RelinquishMagickMemory(p));
+}
+
+static MagickBooleanType InitializeMagickList(ExceptionInfo *exception)
+{
+ if ((magick_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_magick == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&magick_semaphore);
+ if ((magick_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_magick == MagickFalse))
+ {
+ MagickBooleanType
+ status;
+
+ MagickInfo
+ *magick_info;
+
+ magick_list=NewSplayTree(CompareSplayTreeString,
+ (void *(*)(void *)) NULL,DestroyMagickNode);
+ if (magick_list == (SplayTreeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ magick_info=SetMagickInfo("ephemeral");
+ magick_info->stealth=MagickTrue;
+ status=AddValueToSplayTree(magick_list,magick_info->name,magick_info);
+ if (status == MagickFalse)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ magick_info=SetMagickInfo("clipmask");
+ magick_info->stealth=MagickTrue;
+ status=AddValueToSplayTree(magick_list,magick_info->name,magick_info);
+ if (status == MagickFalse)
+ {
+ char
+ *message;
+
+ message=GetExceptionMessage(errno);
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ message=DestroyString(message);
+ }
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ (void) GetModuleInfo((char *) NULL,exception);
+#endif
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ RegisterStaticModules();
+#endif
+ instantiate_magick=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(magick_semaphore);
+ }
+ return(magick_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s M a g i c k C o n f l i c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickConflict() returns MagickTrue if the image format is not a valid
+% image format or conflicts with a logical drive (.e.g. X:).
+%
+% The format of the IsMagickConflict method is:
+%
+% MagickBooleanType IsMagickConflict(const char *magick)
+%
+% A description of each parameter follows:
+%
+% o magick: Specifies the image format.
+%
+*/
+MagickExport MagickBooleanType IsMagickConflict(const char *magick)
+{
+ const DelegateInfo
+ *delegate_info;
+
+ const MagickInfo
+ *magick_info;
+
+ ExceptionInfo
+ *exception;
+
+ assert(magick != (char *) NULL);
+ exception=AcquireExceptionInfo();
+ magick_info=GetMagickInfo(magick,exception);
+ delegate_info=GetDelegateInfo(magick,(char *) NULL,exception);
+ if (delegate_info == (const DelegateInfo *) NULL)
+ delegate_info=GetDelegateInfo((char *) NULL,magick,exception);
+ exception=DestroyExceptionInfo(exception);
+ if ((magick_info == (const MagickInfo *) NULL) &&
+ (delegate_info == (const DelegateInfo *) NULL))
+ return(MagickTrue);
+#if defined(macintosh)
+ return(MACIsMagickConflict(magick));
+#elif defined(vms)
+ return(VMSIsMagickConflict(magick));
+#elif defined(__WINDOWS__)
+ return(NTIsMagickConflict(magick));
+#else
+ return(MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L i s t M a g i c k I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListMagickInfo() lists the image formats to a file.
+%
+% The format of the ListMagickInfo method is:
+%
+% MagickBooleanType ListMagickInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: A file handle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListMagickInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const MagickInfo
+ **magick_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_formats;
+
+ if (file == (FILE *) NULL)
+ file=stdout;
+ magick_info=GetMagickInfoList("*",&number_formats,exception);
+ if (magick_info == (const MagickInfo **) NULL)
+ return(MagickFalse);
+ ClearMagickException(exception);
+#if !defined(MAGICKCORE_MODULES_SUPPORT)
+ (void) fprintf(file," Format Mode Description\n");
+#else
+ (void) fprintf(file," Format Module Mode Description\n");
+#endif
+ (void) fprintf(file,"--------------------------------------------------------"
+ "-----------------------\n");
+ for (i=0; i < (long) number_formats; i++)
+ {
+ if (magick_info[i]->stealth != MagickFalse)
+ continue;
+ (void) fprintf(file,"%9s%c ",magick_info[i]->name != (char *) NULL ?
+ magick_info[i]->name : "",
+ magick_info[i]->blob_support != MagickFalse ? '*' : ' ');
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ {
+ char
+ module[MaxTextExtent];
+
+ *module='\0';
+ if (magick_info[i]->module != (char *) NULL)
+ (void) CopyMagickString(module,magick_info[i]->module,MaxTextExtent);
+ (void) ConcatenateMagickString(module," ",MaxTextExtent);
+ module[9]='\0';
+ (void) fprintf(file,"%9s ",module);
+ }
+#endif
+ (void) fprintf(file,"%c%c%c ",magick_info[i]->decoder ? 'r' : '-',
+ magick_info[i]->encoder ? 'w' : '-',magick_info[i]->encoder != NULL &&
+ magick_info[i]->adjoin != MagickFalse ? '+' : '-');
+ if (magick_info[i]->description != (char *) NULL)
+ (void) fprintf(file," %s",magick_info[i]->description);
+ if (magick_info[i]->version != (char *) NULL)
+ (void) fprintf(file," (%s)",magick_info[i]->version);
+ (void) fprintf(file,"\n");
+ if (magick_info[i]->note != (char *) NULL)
+ {
+ char
+ **text;
+
+ text=StringToList(magick_info[i]->note);
+ if (text != (char **) NULL)
+ {
+ for (j=0; text[j] != (char *) NULL; j++)
+ {
+ (void) fprintf(file," %s\n",text[j]);
+ text[j]=DestroyString(text[j]);
+ }
+ text=(char **) RelinquishMagickMemory(text);
+ }
+ }
+ }
+ (void) fprintf(file,"\n* native blob support\n");
+ (void) fprintf(file,"r read support\n");
+ (void) fprintf(file,"w write support\n");
+ (void) fprintf(file,"+ support for multiple images\n");
+ (void) fflush(file);
+ magick_info=(const MagickInfo **) RelinquishMagickMemory((void *)
+ magick_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k I n s t a n t i a t e d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickInstantiated() returns MagickTrue if the ImageMagick environment
+% is currently instantiated: MagickCoreGenesis() has been called but
+% MagickDestroy() has not.
+%
+% The format of the IsMagickInstantiated method is:
+%
+% MagickBooleanType IsMagickInstantiated(void)
+%
+*/
+MagickExport MagickBooleanType IsMagickInstantiated(void)
+{
+ return(instantiate_magick);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k C o r e G e n e s i s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickCoreGenesis() initializes the MagickCore environment.
+%
+% The format of the MagickCoreGenesis function is:
+%
+% MagickCoreGenesis(const char *path,
+% const MagickBooleanType establish_signal_handlers)
+%
+% A description of each parameter follows:
+%
+% o path: the execution path of the current ImageMagick client.
+%
+% o establish_signal_handlers: set to MagickTrue to use MagickCore's own
+% signal handlers for common signals.
+%
+*/
+
+static SignalHandler *SetMagickSignalHandler(int signal_number,
+ SignalHandler *handler)
+{
+#if defined(MAGICKCORE_HAVE_SIGACTION) && defined(MAGICKCORE_HAVE_SIGEMPTYSET)
+ int
+ status;
+
+ sigset_t
+ mask;
+
+ struct sigaction
+ action,
+ previous_action;
+
+ sigemptyset(&mask);
+ sigaddset(&mask,signal_number);
+ sigprocmask(SIG_BLOCK,&mask,NULL);
+ action.sa_mask=mask;
+ action.sa_handler=handler;
+ action.sa_flags=0;
+#if defined(SA_INTERRUPT)
+ action.sa_flags|=SA_INTERRUPT;
+#endif
+ status=sigaction(signal_number,&action,&previous_action);
+ if (status < 0)
+ return(SIG_ERR);
+ sigprocmask(SIG_UNBLOCK,&mask,NULL);
+ return(previous_action.sa_handler);
+#else
+ return(signal(signal_number,handler));
+#endif
+}
+
+static void MagickSignalHandler(int signal_number)
+{
+#if !defined(MAGICKCORE_HAVE_SIGACTION)
+ (void) signal(signal_number,SIG_IGN);
+#endif
+ AsynchronousDestroyMagickResources();
+ instantiate_magick=MagickFalse;
+ (void) SetMagickSignalHandler(signal_number,signal_handlers[signal_number]);
+#if defined(MAGICKCORE_HAVE_RAISE)
+ if (signal_handlers[signal_number] != MagickSignalHandler)
+ raise(signal_number);
+#endif
+#if !defined(MAGICKCORE_HAVE__EXIT)
+ exit(signal_number);
+#else
+#if defined(SIGHUP)
+ if (signal_number == SIGHUP)
+ exit(signal_number);
+#endif
+#if defined(SIGINT) && !defined(__WINDOWS__)
+ if (signal_number == SIGINT)
+ exit(signal_number);
+#endif
+#if defined(SIGTERM)
+ if (signal_number == SIGTERM)
+ exit(signal_number);
+#endif
+ _exit(signal_number);
+#endif
+}
+
+static SignalHandler *RegisterMagickSignalHandler(int signal_number)
+{
+ SignalHandler
+ *handler;
+
+ handler=SetMagickSignalHandler(signal_number,MagickSignalHandler);
+ if (handler == SIG_ERR)
+ return(handler);
+ if (handler != SIG_DFL)
+ handler=SetMagickSignalHandler(signal_number,handler);
+ else
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Register handler for signal: %d",signal_number);
+ return(handler);
+}
+
+MagickExport void MagickCoreGenesis(const char *path,
+ const MagickBooleanType establish_signal_handlers)
+{
+ char
+ *events,
+ execution_path[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ ExceptionInfo
+ *exception;
+
+ time_t
+ seconds;
+
+ /*
+ Initialize the Magick environment.
+ */
+ (void) setlocale(LC_ALL,"");
+ (void) setlocale(LC_NUMERIC,"C");
+ InitializeSemaphore();
+ seconds=time((time_t *) NULL);
+ events=GetEnvironmentValue("MAGICK_DEBUG");
+ if (events != (char *) NULL)
+ {
+ (void) SetLogEventMask(events);
+ events=DestroyString(events);
+ }
+#if defined(__WINDOWS__)
+#if defined(_DEBUG) && !defined(__BORLANDC__) && !defined(__MINGW32__)
+ if (IsEventLogging() != MagickFalse)
+ {
+ int
+ debug;
+
+ debug=_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+ debug|=_CRTDBG_CHECK_ALWAYS_DF |_CRTDBG_DELAY_FREE_MEM_DF |
+ _CRTDBG_LEAK_CHECK_DF;
+ if (0)
+ {
+ debug=_CrtSetDbgFlag(debug);
+ _ASSERTE(_CrtCheckMemory());
+ }
+ }
+#endif
+#endif
+ /*
+ Set client name and execution path.
+ */
+ (void) GetExecutionPath(execution_path,MaxTextExtent);
+ if ((path != (const char *) NULL) && (*path != '\0'))
+ (void) CopyMagickString(execution_path,path,MaxTextExtent);
+ GetPathComponent(execution_path,TailPath,filename);
+ (void) SetClientName(filename);
+ GetPathComponent(execution_path,HeadPath,execution_path);
+ (void) SetClientPath(execution_path);
+ if (establish_signal_handlers != MagickFalse)
+ {
+ /*
+ Set signal handlers.
+ */
+#if defined(SIGABRT)
+ if (signal_handlers[SIGABRT] == (SignalHandler *) NULL)
+ signal_handlers[SIGABRT]=RegisterMagickSignalHandler(SIGABRT);
+#endif
+#if defined(SIGFPE)
+ if (signal_handlers[SIGFPE] == (SignalHandler *) NULL)
+ signal_handlers[SIGFPE]=RegisterMagickSignalHandler(SIGFPE);
+#endif
+#if defined(SIGHUP)
+ if (signal_handlers[SIGHUP] == (SignalHandler *) NULL)
+ signal_handlers[SIGHUP]=RegisterMagickSignalHandler(SIGHUP);
+#endif
+#if defined(SIGINT) && !defined(__WINDOWS__)
+ if (signal_handlers[SIGINT] == (SignalHandler *) NULL)
+ signal_handlers[SIGINT]=RegisterMagickSignalHandler(SIGINT);
+#endif
+#if defined(SIGQUIT)
+ if (signal_handlers[SIGQUIT] == (SignalHandler *) NULL)
+ signal_handlers[SIGQUIT]=RegisterMagickSignalHandler(SIGQUIT);
+#endif
+#if defined(SIGTERM)
+ if (signal_handlers[SIGTERM] == (SignalHandler *) NULL)
+ signal_handlers[SIGTERM]=RegisterMagickSignalHandler(SIGTERM);
+#endif
+#if defined(SIGXCPU)
+ if (signal_handlers[SIGXCPU] == (SignalHandler *) NULL)
+ signal_handlers[SIGXCPU]=RegisterMagickSignalHandler(SIGXCPU);
+#endif
+#if defined(SIGXFSZ)
+ if (signal_handlers[SIGXFSZ] == (SignalHandler *) NULL)
+ signal_handlers[SIGXFSZ]=RegisterMagickSignalHandler(SIGXFSZ);
+#endif
+ }
+ /*
+ Initialize magick resources.
+ */
+ InitializeMagickResources();
+ exception=AcquireExceptionInfo();
+ (void) GetMagickInfo((char *) NULL,exception);
+ exception=DestroyExceptionInfo(exception);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k C o r e T e r m i n u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickCoreTerminus() destroys the MagickCore environment.
+%
+% The format of the DestroyMagick function is:
+%
+% MagickCoreTerminus(void)
+%
+*/
+MagickExport void MagickCoreTerminus(void)
+{
+#if defined(MAGICKCORE_X11_DELEGATE)
+ DestroyXResources();
+#endif
+ DestroyConstitute();
+ DestroyMimeList();
+ DestroyConfigureList();
+ DestroyTypeList();
+ DestroyColorList();
+#if defined(__WINDOWS__)
+ NTGhostscriptUnLoadDLL();
+#endif
+ DestroyMagicList();
+ DestroyDelegateList();
+ DestroyMagickList();
+ DestroyCoderList();
+ DestroyMagickResources();
+ DestroyImageRegistry();
+ DestroyPixelCacheResources();
+ DestroyPolicyList();
+ DestroyRandomReservoir();
+ DestroyLocaleList();
+ DestroyLogList();
+ instantiate_magick=MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e g i s t e r M a g i c k I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RegisterMagickInfo() adds attributes for a particular image format to the
+% list of supported formats. The attributes include the image format name,
+% a method to read and/or write the format, whether the format supports the
+% saving of more than one frame to the same file or blob, whether the format
+% supports native in-memory I/O, and a brief description of the format.
+%
+% The format of the RegisterMagickInfo method is:
+%
+% MagickInfo *RegisterMagickInfo(MagickInfo *magick_info)
+%
+% A description of each parameter follows:
+%
+% o magick_info: the magick info.
+%
+*/
+MagickExport MagickInfo *RegisterMagickInfo(MagickInfo *magick_info)
+{
+ MagickBooleanType
+ status;
+
+ /*
+ Delete any existing name.
+ */
+ assert(magick_info != (MagickInfo *) NULL);
+ assert(magick_info->signature == MagickSignature);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",magick_info->name);
+ if (magick_list == (SplayTreeInfo *) NULL)
+ return((MagickInfo *) NULL);
+ status=AddValueToSplayTree(magick_list,magick_info->name,magick_info);
+ if (status == MagickFalse)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ return(magick_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t M a g i c k I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetMagickInfo() allocates a MagickInfo structure and initializes the members
+% to default values.
+%
+% The format of the SetMagickInfo method is:
+%
+% MagickInfo *SetMagickInfo(const char *name)
+%
+% A description of each parameter follows:
+%
+% o magick_info: Method SetMagickInfo returns the allocated and initialized
+% MagickInfo structure.
+%
+% o name: a character string that represents the image format associated
+% with the MagickInfo structure.
+%
+*/
+MagickExport MagickInfo *SetMagickInfo(const char *name)
+{
+ MagickInfo
+ *magick_info;
+
+ assert(name != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
+ magick_info=(MagickInfo *) AcquireMagickMemory(sizeof(*magick_info));
+ if (magick_info == (MagickInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(magick_info,0,sizeof(*magick_info));
+ magick_info->name=ConstantString(name);
+ magick_info->adjoin=MagickTrue;
+ magick_info->blob_support=MagickTrue;
+ magick_info->thread_support=(MagickStatusType) (DecoderThreadSupport |
+ EncoderThreadSupport);
+ magick_info->signature=MagickSignature;
+ return(magick_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ U n r e g i s t e r M a g i c k I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnregisterMagickInfo() removes a name from the magick info list. It returns
+% MagickFalse if the name does not exist in the list otherwise MagickTrue.
+%
+% The format of the UnregisterMagickInfo method is:
+%
+% MagickBooleanType UnregisterMagickInfo(const char *name)
+%
+% A description of each parameter follows:
+%
+% o name: a character string that represents the image format we are
+% looking for.
+%
+*/
+MagickExport MagickBooleanType UnregisterMagickInfo(const char *name)
+{
+ register const MagickInfo
+ *p;
+
+ MagickBooleanType
+ status;
+
+ assert(name != (const char *) NULL);
+ if (magick_list == (SplayTreeInfo *) NULL)
+ return(MagickFalse);
+ if (GetNumberOfNodesInSplayTree(magick_list) == 0)
+ return(MagickFalse);
+ AcquireSemaphoreInfo(&magick_semaphore);
+ ResetSplayTreeIterator(magick_list);
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ while (p != (const MagickInfo *) NULL)
+ {
+ if (LocaleCompare(p->name,name) == 0)
+ break;
+ p=(const MagickInfo *) GetNextValueInSplayTree(magick_list);
+ }
+ status=DeleteNodeByValueFromSplayTree(magick_list,p);
+ RelinquishSemaphoreInfo(magick_semaphore);
+ return(status);
+}
diff --git a/magick/magick.h b/magick/magick.h
new file mode 100644
index 0000000..af44c3a
--- /dev/null
+++ b/magick/magick.h
@@ -0,0 +1,138 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore magick methods.
+*/
+#ifndef _MAGICKCORE_MAGICK_H
+#define _MAGICKCORE_MAGICK_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedFormatType,
+ ImplicitFormatType,
+ ExplicitFormatType
+} MagickFormatType;
+
+typedef enum
+{
+ NoThreadSupport = 0x0000,
+ DecoderThreadSupport = 0x0001,
+ EncoderThreadSupport = 0x0002
+} MagickThreadSupport;
+
+typedef Image
+ *DecodeImageHandler(const ImageInfo *,ExceptionInfo *);
+
+typedef MagickBooleanType
+ EncodeImageHandler(const ImageInfo *,Image *);
+
+typedef MagickBooleanType
+ IsImageFormatHandler(const unsigned char *,const size_t);
+
+typedef struct _MagickInfo
+{
+ char
+ *name,
+ *description,
+ *version,
+ *note,
+ *module;
+
+ ImageInfo
+ *image_info;
+
+ DecodeImageHandler
+ *decoder;
+
+ EncodeImageHandler
+ *encoder;
+
+ IsImageFormatHandler
+ *magick;
+
+ void
+ *client_data;
+
+ MagickBooleanType
+ adjoin,
+ raw,
+ endian_support,
+ blob_support,
+ seekable_stream;
+
+ MagickFormatType
+ format_type;
+
+ MagickStatusType
+ thread_support;
+
+ MagickBooleanType
+ stealth;
+
+ struct _MagickInfo
+ *previous,
+ *next; /* deprecated, use GetMagickInfoList() */
+
+ unsigned long
+ signature;
+} MagickInfo;
+
+extern MagickExport char
+ **GetMagickList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const char
+ *GetMagickDescription(const MagickInfo *);
+
+extern MagickExport DecodeImageHandler
+ *GetImageDecoder(const MagickInfo *);
+
+extern MagickExport EncodeImageHandler
+ *GetImageEncoder(const MagickInfo *);
+
+extern MagickExport MagickBooleanType
+ GetImageMagick(const unsigned char *,const size_t,char *),
+ GetMagickAdjoin(const MagickInfo *),
+ GetMagickBlobSupport(const MagickInfo *),
+ GetMagickEndianSupport(const MagickInfo *),
+ GetMagickRawSupport(const MagickInfo *),
+ GetMagickSeekableStream(const MagickInfo *),
+ IsMagickInstantiated(void),
+ UnregisterMagickInfo(const char *);
+
+extern const MagickExport MagickInfo
+ *GetMagickInfo(const char *,ExceptionInfo *),
+ **GetMagickInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport MagickInfo
+ *RegisterMagickInfo(MagickInfo *),
+ *SetMagickInfo(const char *);
+
+extern MagickExport MagickStatusType
+ GetMagickThreadSupport(const MagickInfo *);
+
+extern MagickExport void
+ DestroyMagickList(void),
+ MagickCoreGenesis(const char *,const MagickBooleanType),
+ MagickCoreTerminus(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/matrix.c b/magick/matrix.c
new file mode 100644
index 0000000..ddb29a1
--- /dev/null
+++ b/magick/matrix.c
@@ -0,0 +1,418 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M AAA TTTTT RRRR IIIII X X %
+% MM MM A A T R R I X X %
+% M M M AAAAA T RRRR I X %
+% M M A A T R R I X X %
+% M M A A T R R IIIII X X %
+% %
+% %
+% MagickCore Matrix Methods %
+% %
+% Software Design %
+% John Cristy %
+% August 2007 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/matrix.h"
+#include "magick/memory_.h"
+#include "magick/utility.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e M a g i c k M a t r i x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireMagickMatrix() allocates and returns a matrix in the form of an
+% array of pointers to an array of doubles, with all values pre-set to zero.
+%
+% This used to generate the two dimentional matrix, and vectors required
+% for the GaussJordanElimination() method below, solving some system of
+% simultanious equations.
+%
+% The format of the AcquireMagickMatrix method is:
+%
+% double **AcquireMagickMatrix(const unsigned long nptrs,
+% const unsigned long size)
+%
+% A description of each parameter follows:
+%
+% o nptrs: the number pointers for the array of pointers
+% (first dimension)
+%
+% o size: the size of the array of doubles each pointer points to.
+% (second dimension)
+%
+*/
+MagickExport double **AcquireMagickMatrix(const unsigned long nptrs,
+ const unsigned long size)
+{
+ double
+ **matrix;
+
+ register unsigned long
+ i,
+ j;
+
+ matrix=(double **) AcquireQuantumMemory(nptrs,sizeof(*matrix));
+ if (matrix == (double **) NULL)
+ return((double **)NULL);
+
+ for (i=0; i < nptrs; i++)
+ {
+ matrix[i]=(double *) AcquireQuantumMemory(size,sizeof(*matrix[i]));
+ if (matrix[i] == (double *) NULL)
+ {
+ for (j=0; j < i; j++)
+ matrix[j]=(double *) RelinquishMagickMemory(matrix[j]);
+ matrix=(double **) RelinquishMagickMemory(matrix);
+ return((double **) NULL);
+ }
+ /*(void) ResetMagickMemory(matrix[i],0,size*sizeof(*matrix[i])); */
+ for (j=0; j < size; j++)
+ matrix[i][j] = 0.0;
+ }
+ return(matrix);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G a u s s J o r d a n E l i m i n a t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GaussJordanElimination() returns a matrix in reduced row echelon form,
+% while simultaneously reducing and thus solving the augumented results
+% matrix.
+%
+% See also http://en.wikipedia.org/wiki/Gauss-Jordan_elimination
+%
+% The format of the GaussJordanElimination method is:
+%
+% MagickBooleanType GaussJordanElimination(double **matrix,
+% double **vectors, const unsigned long rank, const unsigned long nvecs)
+%
+% A description of each parameter follows:
+%
+% o matrix: the matrix to be reduced, as an 'array of row pointers'.
+%
+% o vectors: the additional matrix argumenting the matrix for row reduction.
+% Producing an 'array of column vectors'.
+%
+% o rank: The size of the matrix (both rows and columns).
+% Also represents the number terms that need to be solved.
+%
+% o nvecs: Number of vectors columns, argumenting the above matrix.
+% Usally 1, but can be more for more complex equation solving.
+%
+% Note that the 'matrix' is given as a 'array of row pointers' of rank size.
+% That is values can be assigned as matrix[row][column] where 'row' is
+% typically the equation, and 'column' is the term of the equation.
+% That is the matrix is in the form of a 'row first array'.
+%
+% However 'vectors' is a 'array of column pointers' which can have any number
+% of columns, with each column array the same 'rank' size as 'matrix'.
+%
+% This allows for simpler handling of the results, especially is only one
+% column 'vector' is all that is required to produce the desired solution.
+%
+% For example, the 'vectors' can consist of a pointer to a simple array of
+% doubles. when only one set of simultanious equations is to be solved from
+% the given set of coefficient weighted terms.
+%
+% double **matrix = AcquireMagickMatrix(8UL,8UL);
+% double coefficents[8];
+% ...
+% GaussJordanElimination(matrix, &coefficents, 8UL, 1UL);
+%
+% However by specifing more 'columns' (as an 'array of vector columns',
+% you can use this function to solve a set of 'separable' equations.
+%
+% For example a distortion function where u = U(x,y) v = V(x,y)
+% And the functions U() and V() have separate coefficents, but are being
+% generated from a common x,y->u,v data set.
+%
+% Another example is generation of a color gradient from a set of colors
+% at specific coordients, such as a list x,y -> r,g,b,a
+% (Reference to be added - Anthony)
+%
+% You can also use the 'vectors' to generate an inverse of the given 'matrix'
+% though as a 'column first array' rather than a 'row first array'. For
+% details see http://en.wikipedia.org/wiki/Gauss-Jordan_elimination
+%
+*/
+MagickExport MagickBooleanType GaussJordanElimination(double **matrix,
+ double **vectors, const unsigned long rank, const unsigned long nvecs)
+{
+#define GaussJordanSwap(x,y) \
+{ \
+ if ((x) != (y)) \
+ { \
+ (x)+=(y); \
+ (y)=(x)-(y); \
+ (x)=(x)-(y); \
+ } \
+}
+
+ double
+ max,
+ scale;
+
+ long
+ column,
+ *columns,
+ *pivots,
+ row,
+ *rows;
+
+ register long
+ i,
+ j,
+ k;
+
+ columns=(long *) AcquireQuantumMemory(rank,sizeof(*columns));
+ rows=(long *) AcquireQuantumMemory(rank,sizeof(*rows));
+ pivots=(long *) AcquireQuantumMemory(rank,sizeof(*pivots));
+ if ((rows == (long *) NULL) || (columns == (long *) NULL) ||
+ (pivots == (long *) NULL))
+ {
+ if (pivots != (long *) NULL)
+ pivots=(long *) RelinquishMagickMemory(pivots);
+ if (columns != (long *) NULL)
+ columns=(long *) RelinquishMagickMemory(columns);
+ if (rows != (long *) NULL)
+ rows=(long *) RelinquishMagickMemory(rows);
+ return(MagickFalse);
+ }
+ (void) ResetMagickMemory(columns,0,rank*sizeof(*columns));
+ (void) ResetMagickMemory(rows,0,rank*sizeof(*rows));
+ (void) ResetMagickMemory(pivots,0,rank*sizeof(*pivots));
+ column=0;
+ row=0;
+ for (i=0; i < (long) rank; i++)
+ {
+ max=0.0;
+ for (j=0; j < (long) rank; j++)
+ if (pivots[j] != 1)
+ {
+ for (k=0; k < (long) rank; k++)
+ if (pivots[k] != 0)
+ {
+ if (pivots[k] > 1)
+ return(MagickFalse);
+ }
+ else
+ if (fabs(matrix[j][k]) >= max)
+ {
+ max=fabs(matrix[j][k]);
+ row=j;
+ column=k;
+ }
+ }
+ pivots[column]++;
+ if (row != column)
+ {
+ for (k=0; k < (long) rank; k++)
+ GaussJordanSwap(matrix[row][k],matrix[column][k]);
+ for (k=0; k < (long) nvecs; k++)
+ GaussJordanSwap(vectors[k][row],vectors[k][column]);
+ }
+ rows[i]=row;
+ columns[i]=column;
+ if (matrix[column][column] == 0.0)
+ return(MagickFalse); /* sigularity */
+ scale=1.0/matrix[column][column];
+ matrix[column][column]=1.0;
+ for (j=0; j < (long) rank; j++)
+ matrix[column][j]*=scale;
+ for (j=0; j < (long) nvecs; j++)
+ vectors[j][column]*=scale;
+ for (j=0; j < (long) rank; j++)
+ if (j != column)
+ {
+ scale=matrix[j][column];
+ matrix[j][column]=0.0;
+ for (k=0; k < (long) rank; k++)
+ matrix[j][k]-=scale*matrix[column][k];
+ for (k=0; k < (long) nvecs; k++)
+ vectors[k][j]-=scale*vectors[k][column];
+ }
+ }
+ for (j=(long) rank-1; j >= 0; j--)
+ if (columns[j] != rows[j])
+ for (i=0; i < (long) rank; i++)
+ GaussJordanSwap(matrix[i][rows[j]],matrix[i][columns[j]]);
+ pivots=(long *) RelinquishMagickMemory(pivots);
+ rows=(long *) RelinquishMagickMemory(rows);
+ columns=(long *) RelinquishMagickMemory(columns);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L e a s t S q u a r e s A d d T e r m s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LeastSquaresAddTerms() adds one set of terms and associate results to the
+% given matrix and vectors for solving using least-squares function fitting.
+%
+% The format of the AcquireMagickMatrix method is:
+%
+% void LeastSquaresAddTerms(double **matrix,double **vectors,
+% const double *terms, const double *results,
+% const unsigned long rank, const unsigned long nvecs);
+%
+% A description of each parameter follows:
+%
+% o matrix: the square matrix to add given terms/results to.
+%
+% o vectors: the result vectors to add terms/results to.
+%
+% o terms: the pre-calculated terms (without the unknown coefficent
+% weights) that forms the equation being added.
+%
+% o results: the result(s) that should be generated from the given terms
+% weighted by the yet-to-be-solved coefficents.
+%
+% o rank: the rank or size of the dimentions of the square matrix.
+% Also the length of vectors, and number of terms being added.
+%
+% o nvecs: Number of result vectors, and number or results being added.
+% Also represents the number of separable systems of equations
+% that is being solved.
+%
+% Example of use...
+%
+% 2 dimentional Affine Equations (which are separable)
+% c0*x + c2*y + c4*1 => u
+% c1*x + c3*y + c5*1 => v
+%
+% double **matrix = AcquireMagickMatrix(3UL,3UL);
+% double **vectors = AcquireMagickMatrix(2UL,3UL);
+% double terms[3], results[2];
+% ...
+% for each given x,y -> u,v
+% terms[0] = x;
+% terms[1] = y;
+% terms[2] = 1;
+% results[0] = u;
+% results[1] = v;
+% LeastSquaresAddTerms(matrix,vectors,terms,results,3UL,2UL);
+% ...
+% if ( GaussJordanElimination(matrix,vectors,3UL,2UL) ) {
+% c0 = vectors[0][0];
+% c2 = vectors[0][1];
+% c4 = vectors[0][2];
+% c1 = vectors[1][0];
+% c3 = vectors[1][1];
+% c5 = vectors[1][2];
+% }
+% else
+% printf("Matrix unsolvable\n);
+% RelinquishMagickMatrix(matrix,3UL);
+% RelinquishMagickMatrix(vectors,2UL);
+%
+*/
+MagickExport void LeastSquaresAddTerms(double **matrix,double **vectors,
+ const double *terms, const double *results, const unsigned long rank,
+ const unsigned long nvecs)
+{
+ register unsigned long
+ i,
+ j;
+
+ for(j=0; j<rank; j++) {
+ for(i=0; i<rank; i++)
+ matrix[i][j] += terms[i] * terms[j];
+ for(i=0; i<nvecs; i++)
+ vectors[i][j] += results[i] * terms[j];
+ }
+
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n q u i s h M a g i c k M a t r i x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishMagickMatrix() frees the previously acquired matrix (array of
+% pointers to arrays of doubles).
+%
+% The format of the RelinquishMagickMatrix method is:
+%
+% double **RelinquishMagickMatrix(double **matrix,
+% const unsigned long nptrs)
+%
+% A description of each parameter follows:
+%
+% o matrix: the matrix to relinquish
+%
+% o nptrs: the first dimention of the acquired matrix (number of pointers)
+%
+*/
+MagickExport double **RelinquishMagickMatrix(double **matrix,
+ const unsigned long nptrs)
+{
+ register unsigned long
+ i;
+
+ if (matrix == (double **) NULL )
+ return(matrix);
+
+ for (i=0; i < nptrs; i++)
+ matrix[i]=(double *) RelinquishMagickMemory(matrix[i]);
+ matrix=(double **) RelinquishMagickMemory(matrix);
+
+ return(matrix);
+}
+
diff --git a/magick/matrix.h b/magick/matrix.h
new file mode 100644
index 0000000..0984d96
--- /dev/null
+++ b/magick/matrix.h
@@ -0,0 +1,41 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore graphic resample methods.
+*/
+#ifndef _MAGICKCORE_MATRIX_H
+#define _MAGICKCORE_MATRIX_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport double
+ **AcquireMagickMatrix(const unsigned long,const unsigned long),
+ **RelinquishMagickMatrix(double **,const unsigned long);
+
+extern MagickExport MagickBooleanType
+ GaussJordanElimination(double **,double **,const unsigned long,
+ const unsigned long);
+
+extern MagickExport void
+ LeastSquaresAddTerms(double **,double **,const double *,const double *,
+ const unsigned long, const unsigned long);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/memory.c b/magick/memory.c
new file mode 100644
index 0000000..077b5f8
--- /dev/null
+++ b/magick/memory.c
@@ -0,0 +1,975 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M EEEEE M M OOO RRRR Y Y %
+% MM MM E MM MM O O R R Y Y %
+% M M M EEE M M M O O RRRR Y %
+% M M E M M O O R R Y %
+% M M EEEEE M M OOO R R Y %
+% %
+% %
+% MagickCore Memory Allocation Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Segregate our memory requirements from any program that calls our API. This
+% should help reduce the risk of others changing our program state or causing
+% memory corruption.
+%
+% Our custom memory allocation manager implements a best-fit allocation policy
+% using segregated free lists. It uses a linear distribution of size classes
+% for lower sizes and a power of two distribution of size classes at higher
+% sizes. It is based on the paper, "Fast Memory Allocation using Lazy Fits."
+% written by Yoo C. Chung.
+%
+% By default, ANSI memory methods are called (e.g. malloc). Use the
+% custom memory allocator by defining MAGICKCORE_EMBEDDABLE_SUPPORT
+% to allocate memory with private anonymous mapping rather than from the
+% heap.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+
+/*
+ Define declarations.
+*/
+#define BlockFooter(block,size) \
+ ((size_t *) ((char *) (block)+(size)-2*sizeof(size_t)))
+#define BlockHeader(block) ((size_t *) (block)-1)
+#define BlockSize 4096
+#define BlockThreshold 1024
+#define AlignedSize (16*sizeof(void *))
+#define MaxBlockExponent 16
+#define MaxBlocks ((BlockThreshold/(4*sizeof(size_t)))+MaxBlockExponent+1)
+#define MaxSegments 1024
+#define MemoryGuard ((0xdeadbeef << 31)+0xdeafdeed)
+#define NextBlock(block) ((char *) (block)+SizeOfBlock(block))
+#define NextBlockInList(block) (*(void **) (block))
+#define PreviousBlock(block) ((char *) (block)-(*((size_t *) (block)-2)))
+#define PreviousBlockBit 0x01
+#define PreviousBlockInList(block) (*((void **) (block)+1))
+#define SegmentSize (2*1024*1024)
+#define SizeMask (~0x01)
+#define SizeOfBlock(block) (*BlockHeader(block) & SizeMask)
+
+/*
+ Typedef declarations.
+*/
+typedef struct _DataSegmentInfo
+{
+ void
+ *allocation,
+ *bound;
+
+ MagickBooleanType
+ mapped;
+
+ size_t
+ length;
+
+ struct _DataSegmentInfo
+ *previous,
+ *next;
+} DataSegmentInfo;
+
+typedef struct _MemoryInfo
+{
+ size_t
+ allocation;
+
+ void
+ *blocks[MaxBlocks+1];
+
+ size_t
+ number_segments;
+
+ DataSegmentInfo
+ *segments[MaxSegments],
+ segment_pool[MaxSegments];
+} MemoryInfo;
+
+typedef struct _MagickMemoryMethods
+{
+ AcquireMemoryHandler
+ acquire_memory_handler;
+
+ ResizeMemoryHandler
+ resize_memory_handler;
+
+ DestroyMemoryHandler
+ destroy_memory_handler;
+} MagickMemoryMethods;
+
+
+/*
+ Global declarations.
+*/
+static MagickMemoryMethods
+ memory_methods = { malloc, realloc, free };
+
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+static MemoryInfo
+ memory_info;
+
+static SemaphoreInfo
+ *memory_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile DataSegmentInfo
+ *free_segments = (DataSegmentInfo *) NULL;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ ExpandHeap(size_t);
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e A l i g n e d M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireAlignedMemory() returns a pointer to a block of memory at least size
+% bytes whose address is a multiple of 16*sizeof(void *).
+%
+% The format of the AcquireAlignedMemory method is:
+%
+% void *AcquireAlignedMemory(const size_t count,const size_t quantum)
+%
+% A description of each parameter follows:
+%
+% o count: the number of quantum elements to allocate.
+%
+% o quantum: the number of bytes in each quantum.
+%
+*/
+MagickExport void *AcquireAlignedMemory(const size_t count,const size_t quantum)
+{
+ size_t
+ size;
+
+ size=count*quantum;
+ if ((count == 0) || (quantum != (size/count)))
+ {
+ errno=ENOMEM;
+ return((void *) NULL);
+ }
+#if defined(MAGICKCORE_HAVE_POSIX_MEMALIGN)
+ {
+ void
+ *memory;
+
+ if (posix_memalign(&memory,AlignedSize,size) == 0)
+ return(memory);
+ }
+#endif
+ return(malloc(size));
+}
+
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e B l o c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireBlock() returns a pointer to a block of memory at least size bytes
+% suitably aligned for any use.
+%
+% The format of the AcquireBlock method is:
+%
+% void *AcquireBlock(const size_t size)
+%
+% A description of each parameter follows:
+%
+% o size: the size of the memory in bytes to allocate.
+%
+*/
+
+static inline size_t AllocationPolicy(size_t size)
+{
+ register size_t
+ blocksize;
+
+ /*
+ The linear distribution.
+ */
+ assert(size != 0);
+ assert(size % (4*sizeof(size_t)) == 0);
+ if (size <= BlockThreshold)
+ return(size/(4*sizeof(size_t)));
+ /*
+ Check for the largest block size.
+ */
+ if (size > (size_t) (BlockThreshold*(1L << (MaxBlockExponent-1L))))
+ return(MaxBlocks-1L);
+ /*
+ Otherwise use a power of two distribution.
+ */
+ blocksize=BlockThreshold/(4*sizeof(size_t));
+ for ( ; size > BlockThreshold; size/=2)
+ blocksize++;
+ assert(blocksize > (BlockThreshold/(4*sizeof(size_t))));
+ assert(blocksize < (MaxBlocks-1L));
+ return(blocksize);
+}
+
+static inline void InsertFreeBlock(void *block,const size_t i)
+{
+ register void
+ *next,
+ *previous;
+
+ size_t
+ size;
+
+ size=SizeOfBlock(block);
+ previous=(void *) NULL;
+ next=memory_info.blocks[i];
+ while ((next != (void *) NULL) && (SizeOfBlock(next) < size))
+ {
+ previous=next;
+ next=NextBlockInList(next);
+ }
+ PreviousBlockInList(block)=previous;
+ NextBlockInList(block)=next;
+ if (previous != (void *) NULL)
+ NextBlockInList(previous)=block;
+ else
+ memory_info.blocks[i]=block;
+ if (next != (void *) NULL)
+ PreviousBlockInList(next)=block;
+}
+
+static inline void RemoveFreeBlock(void *block,const size_t i)
+{
+ register void
+ *next,
+ *previous;
+
+ next=NextBlockInList(block);
+ previous=PreviousBlockInList(block);
+ if (previous == (void *) NULL)
+ memory_info.blocks[i]=next;
+ else
+ NextBlockInList(previous)=next;
+ if (next != (void *) NULL)
+ PreviousBlockInList(next)=previous;
+}
+
+static void *AcquireBlock(size_t size)
+{
+ register size_t
+ i;
+
+ register void
+ *block;
+
+ /*
+ Find free block.
+ */
+ size=(size_t) (size+sizeof(size_t)+6*sizeof(size_t)-1) & -(4U*sizeof(size_t));
+ i=AllocationPolicy(size);
+ block=memory_info.blocks[i];
+ while ((block != (void *) NULL) && (SizeOfBlock(block) < size))
+ block=NextBlockInList(block);
+ if (block == (void *) NULL)
+ {
+ i++;
+ while (memory_info.blocks[i] == (void *) NULL)
+ i++;
+ block=memory_info.blocks[i];
+ if (i >= MaxBlocks)
+ return((void *) NULL);
+ }
+ assert((*BlockHeader(NextBlock(block)) & PreviousBlockBit) == 0);
+ assert(SizeOfBlock(block) >= size);
+ RemoveFreeBlock(block,AllocationPolicy(SizeOfBlock(block)));
+ if (SizeOfBlock(block) > size)
+ {
+ size_t
+ blocksize;
+
+ void
+ *next;
+
+ /*
+ Split block.
+ */
+ next=(char *) block+size;
+ blocksize=SizeOfBlock(block)-size;
+ *BlockHeader(next)=blocksize;
+ *BlockFooter(next,blocksize)=blocksize;
+ InsertFreeBlock(next,AllocationPolicy(blocksize));
+ *BlockHeader(block)=size | (*BlockHeader(block) & ~SizeMask);
+ }
+ assert(size == SizeOfBlock(block));
+ *BlockHeader(NextBlock(block))|=PreviousBlockBit;
+ memory_info.allocation+=size;
+ return(block);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireMagickMemory() returns a pointer to a block of memory at least size
+% bytes suitably aligned for any use.
+%
+% The format of the AcquireMagickMemory method is:
+%
+% void *AcquireMagickMemory(const size_t size)
+%
+% A description of each parameter follows:
+%
+% o size: the size of the memory in bytes to allocate.
+%
+*/
+MagickExport void *AcquireMagickMemory(const size_t size)
+{
+ register void
+ *memory;
+
+#if !defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ memory=memory_methods.acquire_memory_handler(size == 0 ? 1UL : size);
+#else
+ if (free_segments == (DataSegmentInfo *) NULL)
+ {
+ AcquireSemaphoreInfo(&memory_semaphore);
+ if (free_segments == (DataSegmentInfo *) NULL)
+ {
+ register long
+ i;
+
+ assert(2*sizeof(size_t) > (size_t) (~SizeMask));
+ (void) ResetMagickMemory(&memory_info,0,sizeof(memory_info));
+ memory_info.allocation=SegmentSize;
+ memory_info.blocks[MaxBlocks]=(void *) (-1);
+ for (i=0; i < MaxSegments; i++)
+ {
+ if (i != 0)
+ memory_info.segment_pool[i].previous=
+ (&memory_info.segment_pool[i-1]);
+ if (i != (MaxSegments-1))
+ memory_info.segment_pool[i].next=(&memory_info.segment_pool[i+1]);
+ }
+ free_segments=(&memory_info.segment_pool[0]);
+ }
+ RelinquishSemaphoreInfo(memory_semaphore);
+ }
+ AcquireSemaphoreInfo(&memory_semaphore);
+ memory=AcquireBlock(size == 0 ? 1UL : size);
+ if (memory == (void *) NULL)
+ {
+ if (ExpandHeap(size == 0 ? 1UL : size) != MagickFalse)
+ memory=AcquireBlock(size == 0 ? 1UL : size);
+ }
+ RelinquishSemaphoreInfo(memory_semaphore);
+#endif
+ return(memory);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e Q u a n t u m M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireQuantumMemory() returns a pointer to a block of memory at least
+% count * quantum bytes suitably aligned for any use.
+%
+% The format of the AcquireQuantumMemory method is:
+%
+% void *AcquireQuantumMemory(const size_t count,const size_t quantum)
+%
+% A description of each parameter follows:
+%
+% o count: the number of quantum elements to allocate.
+%
+% o quantum: the number of bytes in each quantum.
+%
+*/
+MagickExport void *AcquireQuantumMemory(const size_t count,const size_t quantum)
+{
+ size_t
+ size;
+
+ size=count*quantum;
+ if ((count == 0) || (quantum != (size/count)))
+ {
+ errno=ENOMEM;
+ return((void *) NULL);
+ }
+ return(AcquireMagickMemory(size));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o p y M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CopyMagickMemory() copies size bytes from memory area source to the
+% destination. Copying between objects that overlap will take place
+% correctly. It returns destination.
+%
+% The format of the CopyMagickMemory method is:
+%
+% void *CopyMagickMemory(void *destination,const void *source,
+% const size_t size)
+%
+% A description of each parameter follows:
+%
+% o destination: the destination.
+%
+% o source: the source.
+%
+% o size: the size of the memory in bytes to allocate.
+%
+*/
+MagickExport void *CopyMagickMemory(void *destination,const void *source,
+ const size_t size)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned char
+ *q;
+
+ assert(destination != (void *) NULL);
+ assert(source != (const void *) NULL);
+ p=(const unsigned char *) source;
+ q=(unsigned char *) destination;
+ if (((q+size) < p) || (q > (p+size)))
+ switch (size)
+ {
+ default: return(memcpy(destination,source,size));
+ case 7: *q++=(*p++);
+ case 6: *q++=(*p++);
+ case 5: *q++=(*p++);
+ case 4: *q++=(*p++);
+ case 3: *q++=(*p++);
+ case 2: *q++=(*p++);
+ case 1: *q++=(*p++);
+ case 0: return(destination);
+ }
+ return(memmove(destination,source,size));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagickMemory() deallocates memory associated with the memory manager.
+%
+% The format of the DestroyMagickMemory method is:
+%
+% DestroyMagickMemory(void)
+%
+*/
+MagickExport void DestroyMagickMemory(void)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ register long
+ i;
+
+ AcquireSemaphoreInfo(&memory_semaphore);
+ RelinquishSemaphoreInfo(memory_semaphore);
+ for (i=0; i < (long) memory_info.number_segments; i++)
+ if (memory_info.segments[i]->mapped == MagickFalse)
+ memory_methods.destroy_memory_handler(
+ memory_info.segments[i]->allocation);
+ else
+ (void) UnmapBlob(memory_info.segments[i]->allocation,
+ memory_info.segments[i]->length);
+ free_segments=(DataSegmentInfo *) NULL;
+ (void) ResetMagickMemory(&memory_info,0,sizeof(memory_info));
+ DestroySemaphoreInfo(&memory_semaphore);
+#endif
+}
+
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ E x p a n d H e a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExpandHeap() get more memory from the system. It returns MagickTrue on
+% success otherwise MagickFalse.
+%
+% The format of the ExpandHeap method is:
+%
+% MagickBooleanType ExpandHeap(size_t size)
+%
+% A description of each parameter follows:
+%
+% o size: the size of the memory in bytes we require.
+%
+*/
+static MagickBooleanType ExpandHeap(size_t size)
+{
+ DataSegmentInfo
+ *segment_info;
+
+ MagickBooleanType
+ mapped;
+
+ register long
+ i;
+
+ register void
+ *block;
+
+ size_t
+ blocksize;
+
+ void
+ *segment;
+
+ blocksize=((size+12*sizeof(size_t))+SegmentSize-1) & -SegmentSize;
+ assert(memory_info.number_segments < MaxSegments);
+ segment=MapBlob(-1,IOMode,0,blocksize);
+ mapped=segment != (void *) NULL ? MagickTrue : MagickFalse;
+ if (segment == (void *) NULL)
+ segment=(void *) memory_methods.acquire_memory_handler(blocksize);
+ if (segment == (void *) NULL)
+ return(MagickFalse);
+ segment_info=(DataSegmentInfo *) free_segments;
+ free_segments=segment_info->next;
+ segment_info->mapped=mapped;
+ segment_info->length=blocksize;
+ segment_info->allocation=segment;
+ segment_info->bound=(char *) segment+blocksize;
+ i=(long) memory_info.number_segments-1;
+ for ( ; (i >= 0) && (memory_info.segments[i]->allocation > segment); i--)
+ memory_info.segments[i+1]=memory_info.segments[i];
+ memory_info.segments[i+1]=segment_info;
+ memory_info.number_segments++;
+ size=blocksize-12*sizeof(size_t);
+ block=(char *) segment_info->allocation+4*sizeof(size_t);
+ *BlockHeader(block)=size | PreviousBlockBit;
+ *BlockFooter(block,size)=size;
+ InsertFreeBlock(block,AllocationPolicy(size));
+ block=NextBlock(block);
+ assert(block < segment_info->bound);
+ *BlockHeader(block)=2*sizeof(size_t);
+ *BlockHeader(NextBlock(block))=PreviousBlockBit;
+ return(MagickTrue);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k M e m o r y M e t h o d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickMemoryMethods() gets the methods to acquire, resize, and destroy
+% memory.
+%
+% The format of the GetMagickMemoryMethods() method is:
+%
+% void GetMagickMemoryMethods(AcquireMemoryHandler *acquire_memory_handler,
+% ResizeMemoryHandler *resize_memory_handler,
+% DestroyMemoryHandler *destroy_memory_handler)
+%
+% A description of each parameter follows:
+%
+% o acquire_memory_handler: method to acquire memory (e.g. malloc).
+%
+% o resize_memory_handler: method to resize memory (e.g. realloc).
+%
+% o destroy_memory_handler: method to destroy memory (e.g. free).
+%
+*/
+MagickExport void GetMagickMemoryMethods(
+ AcquireMemoryHandler *acquire_memory_handler,
+ ResizeMemoryHandler *resize_memory_handler,
+ DestroyMemoryHandler *destroy_memory_handler)
+{
+ assert(acquire_memory_handler != (AcquireMemoryHandler *) NULL);
+ assert(resize_memory_handler != (ResizeMemoryHandler *) NULL);
+ assert(destroy_memory_handler != (DestroyMemoryHandler *) NULL);
+ *acquire_memory_handler=memory_methods.acquire_memory_handler;
+ *resize_memory_handler=memory_methods.resize_memory_handler;
+ *destroy_memory_handler=memory_methods.destroy_memory_handler;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n q u i s h A l i g n e d M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishAlignedMemory() frees memory acquired with AcquireAlignedMemory()
+% or reuse.
+%
+% The format of the RelinquishAlignedMemory method is:
+%
+% void *RelinquishAlignedMemory(void *memory)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a block of memory to free for reuse.
+%
+*/
+MagickExport void *RelinquishAlignedMemory(void *memory)
+{
+ if (memory == (void *) NULL)
+ return((void *) NULL);
+ free(memory);
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n q u i s h M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishMagickMemory() frees memory acquired with AcquireMagickMemory()
+% or AcquireQuantumMemory() for reuse.
+%
+% The format of the RelinquishMagickMemory method is:
+%
+% void *RelinquishMagickMemory(void *memory)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a block of memory to free for reuse.
+%
+*/
+MagickExport void *RelinquishMagickMemory(void *memory)
+{
+ if (memory == (void *) NULL)
+ return((void *) NULL);
+#if !defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ memory_methods.destroy_memory_handler(memory);
+#else
+ assert((SizeOfBlock(memory) % (4*sizeof(size_t))) == 0);
+ assert((*BlockHeader(NextBlock(memory)) & PreviousBlockBit) != 0);
+ AcquireSemaphoreInfo(&memory_semaphore);
+ if ((*BlockHeader(memory) & PreviousBlockBit) == 0)
+ {
+ void
+ *previous;
+
+ /*
+ Coalesce with previous adjacent block.
+ */
+ previous=PreviousBlock(memory);
+ RemoveFreeBlock(previous,AllocationPolicy(SizeOfBlock(previous)));
+ *BlockHeader(previous)=(SizeOfBlock(previous)+SizeOfBlock(memory)) |
+ (*BlockHeader(previous) & ~SizeMask);
+ memory=previous;
+ }
+ if ((*BlockHeader(NextBlock(NextBlock(memory))) & PreviousBlockBit) == 0)
+ {
+ void
+ *next;
+
+ /*
+ Coalesce with next adjacent block.
+ */
+ next=NextBlock(memory);
+ RemoveFreeBlock(next,AllocationPolicy(SizeOfBlock(next)));
+ *BlockHeader(memory)=(SizeOfBlock(memory)+SizeOfBlock(next)) |
+ (*BlockHeader(memory) & ~SizeMask);
+ }
+ *BlockFooter(memory,SizeOfBlock(memory))=SizeOfBlock(memory);
+ *BlockHeader(NextBlock(memory))&=(~PreviousBlockBit);
+ InsertFreeBlock(memory,AllocationPolicy(SizeOfBlock(memory)));
+ RelinquishSemaphoreInfo(memory_semaphore);
+#endif
+ return((void *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetMagickMemory() fills the first size bytes of the memory area pointed to
+% by memory with the constant byte c.
+%
+% The format of the ResetMagickMemory method is:
+%
+% void *ResetMagickMemory(void *memory,int byte,const size_t size)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a memory allocation.
+%
+% o byte: Set the memory to this value.
+%
+% o size: Size of the memory to reset.
+%
+*/
+MagickExport void *ResetMagickMemory(void *memory,int byte,const size_t size)
+{
+ assert(memory != (void *) NULL);
+ return(memset(memory,byte,size));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s i z e M a g i c k M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResizeMagickMemory() changes the size of the memory and returns a pointer to
+% the (possibly moved) block. The contents will be unchanged up to the
+% lesser of the new and old sizes.
+%
+% The format of the ResizeMagickMemory method is:
+%
+% void *ResizeMagickMemory(void *memory,const size_t size)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a memory allocation.
+%
+% o size: the new size of the allocated memory.
+%
+*/
+
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+static inline void *ResizeBlock(void *block,size_t size)
+{
+ register void
+ *memory;
+
+ if (block == (void *) NULL)
+ return(AcquireBlock(size));
+ memory=AcquireBlock(size);
+ if (memory == (void *) NULL)
+ return((void *) NULL);
+ if (size <= (SizeOfBlock(block)-sizeof(size_t)))
+ (void) memcpy(memory,block,size);
+ else
+ (void) memcpy(memory,block,SizeOfBlock(block)-sizeof(size_t));
+ memory_info.allocation+=size;
+ return(memory);
+}
+#endif
+
+MagickExport void *ResizeMagickMemory(void *memory,const size_t size)
+{
+ register void
+ *block;
+
+ if (memory == (void *) NULL)
+ return(AcquireMagickMemory(size));
+#if !defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ block=memory_methods.resize_memory_handler(memory,size == 0 ? 1UL : size);
+ if (block == (void *) NULL)
+ memory=RelinquishMagickMemory(memory);
+#else
+ AcquireSemaphoreInfo(&memory_semaphore);
+ block=ResizeBlock(memory,size == 0 ? 1UL : size);
+ if (block == (void *) NULL)
+ {
+ if (ExpandHeap(size == 0 ? 1UL : size) == MagickFalse)
+ {
+ RelinquishSemaphoreInfo(memory_semaphore);
+ memory=RelinquishMagickMemory(memory);
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ }
+ block=ResizeBlock(memory,size == 0 ? 1UL : size);
+ assert(block != (void *) NULL);
+ }
+ RelinquishSemaphoreInfo(memory_semaphore);
+ memory=RelinquishMagickMemory(memory);
+#endif
+ return(block);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s i z e Q u a n t u m M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResizeQuantumMemory() changes the size of the memory and returns a pointer
+% to the (possibly moved) block. The contents will be unchanged up to the
+% lesser of the new and old sizes.
+%
+% The format of the ResizeQuantumMemory method is:
+%
+% void *ResizeQuantumMemory(void *memory,const size_t count,
+% const size_t quantum)
+%
+% A description of each parameter follows:
+%
+% o memory: A pointer to a memory allocation.
+%
+% o count: the number of quantum elements to allocate.
+%
+% o quantum: the number of bytes in each quantum.
+%
+*/
+MagickExport void *ResizeQuantumMemory(void *memory,const size_t count,
+ const size_t quantum)
+{
+ size_t
+ size;
+
+ size=count*quantum;
+ if ((count == 0) || (quantum != (size/count)))
+ {
+ memory=RelinquishMagickMemory(memory);
+ errno=ENOMEM;
+ return((void *) NULL);
+ }
+ return(ResizeMagickMemory(memory,size));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t M a g i c k M e m o r y M e t h o d s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetMagickMemoryMethods() sets the methods to acquire, resize, and destroy
+% memory.
+%
+% The format of the SetMagickMemoryMethods() method is:
+%
+% SetMagickMemoryMethods(AcquireMemoryHandler acquire_memory_handler,
+% ResizeMemoryHandler resize_memory_handler,
+% DestroyMemoryHandler destroy_memory_handler)
+%
+% A description of each parameter follows:
+%
+% o acquire_memory_handler: method to acquire memory (e.g. malloc).
+%
+% o resize_memory_handler: method to resize memory (e.g. realloc).
+%
+% o destroy_memory_handler: method to destroy memory (e.g. free).
+%
+*/
+MagickExport void SetMagickMemoryMethods(
+ AcquireMemoryHandler acquire_memory_handler,
+ ResizeMemoryHandler resize_memory_handler,
+ DestroyMemoryHandler destroy_memory_handler)
+{
+ /*
+ Set memory methods.
+ */
+ if (acquire_memory_handler != (AcquireMemoryHandler) NULL)
+ memory_methods.acquire_memory_handler=acquire_memory_handler;
+ if (resize_memory_handler != (ResizeMemoryHandler) NULL)
+ memory_methods.resize_memory_handler=resize_memory_handler;
+ if (destroy_memory_handler != (DestroyMemoryHandler) NULL)
+ memory_methods.destroy_memory_handler=destroy_memory_handler;
+}
diff --git a/magick/memory_.h b/magick/memory_.h
new file mode 100644
index 0000000..8cf6cca
--- /dev/null
+++ b/magick/memory_.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore memory methods.
+*/
+#ifndef _MAGICKCORE_MEMORY_H
+#define _MAGICKCORE_MEMORY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef void
+ *(*AcquireMemoryHandler)(size_t),
+ (*DestroyMemoryHandler)(void *),
+ *(*ResizeMemoryHandler)(void *,size_t);
+
+extern MagickExport void
+ *AcquireAlignedMemory(const size_t,const size_t),
+ *AcquireMagickMemory(const size_t),
+ *AcquireQuantumMemory(const size_t,const size_t),
+ *CopyMagickMemory(void *,const void *,const size_t),
+ DestroyMagickMemory(void),
+ GetMagickMemoryMethods(AcquireMemoryHandler *,ResizeMemoryHandler *,
+ DestroyMemoryHandler *),
+ *RelinquishAlignedMemory(void *),
+ *RelinquishMagickMemory(void *),
+ *ResetMagickMemory(void *,int,const size_t),
+ *ResizeMagickMemory(void *,const size_t),
+ *ResizeQuantumMemory(void *,const size_t,const size_t),
+ SetMagickMemoryMethods(AcquireMemoryHandler,ResizeMemoryHandler,
+ DestroyMemoryHandler);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/methods.h b/magick/methods.h
new file mode 100644
index 0000000..b62d342
--- /dev/null
+++ b/magick/methods.h
@@ -0,0 +1,1367 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore API methods prefix.
+
+ nm .libs/libMagickCore.a | grep ' T ' | \
+ awk '{ printf("#define %s PrependMagickMethod(%s)\n", $3, $3); }' | \
+ sort
+*/
+#ifndef _MAGICKCORE_METHOD_H
+#define _MAGICKCORE_METHOD_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_NAMESPACE_PREFIX)
+
+#if defined(__STDC__)
+#define PrescanMagickPrefix(prefix,method) prefix ## method
+#else
+#define PrescanMagickPrefix(prefix,method) prefix(method)
+#endif
+#define EvaluateMagickPrefix(prefix,method) PrescanMagickPrefix(prefix,method)
+#define PrependMagickMethod(method) \
+ EvaluateMagickPrefix(MAGICKCORE_NAMESPACE_PREFIX,method)
+
+#define AcquireAlignedMemory PrependMagickMethod(AcquireAlignedMemory)
+#define AcquireCacheViewIndexes PrependMagickMethod(AcquireCacheViewIndexes)
+#define AcquireCacheViewPixels PrependMagickMethod(AcquireCacheViewPixels)
+#define AcquireCacheView PrependMagickMethod(AcquireCacheView)
+#define AcquireDrawInfo PrependMagickMethod(AcquireDrawInfo)
+#define AcquireExceptionInfo PrependMagickMethod(AcquireExceptionInfo)
+#define AcquireFxInfo PrependMagickMethod(AcquireFxInfo)
+#define AcquireImageColormap PrependMagickMethod(AcquireImageColormap)
+#define AcquireImageInfo PrependMagickMethod(AcquireImageInfo)
+#define AcquireImagePixels PrependMagickMethod(AcquireImagePixels)
+#define AcquireImage PrependMagickMethod(AcquireImage)
+#define AcquireIndexes PrependMagickMethod(AcquireIndexes)
+#define AcquireMagickMatrix PrependMagickMethod(AcquireMagickMatrix)
+#define AcquireMagickMemory PrependMagickMethod(AcquireMagickMemory)
+#define AcquireMagickResource PrependMagickMethod(AcquireMagickResource)
+#define AcquireMemory PrependMagickMethod(AcquireMemory)
+#define AcquireNextImage PrependMagickMethod(AcquireNextImage)
+#define AcquireOneCacheViewPixel PrependMagickMethod(AcquireOneCacheViewPixel)
+#define AcquireOneCacheViewVirtualPixel PrependMagickMethod(AcquireOneCacheViewVirtualPixel)
+#define AcquireOneMagickPixel PrependMagickMethod(AcquireOneMagickPixel)
+#define AcquireOnePixel PrependMagickMethod(AcquireOnePixel)
+#define AcquireOneVirtualPixel PrependMagickMethod(AcquireOneVirtualPixel)
+#define AcquirePixelCacheInfo PrependMagickMethod(AcquirePixelCacheInfo)
+#define AcquirePixelCacheNexus PrependMagickMethod(AcquirePixelCacheNexus)
+#define AcquirePixels PrependMagickMethod(AcquirePixels)
+#define AcquireQuantizeInfo PrependMagickMethod(AcquireQuantizeInfo)
+#define AcquireQuantumInfo PrependMagickMethod(AcquireQuantumInfo)
+#define AcquireQuantumMemory PrependMagickMethod(AcquireQuantumMemory)
+#define AcquireRandomInfo PrependMagickMethod(AcquireRandomInfo)
+#define AcquireResampleFilter PrependMagickMethod(AcquireResampleFilter)
+#define AcquireResizeFilter PrependMagickMethod(AcquireResizeFilter)
+#define AcquireSemaphoreInfo PrependMagickMethod(AcquireSemaphoreInfo)
+#define AcquireSignatureInfo PrependMagickMethod(AcquireSignatureInfo)
+#define AcquireStreamInfo PrependMagickMethod(AcquireStreamInfo)
+#define AcquireStringInfo PrependMagickMethod(AcquireStringInfo)
+#define AcquireString PrependMagickMethod(AcquireString)
+#define AcquireTimerInfo PrependMagickMethod(AcquireTimerInfo)
+#define AcquireTokenInfo PrependMagickMethod(AcquireTokenInfo)
+#define AcquireUniqueFilename PrependMagickMethod(AcquireUniqueFilename)
+#define AcquireUniqueFileResource PrependMagickMethod(AcquireUniqueFileResource)
+#define AcquireUniqueSymbolicLink PrependMagickMethod(AcquireUniqueSymbolicLink)
+#define AdaptiveBlurImageChannel PrependMagickMethod(AdaptiveBlurImageChannel)
+#define AdaptiveBlurImage PrependMagickMethod(AdaptiveBlurImage)
+#define AdaptiveResizeImage PrependMagickMethod(AdaptiveResizeImage)
+#define AdaptiveSharpenImageChannel PrependMagickMethod(AdaptiveSharpenImageChannel)
+#define AdaptiveSharpenImage PrependMagickMethod(AdaptiveSharpenImage)
+#define AdaptiveThresholdImage PrependMagickMethod(AdaptiveThresholdImage)
+#define AddChildToXMLTree PrependMagickMethod(AddChildToXMLTree)
+#define AddNoiseImageChannel PrependMagickMethod(AddNoiseImageChannel)
+#define AddNoiseImage PrependMagickMethod(AddNoiseImage)
+#define AddPathToXMLTree PrependMagickMethod(AddPathToXMLTree)
+#define AddValueToSplayTree PrependMagickMethod(AddValueToSplayTree)
+#define AffineTransformImage PrependMagickMethod(AffineTransformImage)
+#define AffinityImage PrependMagickMethod(AffinityImage)
+#define AffinityImages PrependMagickMethod(AffinityImages)
+#define AllocateImageColormap PrependMagickMethod(AllocateImageColormap)
+#define AllocateImage PrependMagickMethod(AllocateImage)
+#define AllocateNextImage PrependMagickMethod(AllocateNextImage)
+#define AllocateSemaphoreInfo PrependMagickMethod(AllocateSemaphoreInfo)
+#define AllocateString PrependMagickMethod(AllocateString)
+#define analyzeImage PrependMagickMethod(analyzeImage)
+#define AnimateImages PrependMagickMethod(AnimateImages)
+#define AnnotateImage PrependMagickMethod(AnnotateImage)
+#define AppendImageFormat PrependMagickMethod(AppendImageFormat)
+#define AppendImages PrependMagickMethod(AppendImages)
+#define AppendImageToList PrependMagickMethod(AppendImageToList)
+#define AppendValueToLinkedList PrependMagickMethod(AppendValueToLinkedList)
+#define Ascii85Encode PrependMagickMethod(Ascii85Encode)
+#define Ascii85Flush PrependMagickMethod(Ascii85Flush)
+#define Ascii85Initialize PrependMagickMethod(Ascii85Initialize)
+#define AsynchronousDestroyMagickResources PrependMagickMethod(AsynchronousDestroyMagickResources)
+#define AttachBlob PrependMagickMethod(AttachBlob)
+#define AverageImages PrependMagickMethod(AverageImages)
+#define Base64Decode PrependMagickMethod(Base64Decode)
+#define Base64Encode PrependMagickMethod(Base64Encode)
+#define BilevelImageChannel PrependMagickMethod(BilevelImageChannel)
+#define BilevelImage PrependMagickMethod(BilevelImage)
+#define BlackThresholdImageChannel PrependMagickMethod(BlackThresholdImageChannel)
+#define BlackThresholdImage PrependMagickMethod(BlackThresholdImage)
+#define BlobToFile PrependMagickMethod(BlobToFile)
+#define BlobToImage PrependMagickMethod(BlobToImage)
+#define BlueShiftImage PrependMagickMethod(BlueShiftImage)
+#define BlurImageChannel PrependMagickMethod(BlurImageChannel)
+#define BlurImage PrependMagickMethod(BlurImage)
+#define BorderImage PrependMagickMethod(BorderImage)
+#define CanonicalXMLContent PrependMagickMethod(CanonicalXMLContent)
+#define CatchException PrependMagickMethod(CatchException)
+#define CatchImageException PrependMagickMethod(CatchImageException)
+#define ChannelImage PrependMagickMethod(ChannelImage)
+#define ChannelThresholdImage PrependMagickMethod(ChannelThresholdImage)
+#define CharcoalImage PrependMagickMethod(CharcoalImage)
+#define ChopImage PrependMagickMethod(ChopImage)
+#define ChopPathComponents PrependMagickMethod(ChopPathComponents)
+#define ClearLinkedList PrependMagickMethod(ClearLinkedList)
+#define ClearMagickException PrependMagickMethod(ClearMagickException)
+#define ClipImagePath PrependMagickMethod(ClipImagePath)
+#define ClipImage PrependMagickMethod(ClipImage)
+#define ClipPathImage PrependMagickMethod(ClipPathImage)
+#define CloneBlobInfo PrependMagickMethod(CloneBlobInfo)
+#define CloneCacheView PrependMagickMethod(CloneCacheView)
+#define CloneDrawInfo PrependMagickMethod(CloneDrawInfo)
+#define CloneImageArtifacts PrependMagickMethod(CloneImageArtifacts)
+#define CloneImageAttributes PrependMagickMethod(CloneImageAttributes)
+#define CloneImageInfo PrependMagickMethod(CloneImageInfo)
+#define CloneImageList PrependMagickMethod(CloneImageList)
+#define CloneImageOptions PrependMagickMethod(CloneImageOptions)
+#define CloneImage PrependMagickMethod(CloneImage)
+#define CloneImageProfiles PrependMagickMethod(CloneImageProfiles)
+#define CloneImageProperties PrependMagickMethod(CloneImageProperties)
+#define CloneImages PrependMagickMethod(CloneImages)
+#define CloneMemory PrependMagickMethod(CloneMemory)
+#define CloneMontageInfo PrependMagickMethod(CloneMontageInfo)
+#define ClonePixelCacheMethods PrependMagickMethod(ClonePixelCacheMethods)
+#define CloneQuantizeInfo PrependMagickMethod(CloneQuantizeInfo)
+#define CloneSplayTree PrependMagickMethod(CloneSplayTree)
+#define CloneStringInfo PrependMagickMethod(CloneStringInfo)
+#define CloneString PrependMagickMethod(CloneString)
+#define CloseBlob PrependMagickMethod(CloseBlob)
+#define CloseCacheView PrependMagickMethod(CloseCacheView)
+#define CloseMagickLog PrependMagickMethod(CloseMagickLog)
+#define ClutImageChannel PrependMagickMethod(ClutImageChannel)
+#define ClutImage PrependMagickMethod(ClutImage)
+#define CoalesceImages PrependMagickMethod(CoalesceImages)
+#define ColorDecisionListImage PrependMagickMethod(ColorDecisionListImage)
+#define ColorFloodfillImage PrependMagickMethod(ColorFloodfillImage)
+#define ColorizeImage PrependMagickMethod(ColorizeImage)
+#define CombineImages PrependMagickMethod(CombineImages)
+#define CompareHashmapStringInfo PrependMagickMethod(CompareHashmapStringInfo)
+#define CompareHashmapString PrependMagickMethod(CompareHashmapString)
+#define CompareImageChannels PrependMagickMethod(CompareImageChannels)
+#define CompareImageLayers PrependMagickMethod(CompareImageLayers)
+#define CompareImages PrependMagickMethod(CompareImages)
+#define CompareSplayTreeStringInfo PrependMagickMethod(CompareSplayTreeStringInfo)
+#define CompareSplayTreeString PrependMagickMethod(CompareSplayTreeString)
+#define CompareStringInfo PrependMagickMethod(CompareStringInfo)
+#define CompositeImageChannel PrependMagickMethod(CompositeImageChannel)
+#define CompositeImage PrependMagickMethod(CompositeImage)
+#define CompositeLayers PrependMagickMethod(CompositeLayers)
+#define CompressImageColormap PrependMagickMethod(CompressImageColormap)
+#define ConcatenateColorComponent PrependMagickMethod(ConcatenateColorComponent)
+#define ConcatenateMagickString PrependMagickMethod(ConcatenateMagickString)
+#define ConcatenateStringInfo PrependMagickMethod(ConcatenateStringInfo)
+#define ConcatenateString PrependMagickMethod(ConcatenateString)
+#define ConfigureFileToStringInfo PrependMagickMethod(ConfigureFileToStringInfo)
+#define ConsolidateCMYKImages PrependMagickMethod(ConsolidateCMYKImages)
+#define ConstantString PrependMagickMethod(ConstantString)
+#define ConstituteImage PrependMagickMethod(ConstituteImage)
+#define ContinueTimer PrependMagickMethod(ContinueTimer)
+#define ContrastImage PrependMagickMethod(ContrastImage)
+#define ContrastStretchImageChannel PrependMagickMethod(ContrastStretchImageChannel)
+#define ContrastStretchImage PrependMagickMethod(ContrastStretchImage)
+#define ConvertHSBToRGB PrependMagickMethod(ConvertHSBToRGB)
+#define ConvertHSLToRGB PrependMagickMethod(ConvertHSLToRGB)
+#define ConvertHWBToRGB PrependMagickMethod(ConvertHWBToRGB)
+#define ConvertRGBToHSB PrependMagickMethod(ConvertRGBToHSB)
+#define ConvertRGBToHSL PrependMagickMethod(ConvertRGBToHSL)
+#define ConvertRGBToHWB PrependMagickMethod(ConvertRGBToHWB)
+#define ConvolveImageChannel PrependMagickMethod(ConvolveImageChannel)
+#define ConvolveImage PrependMagickMethod(ConvolveImage)
+#define CopyMagickMemory PrependMagickMethod(CopyMagickMemory)
+#define CopyMagickString PrependMagickMethod(CopyMagickString)
+#define CropImage PrependMagickMethod(CropImage)
+#define CycleColormapImage PrependMagickMethod(CycleColormapImage)
+#define DecipherImage PrependMagickMethod(DecipherImage)
+#define DeconstructImages PrependMagickMethod(DeconstructImages)
+#define DefineImageArtifact PrependMagickMethod(DefineImageArtifact)
+#define DefineImageOption PrependMagickMethod(DefineImageOption)
+#define DefineImageProperty PrependMagickMethod(DefineImageProperty)
+#define DefineImageRegistry PrependMagickMethod(DefineImageRegistry)
+#define DeleteImageArtifact PrependMagickMethod(DeleteImageArtifact)
+#define DeleteImageAttribute PrependMagickMethod(DeleteImageAttribute)
+#define DeleteImageFromList PrependMagickMethod(DeleteImageFromList)
+#define DeleteImageList PrependMagickMethod(DeleteImageList)
+#define DeleteImageOption PrependMagickMethod(DeleteImageOption)
+#define DeleteImageProfile PrependMagickMethod(DeleteImageProfile)
+#define DeleteImageProperty PrependMagickMethod(DeleteImageProperty)
+#define DeleteImageRegistry PrependMagickMethod(DeleteImageRegistry)
+#define DeleteImages PrependMagickMethod(DeleteImages)
+#define DeleteMagickRegistry PrependMagickMethod(DeleteMagickRegistry)
+#define DeleteNodeByValueFromSplayTree PrependMagickMethod(DeleteNodeByValueFromSplayTree)
+#define DeleteNodeFromSplayTree PrependMagickMethod(DeleteNodeFromSplayTree)
+#define DescribeImage PrependMagickMethod(DescribeImage)
+#define DeskewImage PrependMagickMethod(DeskewImage)
+#define DespeckleImage PrependMagickMethod(DespeckleImage)
+#define DestroyBlob PrependMagickMethod(DestroyBlob)
+#define DestroyCacheView PrependMagickMethod(DestroyCacheView)
+#define DestroyCoderList PrependMagickMethod(DestroyCoderList)
+#define DestroyColorList PrependMagickMethod(DestroyColorList)
+#define DestroyConfigureList PrependMagickMethod(DestroyConfigureList)
+#define DestroyConfigureOptions PrependMagickMethod(DestroyConfigureOptions)
+#define DestroyConstitute PrependMagickMethod(DestroyConstitute)
+#define DestroyDelegateList PrependMagickMethod(DestroyDelegateList)
+#define DestroyDrawInfo PrependMagickMethod(DestroyDrawInfo)
+#define DestroyExceptionInfo PrependMagickMethod(DestroyExceptionInfo)
+#define DestroyFxInfo PrependMagickMethod(DestroyFxInfo)
+#define DestroyHashmap PrependMagickMethod(DestroyHashmap)
+#define DestroyImageArtifacts PrependMagickMethod(DestroyImageArtifacts)
+#define DestroyImageAttributes PrependMagickMethod(DestroyImageAttributes)
+#define DestroyImageInfo PrependMagickMethod(DestroyImageInfo)
+#define DestroyImageList PrependMagickMethod(DestroyImageList)
+#define DestroyImageOptions PrependMagickMethod(DestroyImageOptions)
+#define DestroyImagePixels PrependMagickMethod(DestroyImagePixels)
+#define DestroyImage PrependMagickMethod(DestroyImage)
+#define DestroyImageProfiles PrependMagickMethod(DestroyImageProfiles)
+#define DestroyImageProperties PrependMagickMethod(DestroyImageProperties)
+#define DestroyImageRegistry PrependMagickMethod(DestroyImageRegistry)
+#define DestroyImages PrependMagickMethod(DestroyImages)
+#define DestroyLinkedList PrependMagickMethod(DestroyLinkedList)
+#define DestroyLocaleList PrependMagickMethod(DestroyLocaleList)
+#define DestroyLocaleOptions PrependMagickMethod(DestroyLocaleOptions)
+#define DestroyLogList PrependMagickMethod(DestroyLogList)
+#define DestroyMagickList PrependMagickMethod(DestroyMagickList)
+#define DestroyMagickMemory PrependMagickMethod(DestroyMagickMemory)
+#define DestroyMagick PrependMagickMethod(DestroyMagick)
+#define DestroyMagickRegistry PrependMagickMethod(DestroyMagickRegistry)
+#define DestroyMagickResources PrependMagickMethod(DestroyMagickResources)
+#define DestroyMagicList PrependMagickMethod(DestroyMagicList)
+#define DestroyMimeList PrependMagickMethod(DestroyMimeList)
+#define DestroyMontageInfo PrependMagickMethod(DestroyMontageInfo)
+#define DestroyPixelCache PrependMagickMethod(DestroyPixelCache)
+#define DestroyPixelCacheNexus PrependMagickMethod(DestroyPixelCacheNexus)
+#define DestroyPixelCacheResources PrependMagickMethod(DestroyPixelCacheResources)
+#define DestroyPolicyList PrependMagickMethod(DestroyPolicyList)
+#define DestroyQuantizeInfo PrependMagickMethod(DestroyQuantizeInfo)
+#define DestroyQuantumInfo PrependMagickMethod(DestroyQuantumInfo)
+#define DestroyRandomInfo PrependMagickMethod(DestroyRandomInfo)
+#define DestroyRandomReservoir PrependMagickMethod(DestroyRandomReservoir)
+#define DestroyResampleFilter PrependMagickMethod(DestroyResampleFilter)
+#define DestroyResizeFilter PrependMagickMethod(DestroyResizeFilter)
+#define DestroySemaphoreInfo PrependMagickMethod(DestroySemaphoreInfo)
+#define DestroySemaphore PrependMagickMethod(DestroySemaphore)
+#define DestroySignatureInfo PrependMagickMethod(DestroySignatureInfo)
+#define DestroySplayTree PrependMagickMethod(DestroySplayTree)
+#define DestroyStreamInfo PrependMagickMethod(DestroyStreamInfo)
+#define DestroyStringInfo PrependMagickMethod(DestroyStringInfo)
+#define DestroyStringList PrependMagickMethod(DestroyStringList)
+#define DestroyString PrependMagickMethod(DestroyString)
+#define DestroyThresholdMap PrependMagickMethod(DestroyThresholdMap)
+#define DestroyTimerInfo PrependMagickMethod(DestroyTimerInfo)
+#define DestroyTokenInfo PrependMagickMethod(DestroyTokenInfo)
+#define DestroyTypeList PrependMagickMethod(DestroyTypeList)
+#define DestroyXMLTree PrependMagickMethod(DestroyXMLTree)
+#define DestroyXResources PrependMagickMethod(DestroyXResources)
+#define DestroyXWidget PrependMagickMethod(DestroyXWidget)
+#define DetachBlob PrependMagickMethod(DetachBlob)
+#define DisassociateImageStream PrependMagickMethod(DisassociateImageStream)
+#define DispatchImage PrependMagickMethod(DispatchImage)
+#define DisplayImages PrependMagickMethod(DisplayImages)
+#define DisposeImages PrependMagickMethod(DisposeImages)
+#define DistortImage PrependMagickMethod(DistortImage)
+#define DrawAffineImage PrependMagickMethod(DrawAffineImage)
+#define DrawClipPath PrependMagickMethod(DrawClipPath)
+#define DrawGradientImage PrependMagickMethod(DrawGradientImage)
+#define DrawImage PrependMagickMethod(DrawImage)
+#define DrawPatternPath PrependMagickMethod(DrawPatternPath)
+#define DrawPrimitive PrependMagickMethod(DrawPrimitive)
+#define DuplicateBlob PrependMagickMethod(DuplicateBlob)
+#define EdgeImage PrependMagickMethod(EdgeImage)
+#define EmbossImage PrependMagickMethod(EmbossImage)
+#define EncipherImage PrependMagickMethod(EncipherImage)
+#define EnhanceImage PrependMagickMethod(EnhanceImage)
+#define EOFBlob PrependMagickMethod(EOFBlob)
+#define EqualizeImageChannel PrependMagickMethod(EqualizeImageChannel)
+#define EqualizeImage PrependMagickMethod(EqualizeImage)
+#define EscapeString PrependMagickMethod(EscapeString)
+#define EvaluateImageChannel PrependMagickMethod(EvaluateImageChannel)
+#define EvaluateImage PrependMagickMethod(EvaluateImage)
+#define ExcerptImage PrependMagickMethod(ExcerptImage)
+#define ExpandAffine PrependMagickMethod(ExpandAffine)
+#define ExpandFilename PrependMagickMethod(ExpandFilename)
+#define ExpandFilenames PrependMagickMethod(ExpandFilenames)
+#define ExportImagePixels PrependMagickMethod(ExportImagePixels)
+#define ExportQuantumPixels PrependMagickMethod(ExportQuantumPixels)
+#define ExtentImage PrependMagickMethod(ExtentImage)
+#define ExtractSubimageFromImage PrependMagickMethod(ExtractSubimageFromImage)
+#define FileToBlob PrependMagickMethod(FileToBlob)
+#define FileToImage PrependMagickMethod(FileToImage)
+#define FileToStringInfo PrependMagickMethod(FileToStringInfo)
+#define FileToString PrependMagickMethod(FileToString)
+#define FinalizeSignature PrependMagickMethod(FinalizeSignature)
+#define FlattenImages PrependMagickMethod(FlattenImages)
+#define FlipImage PrependMagickMethod(FlipImage)
+#define FloodfillPaintImage PrependMagickMethod(FloodfillPaintImage)
+#define FlopImage PrependMagickMethod(FlopImage)
+#define FormatImageAttributeList PrependMagickMethod(FormatImageAttributeList)
+#define FormatImageAttribute PrependMagickMethod(FormatImageAttribute)
+#define FormatImagePropertyList PrependMagickMethod(FormatImagePropertyList)
+#define FormatImageProperty PrependMagickMethod(FormatImageProperty)
+#define FormatMagickCaption PrependMagickMethod(FormatMagickCaption)
+#define FormatMagickSize PrependMagickMethod(FormatMagickSize)
+#define FormatMagickStringList PrependMagickMethod(FormatMagickStringList)
+#define FormatMagickString PrependMagickMethod(FormatMagickString)
+#define FormatMagickTime PrependMagickMethod(FormatMagickTime)
+#define FormatStringList PrependMagickMethod(FormatStringList)
+#define FormatString PrependMagickMethod(FormatString)
+#define ForwardFourierTransformImage PrependMagickMethod(ForwardFourierTransformImage)
+#define FrameImage PrependMagickMethod(FrameImage)
+#define FunctionImageChannel PrependMagickMethod(FunctionImageChannel)
+#define FunctionImage PrependMagickMethod(FunctionImage)
+#define FuzzyColorCompare PrependMagickMethod(FuzzyColorCompare)
+#define FuzzyColorMatch PrependMagickMethod(FuzzyColorMatch)
+#define FuzzyOpacityCompare PrependMagickMethod(FuzzyOpacityCompare)
+#define FxEvaluateChannelExpression PrependMagickMethod(FxEvaluateChannelExpression)
+#define FxEvaluateExpression PrependMagickMethod(FxEvaluateExpression)
+#define FxImageChannel PrependMagickMethod(FxImageChannel)
+#define FxImage PrependMagickMethod(FxImage)
+#define FxPreprocessExpression PrependMagickMethod(FxPreprocessExpression)
+#define GammaImageChannel PrependMagickMethod(GammaImageChannel)
+#define GammaImage PrependMagickMethod(GammaImage)
+#define GaussianBlurImageChannel PrependMagickMethod(GaussianBlurImageChannel)
+#define GaussianBlurImage PrependMagickMethod(GaussianBlurImage)
+#define GaussJordanElimination PrependMagickMethod(GaussJordanElimination)
+#define GenerateDifferentialNoise PrependMagickMethod(GenerateDifferentialNoise)
+#define GetAffineMatrix PrependMagickMethod(GetAffineMatrix)
+#define GetAuthenticIndexQueue PrependMagickMethod(GetAuthenticIndexQueue)
+#define GetAuthenticPixelCacheNexus PrependMagickMethod(GetAuthenticPixelCacheNexus)
+#define GetAuthenticPixelQueue PrependMagickMethod(GetAuthenticPixelQueue)
+#define GetAuthenticPixels PrependMagickMethod(GetAuthenticPixels)
+#define GetBlobError PrependMagickMethod(GetBlobError)
+#define GetBlobFileHandle PrependMagickMethod(GetBlobFileHandle)
+#define GetBlobInfo PrependMagickMethod(GetBlobInfo)
+#define GetBlobProperties PrependMagickMethod(GetBlobProperties)
+#define GetBlobSize PrependMagickMethod(GetBlobSize)
+#define GetBlobStreamData PrependMagickMethod(GetBlobStreamData)
+#define GetBlobStreamHandler PrependMagickMethod(GetBlobStreamHandler)
+#define GetCacheViewAuthenticIndexQueue PrependMagickMethod(GetCacheViewAuthenticIndexQueue)
+#define GetCacheViewAuthenticPixelQueue PrependMagickMethod(GetCacheViewAuthenticPixelQueue)
+#define GetCacheViewAuthenticPixels PrependMagickMethod(GetCacheViewAuthenticPixels)
+#define GetCacheViewColorspace PrependMagickMethod(GetCacheViewColorspace)
+#define GetCacheViewException PrependMagickMethod(GetCacheViewException)
+#define GetCacheViewExtent PrependMagickMethod(GetCacheViewExtent)
+#define GetCacheViewIndexes PrependMagickMethod(GetCacheViewIndexes)
+#define GetCacheViewPixels PrependMagickMethod(GetCacheViewPixels)
+#define GetCacheView PrependMagickMethod(GetCacheView)
+#define GetCacheViewStorageClass PrependMagickMethod(GetCacheViewStorageClass)
+#define GetCacheViewVirtualIndexQueue PrependMagickMethod(GetCacheViewVirtualIndexQueue)
+#define GetCacheViewVirtualPixelQueue PrependMagickMethod(GetCacheViewVirtualPixelQueue)
+#define GetCacheViewVirtualPixels PrependMagickMethod(GetCacheViewVirtualPixels)
+#define GetClientName PrependMagickMethod(GetClientName)
+#define GetClientPath PrependMagickMethod(GetClientPath)
+#define GetCoderInfoList PrependMagickMethod(GetCoderInfoList)
+#define GetCoderInfo PrependMagickMethod(GetCoderInfo)
+#define GetCoderList PrependMagickMethod(GetCoderList)
+#define GetColorInfoList PrependMagickMethod(GetColorInfoList)
+#define GetColorInfo PrependMagickMethod(GetColorInfo)
+#define GetColorList PrependMagickMethod(GetColorList)
+#define GetColorTuple PrependMagickMethod(GetColorTuple)
+#define GetConfigureBlob PrependMagickMethod(GetConfigureBlob)
+#define GetConfigureInfoList PrependMagickMethod(GetConfigureInfoList)
+#define GetConfigureInfo PrependMagickMethod(GetConfigureInfo)
+#define GetConfigureList PrependMagickMethod(GetConfigureList)
+#define GetConfigureOption PrependMagickMethod(GetConfigureOption)
+#define GetConfigureOptions PrependMagickMethod(GetConfigureOptions)
+#define GetConfigurePaths PrependMagickMethod(GetConfigurePaths)
+#define GetConfigureValue PrependMagickMethod(GetConfigureValue)
+#define GetDelegateCommand PrependMagickMethod(GetDelegateCommand)
+#define GetDelegateCommands PrependMagickMethod(GetDelegateCommands)
+#define GetDelegateInfoList PrependMagickMethod(GetDelegateInfoList)
+#define GetDelegateInfo PrependMagickMethod(GetDelegateInfo)
+#define GetDelegateList PrependMagickMethod(GetDelegateList)
+#define GetDelegateMode PrependMagickMethod(GetDelegateMode)
+#define GetDelegateThreadSupport PrependMagickMethod(GetDelegateThreadSupport)
+#define GetDrawInfo PrependMagickMethod(GetDrawInfo)
+#define GetElapsedTime PrependMagickMethod(GetElapsedTime)
+#define GetEnvironmentValue PrependMagickMethod(GetEnvironmentValue)
+#define GetExceptionInfo PrependMagickMethod(GetExceptionInfo)
+#define GetExceptionMessage PrependMagickMethod(GetExceptionMessage)
+#define GetExecutionPath PrependMagickMethod(GetExecutionPath)
+#define GetFirstImageInList PrependMagickMethod(GetFirstImageInList)
+#define GetGeometry PrependMagickMethod(GetGeometry)
+#define GetImageAlphaChannel PrependMagickMethod(GetImageAlphaChannel)
+#define GetImageArtifact PrependMagickMethod(GetImageArtifact)
+#define GetImageAttribute PrependMagickMethod(GetImageAttribute)
+#define GetImageBoundingBox PrependMagickMethod(GetImageBoundingBox)
+#define GetImageChannelDepth PrependMagickMethod(GetImageChannelDepth)
+#define GetImageChannelDistortion PrependMagickMethod(GetImageChannelDistortion)
+#define GetImageChannelDistortions PrependMagickMethod(GetImageChannelDistortions)
+#define GetImageChannelExtrema PrependMagickMethod(GetImageChannelExtrema)
+#define GetImageChannelKurtosis PrependMagickMethod(GetImageChannelKurtosis)
+#define GetImageChannelMean PrependMagickMethod(GetImageChannelMean)
+#define GetImageChannelRange PrependMagickMethod(GetImageChannelRange)
+#define GetImageChannelStatistics PrependMagickMethod(GetImageChannelStatistics)
+#define GetImageClipMask PrependMagickMethod(GetImageClipMask)
+#define GetImageClippingPathAttribute PrependMagickMethod(GetImageClippingPathAttribute)
+#define GetImageDecoder PrependMagickMethod(GetImageDecoder)
+#define GetImageDepth PrependMagickMethod(GetImageDepth)
+#define GetImageDistortion PrependMagickMethod(GetImageDistortion)
+#define GetImageDynamicThreshold PrependMagickMethod(GetImageDynamicThreshold)
+#define GetImageEncoder PrependMagickMethod(GetImageEncoder)
+#define GetImageException PrependMagickMethod(GetImageException)
+#define GetImageExtent PrependMagickMethod(GetImageExtent)
+#define GetImageExtrema PrependMagickMethod(GetImageExtrema)
+#define GetImageFromList PrependMagickMethod(GetImageFromList)
+#define GetImageFromMagickRegistry PrependMagickMethod(GetImageFromMagickRegistry)
+#define GetImageGeometry PrependMagickMethod(GetImageGeometry)
+#define GetImageHistogram PrependMagickMethod(GetImageHistogram)
+#define GetImageIndexInList PrependMagickMethod(GetImageIndexInList)
+#define GetImageInfo PrependMagickMethod(GetImageInfo)
+#define GetImageKurtosis PrependMagickMethod(GetImageKurtosis)
+#define GetImageListIndex PrependMagickMethod(GetImageListIndex)
+#define GetImageListLength PrependMagickMethod(GetImageListLength)
+#define GetImageList PrependMagickMethod(GetImageList)
+#define GetImageListSize PrependMagickMethod(GetImageListSize)
+#define GetImageMagick PrependMagickMethod(GetImageMagick)
+#define GetImageMask PrependMagickMethod(GetImageMask)
+#define GetImageMean PrependMagickMethod(GetImageMean)
+#define GetImageOption PrependMagickMethod(GetImageOption)
+#define GetImagePixelCache PrependMagickMethod(GetImagePixelCache)
+#define GetImagePixels PrependMagickMethod(GetImagePixels)
+#define GetImageProfile PrependMagickMethod(GetImageProfile)
+#define GetImageProperty PrependMagickMethod(GetImageProperty)
+#define GetImageQuantizeError PrependMagickMethod(GetImageQuantizeError)
+#define GetImageQuantumDepth PrependMagickMethod(GetImageQuantumDepth)
+#define GetImageRange PrependMagickMethod(GetImageRange)
+#define GetImageRegistry PrependMagickMethod(GetImageRegistry)
+#define GetImageTotalInkDensity PrependMagickMethod(GetImageTotalInkDensity)
+#define GetImageType PrependMagickMethod(GetImageType)
+#define GetImageVirtualPixelMethod PrependMagickMethod(GetImageVirtualPixelMethod)
+#define GetIndexes PrependMagickMethod(GetIndexes)
+#define GetLastImageInList PrependMagickMethod(GetLastImageInList)
+#define GetLastValueInLinkedList PrependMagickMethod(GetLastValueInLinkedList)
+#define GetLocaleExceptionMessage PrependMagickMethod(GetLocaleExceptionMessage)
+#define GetLocaleInfoList PrependMagickMethod(GetLocaleInfoList)
+#define GetLocaleInfo_ PrependMagickMethod(GetLocaleInfo_)
+#define GetLocaleList PrependMagickMethod(GetLocaleList)
+#define GetLocaleMessage PrependMagickMethod(GetLocaleMessage)
+#define GetLocaleOptions PrependMagickMethod(GetLocaleOptions)
+#define GetLocaleValue PrependMagickMethod(GetLocaleValue)
+#define GetLogInfoList PrependMagickMethod(GetLogInfoList)
+#define GetLogList PrependMagickMethod(GetLogList)
+#define GetLogName PrependMagickMethod(GetLogName)
+#define GetMagicInfoList PrependMagickMethod(GetMagicInfoList)
+#define GetMagicInfo PrependMagickMethod(GetMagicInfo)
+#define GetMagickAdjoin PrependMagickMethod(GetMagickAdjoin)
+#define GetMagickBlobSupport PrependMagickMethod(GetMagickBlobSupport)
+#define GetMagickCopyright PrependMagickMethod(GetMagickCopyright)
+#define GetMagickDescription PrependMagickMethod(GetMagickDescription)
+#define GetMagickEndianSupport PrependMagickMethod(GetMagickEndianSupport)
+#define GetMagickGeometry PrependMagickMethod(GetMagickGeometry)
+#define GetMagickHomeURL PrependMagickMethod(GetMagickHomeURL)
+#define GetMagickInfoList PrependMagickMethod(GetMagickInfoList)
+#define GetMagickInfo PrependMagickMethod(GetMagickInfo)
+#define GetMagickList PrependMagickMethod(GetMagickList)
+#define GetMagickMemoryMethods PrependMagickMethod(GetMagickMemoryMethods)
+#define GetMagickOptions PrependMagickMethod(GetMagickOptions)
+#define GetMagickPackageName PrependMagickMethod(GetMagickPackageName)
+#define GetMagickPixelPacket PrependMagickMethod(GetMagickPixelPacket)
+#define GetMagickProperty PrependMagickMethod(GetMagickProperty)
+#define GetMagickQuantumDepth PrependMagickMethod(GetMagickQuantumDepth)
+#define GetMagickQuantumRange PrependMagickMethod(GetMagickQuantumRange)
+#define GetMagickRawSupport PrependMagickMethod(GetMagickRawSupport)
+#define GetMagickRegistry PrependMagickMethod(GetMagickRegistry)
+#define GetMagickReleaseDate PrependMagickMethod(GetMagickReleaseDate)
+#define GetMagickResourceLimit PrependMagickMethod(GetMagickResourceLimit)
+#define GetMagickResource PrependMagickMethod(GetMagickResource)
+#define GetMagickSeekableStream PrependMagickMethod(GetMagickSeekableStream)
+#define GetMagickThreadSupport PrependMagickMethod(GetMagickThreadSupport)
+#define GetMagickToken PrependMagickMethod(GetMagickToken)
+#define GetMagickVersion PrependMagickMethod(GetMagickVersion)
+#define GetMagicList PrependMagickMethod(GetMagicList)
+#define GetMagicName PrependMagickMethod(GetMagicName)
+#define GetMimeDescription PrependMagickMethod(GetMimeDescription)
+#define GetMimeInfoList PrependMagickMethod(GetMimeInfoList)
+#define GetMimeInfo PrependMagickMethod(GetMimeInfo)
+#define GetMimeList PrependMagickMethod(GetMimeList)
+#define GetMimeType PrependMagickMethod(GetMimeType)
+#define GetMonitorHandler PrependMagickMethod(GetMonitorHandler)
+#define GetMontageInfo PrependMagickMethod(GetMontageInfo)
+#define GetMultilineTypeMetrics PrependMagickMethod(GetMultilineTypeMetrics)
+#define GetNextImageArtifact PrependMagickMethod(GetNextImageArtifact)
+#define GetNextImageAttribute PrependMagickMethod(GetNextImageAttribute)
+#define GetNextImageInList PrependMagickMethod(GetNextImageInList)
+#define GetNextImageOption PrependMagickMethod(GetNextImageOption)
+#define GetNextImage PrependMagickMethod(GetNextImage)
+#define GetNextImageProfile PrependMagickMethod(GetNextImageProfile)
+#define GetNextImageProperty PrependMagickMethod(GetNextImageProperty)
+#define GetNextImageRegistry PrependMagickMethod(GetNextImageRegistry)
+#define GetNextKeyInHashmap PrependMagickMethod(GetNextKeyInHashmap)
+#define GetNextKeyInSplayTree PrependMagickMethod(GetNextKeyInSplayTree)
+#define GetNextValueInHashmap PrependMagickMethod(GetNextValueInHashmap)
+#define GetNextValueInLinkedList PrependMagickMethod(GetNextValueInLinkedList)
+#define GetNextValueInSplayTree PrependMagickMethod(GetNextValueInSplayTree)
+#define GetNextXMLTreeTag PrependMagickMethod(GetNextXMLTreeTag)
+#define GetNumberColors PrependMagickMethod(GetNumberColors)
+#define GetNumberOfElementsInLinkedList PrependMagickMethod(GetNumberOfElementsInLinkedList)
+#define GetNumberOfEntriesInHashmap PrependMagickMethod(GetNumberOfEntriesInHashmap)
+#define GetNumberOfNodesInSplayTree PrependMagickMethod(GetNumberOfNodesInSplayTree)
+#define GetNumberScenes PrependMagickMethod(GetNumberScenes)
+#define GetOneAuthenticPixel PrependMagickMethod(GetOneAuthenticPixel)
+#define GetOneCacheViewAuthenticPixel PrependMagickMethod(GetOneCacheViewAuthenticPixel)
+#define GetOneCacheViewVirtualMethodPixel PrependMagickMethod(GetOneCacheViewVirtualMethodPixel)
+#define GetOneCacheViewVirtualPixel PrependMagickMethod(GetOneCacheViewVirtualPixel)
+#define GetOnePixel PrependMagickMethod(GetOnePixel)
+#define GetOneVirtualMagickPixel PrependMagickMethod(GetOneVirtualMagickPixel)
+#define GetOneVirtualMethodPixel PrependMagickMethod(GetOneVirtualMethodPixel)
+#define GetOneVirtualPixel PrependMagickMethod(GetOneVirtualPixel)
+#define GetOptimalKernelWidth1D PrependMagickMethod(GetOptimalKernelWidth1D)
+#define GetOptimalKernelWidth2D PrependMagickMethod(GetOptimalKernelWidth2D)
+#define GetOptimalKernelWidth PrependMagickMethod(GetOptimalKernelWidth)
+#define GetPageGeometry PrependMagickMethod(GetPageGeometry)
+#define GetPathAttributes PrependMagickMethod(GetPathAttributes)
+#define GetPathComponent PrependMagickMethod(GetPathComponent)
+#define GetPathComponents PrependMagickMethod(GetPathComponents)
+#define GetPixelCacheColorspace PrependMagickMethod(GetPixelCacheColorspace)
+#define GetPixelCacheMethods PrependMagickMethod(GetPixelCacheMethods)
+#define GetPixelCacheNexusExtent PrependMagickMethod(GetPixelCacheNexusExtent)
+#define GetPixelCacheNexusIndexes PrependMagickMethod(GetPixelCacheNexusIndexes)
+#define GetPixelCacheNexusPixels PrependMagickMethod(GetPixelCacheNexusPixels)
+#define GetPixelCacheStorageClass PrependMagickMethod(GetPixelCacheStorageClass)
+#define GetPixelCacheVirtualMethod PrependMagickMethod(GetPixelCacheVirtualMethod)
+#define GetPixels PrependMagickMethod(GetPixels)
+#define GetPolicyInfoList PrependMagickMethod(GetPolicyInfoList)
+#define GetPolicyList PrependMagickMethod(GetPolicyList)
+#define GetPolicyValue PrependMagickMethod(GetPolicyValue)
+#define GetPreviousImageInList PrependMagickMethod(GetPreviousImageInList)
+#define GetPreviousImage PrependMagickMethod(GetPreviousImage)
+#define GetPseudoRandomValue PrependMagickMethod(GetPseudoRandomValue)
+#define GetQuantizeInfo PrependMagickMethod(GetQuantizeInfo)
+#define GetQuantumExtent PrependMagickMethod(GetQuantumExtent)
+#define GetQuantumInfo PrependMagickMethod(GetQuantumInfo)
+#define GetQuantumPixels PrependMagickMethod(GetQuantumPixels)
+#define GetQuantumType PrependMagickMethod(GetQuantumType)
+#define GetRandomKey PrependMagickMethod(GetRandomKey)
+#define GetRandomValue PrependMagickMethod(GetRandomValue)
+#define GetResizeFilterSupport PrependMagickMethod(GetResizeFilterSupport)
+#define GetResizeFilterWeight PrependMagickMethod(GetResizeFilterWeight)
+#define GetSignatureBlocksize PrependMagickMethod(GetSignatureBlocksize)
+#define GetSignatureDigest PrependMagickMethod(GetSignatureDigest)
+#define GetSignatureDigestsize PrependMagickMethod(GetSignatureDigestsize)
+#define GetStreamInfoClientData PrependMagickMethod(GetStreamInfoClientData)
+#define GetStringInfoDatum PrependMagickMethod(GetStringInfoDatum)
+#define GetStringInfoLength PrependMagickMethod(GetStringInfoLength)
+#define GetStringInfoPath PrependMagickMethod(GetStringInfoPath)
+#define GetThresholdMapFile PrependMagickMethod(GetThresholdMapFile)
+#define GetThresholdMap PrependMagickMethod(GetThresholdMap)
+#define GetTimerInfo PrependMagickMethod(GetTimerInfo)
+#define GetTypeInfoByFamily PrependMagickMethod(GetTypeInfoByFamily)
+#define GetTypeInfoList PrependMagickMethod(GetTypeInfoList)
+#define GetTypeInfo PrependMagickMethod(GetTypeInfo)
+#define GetTypeList PrependMagickMethod(GetTypeList)
+#define GetTypeMetrics PrependMagickMethod(GetTypeMetrics)
+#define GetUserTime PrependMagickMethod(GetUserTime)
+#define GetValueFromHashmap PrependMagickMethod(GetValueFromHashmap)
+#define GetValueFromLinkedList PrependMagickMethod(GetValueFromLinkedList)
+#define GetValueFromSplayTree PrependMagickMethod(GetValueFromSplayTree)
+#define GetVirtualIndexesFromNexus PrependMagickMethod(GetVirtualIndexesFromNexus)
+#define GetVirtualIndexQueue PrependMagickMethod(GetVirtualIndexQueue)
+#define GetVirtualPixelQueue PrependMagickMethod(GetVirtualPixelQueue)
+#define GetVirtualPixelsFromNexus PrependMagickMethod(GetVirtualPixelsFromNexus)
+#define GetVirtualPixelsNexus PrependMagickMethod(GetVirtualPixelsNexus)
+#define GetVirtualPixels PrependMagickMethod(GetVirtualPixels)
+#define GetXMLTreeAttribute PrependMagickMethod(GetXMLTreeAttribute)
+#define GetXMLTreeAttributes PrependMagickMethod(GetXMLTreeAttributes)
+#define GetXMLTreeChild PrependMagickMethod(GetXMLTreeChild)
+#define GetXMLTreeContent PrependMagickMethod(GetXMLTreeContent)
+#define GetXMLTreeOrdered PrependMagickMethod(GetXMLTreeOrdered)
+#define GetXMLTreePath PrependMagickMethod(GetXMLTreePath)
+#define GetXMLTreeProcessingInstructions PrependMagickMethod(GetXMLTreeProcessingInstructions)
+#define GetXMLTreeSibling PrependMagickMethod(GetXMLTreeSibling)
+#define GetXMLTreeTag PrependMagickMethod(GetXMLTreeTag)
+#define GlobExpression PrependMagickMethod(GlobExpression)
+#define GradientImage PrependMagickMethod(GradientImage)
+#define GravityAdjustGeometry PrependMagickMethod(GravityAdjustGeometry)
+#define HaldClutImageChannel PrependMagickMethod(HaldClutImageChannel)
+#define HaldClutImage PrependMagickMethod(HaldClutImage)
+#define HashPointerType PrependMagickMethod(HashPointerType)
+#define HashStringInfoType PrependMagickMethod(HashStringInfoType)
+#define HashStringType PrependMagickMethod(HashStringType)
+#define HSLTransform PrependMagickMethod(HSLTransform)
+#define Huffman2DEncodeImage PrependMagickMethod(Huffman2DEncodeImage)
+#define HuffmanDecodeImage PrependMagickMethod(HuffmanDecodeImage)
+#define HuffmanEncodeImage PrependMagickMethod(HuffmanEncodeImage)
+#define IdentifyImage PrependMagickMethod(IdentifyImage)
+#define IdentityAffine PrependMagickMethod(IdentityAffine)
+#define ImageListToArray PrependMagickMethod(ImageListToArray)
+#define ImagesToBlob PrependMagickMethod(ImagesToBlob)
+#define ImageToBlob PrependMagickMethod(ImageToBlob)
+#define ImageToFile PrependMagickMethod(ImageToFile)
+#define ImplodeImage PrependMagickMethod(ImplodeImage)
+#define ImportImagePixels PrependMagickMethod(ImportImagePixels)
+#define ImportQuantumPixels PrependMagickMethod(ImportQuantumPixels)
+#define increase PrependMagickMethod(increase)
+#define InheritException PrependMagickMethod(InheritException)
+#define InitializeMagick PrependMagickMethod(InitializeMagick)
+#define InitializeMagickResources PrependMagickMethod(InitializeMagickResources)
+#define InitializeSemaphore PrependMagickMethod(InitializeSemaphore)
+#define InitializeSignature PrependMagickMethod(InitializeSignature)
+#define InjectImageBlob PrependMagickMethod(InjectImageBlob)
+#define InsertImageInList PrependMagickMethod(InsertImageInList)
+#define InsertTagIntoXMLTree PrependMagickMethod(InsertTagIntoXMLTree)
+#define InsertValueInLinkedList PrependMagickMethod(InsertValueInLinkedList)
+#define InsertValueInSortedLinkedList PrependMagickMethod(InsertValueInSortedLinkedList)
+#define InterpolatePixelColor PrependMagickMethod(InterpolatePixelColor)
+#define InterpretImageAttributes PrependMagickMethod(InterpretImageAttributes)
+#define InterpretImageFilename PrependMagickMethod(InterpretImageFilename)
+#define InterpretImageProperties PrependMagickMethod(InterpretImageProperties)
+#define InverseFourierTransformImage PrependMagickMethod(InverseFourierTransformImage)
+#define InvokeDelegate PrependMagickMethod(InvokeDelegate)
+#define InvokeDynamicImageFilter PrependMagickMethod(InvokeDynamicImageFilter)
+#define IsBlobExempt PrependMagickMethod(IsBlobExempt)
+#define IsBlobSeekable PrependMagickMethod(IsBlobSeekable)
+#define IsBlobTemporary PrependMagickMethod(IsBlobTemporary)
+#define IsColorSimilar PrependMagickMethod(IsColorSimilar)
+#define IsEventLogging PrependMagickMethod(IsEventLogging)
+#define IsGeometry PrependMagickMethod(IsGeometry)
+#define IsGlob PrependMagickMethod(IsGlob)
+#define IsGrayImage PrependMagickMethod(IsGrayImage)
+#define IsHashmapEmpty PrependMagickMethod(IsHashmapEmpty)
+#define IsHighDynamicRangeImage PrependMagickMethod(IsHighDynamicRangeImage)
+#define IsHistogramImage PrependMagickMethod(IsHistogramImage)
+#define IsImageObject PrependMagickMethod(IsImageObject)
+#define IsImagesEqual PrependMagickMethod(IsImagesEqual)
+#define IsImageSimilar PrependMagickMethod(IsImageSimilar)
+#define IsLinkedListEmpty PrependMagickMethod(IsLinkedListEmpty)
+#define IsMagickColorSimilar PrependMagickMethod(IsMagickColorSimilar)
+#define IsMagickConflict PrependMagickMethod(IsMagickConflict)
+#define IsMagickInstantiated PrependMagickMethod(IsMagickInstantiated)
+#define IsMagickOption PrependMagickMethod(IsMagickOption)
+#define IsMagickTrue PrependMagickMethod(IsMagickTrue)
+#define IsMonochromeImage PrependMagickMethod(IsMonochromeImage)
+#define IsOpacitySimilar PrependMagickMethod(IsOpacitySimilar)
+#define IsOpaqueImage PrependMagickMethod(IsOpaqueImage)
+#define IsPaletteImage PrependMagickMethod(IsPaletteImage)
+#define IsPathAccessible PrependMagickMethod(IsPathAccessible)
+#define IsRightsAuthorized PrependMagickMethod(IsRightsAuthorized)
+#define IsSceneGeometry PrependMagickMethod(IsSceneGeometry)
+#define IsSubimage PrependMagickMethod(IsSubimage)
+#define IsTaintImage PrependMagickMethod(IsTaintImage)
+#define LeastSquaresAddTerms PrependMagickMethod(LeastSquaresAddTerms)
+#define LevelImageChannel PrependMagickMethod(LevelImageChannel)
+#define LevelImageColors PrependMagickMethod(LevelImageColors)
+#define LevelImage PrependMagickMethod(LevelImage)
+#define LevelizeImageChannel PrependMagickMethod(LevelizeImageChannel)
+#define LiberateMemory PrependMagickMethod(LiberateMemory)
+#define LiberateSemaphoreInfo PrependMagickMethod(LiberateSemaphoreInfo)
+#define LinearStretchImage PrependMagickMethod(LinearStretchImage)
+#define LinkedListToArray PrependMagickMethod(LinkedListToArray)
+#define LiquidRescaleImage PrependMagickMethod(LiquidRescaleImage)
+#define ListCoderInfo PrependMagickMethod(ListCoderInfo)
+#define ListColorInfo PrependMagickMethod(ListColorInfo)
+#define ListConfigureInfo PrependMagickMethod(ListConfigureInfo)
+#define ListDelegateInfo PrependMagickMethod(ListDelegateInfo)
+#define ListFiles PrependMagickMethod(ListFiles)
+#define ListLocaleInfo PrependMagickMethod(ListLocaleInfo)
+#define ListLogInfo PrependMagickMethod(ListLogInfo)
+#define ListMagicInfo PrependMagickMethod(ListMagicInfo)
+#define ListMagickInfo PrependMagickMethod(ListMagickInfo)
+#define ListMagickOptions PrependMagickMethod(ListMagickOptions)
+#define ListMagickResourceInfo PrependMagickMethod(ListMagickResourceInfo)
+#define ListMimeInfo PrependMagickMethod(ListMimeInfo)
+#define ListModuleInfo PrependMagickMethod(ListModuleInfo)
+#define ListPolicyInfo PrependMagickMethod(ListPolicyInfo)
+#define ListThresholdMapFile PrependMagickMethod(ListThresholdMapFile)
+#define ListThresholdMaps PrependMagickMethod(ListThresholdMaps)
+#define ListTypeInfo PrependMagickMethod(ListTypeInfo)
+#define LoadFontConfigFonts PrependMagickMethod(LoadFontConfigFonts)
+#define LoadMimeLists PrependMagickMethod(LoadMimeLists)
+#define LocaleCompare PrependMagickMethod(LocaleCompare)
+#define LocaleLower PrependMagickMethod(LocaleLower)
+#define LocaleNCompare PrependMagickMethod(LocaleNCompare)
+#define LocaleUpper PrependMagickMethod(LocaleUpper)
+#define LockSemaphoreInfo PrependMagickMethod(LockSemaphoreInfo)
+#define LogMagickEventList PrependMagickMethod(LogMagickEventList)
+#define LogMagickEvent PrependMagickMethod(LogMagickEvent)
+#define LZWEncodeImage PrependMagickMethod(LZWEncodeImage)
+#define MagickCoreGenesis PrependMagickMethod(MagickCoreGenesis)
+#define MagickCoreTerminus PrependMagickMethod(MagickCoreTerminus)
+#define MagickCreateThreadKey PrependMagickMethod(MagickCreateThreadKey)
+#define MagickDeleteThreadKey PrependMagickMethod(MagickDeleteThreadKey)
+#define MagickError PrependMagickMethod(MagickError)
+#define MagickFatalError PrependMagickMethod(MagickFatalError)
+#define MagickGetThreadValue PrependMagickMethod(MagickGetThreadValue)
+#define MagickIncarnate PrependMagickMethod(MagickIncarnate)
+#define MagickMonitor PrependMagickMethod(MagickMonitor)
+#define MagickOptionToMnemonic PrependMagickMethod(MagickOptionToMnemonic)
+#define MagickSetThreadValue PrependMagickMethod(MagickSetThreadValue)
+#define MagickToMime PrependMagickMethod(MagickToMime)
+#define MagickWarning PrependMagickMethod(MagickWarning)
+#define MagnifyImage PrependMagickMethod(MagnifyImage)
+#define MapBlob PrependMagickMethod(MapBlob)
+#define MapImage PrependMagickMethod(MapImage)
+#define MapImages PrependMagickMethod(MapImages)
+#define MatteFloodfillImage PrependMagickMethod(MatteFloodfillImage)
+#define MedianFilterImage PrependMagickMethod(MedianFilterImage)
+#define MergeImageLayers PrependMagickMethod(MergeImageLayers)
+#define MinifyImage PrependMagickMethod(MinifyImage)
+#define ModifyImage PrependMagickMethod(ModifyImage)
+#define ModulateImage PrependMagickMethod(ModulateImage)
+#define MontageImageList PrependMagickMethod(MontageImageList)
+#define MontageImages PrependMagickMethod(MontageImages)
+#define MorphImages PrependMagickMethod(MorphImages)
+#define MosaicImages PrependMagickMethod(MosaicImages)
+#define MotionBlurImageChannel PrependMagickMethod(MotionBlurImageChannel)
+#define MotionBlurImage PrependMagickMethod(MotionBlurImage)
+#define MSBOrderLong PrependMagickMethod(MSBOrderLong)
+#define MSBOrderShort PrependMagickMethod(MSBOrderShort)
+#define MultilineCensus PrependMagickMethod(MultilineCensus)
+#define NegateImageChannel PrependMagickMethod(NegateImageChannel)
+#define NegateImage PrependMagickMethod(NegateImage)
+#define NewHashmap PrependMagickMethod(NewHashmap)
+#define NewImageList PrependMagickMethod(NewImageList)
+#define NewLinkedList PrependMagickMethod(NewLinkedList)
+#define NewMagickImage PrependMagickMethod(NewMagickImage)
+#define NewSplayTree PrependMagickMethod(NewSplayTree)
+#define NewXMLTree PrependMagickMethod(NewXMLTree)
+#define NewXMLTreeTag PrependMagickMethod(NewXMLTreeTag)
+#define NormalizeImageChannel PrependMagickMethod(NormalizeImageChannel)
+#define NormalizeImage PrependMagickMethod(NormalizeImage)
+#define OilPaintImage PrependMagickMethod(OilPaintImage)
+#define OpaqueImage PrependMagickMethod(OpaqueImage)
+#define OpaquePaintImageChannel PrependMagickMethod(OpaquePaintImageChannel)
+#define OpaquePaintImage PrependMagickMethod(OpaquePaintImage)
+#define OpenBlob PrependMagickMethod(OpenBlob)
+#define OpenCacheView PrependMagickMethod(OpenCacheView)
+#define OpenMagickStream PrependMagickMethod(OpenMagickStream)
+#define OpenStream PrependMagickMethod(OpenStream)
+#define OptimizeImageLayers PrependMagickMethod(OptimizeImageLayers)
+#define OptimizeImageTransparency PrependMagickMethod(OptimizeImageTransparency)
+#define OptimizePlusImageLayers PrependMagickMethod(OptimizePlusImageLayers)
+#define OrderedDitherImageChannel PrependMagickMethod(OrderedDitherImageChannel)
+#define OrderedDitherImage PrependMagickMethod(OrderedDitherImage)
+#define OrderedPosterizeImageChannel PrependMagickMethod(OrderedPosterizeImageChannel)
+#define OrderedPosterizeImage PrependMagickMethod(OrderedPosterizeImage)
+#define PackbitsEncodeImage PrependMagickMethod(PackbitsEncodeImage)
+#define PaintFloodfillImage PrependMagickMethod(PaintFloodfillImage)
+#define PaintOpaqueImageChannel PrependMagickMethod(PaintOpaqueImageChannel)
+#define PaintOpaqueImage PrependMagickMethod(PaintOpaqueImage)
+#define PaintTransparentImage PrependMagickMethod(PaintTransparentImage)
+#define ParseAbsoluteGeometry PrependMagickMethod(ParseAbsoluteGeometry)
+#define ParseAffineGeometry PrependMagickMethod(ParseAffineGeometry)
+#define ParseChannelOption PrependMagickMethod(ParseChannelOption)
+#define ParseGeometry PrependMagickMethod(ParseGeometry)
+#define ParseGravityGeometry PrependMagickMethod(ParseGravityGeometry)
+#define ParseImageGeometry PrependMagickMethod(ParseImageGeometry)
+#define ParseMagickOption PrependMagickMethod(ParseMagickOption)
+#define ParseMetaGeometry PrependMagickMethod(ParseMetaGeometry)
+#define ParsePageGeometry PrependMagickMethod(ParsePageGeometry)
+#define ParseRegionGeometry PrependMagickMethod(ParseRegionGeometry)
+#define ParseSizeGeometry PrependMagickMethod(ParseSizeGeometry)
+#define PasskeyDecipherImage PrependMagickMethod(PasskeyDecipherImage)
+#define PasskeyEncipherImage PrependMagickMethod(PasskeyEncipherImage)
+#define PersistPixelCache PrependMagickMethod(PersistPixelCache)
+#define PingBlob PrependMagickMethod(PingBlob)
+#define PingImage PrependMagickMethod(PingImage)
+#define PingImages PrependMagickMethod(PingImages)
+#define PlasmaImage PrependMagickMethod(PlasmaImage)
+#define PlasmaImageProxy PrependMagickMethod(PlasmaImageProxy)
+#define PolaroidImage PrependMagickMethod(PolaroidImage)
+#define PopImageList PrependMagickMethod(PopImageList)
+#define PopImagePixels PrependMagickMethod(PopImagePixels)
+#define PosterizeImage PrependMagickMethod(PosterizeImage)
+#define PostscriptGeometry PrependMagickMethod(PostscriptGeometry)
+#define PrependImageToList PrependMagickMethod(PrependImageToList)
+#define PreviewImage PrependMagickMethod(PreviewImage)
+#define PrintStringInfo PrependMagickMethod(PrintStringInfo)
+#define process_message PrependMagickMethod(process_message)
+#define ProfileImage PrependMagickMethod(ProfileImage)
+#define PruneTagFromXMLTree PrependMagickMethod(PruneTagFromXMLTree)
+#define PushImageList PrependMagickMethod(PushImageList)
+#define PushImagePixels PrependMagickMethod(PushImagePixels)
+#define PutEntryInHashmap PrependMagickMethod(PutEntryInHashmap)
+#define QuantizationError PrependMagickMethod(QuantizationError)
+#define QuantizeImage PrependMagickMethod(QuantizeImage)
+#define QuantizeImages PrependMagickMethod(QuantizeImages)
+#define QueryColorDatabase PrependMagickMethod(QueryColorDatabase)
+#define QueryColorname PrependMagickMethod(QueryColorname)
+#define QueryMagickColorname PrependMagickMethod(QueryMagickColorname)
+#define QueryMagickColor PrependMagickMethod(QueryMagickColor)
+#define QueueAuthenticNexus PrependMagickMethod(QueueAuthenticNexus)
+#define QueueAuthenticPixels PrependMagickMethod(QueueAuthenticPixels)
+#define QueueCacheViewAuthenticPixels PrependMagickMethod(QueueCacheViewAuthenticPixels)
+#define RadialBlurImageChannel PrependMagickMethod(RadialBlurImageChannel)
+#define RadialBlurImage PrependMagickMethod(RadialBlurImage)
+#define RaiseImage PrependMagickMethod(RaiseImage)
+#define RandomChannelThresholdImage PrependMagickMethod(RandomChannelThresholdImage)
+#define RandomThresholdImageChannel PrependMagickMethod(RandomThresholdImageChannel)
+#define RandomThresholdImage PrependMagickMethod(RandomThresholdImage)
+#define ReacquireMemory PrependMagickMethod(ReacquireMemory)
+#define ReadBlobByte PrependMagickMethod(ReadBlobByte)
+#define ReadBlobDouble PrependMagickMethod(ReadBlobDouble)
+#define ReadBlobFloat PrependMagickMethod(ReadBlobFloat)
+#define ReadBlobLongLong PrependMagickMethod(ReadBlobLongLong)
+#define ReadBlobLong PrependMagickMethod(ReadBlobLong)
+#define ReadBlobLSBLong PrependMagickMethod(ReadBlobLSBLong)
+#define ReadBlobLSBShort PrependMagickMethod(ReadBlobLSBShort)
+#define ReadBlobMSBLong PrependMagickMethod(ReadBlobMSBLong)
+#define ReadBlobMSBShort PrependMagickMethod(ReadBlobMSBShort)
+#define ReadBlob PrependMagickMethod(ReadBlob)
+#define ReadBlobShort PrependMagickMethod(ReadBlobShort)
+#define ReadBlobString PrependMagickMethod(ReadBlobString)
+#define ReadImage PrependMagickMethod(ReadImage)
+#define ReadImages PrependMagickMethod(ReadImages)
+#define ReadInlineImage PrependMagickMethod(ReadInlineImage)
+#define ReadStream PrependMagickMethod(ReadStream)
+#define RecolorImage PrependMagickMethod(RecolorImage)
+#define ReduceNoiseImage PrependMagickMethod(ReduceNoiseImage)
+#define ReferenceBlob PrependMagickMethod(ReferenceBlob)
+#define ReferenceImage PrependMagickMethod(ReferenceImage)
+#define ReferencePixelCache PrependMagickMethod(ReferencePixelCache)
+#define RegisterARTImage PrependMagickMethod(RegisterARTImage)
+#define RegisterAVIImage PrependMagickMethod(RegisterAVIImage)
+#define RegisterAVSImage PrependMagickMethod(RegisterAVSImage)
+#define RegisterBMPImage PrependMagickMethod(RegisterBMPImage)
+#define RegisterBRAILLEImage PrependMagickMethod(RegisterBRAILLEImage)
+#define RegisterCALSImage PrependMagickMethod(RegisterCALSImage)
+#define RegisterCAPTIONImage PrependMagickMethod(RegisterCAPTIONImage)
+#define RegisterCINImage PrependMagickMethod(RegisterCINImage)
+#define RegisterCIPImage PrependMagickMethod(RegisterCIPImage)
+#define RegisterCLIPImage PrependMagickMethod(RegisterCLIPImage)
+#define RegisterCMYKImage PrependMagickMethod(RegisterCMYKImage)
+#define RegisterCUTImage PrependMagickMethod(RegisterCUTImage)
+#define RegisterDCMImage PrependMagickMethod(RegisterDCMImage)
+#define RegisterDDSImage PrependMagickMethod(RegisterDDSImage)
+#define RegisterDIBImage PrependMagickMethod(RegisterDIBImage)
+#define RegisterDJVUImage PrependMagickMethod(RegisterDJVUImage)
+#define RegisterDNGImage PrependMagickMethod(RegisterDNGImage)
+#define RegisterDOTImage PrependMagickMethod(RegisterDOTImage)
+#define RegisterDPSImage PrependMagickMethod(RegisterDPSImage)
+#define RegisterDPXImage PrependMagickMethod(RegisterDPXImage)
+#define RegisterEPTImage PrependMagickMethod(RegisterEPTImage)
+#define RegisterFAXImage PrependMagickMethod(RegisterFAXImage)
+#define RegisterFITSImage PrependMagickMethod(RegisterFITSImage)
+#define RegisterGIFImage PrependMagickMethod(RegisterGIFImage)
+#define RegisterGRADIENTImage PrependMagickMethod(RegisterGRADIENTImage)
+#define RegisterGRAYImage PrependMagickMethod(RegisterGRAYImage)
+#define RegisterHALDImage PrependMagickMethod(RegisterHALDImage)
+#define RegisterHISTOGRAMImage PrependMagickMethod(RegisterHISTOGRAMImage)
+#define RegisterHRZImage PrependMagickMethod(RegisterHRZImage)
+#define RegisterHTMLImage PrependMagickMethod(RegisterHTMLImage)
+#define RegisterICONImage PrependMagickMethod(RegisterICONImage)
+#define RegisterINFOImage PrependMagickMethod(RegisterINFOImage)
+#define RegisterINLINEImage PrependMagickMethod(RegisterINLINEImage)
+#define RegisterIPLImage PrependMagickMethod(RegisterIPLImage)
+#define RegisterJP2Image PrependMagickMethod(RegisterJP2Image)
+#define RegisterJPEGImage PrependMagickMethod(RegisterJPEGImage)
+#define RegisterLABELImage PrependMagickMethod(RegisterLABELImage)
+#define RegisterMAGICKImage PrependMagickMethod(RegisterMAGICKImage)
+#define RegisterMagickInfo PrependMagickMethod(RegisterMagickInfo)
+#define RegisterMAPImage PrependMagickMethod(RegisterMAPImage)
+#define RegisterMATImage PrependMagickMethod(RegisterMATImage)
+#define RegisterMATTEImage PrependMagickMethod(RegisterMATTEImage)
+#define RegisterMETAImage PrependMagickMethod(RegisterMETAImage)
+#define RegisterMIFFImage PrependMagickMethod(RegisterMIFFImage)
+#define RegisterMONOImage PrependMagickMethod(RegisterMONOImage)
+#define RegisterMPCImage PrependMagickMethod(RegisterMPCImage)
+#define RegisterMPEGImage PrependMagickMethod(RegisterMPEGImage)
+#define RegisterMPRImage PrependMagickMethod(RegisterMPRImage)
+#define RegisterMSLImage PrependMagickMethod(RegisterMSLImage)
+#define RegisterMTVImage PrependMagickMethod(RegisterMTVImage)
+#define RegisterMVGImage PrependMagickMethod(RegisterMVGImage)
+#define RegisterNULLImage PrependMagickMethod(RegisterNULLImage)
+#define RegisterOTBImage PrependMagickMethod(RegisterOTBImage)
+#define RegisterPALMImage PrependMagickMethod(RegisterPALMImage)
+#define RegisterPATTERNImage PrependMagickMethod(RegisterPATTERNImage)
+#define RegisterPCDImage PrependMagickMethod(RegisterPCDImage)
+#define RegisterPCLImage PrependMagickMethod(RegisterPCLImage)
+#define RegisterPCXImage PrependMagickMethod(RegisterPCXImage)
+#define RegisterPDBImage PrependMagickMethod(RegisterPDBImage)
+#define RegisterPDFImage PrependMagickMethod(RegisterPDFImage)
+#define RegisterPICTImage PrependMagickMethod(RegisterPICTImage)
+#define RegisterPIXImage PrependMagickMethod(RegisterPIXImage)
+#define RegisterPLASMAImage PrependMagickMethod(RegisterPLASMAImage)
+#define RegisterPNGImage PrependMagickMethod(RegisterPNGImage)
+#define RegisterPNMImage PrependMagickMethod(RegisterPNMImage)
+#define RegisterPREVIEWImage PrependMagickMethod(RegisterPREVIEWImage)
+#define RegisterPS2Image PrependMagickMethod(RegisterPS2Image)
+#define RegisterPS3Image PrependMagickMethod(RegisterPS3Image)
+#define RegisterPSDImage PrependMagickMethod(RegisterPSDImage)
+#define RegisterPSImage PrependMagickMethod(RegisterPSImage)
+#define RegisterPWPImage PrependMagickMethod(RegisterPWPImage)
+#define RegisterRAWImage PrependMagickMethod(RegisterRAWImage)
+#define RegisterRGBImage PrependMagickMethod(RegisterRGBImage)
+#define RegisterRLAImage PrependMagickMethod(RegisterRLAImage)
+#define RegisterRLEImage PrependMagickMethod(RegisterRLEImage)
+#define RegisterSCRImage PrependMagickMethod(RegisterSCRImage)
+#define RegisterSCTImage PrependMagickMethod(RegisterSCTImage)
+#define RegisterSFWImage PrependMagickMethod(RegisterSFWImage)
+#define RegisterSGIImage PrependMagickMethod(RegisterSGIImage)
+#define RegisterStaticModules PrependMagickMethod(RegisterStaticModules)
+#define RegisterSTEGANOImage PrependMagickMethod(RegisterSTEGANOImage)
+#define RegisterSUNImage PrependMagickMethod(RegisterSUNImage)
+#define RegisterSVGImage PrependMagickMethod(RegisterSVGImage)
+#define RegisterTGAImage PrependMagickMethod(RegisterTGAImage)
+#define RegisterTHUMBNAILImage PrependMagickMethod(RegisterTHUMBNAILImage)
+#define RegisterTIFFImage PrependMagickMethod(RegisterTIFFImage)
+#define RegisterTILEImage PrependMagickMethod(RegisterTILEImage)
+#define RegisterTIMImage PrependMagickMethod(RegisterTIMImage)
+#define RegisterTTFImage PrependMagickMethod(RegisterTTFImage)
+#define RegisterTXTImage PrependMagickMethod(RegisterTXTImage)
+#define RegisterUILImage PrependMagickMethod(RegisterUILImage)
+#define RegisterURLImage PrependMagickMethod(RegisterURLImage)
+#define RegisterUYVYImage PrependMagickMethod(RegisterUYVYImage)
+#define RegisterVICARImage PrependMagickMethod(RegisterVICARImage)
+#define RegisterVIDImage PrependMagickMethod(RegisterVIDImage)
+#define RegisterVIFFImage PrependMagickMethod(RegisterVIFFImage)
+#define RegisterWBMPImage PrependMagickMethod(RegisterWBMPImage)
+#define RegisterWPGImage PrependMagickMethod(RegisterWPGImage)
+#define RegisterXBMImage PrependMagickMethod(RegisterXBMImage)
+#define RegisterXCFImage PrependMagickMethod(RegisterXCFImage)
+#define RegisterXCImage PrependMagickMethod(RegisterXCImage)
+#define RegisterXImage PrependMagickMethod(RegisterXImage)
+#define RegisterXPMImage PrependMagickMethod(RegisterXPMImage)
+#define RegisterXPSImage PrependMagickMethod(RegisterXPSImage)
+#define RegisterXWDImage PrependMagickMethod(RegisterXWDImage)
+#define RegisterYCBCRImage PrependMagickMethod(RegisterYCBCRImage)
+#define RegisterYUVImage PrependMagickMethod(RegisterYUVImage)
+#define RelinquishAlignedMemory PrependMagickMethod(RelinquishAlignedMemory)
+#define RelinquishMagickMatrix PrependMagickMethod(RelinquishMagickMatrix)
+#define RelinquishMagickMemory PrependMagickMethod(RelinquishMagickMemory)
+#define RelinquishMagickResource PrependMagickMethod(RelinquishMagickResource)
+#define RelinquishSemaphoreInfo PrependMagickMethod(RelinquishSemaphoreInfo)
+#define RelinquishUniqueFileResource PrependMagickMethod(RelinquishUniqueFileResource)
+#define RemapImage PrependMagickMethod(RemapImage)
+#define RemapImages PrependMagickMethod(RemapImages)
+#define RemoteDisplayCommand PrependMagickMethod(RemoteDisplayCommand)
+#define RemoveDuplicateLayers PrependMagickMethod(RemoveDuplicateLayers)
+#define RemoveElementByValueFromLinkedList PrependMagickMethod(RemoveElementByValueFromLinkedList)
+#define RemoveElementFromLinkedList PrependMagickMethod(RemoveElementFromLinkedList)
+#define RemoveEntryFromHashmap PrependMagickMethod(RemoveEntryFromHashmap)
+#define RemoveFirstImageFromList PrependMagickMethod(RemoveFirstImageFromList)
+#define RemoveImageArtifact PrependMagickMethod(RemoveImageArtifact)
+#define RemoveImageFromList PrependMagickMethod(RemoveImageFromList)
+#define RemoveImageOption PrependMagickMethod(RemoveImageOption)
+#define RemoveImageProfile PrependMagickMethod(RemoveImageProfile)
+#define RemoveImageProperty PrependMagickMethod(RemoveImageProperty)
+#define RemoveImageRegistry PrependMagickMethod(RemoveImageRegistry)
+#define RemoveLastElementFromLinkedList PrependMagickMethod(RemoveLastElementFromLinkedList)
+#define RemoveLastImageFromList PrependMagickMethod(RemoveLastImageFromList)
+#define RemoveNodeByValueFromSplayTree PrependMagickMethod(RemoveNodeByValueFromSplayTree)
+#define RemoveNodeFromSplayTree PrependMagickMethod(RemoveNodeFromSplayTree)
+#define RemoveZeroDelayLayers PrependMagickMethod(RemoveZeroDelayLayers)
+#define ReplaceImageInList PrependMagickMethod(ReplaceImageInList)
+#define ResampleImage PrependMagickMethod(ResampleImage)
+#define ResamplePixelColor PrependMagickMethod(ResamplePixelColor)
+#define ResetHashmapIterator PrependMagickMethod(ResetHashmapIterator)
+#define ResetImageArtifactIterator PrependMagickMethod(ResetImageArtifactIterator)
+#define ResetImageAttributeIterator PrependMagickMethod(ResetImageAttributeIterator)
+#define ResetImageOptionIterator PrependMagickMethod(ResetImageOptionIterator)
+#define ResetImagePage PrependMagickMethod(ResetImagePage)
+#define ResetImageProfileIterator PrependMagickMethod(ResetImageProfileIterator)
+#define ResetImagePropertyIterator PrependMagickMethod(ResetImagePropertyIterator)
+#define ResetImageRegistryIterator PrependMagickMethod(ResetImageRegistryIterator)
+#define ResetLinkedListIterator PrependMagickMethod(ResetLinkedListIterator)
+#define ResetMagickMemory PrependMagickMethod(ResetMagickMemory)
+#define ResetSplayTreeIterator PrependMagickMethod(ResetSplayTreeIterator)
+#define ResetStringInfo PrependMagickMethod(ResetStringInfo)
+#define ResetTimer PrependMagickMethod(ResetTimer)
+#define ResizeImage PrependMagickMethod(ResizeImage)
+#define ResizeMagickMemory PrependMagickMethod(ResizeMagickMemory)
+#define ResizeQuantumMemory PrependMagickMethod(ResizeQuantumMemory)
+#define ReverseImageList PrependMagickMethod(ReverseImageList)
+#define RGBTransformImage PrependMagickMethod(RGBTransformImage)
+#define RollImage PrependMagickMethod(RollImage)
+#define RotateImage PrependMagickMethod(RotateImage)
+#define SampleImage PrependMagickMethod(SampleImage)
+#define ScaleImage PrependMagickMethod(ScaleImage)
+#define ScaleResampleFilter PrependMagickMethod(ScaleResampleFilter)
+#define SeedPseudoRandomGenerator PrependMagickMethod(SeedPseudoRandomGenerator)
+#define SeekBlob PrependMagickMethod(SeekBlob)
+#define SegmentImage PrependMagickMethod(SegmentImage)
+#define SelectiveBlurImageChannel PrependMagickMethod(SelectiveBlurImageChannel)
+#define SelectiveBlurImage PrependMagickMethod(SelectiveBlurImage)
+#define SeparateImageChannel PrependMagickMethod(SeparateImageChannel)
+#define SeparateImages PrependMagickMethod(SeparateImages)
+#define SepiaToneImage PrependMagickMethod(SepiaToneImage)
+#define SetBlobExempt PrependMagickMethod(SetBlobExempt)
+#define SetBlobExtent PrependMagickMethod(SetBlobExtent)
+#define SetCacheThreshold PrependMagickMethod(SetCacheThreshold)
+#define SetCacheViewPixels PrependMagickMethod(SetCacheViewPixels)
+#define SetCacheViewStorageClass PrependMagickMethod(SetCacheViewStorageClass)
+#define SetCacheViewVirtualPixelMethod PrependMagickMethod(SetCacheViewVirtualPixelMethod)
+#define SetClientName PrependMagickMethod(SetClientName)
+#define SetClientPath PrependMagickMethod(SetClientPath)
+#define SetErrorHandler PrependMagickMethod(SetErrorHandler)
+#define SetExceptionInfo PrependMagickMethod(SetExceptionInfo)
+#define SetFatalErrorHandler PrependMagickMethod(SetFatalErrorHandler)
+#define SetGeometryInfo PrependMagickMethod(SetGeometryInfo)
+#define SetGeometry PrependMagickMethod(SetGeometry)
+#define SetHeaderFromIPL PrependMagickMethod(SetHeaderFromIPL)
+#define SetImageAlphaChannel PrependMagickMethod(SetImageAlphaChannel)
+#define SetImageArtifact PrependMagickMethod(SetImageArtifact)
+#define SetImageAttribute PrependMagickMethod(SetImageAttribute)
+#define SetImageBackgroundColor PrependMagickMethod(SetImageBackgroundColor)
+#define SetImageChannelDepth PrependMagickMethod(SetImageChannelDepth)
+#define SetImageClipMask PrependMagickMethod(SetImageClipMask)
+#define SetImageColorspace PrependMagickMethod(SetImageColorspace)
+#define SetImageDepth PrependMagickMethod(SetImageDepth)
+#define SetImageExtent PrependMagickMethod(SetImageExtent)
+#define SetImageInfoBlob PrependMagickMethod(SetImageInfoBlob)
+#define SetImageInfoFile PrependMagickMethod(SetImageInfoFile)
+#define SetImageInfo PrependMagickMethod(SetImageInfo)
+#define SetImageInfoProgressMonitor PrependMagickMethod(SetImageInfoProgressMonitor)
+#define SetImageList PrependMagickMethod(SetImageList)
+#define SetImageMask PrependMagickMethod(SetImageMask)
+#define SetImageOpacity PrependMagickMethod(SetImageOpacity)
+#define SetImageOption PrependMagickMethod(SetImageOption)
+#define SetImagePixels PrependMagickMethod(SetImagePixels)
+#define SetImage PrependMagickMethod(SetImage)
+#define SetImageProfile PrependMagickMethod(SetImageProfile)
+#define SetImageProgressMonitor PrependMagickMethod(SetImageProgressMonitor)
+#define SetImageProperty PrependMagickMethod(SetImageProperty)
+#define SetImageRegistry PrependMagickMethod(SetImageRegistry)
+#define SetImageStorageClass PrependMagickMethod(SetImageStorageClass)
+#define SetImageType PrependMagickMethod(SetImageType)
+#define SetImageVirtualPixelMethod PrependMagickMethod(SetImageVirtualPixelMethod)
+#define SetLogEventMask PrependMagickMethod(SetLogEventMask)
+#define SetLogFormat PrependMagickMethod(SetLogFormat)
+#define SetLogName PrependMagickMethod(SetLogName)
+#define SetMagickInfo PrependMagickMethod(SetMagickInfo)
+#define SetMagickMemoryMethods PrependMagickMethod(SetMagickMemoryMethods)
+#define SetMagickRegistry PrependMagickMethod(SetMagickRegistry)
+#define SetMagickResourceLimit PrependMagickMethod(SetMagickResourceLimit)
+#define SetMonitorHandler PrependMagickMethod(SetMonitorHandler)
+#define SetPixelCacheMethods PrependMagickMethod(SetPixelCacheMethods)
+#define SetPixelCacheVirtualMethod PrependMagickMethod(SetPixelCacheVirtualMethod)
+#define SetQuantumAlphaType PrependMagickMethod(SetQuantumAlphaType)
+#define SetQuantumDepth PrependMagickMethod(SetQuantumDepth)
+#define SetQuantumFormat PrependMagickMethod(SetQuantumFormat)
+#define SetQuantumImageType PrependMagickMethod(SetQuantumImageType)
+#define SetQuantumMinIsWhite PrependMagickMethod(SetQuantumMinIsWhite)
+#define SetQuantumPack PrependMagickMethod(SetQuantumPack)
+#define SetQuantumPad PrependMagickMethod(SetQuantumPad)
+#define SetQuantumQuantum PrependMagickMethod(SetQuantumQuantum)
+#define SetQuantumScale PrependMagickMethod(SetQuantumScale)
+#define SetRandomKey PrependMagickMethod(SetRandomKey)
+#define SetRandomTrueRandom PrependMagickMethod(SetRandomTrueRandom)
+#define SetResampleFilterInterpolateMethod PrependMagickMethod(SetResampleFilterInterpolateMethod)
+#define SetResampleFilter PrependMagickMethod(SetResampleFilter)
+#define SetResampleFilterVirtualPixelMethod PrependMagickMethod(SetResampleFilterVirtualPixelMethod)
+#define SetResizeFilterSupport PrependMagickMethod(SetResizeFilterSupport)
+#define SetSignatureDigest PrependMagickMethod(SetSignatureDigest)
+#define SetStreamInfoClientData PrependMagickMethod(SetStreamInfoClientData)
+#define SetStreamInfoMap PrependMagickMethod(SetStreamInfoMap)
+#define SetStreamInfoStorageType PrependMagickMethod(SetStreamInfoStorageType)
+#define SetStringInfoDatum PrependMagickMethod(SetStringInfoDatum)
+#define SetStringInfoLength PrependMagickMethod(SetStringInfoLength)
+#define SetStringInfoPath PrependMagickMethod(SetStringInfoPath)
+#define SetStringInfo PrependMagickMethod(SetStringInfo)
+#define SetWarningHandler PrependMagickMethod(SetWarningHandler)
+#define SetXMLTreeAttribute PrependMagickMethod(SetXMLTreeAttribute)
+#define SetXMLTreeContent PrependMagickMethod(SetXMLTreeContent)
+#define ShadeImage PrependMagickMethod(ShadeImage)
+#define ShadowImage PrependMagickMethod(ShadowImage)
+#define SharpenImageChannel PrependMagickMethod(SharpenImageChannel)
+#define SharpenImage PrependMagickMethod(SharpenImage)
+#define ShaveImage PrependMagickMethod(ShaveImage)
+#define ShearImage PrependMagickMethod(ShearImage)
+#define ShiftImageList PrependMagickMethod(ShiftImageList)
+#define SigmoidalContrastImageChannel PrependMagickMethod(SigmoidalContrastImageChannel)
+#define SigmoidalContrastImage PrependMagickMethod(SigmoidalContrastImage)
+#define SignatureImage PrependMagickMethod(SignatureImage)
+#define SimilarityImage PrependMagickMethod(SimilarityImage)
+#define SizeBlob PrependMagickMethod(SizeBlob)
+#define SketchImage PrependMagickMethod(SketchImage)
+#define SolarizeImage PrependMagickMethod(SolarizeImage)
+#define SortColormapByIntensity PrependMagickMethod(SortColormapByIntensity)
+#define SparseColorImage PrependMagickMethod(SparseColorImage)
+#define SpliceImageIntoList PrependMagickMethod(SpliceImageIntoList)
+#define SpliceImageList PrependMagickMethod(SpliceImageList)
+#define SpliceImage PrependMagickMethod(SpliceImage)
+#define SplitImageList PrependMagickMethod(SplitImageList)
+#define SplitStringInfo PrependMagickMethod(SplitStringInfo)
+#define SpreadImage PrependMagickMethod(SpreadImage)
+#define StartTimer PrependMagickMethod(StartTimer)
+#define SteganoImage PrependMagickMethod(SteganoImage)
+#define StereoAnaglyphImage PrependMagickMethod(StereoAnaglyphImage)
+#define StereoImage PrependMagickMethod(StereoImage)
+#define StreamImage PrependMagickMethod(StreamImage)
+#define StringInfoToHexString PrependMagickMethod(StringInfoToHexString)
+#define StringInfoToString PrependMagickMethod(StringInfoToString)
+#define StringToArgv PrependMagickMethod(StringToArgv)
+#define StringToDouble PrependMagickMethod(StringToDouble)
+#define StringToken PrependMagickMethod(StringToken)
+#define StringToList PrependMagickMethod(StringToList)
+#define StringToStringInfo PrependMagickMethod(StringToStringInfo)
+#define StripImage PrependMagickMethod(StripImage)
+#define Strip PrependMagickMethod(Strip)
+#define StripString PrependMagickMethod(StripString)
+#define SubstituteString PrependMagickMethod(SubstituteString)
+#define SwirlImage PrependMagickMethod(SwirlImage)
+#define SyncAuthenticPixelCacheNexus PrependMagickMethod(SyncAuthenticPixelCacheNexus)
+#define SyncAuthenticPixels PrependMagickMethod(SyncAuthenticPixels)
+#define SyncCacheViewAuthenticPixels PrependMagickMethod(SyncCacheViewAuthenticPixels)
+#define SyncCacheViewPixels PrependMagickMethod(SyncCacheViewPixels)
+#define SyncCacheView PrependMagickMethod(SyncCacheView)
+#define SyncImageList PrependMagickMethod(SyncImageList)
+#define SyncImagePixels PrependMagickMethod(SyncImagePixels)
+#define SyncImage PrependMagickMethod(SyncImage)
+#define SyncImageProfiles PrependMagickMethod(SyncImageProfiles)
+#define SyncImageSettings PrependMagickMethod(SyncImageSettings)
+#define SyncImagesSettings PrependMagickMethod(SyncImagesSettings)
+#define SyncNextImageInList PrependMagickMethod(SyncNextImageInList)
+#define SystemCommand PrependMagickMethod(SystemCommand)
+#define TellBlob PrependMagickMethod(TellBlob)
+#define TemporaryFilename PrependMagickMethod(TemporaryFilename)
+#define TextureImage PrependMagickMethod(TextureImage)
+#define ThresholdImageChannel PrependMagickMethod(ThresholdImageChannel)
+#define ThresholdImage PrependMagickMethod(ThresholdImage)
+#define ThrowException PrependMagickMethod(ThrowException)
+#define ThrowMagickExceptionList PrependMagickMethod(ThrowMagickExceptionList)
+#define ThrowMagickException PrependMagickMethod(ThrowMagickException)
+#define ThumbnailImage PrependMagickMethod(ThumbnailImage)
+#define TintImage PrependMagickMethod(TintImage)
+#define Tokenizer PrependMagickMethod(Tokenizer)
+#define TransformColorspace PrependMagickMethod(TransformColorspace)
+#define TransformHSL PrependMagickMethod(TransformHSL)
+#define TransformImageColorspace PrependMagickMethod(TransformImageColorspace)
+#define TransformImage PrependMagickMethod(TransformImage)
+#define TransformImages PrependMagickMethod(TransformImages)
+#define TransformRGBImage PrependMagickMethod(TransformRGBImage)
+#define TranslateText PrependMagickMethod(TranslateText)
+#define TransparentImage PrependMagickMethod(TransparentImage)
+#define TransparentPaintImageChroma PrependMagickMethod(TransparentPaintImageChroma)
+#define TransparentPaintImage PrependMagickMethod(TransparentPaintImage)
+#define TransposeImage PrependMagickMethod(TransposeImage)
+#define TransverseImage PrependMagickMethod(TransverseImage)
+#define TrimImage PrependMagickMethod(TrimImage)
+#define UniqueImageColors PrependMagickMethod(UniqueImageColors)
+#define UnlockSemaphoreInfo PrependMagickMethod(UnlockSemaphoreInfo)
+#define UnmapBlob PrependMagickMethod(UnmapBlob)
+#define UnregisterARTImage PrependMagickMethod(UnregisterARTImage)
+#define UnregisterAVIImage PrependMagickMethod(UnregisterAVIImage)
+#define UnregisterAVSImage PrependMagickMethod(UnregisterAVSImage)
+#define UnregisterBMPImage PrependMagickMethod(UnregisterBMPImage)
+#define UnregisterBRAILLEImage PrependMagickMethod(UnregisterBRAILLEImage)
+#define UnregisterCALSImage PrependMagickMethod(UnregisterCALSImage)
+#define UnregisterCAPTIONImage PrependMagickMethod(UnregisterCAPTIONImage)
+#define UnregisterCINImage PrependMagickMethod(UnregisterCINImage)
+#define UnregisterCIPImage PrependMagickMethod(UnregisterCIPImage)
+#define UnregisterCLIPImage PrependMagickMethod(UnregisterCLIPImage)
+#define UnregisterCMYKImage PrependMagickMethod(UnregisterCMYKImage)
+#define UnregisterCUTImage PrependMagickMethod(UnregisterCUTImage)
+#define UnregisterDCMImage PrependMagickMethod(UnregisterDCMImage)
+#define UnregisterDDSImage PrependMagickMethod(UnregisterDDSImage)
+#define UnregisterDIBImage PrependMagickMethod(UnregisterDIBImage)
+#define UnregisterDJVUImage PrependMagickMethod(UnregisterDJVUImage)
+#define UnregisterDNGImage PrependMagickMethod(UnregisterDNGImage)
+#define UnregisterDOTImage PrependMagickMethod(UnregisterDOTImage)
+#define UnregisterDPSImage PrependMagickMethod(UnregisterDPSImage)
+#define UnregisterDPXImage PrependMagickMethod(UnregisterDPXImage)
+#define UnregisterEPTImage PrependMagickMethod(UnregisterEPTImage)
+#define UnregisterFAXImage PrependMagickMethod(UnregisterFAXImage)
+#define UnregisterFITSImage PrependMagickMethod(UnregisterFITSImage)
+#define UnregisterGIFImage PrependMagickMethod(UnregisterGIFImage)
+#define UnregisterGRADIENTImage PrependMagickMethod(UnregisterGRADIENTImage)
+#define UnregisterGRAYImage PrependMagickMethod(UnregisterGRAYImage)
+#define UnregisterHALDImage PrependMagickMethod(UnregisterHALDImage)
+#define UnregisterHISTOGRAMImage PrependMagickMethod(UnregisterHISTOGRAMImage)
+#define UnregisterHRZImage PrependMagickMethod(UnregisterHRZImage)
+#define UnregisterHTMLImage PrependMagickMethod(UnregisterHTMLImage)
+#define UnregisterICONImage PrependMagickMethod(UnregisterICONImage)
+#define UnregisterINFOImage PrependMagickMethod(UnregisterINFOImage)
+#define UnregisterINLINEImage PrependMagickMethod(UnregisterINLINEImage)
+#define UnregisterIPLImage PrependMagickMethod(UnregisterIPLImage)
+#define UnregisterJP2Image PrependMagickMethod(UnregisterJP2Image)
+#define UnregisterJPEGImage PrependMagickMethod(UnregisterJPEGImage)
+#define UnregisterLABELImage PrependMagickMethod(UnregisterLABELImage)
+#define UnregisterMAGICKImage PrependMagickMethod(UnregisterMAGICKImage)
+#define UnregisterMagickInfo PrependMagickMethod(UnregisterMagickInfo)
+#define UnregisterMAPImage PrependMagickMethod(UnregisterMAPImage)
+#define UnregisterMATImage PrependMagickMethod(UnregisterMATImage)
+#define UnregisterMATTEImage PrependMagickMethod(UnregisterMATTEImage)
+#define UnregisterMETAImage PrependMagickMethod(UnregisterMETAImage)
+#define UnregisterMIFFImage PrependMagickMethod(UnregisterMIFFImage)
+#define UnregisterMONOImage PrependMagickMethod(UnregisterMONOImage)
+#define UnregisterMPCImage PrependMagickMethod(UnregisterMPCImage)
+#define UnregisterMPEGImage PrependMagickMethod(UnregisterMPEGImage)
+#define UnregisterMPRImage PrependMagickMethod(UnregisterMPRImage)
+#define UnregisterMSLImage PrependMagickMethod(UnregisterMSLImage)
+#define UnregisterMTVImage PrependMagickMethod(UnregisterMTVImage)
+#define UnregisterMVGImage PrependMagickMethod(UnregisterMVGImage)
+#define UnregisterNULLImage PrependMagickMethod(UnregisterNULLImage)
+#define UnregisterOTBImage PrependMagickMethod(UnregisterOTBImage)
+#define UnregisterPALMImage PrependMagickMethod(UnregisterPALMImage)
+#define UnregisterPATTERNImage PrependMagickMethod(UnregisterPATTERNImage)
+#define UnregisterPCDImage PrependMagickMethod(UnregisterPCDImage)
+#define UnregisterPCLImage PrependMagickMethod(UnregisterPCLImage)
+#define UnregisterPCXImage PrependMagickMethod(UnregisterPCXImage)
+#define UnregisterPDBImage PrependMagickMethod(UnregisterPDBImage)
+#define UnregisterPDFImage PrependMagickMethod(UnregisterPDFImage)
+#define UnregisterPICTImage PrependMagickMethod(UnregisterPICTImage)
+#define UnregisterPIXImage PrependMagickMethod(UnregisterPIXImage)
+#define UnregisterPLASMAImage PrependMagickMethod(UnregisterPLASMAImage)
+#define UnregisterPNGImage PrependMagickMethod(UnregisterPNGImage)
+#define UnregisterPNMImage PrependMagickMethod(UnregisterPNMImage)
+#define UnregisterPREVIEWImage PrependMagickMethod(UnregisterPREVIEWImage)
+#define UnregisterPS2Image PrependMagickMethod(UnregisterPS2Image)
+#define UnregisterPS3Image PrependMagickMethod(UnregisterPS3Image)
+#define UnregisterPSDImage PrependMagickMethod(UnregisterPSDImage)
+#define UnregisterPSImage PrependMagickMethod(UnregisterPSImage)
+#define UnregisterPWPImage PrependMagickMethod(UnregisterPWPImage)
+#define UnregisterRAWImage PrependMagickMethod(UnregisterRAWImage)
+#define UnregisterRGBImage PrependMagickMethod(UnregisterRGBImage)
+#define UnregisterRLAImage PrependMagickMethod(UnregisterRLAImage)
+#define UnregisterRLEImage PrependMagickMethod(UnregisterRLEImage)
+#define UnregisterSCRImage PrependMagickMethod(UnregisterSCRImage)
+#define UnregisterSCTImage PrependMagickMethod(UnregisterSCTImage)
+#define UnregisterSFWImage PrependMagickMethod(UnregisterSFWImage)
+#define UnregisterSGIImage PrependMagickMethod(UnregisterSGIImage)
+#define UnregisterStaticModules PrependMagickMethod(UnregisterStaticModules)
+#define UnregisterSTEGANOImage PrependMagickMethod(UnregisterSTEGANOImage)
+#define UnregisterSUNImage PrependMagickMethod(UnregisterSUNImage)
+#define UnregisterSVGImage PrependMagickMethod(UnregisterSVGImage)
+#define UnregisterTGAImage PrependMagickMethod(UnregisterTGAImage)
+#define UnregisterTHUMBNAILImage PrependMagickMethod(UnregisterTHUMBNAILImage)
+#define UnregisterTIFFImage PrependMagickMethod(UnregisterTIFFImage)
+#define UnregisterTILEImage PrependMagickMethod(UnregisterTILEImage)
+#define UnregisterTIMImage PrependMagickMethod(UnregisterTIMImage)
+#define UnregisterTTFImage PrependMagickMethod(UnregisterTTFImage)
+#define UnregisterTXTImage PrependMagickMethod(UnregisterTXTImage)
+#define UnregisterUILImage PrependMagickMethod(UnregisterUILImage)
+#define UnregisterURLImage PrependMagickMethod(UnregisterURLImage)
+#define UnregisterUYVYImage PrependMagickMethod(UnregisterUYVYImage)
+#define UnregisterVICARImage PrependMagickMethod(UnregisterVICARImage)
+#define UnregisterVIDImage PrependMagickMethod(UnregisterVIDImage)
+#define UnregisterVIFFImage PrependMagickMethod(UnregisterVIFFImage)
+#define UnregisterWBMPImage PrependMagickMethod(UnregisterWBMPImage)
+#define UnregisterWPGImage PrependMagickMethod(UnregisterWPGImage)
+#define UnregisterXBMImage PrependMagickMethod(UnregisterXBMImage)
+#define UnregisterXCFImage PrependMagickMethod(UnregisterXCFImage)
+#define UnregisterXCImage PrependMagickMethod(UnregisterXCImage)
+#define UnregisterXImage PrependMagickMethod(UnregisterXImage)
+#define UnregisterXPMImage PrependMagickMethod(UnregisterXPMImage)
+#define UnregisterXPSImage PrependMagickMethod(UnregisterXPSImage)
+#define UnregisterXWDImage PrependMagickMethod(UnregisterXWDImage)
+#define UnregisterYCBCRImage PrependMagickMethod(UnregisterYCBCRImage)
+#define UnregisterYUVImage PrependMagickMethod(UnregisterYUVImage)
+#define UnsharpMaskImageChannel PrependMagickMethod(UnsharpMaskImageChannel)
+#define UnsharpMaskImage PrependMagickMethod(UnsharpMaskImage)
+#define UnshiftImageList PrependMagickMethod(UnshiftImageList)
+#define UpdateSignature PrependMagickMethod(UpdateSignature)
+#define ValidateColormapIndex PrependMagickMethod(ValidateColormapIndex)
+#define VignetteImage PrependMagickMethod(VignetteImage)
+#define WaveImage PrependMagickMethod(WaveImage)
+#define WhiteThresholdImageChannel PrependMagickMethod(WhiteThresholdImageChannel)
+#define WhiteThresholdImage PrependMagickMethod(WhiteThresholdImage)
+#define WriteBlobByte PrependMagickMethod(WriteBlobByte)
+#define WriteBlobFloat PrependMagickMethod(WriteBlobFloat)
+#define WriteBlobLong PrependMagickMethod(WriteBlobLong)
+#define WriteBlobLSBLong PrependMagickMethod(WriteBlobLSBLong)
+#define WriteBlobLSBShort PrependMagickMethod(WriteBlobLSBShort)
+#define WriteBlobMSBLong PrependMagickMethod(WriteBlobMSBLong)
+#define WriteBlobMSBShort PrependMagickMethod(WriteBlobMSBShort)
+#define WriteBlob PrependMagickMethod(WriteBlob)
+#define WriteBlobShort PrependMagickMethod(WriteBlobShort)
+#define WriteBlobString PrependMagickMethod(WriteBlobString)
+#define WriteImage PrependMagickMethod(WriteImage)
+#define WriteImages PrependMagickMethod(WriteImages)
+#define WriteStream PrependMagickMethod(WriteStream)
+#define XAnimateBackgroundImage PrependMagickMethod(XAnimateBackgroundImage)
+#define XAnimateImages PrependMagickMethod(XAnimateImages)
+#define XAnnotateImage PrependMagickMethod(XAnnotateImage)
+#define XBestFont PrependMagickMethod(XBestFont)
+#define XBestIconSize PrependMagickMethod(XBestIconSize)
+#define XBestPixel PrependMagickMethod(XBestPixel)
+#define XBestVisualInfo PrependMagickMethod(XBestVisualInfo)
+#define XCheckDefineCursor PrependMagickMethod(XCheckDefineCursor)
+#define XCheckRefreshWindows PrependMagickMethod(XCheckRefreshWindows)
+#define XClientMessage PrependMagickMethod(XClientMessage)
+#define XColorBrowserWidget PrependMagickMethod(XColorBrowserWidget)
+#define XCommandWidget PrependMagickMethod(XCommandWidget)
+#define XConfigureImageColormap PrependMagickMethod(XConfigureImageColormap)
+#define XConfirmWidget PrependMagickMethod(XConfirmWidget)
+#define XConstrainWindowPosition PrependMagickMethod(XConstrainWindowPosition)
+#define XDelay PrependMagickMethod(XDelay)
+#define XDestroyResourceInfo PrependMagickMethod(XDestroyResourceInfo)
+#define XDestroyWindowColors PrependMagickMethod(XDestroyWindowColors)
+#define XDialogWidget PrependMagickMethod(XDialogWidget)
+#define XDisplayBackgroundImage PrependMagickMethod(XDisplayBackgroundImage)
+#define XDisplayImageInfo PrependMagickMethod(XDisplayImageInfo)
+#define XDisplayImage PrependMagickMethod(XDisplayImage)
+#define XDrawImage PrependMagickMethod(XDrawImage)
+#define XError PrependMagickMethod(XError)
+#define XFileBrowserWidget PrependMagickMethod(XFileBrowserWidget)
+#define XFontBrowserWidget PrependMagickMethod(XFontBrowserWidget)
+#define XFreeResources PrependMagickMethod(XFreeResources)
+#define XFreeStandardColormap PrependMagickMethod(XFreeStandardColormap)
+#define XGetAnnotateInfo PrependMagickMethod(XGetAnnotateInfo)
+#define XGetImportInfo PrependMagickMethod(XGetImportInfo)
+#define XGetMapInfo PrependMagickMethod(XGetMapInfo)
+#define XGetPixelPacket PrependMagickMethod(XGetPixelPacket)
+#define XGetResourceClass PrependMagickMethod(XGetResourceClass)
+#define XGetResourceDatabase PrependMagickMethod(XGetResourceDatabase)
+#define XGetResourceInfo PrependMagickMethod(XGetResourceInfo)
+#define XGetResourceInstance PrependMagickMethod(XGetResourceInstance)
+#define XGetScreenDensity PrependMagickMethod(XGetScreenDensity)
+#define XGetWindowColor PrependMagickMethod(XGetWindowColor)
+#define XGetWindowInfo PrependMagickMethod(XGetWindowInfo)
+#define XHighlightEllipse PrependMagickMethod(XHighlightEllipse)
+#define XHighlightLine PrependMagickMethod(XHighlightLine)
+#define XHighlightRectangle PrependMagickMethod(XHighlightRectangle)
+#define XImportImage PrependMagickMethod(XImportImage)
+#define XInfoWidget PrependMagickMethod(XInfoWidget)
+#define XInitializeWindows PrependMagickMethod(XInitializeWindows)
+#define XListBrowserWidget PrependMagickMethod(XListBrowserWidget)
+#define XMagickProgressMonitor PrependMagickMethod(XMagickProgressMonitor)
+#define XMakeCursor PrependMagickMethod(XMakeCursor)
+#define XMakeImage PrependMagickMethod(XMakeImage)
+#define XMakeMagnifyImage PrependMagickMethod(XMakeMagnifyImage)
+#define XMakeStandardColormap PrependMagickMethod(XMakeStandardColormap)
+#define XMakeWindow PrependMagickMethod(XMakeWindow)
+#define XMenuWidget PrependMagickMethod(XMenuWidget)
+#define XMLTreeInfoToXML PrependMagickMethod(XMLTreeInfoToXML)
+#define XNoticeWidget PrependMagickMethod(XNoticeWidget)
+#define XPreferencesWidget PrependMagickMethod(XPreferencesWidget)
+#define XProgressMonitorWidget PrependMagickMethod(XProgressMonitorWidget)
+#define XQueryColorDatabase PrependMagickMethod(XQueryColorDatabase)
+#define XQueryPosition PrependMagickMethod(XQueryPosition)
+#define XRefreshWindow PrependMagickMethod(XRefreshWindow)
+#define XRemoteCommand PrependMagickMethod(XRemoteCommand)
+#define XRetainWindowColors PrependMagickMethod(XRetainWindowColors)
+#define XSetCursorState PrependMagickMethod(XSetCursorState)
+#define XSetWindows PrependMagickMethod(XSetWindows)
+#define XTextViewWidget PrependMagickMethod(XTextViewWidget)
+#define XUserPreferences PrependMagickMethod(XUserPreferences)
+#define XWarning PrependMagickMethod(XWarning)
+#define XWindowByID PrependMagickMethod(XWindowByID)
+#define XWindowByName PrependMagickMethod(XWindowByName)
+#define XWindowByProperty PrependMagickMethod(XWindowByProperty)
+#define ZLIBEncodeImage PrependMagickMethod(ZLIBEncodeImage)
+#define ZoomImage PrependMagickMethod(ZoomImage)
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/mime-private.h b/magick/mime-private.h
new file mode 100644
index 0000000..1ea2c47
--- /dev/null
+++ b/magick/mime-private.h
@@ -0,0 +1,38 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ The ImageMagick mime private methods.
+*/
+#ifndef _MAGICK_MIME_PRIVATE_H
+#define _MAGICK_MIME_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedData,
+ StringData,
+ ByteData,
+ ShortData,
+ LongData
+} DataType;
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/mime.c b/magick/mime.c
new file mode 100644
index 0000000..c00be35
--- /dev/null
+++ b/magick/mime.c
@@ -0,0 +1,1080 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% M M IIIII M M EEEEE %
+% MM MM I MM MM E %
+% M M M I M M M EEE %
+% M M I M M E %
+% M M IIIII M M EEEEE %
+% %
+% %
+% MagickCore Mime Methods %
+% %
+% Software Design %
+% July 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.imagemagick.org/MagicksToolkit/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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/memory_.h"
+#include "magick/mime.h"
+#include "magick/mime-private.h"
+#include "magick/option.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define MimeFilename "mime.xml"
+
+/*
+ Typedef declaration.
+*/
+struct _MimeInfo
+{
+ char
+ *path,
+ *type,
+ *description,
+ *pattern;
+
+ long
+ priority;
+
+ MagickOffsetType
+ offset;
+
+ size_t
+ extent;
+
+ DataType
+ data_type;
+
+ long
+ mask,
+ value;
+
+ EndianType
+ endian;
+
+ size_t
+ length;
+
+ unsigned char
+ *magic;
+
+ MagickBooleanType
+ stealth;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Static declarations.
+*/
+static const char
+ *MimeMap = (char *)
+ "<?xml version=\"1.0\"?>"
+ "<mimemap>"
+ "</mimemap>";
+
+static LinkedListInfo
+ *mime_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *mime_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_mime = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeMimeList(ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M i m e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMimeList() deallocates memory associated with the mime list.
+%
+% The format of the DestroyMimeList method is:
+%
+% DestroyMimeList(void)
+%
+*/
+
+static void *DestroyMimeElement(void *mime_info)
+{
+ register MimeInfo
+ *p;
+
+ p=(MimeInfo *) mime_info;
+ if (p->magic != (unsigned char *) NULL)
+ p->magic=(unsigned char *) RelinquishMagickMemory(p->magic);
+ if (p->pattern != (char *) NULL)
+ p->pattern=DestroyString(p->pattern);
+ if (p->description != (char *) NULL)
+ p->description=DestroyString(p->description);
+ if (p->type != (char *) NULL)
+ p->type=DestroyString(p->type);
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ p=(MimeInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyMimeList(void)
+{
+ AcquireSemaphoreInfo(&mime_semaphore);
+ if (mime_list != (LinkedListInfo *) NULL)
+ mime_list=DestroyLinkedList(mime_list,DestroyMimeElement);
+ instantiate_mime=MagickFalse;
+ RelinquishSemaphoreInfo(mime_semaphore);
+ DestroySemaphoreInfo(&mime_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M i m e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMimeInfo() attempts to classify the content to identify which mime type
+% is associated with the content, if any.
+%
+% The format of the GetMimeInfo method is:
+%
+% const MimeInfo *GetMimeInfo(const char *filename,
+% const unsigned char *magic,const size_t length,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: If we cannot not classify the string, we attempt to classify
+% based on the filename (e.g. *.pdf returns application/pdf).
+%
+% o magic: A binary string generally representing the first few characters
+% of the image file or blob.
+%
+% o length: the length of the binary signature.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const MimeInfo *GetMimeInfo(const char *filename,
+ const unsigned char *magic,const size_t length,ExceptionInfo *exception)
+{
+ const MimeInfo
+ *mime_info;
+
+ EndianType
+ endian;
+
+ long
+ value;
+
+ register const MimeInfo
+ *p;
+
+ register const unsigned char
+ *q;
+
+ register long
+ i;
+
+ unsigned long
+ lsb_first;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((mime_list == (LinkedListInfo *) NULL) ||
+ (instantiate_mime == MagickFalse))
+ if (InitializeMimeList(exception) == MagickFalse)
+ return((const MimeInfo *) NULL);
+ if ((mime_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(mime_list) != MagickFalse))
+ return((const MimeInfo *) NULL);
+ if ((magic == (const unsigned char *) NULL) || (length == 0))
+ return((const MimeInfo *) GetValueFromLinkedList(mime_list,0));
+ if (length == 0)
+ return((const MimeInfo *) NULL);
+ /*
+ Search for mime tag.
+ */
+ mime_info=(const MimeInfo *) NULL;
+ lsb_first=1;
+ AcquireSemaphoreInfo(&mime_semaphore);
+ ResetLinkedListIterator(mime_list);
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ while (p != (const MimeInfo *) NULL)
+ {
+ assert(p->offset >= 0);
+ if (mime_info != (const MimeInfo *) NULL)
+ if (p->priority > mime_info->priority)
+ {
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ continue;
+ }
+ if ((p->pattern != (char *) NULL) && (filename != (char *) NULL))
+ {
+ if (GlobExpression(filename,p->pattern,MagickFalse) != MagickFalse)
+ mime_info=p;
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ continue;
+ }
+ switch (p->data_type)
+ {
+ case ByteData:
+ {
+ if ((size_t) (p->offset+4) > length)
+ break;
+ q=magic+p->offset;
+ value=(*q++);
+ if (p->mask == 0)
+ {
+ if (p->value == value)
+ mime_info=p;
+ }
+ else
+ {
+ if ((p->value & p->mask) == value)
+ mime_info=p;
+ }
+ break;
+ }
+ case ShortData:
+ {
+ if ((size_t) (p->offset+4) > length)
+ break;
+ q=magic+p->offset;
+ endian=p->endian;
+ if (p->endian == UndefinedEndian)
+ endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
+ if (endian == LSBEndian)
+ {
+ value=(*q++);
+ value|=(*q++) << 8;
+ }
+ else
+ {
+ value=(*q++) << 8;
+ value|=(*q++);
+ }
+ if (p->mask == 0)
+ {
+ if (p->value == value)
+ mime_info=p;
+ }
+ else
+ {
+ if ((p->value & p->mask) == value)
+ mime_info=p;
+ }
+ break;
+ }
+ case LongData:
+ {
+ if ((size_t) (p->offset+4) > length)
+ break;
+ q=magic+p->offset;
+ endian=p->endian;
+ if (p->endian == UndefinedEndian)
+ endian=(*(char *) &lsb_first) == 1 ? LSBEndian : MSBEndian;
+ if (endian == LSBEndian)
+ {
+ value=(*q++);
+ value|=(*q++) << 8;
+ value|=(*q++) << 16;
+ value|=(*q++) << 24;
+ }
+ else
+ {
+ value=(*q++) << 24;
+ value|=(*q++) << 16;
+ value|=(*q++) << 8;
+ value|=(*q++);
+ }
+ if (p->mask == 0)
+ {
+ if (p->value == value)
+ mime_info=p;
+ }
+ else
+ {
+ if ((p->value & p->mask) == value)
+ mime_info=p;
+ }
+ break;
+ }
+ case StringData:
+ default:
+ {
+ for (i=0; i <= (long) p->extent; i++)
+ {
+ if ((size_t) (p->offset+i+p->length) > length)
+ break;
+ if (memcmp(magic+p->offset+i,p->magic,p->length) == 0)
+ {
+ mime_info=p;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ }
+ if (p != (const MimeInfo *) NULL)
+ (void) InsertValueInLinkedList(mime_list,0,
+ RemoveElementByValueFromLinkedList(mime_list,p));
+ RelinquishSemaphoreInfo(mime_semaphore);
+ return(mime_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M i m e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMimeInfoList() returns any image aliases that match the specified
+% pattern.
+%
+% The magic of the GetMimeInfoList function is:
+%
+% const MimeInfo **GetMimeInfoList(const char *pattern,
+% unsigned long *number_aliases,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_aliases: This integer returns the number of magics in the
+% list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MimeInfoCompare(const void *x,const void *y)
+{
+ const MimeInfo
+ **p,
+ **q;
+
+ p=(const MimeInfo **) x,
+ q=(const MimeInfo **) y;
+ if (strcasecmp((*p)->path,(*q)->path) == 0)
+ return(strcasecmp((*p)->type,(*q)->type));
+ return(strcasecmp((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const MimeInfo **GetMimeInfoList(const char *pattern,
+ unsigned long *number_aliases,ExceptionInfo *exception)
+{
+ const MimeInfo
+ **aliases;
+
+ register const MimeInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate mime list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_aliases != (unsigned long *) NULL);
+ *number_aliases=0;
+ p=GetMimeInfo((char *) NULL,(unsigned char *) "*",0,exception);
+ if (p == (const MimeInfo *) NULL)
+ return((const MimeInfo **) NULL);
+ aliases=(const MimeInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(mime_list)+1UL,sizeof(*aliases));
+ if (aliases == (const MimeInfo **) NULL)
+ return((const MimeInfo **) NULL);
+ /*
+ Generate mime list.
+ */
+ AcquireSemaphoreInfo(&mime_semaphore);
+ ResetLinkedListIterator(mime_list);
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ for (i=0; p != (const MimeInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->type,pattern,MagickFalse) != MagickFalse))
+ aliases[i++]=p;
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ }
+ RelinquishSemaphoreInfo(mime_semaphore);
+ qsort((void *) aliases,(size_t) i,sizeof(*aliases),MimeInfoCompare);
+ aliases[i]=(MimeInfo *) NULL;
+ *number_aliases=(unsigned long) i;
+ return(aliases);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M i m e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMimeList() returns any image format alias that matches the specified
+% pattern.
+%
+% The format of the GetMimeList function is:
+%
+% char **GetMimeList(const char *pattern,unsigned long *number_aliases,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_aliases: This integer returns the number of image format aliases
+% in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int MimeCompare(const void *x,const void *y)
+{
+ register char
+ *p,
+ *q;
+
+ p=(char *) x;
+ q=(char *) y;
+ return(strcasecmp(p,q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetMimeList(const char *pattern,
+ unsigned long *number_aliases,ExceptionInfo *exception)
+{
+ char
+ **aliases;
+
+ register const MimeInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate configure list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_aliases != (unsigned long *) NULL);
+ *number_aliases=0;
+ p=GetMimeInfo((char *) NULL,(unsigned char *) "*",0,exception);
+ if (p == (const MimeInfo *) NULL)
+ return((char **) NULL);
+ aliases=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(mime_list)+1UL,sizeof(*aliases));
+ if (aliases == (char **) NULL)
+ return((char **) NULL);
+ AcquireSemaphoreInfo(&mime_semaphore);
+ ResetLinkedListIterator(mime_list);
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ for (i=0; p != (const MimeInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->type,pattern,MagickFalse) != MagickFalse))
+ aliases[i++]=ConstantString(p->type);
+ p=(const MimeInfo *) GetNextValueInLinkedList(mime_list);
+ }
+ RelinquishSemaphoreInfo(mime_semaphore);
+ qsort((void *) aliases,(size_t) i,sizeof(*aliases),MimeCompare);
+ aliases[i]=(char *) NULL;
+ *number_aliases=(unsigned long) i;
+ return(aliases);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M i m e D e s c r i p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMimeDescription() returns the mime type description.
+%
+% The format of the GetMimeDescription method is:
+%
+% const char *GetMimeDescription(const MimeInfo *mime_info)
+%
+% A description of each parameter follows:
+%
+% o mime_info: The magic info.
+%
+*/
+MagickExport const char *GetMimeDescription(const MimeInfo *mime_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(mime_info != (MimeInfo *) NULL);
+ assert(mime_info->signature == MagickSignature);
+ return(mime_info->description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M i m e T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMimeType() returns the mime type.
+%
+% The format of the GetMimeType method is:
+%
+% const char *GetMimeType(const MimeInfo *mime_info)
+%
+% A description of each parameter follows:
+%
+% o mime_info: The magic info.
+%
+*/
+MagickExport const char *GetMimeType(const MimeInfo *mime_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(mime_info != (MimeInfo *) NULL);
+ assert(mime_info->signature == MagickSignature);
+ return(mime_info->type);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e M i m e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeMimeList() initializes the mime list.
+%
+% The format of the InitializeMimeList method is:
+%
+% MagickBooleanType InitializeMimeList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializeMimeList(ExceptionInfo *exception)
+{
+ if ((mime_list == (LinkedListInfo *) NULL) &&
+ (instantiate_mime == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&mime_semaphore);
+ if ((mime_list == (LinkedListInfo *) NULL) &&
+ (instantiate_mime == MagickFalse))
+ {
+ (void) LoadMimeLists(MimeFilename,exception);
+ instantiate_mime=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(mime_semaphore);
+ }
+ return(mime_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t M i m e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListMimeInfo() lists the magic info to a file.
+%
+% The format of the ListMimeInfo method is:
+%
+% MagickBooleanType ListMimeInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListMimeInfo(FILE *file,ExceptionInfo *exception)
+{
+ const char
+ *path;
+
+ const MimeInfo
+ **mime_info;
+
+ long
+ j;
+
+ register long
+ i;
+
+ unsigned long
+ number_aliases;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ mime_info=GetMimeInfoList("*",&number_aliases,exception);
+ if (mime_info == (const MimeInfo **) NULL)
+ return(MagickFalse);
+ j=0;
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_aliases; i++)
+ {
+ if (mime_info[i]->stealth != MagickFalse)
+ continue;
+ if ((path == (const char *) NULL) ||
+ (strcasecmp(path,mime_info[i]->path) != 0))
+ {
+ if (mime_info[i]->path != (char *) NULL)
+ (void) fprintf(file,"\nPath: %s\n\n",mime_info[i]->path);
+ (void) fprintf(file,"Type Description\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ }
+ path=mime_info[i]->path;
+ (void) fprintf(file,"%s",mime_info[i]->type);
+ if (strlen(mime_info[i]->type) <= 25)
+ {
+ for (j=(long) strlen(mime_info[i]->type); j <= 27; j++)
+ (void) fprintf(file," ");
+ }
+ else
+ {
+ (void) fprintf(file,"\n");
+ for (j=0; j <= 27; j++)
+ (void) fprintf(file," ");
+ }
+ if (mime_info[i]->description != (char *) NULL)
+ (void) fprintf(file,"%s",mime_info[i]->description);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ mime_info=(const MimeInfo **) RelinquishMagickMemory((void *) mime_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d M i m e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadMimeList() loads the magic configuration file which provides a mapping
+% between magic attributes and a magic name.
+%
+% The format of the LoadMimeList method is:
+%
+% MagickBooleanType LoadMimeList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The mime list in XML format.
+%
+% o filename: The mime list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadMimeList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ const char
+ *attribute;
+
+ MimeInfo
+ *mime_info = (MimeInfo *) NULL;
+
+ MagickBooleanType
+ status;
+
+ XMLTreeInfo
+ *mime,
+ *mime_map,
+ *include;
+
+ /*
+ Load the mime map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading mime map \"%s\" ...",filename);
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (mime_list == (LinkedListInfo *) NULL)
+ {
+ mime_list=NewLinkedList(0);
+ if (mime_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ mime_map=NewXMLTree(xml,exception);
+ if (mime_map == (XMLTreeInfo *) NULL)
+ return(MagickFalse);
+ status=MagickTrue;
+ include=GetXMLTreeChild(mime_map,"include");
+ while (include != (XMLTreeInfo *) NULL)
+ {
+ /*
+ Process include element.
+ */
+ attribute=GetXMLTreeAttribute(include,"file");
+ if (attribute != (const char *) NULL)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",filename);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*attribute == *DirectorySeparator)
+ (void) CopyMagickString(path,attribute,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,attribute,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadMimeList(xml,path,depth+1,exception);
+ xml=DestroyString(xml);
+ }
+ }
+ }
+ include=GetNextXMLTreeTag(include);
+ }
+ mime=GetXMLTreeChild(mime_map,"mime");
+ while (mime != (XMLTreeInfo *) NULL)
+ {
+ const char
+ *attribute;
+
+ /*
+ Process mime element.
+ */
+ mime_info=(MimeInfo *) AcquireMagickMemory(sizeof(*mime_info));
+ if (mime_info == (MimeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(mime_info,0,sizeof(*mime_info));
+ mime_info->path=ConstantString(filename);
+ mime_info->signature=MagickSignature;
+ attribute=GetXMLTreeAttribute(mime,"data-type");
+ if (attribute != (const char *) NULL)
+ mime_info->data_type=(DataType) ParseMagickOption(MagickDataTypeOptions,
+ MagickTrue,attribute);
+ attribute=GetXMLTreeAttribute(mime,"description");
+ if (attribute != (const char *) NULL)
+ mime_info->description=ConstantString(attribute);
+ attribute=GetXMLTreeAttribute(mime,"endian");
+ if (attribute != (const char *) NULL)
+ mime_info->endian=(EndianType) ParseMagickOption(MagickEndianOptions,
+ MagickTrue,attribute);
+ attribute=GetXMLTreeAttribute(mime,"magic");
+ if (attribute != (const char *) NULL)
+ {
+ char
+ *token;
+
+ const char
+ *p;
+
+ register unsigned char
+ *q;
+
+ token=AcquireString(attribute);
+ (void) SubstituteString((char **) &token,"<","<");
+ (void) SubstituteString((char **) &token,"&","&");
+ (void) SubstituteString((char **) &token,""","\"");
+ mime_info->magic=(unsigned char *) AcquireString(token);
+ q=mime_info->magic;
+ for (p=token; *p != '\0'; )
+ {
+ if (*p == '\\')
+ {
+ p++;
+ if (isdigit((int) ((unsigned char) *p)) != 0)
+ {
+ char
+ *end;
+
+ *q++=(unsigned char) strtol(p,&end,8);
+ p+=(end-p);
+ mime_info->length++;
+ continue;
+ }
+ switch (*p)
+ {
+ case 'b': *q='\b'; break;
+ case 'f': *q='\f'; break;
+ case 'n': *q='\n'; break;
+ case 'r': *q='\r'; break;
+ case 't': *q='\t'; break;
+ case 'v': *q='\v'; break;
+ case 'a': *q='a'; break;
+ case '?': *q='\?'; break;
+ default: *q=(unsigned char) (*p); break;
+ }
+ p++;
+ q++;
+ mime_info->length++;
+ continue;
+ }
+ *q++=(unsigned char) (*p++);
+ mime_info->length++;
+ }
+ token=DestroyString(token);
+ if (mime_info->data_type != StringData)
+ mime_info->value=strtol((char *) mime_info->magic,(char **) NULL,0);
+ }
+ attribute=GetXMLTreeAttribute(mime,"mask");
+ if (attribute != (const char *) NULL)
+ mime_info->mask=strtol(attribute,(char **) NULL,0);
+ attribute=GetXMLTreeAttribute(mime,"offset");
+ if (attribute != (const char *) NULL)
+ {
+ char
+ *c;
+
+ mime_info->offset=(MagickOffsetType) strtol(attribute,&c,0);
+ if (*c == ':')
+ mime_info->extent=(size_t) strtol(c+1,(char **) NULL,0);
+ }
+ attribute=GetXMLTreeAttribute(mime,"pattern");
+ if (attribute != (const char *) NULL)
+ mime_info->pattern=ConstantString(attribute);
+ attribute=GetXMLTreeAttribute(mime,"priority");
+ if (attribute != (const char *) NULL)
+ mime_info->priority=strtol(attribute,(char **) NULL,0);
+ attribute=GetXMLTreeAttribute(mime,"stealth");
+ if (attribute != (const char *) NULL)
+ mime_info->stealth=IsMagickTrue(attribute);
+ attribute=GetXMLTreeAttribute(mime,"type");
+ if (attribute != (const char *) NULL)
+ mime_info->type=ConstantString(attribute);
+ status=AppendValueToLinkedList(mime_list,mime_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
+ mime=GetNextXMLTreeTag(mime);
+ }
+ mime_map=DestroyXMLTree(mime_map);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d M i m e L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadMimeList() loads one or more magic configuration file which provides a
+% mapping between magic attributes and a magic name.
+%
+% The format of the LoadMimeLists method is:
+%
+% MagickBooleanType LoadMimeLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType LoadMimeLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadMimeList(MimeMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadMimeList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((mime_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(mime_list) != MagickFalse))
+ status|=LoadMimeList(MimeMap,"built-in",0,exception);
+ else
+ ClearMagickException(exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ M a g i c k T o M i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickToMime() returns the officially registered (or de facto) MIME
+% media-type corresponding to a magick string. If there is no registered
+% media-type, then the string "image/x-magick" (all lower case) is returned.
+% The returned string must be deallocated by the user.
+%
+% The format of the MagickToMime method is:
+%
+% char *MagickToMime(const char *magick)
+%
+% A description of each parameter follows.
+%
+% o magick: ImageMagick format specification "magick" tag.
+%
+*/
+MagickExport char *MagickToMime(const char *magick)
+{
+ char
+ filename[MaxTextExtent],
+ media[MaxTextExtent];
+
+ const MimeInfo
+ *mime_info;
+
+ ExceptionInfo
+ *exception;
+
+ (void) FormatMagickString(filename,MaxTextExtent,"file.%s",magick);
+ LocaleLower(filename);
+ exception=AcquireExceptionInfo();
+ mime_info=GetMimeInfo(filename,(unsigned char *) " ",1,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (mime_info != (const MimeInfo *) NULL)
+ return(ConstantString(GetMimeType(mime_info)));
+ (void) FormatMagickString(media,MaxTextExtent,"image/x-%s",magick);
+ LocaleLower(media+8);
+ return(ConstantString(media));
+}
diff --git a/magick/mime.h b/magick/mime.h
new file mode 100644
index 0000000..81ec707
--- /dev/null
+++ b/magick/mime.h
@@ -0,0 +1,51 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ The ImageMagick mime methods.
+*/
+#ifndef _MIME_MIME_H
+#define _MIME_MIME_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _MimeInfo
+ MimeInfo;
+
+extern MagickExport char
+ **GetMimeList(const char *,unsigned long *,ExceptionInfo *),
+ *MagickToMime(const char *);
+
+extern MagickExport const char
+ *GetMimeDescription(const MimeInfo *),
+ *GetMimeType(const MimeInfo *);
+
+extern MagickExport MagickBooleanType
+ ListMimeInfo(FILE *,ExceptionInfo *),
+ LoadMimeLists(const char *,ExceptionInfo *);
+
+extern MagickExport const MimeInfo
+ *GetMimeInfo(const char *,const unsigned char *,const size_t,ExceptionInfo *),
+ **GetMimeInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyMimeList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/module.c b/magick/module.c
new file mode 100644
index 0000000..60687f2
--- /dev/null
+++ b/magick/module.c
@@ -0,0 +1,1541 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M OOO DDDD U U L EEEEE %
+% MM MM O O D D U U L E %
+% M M M O O D D U U L EEE %
+% M M O O D D U U L E %
+% M M OOO DDDD UUU LLLLL EEEEE %
+% %
+% %
+% MagickCore Module Methods %
+% %
+% Software Design %
+% Bob Friesenhahn %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/coder.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/log.h"
+#include "magick/hashmap.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/module.h"
+#include "magick/policy.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/static.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+#include "ltdl.h"
+typedef lt_dlhandle ModuleHandle;
+#else
+typedef void *ModuleHandle;
+#endif
+
+/*
+ Define declarations.
+*/
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+# define ModuleGlobExpression "*.la"
+#else
+# if defined(_DEBUG)
+# define ModuleGlobExpression "IM_MOD_DB_*.dll"
+# else
+# define ModuleGlobExpression "IM_MOD_RL_*.dll"
+# endif
+#endif
+
+/*
+ Global declarations.
+*/
+static SemaphoreInfo
+ *module_semaphore = (SemaphoreInfo *) NULL;
+
+static SplayTreeInfo
+ *module_list = (SplayTreeInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_module = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static const ModuleInfo
+ *RegisterModule(const ModuleInfo *,ExceptionInfo *);
+
+static MagickBooleanType
+ GetMagickModulePath(const char *,MagickModuleType,char *,ExceptionInfo *),
+ InitializeModuleList(ExceptionInfo *),
+ UnregisterModule(const ModuleInfo *,ExceptionInfo *);
+
+static void
+ TagToCoderModuleName(const char *,char *),
+ TagToFilterModuleName(const char *,char *),
+ TagToModuleName(const char *,const char *,char *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e M o d u l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireModuleInfo() allocates the ModuleInfo structure.
+%
+% The format of the AcquireModuleInfo method is:
+%
+% ModuleInfo *AcquireModuleInfo(const char *path,const char *tag)
+%
+% A description of each parameter follows:
+%
+% o path: the path associated with the tag.
+%
+% o tag: a character string that represents the image format we are
+% looking for.
+%
+*/
+MagickExport ModuleInfo *AcquireModuleInfo(const char *path,const char *tag)
+{
+ ModuleInfo
+ *module_info;
+
+ module_info=(ModuleInfo *) AcquireMagickMemory(sizeof(*module_info));
+ if (module_info == (ModuleInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(module_info,0,sizeof(*module_info));
+ if (path != (const char *) NULL)
+ module_info->path=ConstantString(path);
+ if (tag != (const char *) NULL)
+ module_info->tag=ConstantString(tag);
+ module_info->timestamp=time(0);
+ module_info->signature=MagickSignature;
+ return(module_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y M o d u l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyModuleList() unregisters any previously loaded modules and exits
+% the module loaded environment.
+%
+% The format of the DestroyModuleList module is:
+%
+% void DestroyModuleList(void)
+%
+*/
+MagickExport void DestroyModuleList(void)
+{
+ /*
+ Destroy magick modules.
+ */
+ AcquireSemaphoreInfo(&module_semaphore);
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ if (module_list != (SplayTreeInfo *) NULL)
+ module_list=DestroySplayTree(module_list);
+ if (instantiate_module != MagickFalse)
+ (void) lt_dlexit();
+#endif
+ instantiate_module=MagickFalse;
+ RelinquishSemaphoreInfo(module_semaphore);
+ DestroySemaphoreInfo(&module_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M o d u l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetModuleInfo() returns a pointer to a ModuleInfo structure that matches the
+% specified tag. If tag is NULL, the head of the module list is returned. If
+% no modules are loaded, or the requested module is not found, NULL is
+% returned.
+%
+% The format of the GetModuleInfo module is:
+%
+% ModuleInfo *GetModuleInfo(const char *tag,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o tag: a character string that represents the image format we are
+% looking for.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ModuleInfo *GetModuleInfo(const char *tag,ExceptionInfo *exception)
+{
+ if ((module_list == (SplayTreeInfo *) NULL) ||
+ (instantiate_module == MagickFalse))
+ if (InitializeModuleList(exception) == MagickFalse)
+ return((ModuleInfo *) NULL);
+ if ((module_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(module_list) == 0))
+ return((ModuleInfo *) NULL);
+ if ((tag == (const char *) NULL) || (LocaleCompare(tag,"*") == 0))
+ {
+ ModuleInfo
+ *p;
+
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+ if (LocaleCompare(tag,"*") == 0)
+ (void) OpenModules(exception);
+#endif
+ AcquireSemaphoreInfo(&module_semaphore);
+ ResetSplayTreeIterator(module_list);
+ p=(ModuleInfo *) GetNextValueInSplayTree(module_list);
+ RelinquishSemaphoreInfo(module_semaphore);
+ return(p);
+ }
+ return((ModuleInfo *) GetValueFromSplayTree(module_list,tag));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M o d u l e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetModuleInfoList() returns any modules that match the specified pattern.
+%
+% The format of the GetModuleInfoList function is:
+%
+% const ModuleInfo **GetModuleInfoList(const char *pattern,
+% unsigned long *number_modules,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_modules: This integer returns the number of modules in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ModuleInfoCompare(const void *x,const void *y)
+{
+ const ModuleInfo
+ **p,
+ **q;
+
+ p=(const ModuleInfo **) x,
+ q=(const ModuleInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->tag,(*q)->tag));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const ModuleInfo **GetModuleInfoList(const char *pattern,
+ unsigned long *number_modules,ExceptionInfo *exception)
+{
+ const ModuleInfo
+ **modules;
+
+ register const ModuleInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate module list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_modules != (unsigned long *) NULL);
+ *number_modules=0;
+ p=GetModuleInfo("*",exception);
+ if (p == (const ModuleInfo *) NULL)
+ return((const ModuleInfo **) NULL);
+ modules=(const ModuleInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(module_list)+1UL,sizeof(*modules));
+ if (modules == (const ModuleInfo **) NULL)
+ return((const ModuleInfo **) NULL);
+ /*
+ Generate module list.
+ */
+ AcquireSemaphoreInfo(&module_semaphore);
+ ResetSplayTreeIterator(module_list);
+ p=(const ModuleInfo *) GetNextValueInSplayTree(module_list);
+ for (i=0; p != (const ModuleInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->tag,pattern,MagickFalse) != MagickFalse))
+ modules[i++]=p;
+ p=(const ModuleInfo *) GetNextValueInSplayTree(module_list);
+ }
+ RelinquishSemaphoreInfo(module_semaphore);
+ qsort((void *) modules,(size_t) i,sizeof(*modules),ModuleInfoCompare);
+ modules[i]=(ModuleInfo *) NULL;
+ *number_modules=(unsigned long) i;
+ return(modules);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M o d u l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetModuleList() returns any image format modules that match the specified
+% pattern.
+%
+% The format of the GetModuleList function is:
+%
+% char **GetModuleList(const char *pattern,unsigned long *number_modules,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_modules: This integer returns the number of modules in the
+% list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int ModuleCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static inline int MagickReadDirectory(DIR *directory,struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(MAGICKCORE_HAVE_READDIR_R)
+ return(readdir_r(directory,entry,result));
+#else
+ (void) entry;
+ errno=0;
+ *result=readdir(directory);
+ return(errno);
+#endif
+}
+
+MagickExport char **GetModuleList(const char *pattern,
+ unsigned long *number_modules,ExceptionInfo *exception)
+{
+ char
+ **modules,
+ filename[MaxTextExtent],
+ module_path[MaxTextExtent],
+ path[MaxTextExtent];
+
+ DIR
+ *directory;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ struct dirent
+ *buffer,
+ *entry;
+
+ unsigned long
+ max_entries;
+
+ /*
+ Locate all modules in the coder path.
+ */
+ TagToCoderModuleName("magick",filename);
+ length=GetMagickModulePath(filename,MagickImageCoderModule,module_path,
+ exception);
+ if (length == 0)
+ return((char **) NULL);
+ GetPathComponent(module_path,HeadPath,path);
+ max_entries=255;
+ modules=(char **) AcquireQuantumMemory((size_t) max_entries+1UL,
+ sizeof(*modules));
+ if (modules == (char **) NULL)
+ return((char **) NULL);
+ *modules=(char *) NULL;
+ directory=opendir(path);
+ if (directory == (DIR *) NULL)
+ {
+ modules=(char **) RelinquishMagickMemory(modules);
+ return((char **) NULL);
+ }
+ buffer=(struct dirent *) AcquireMagickMemory(sizeof(*buffer)+FILENAME_MAX+1);
+ if (buffer == (struct dirent *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ i=0;
+ while ((MagickReadDirectory(directory,buffer,&entry) == 0) &&
+ (entry != (struct dirent *) NULL))
+ {
+ status=GlobExpression(entry->d_name,ModuleGlobExpression,MagickFalse);
+ if (status == MagickFalse)
+ continue;
+ if (GlobExpression(entry->d_name,pattern,MagickFalse) == MagickFalse)
+ continue;
+ if (i >= (long) max_entries)
+ {
+ modules=(char **) NULL;
+ if (~max_entries > max_entries)
+ modules=(char **) ResizeQuantumMemory(modules,(size_t)
+ (max_entries << 1),sizeof(*modules));
+ max_entries<<=1;
+ if (modules == (char **) NULL)
+ break;
+ }
+ /*
+ Add new module name to list.
+ */
+ modules[i]=AcquireString((char *) NULL);
+ GetPathComponent(entry->d_name,BasePath,modules[i]);
+ LocaleUpper(modules[i]);
+ if (LocaleNCompare("IM_MOD_",modules[i],7) == 0)
+ {
+ (void) CopyMagickString(modules[i],modules[i]+10,MaxTextExtent);
+ modules[i][strlen(modules[i])-1]='\0';
+ }
+ i++;
+ }
+ buffer=(struct dirent *) RelinquishMagickMemory(buffer);
+ (void) closedir(directory);
+ if (modules == (char **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ConfigureError,
+ "MemoryAllocationFailed","`%s'",pattern);
+ return((char **) NULL);
+ }
+ qsort((void *) modules,(size_t) i,sizeof(*modules),ModuleCompare);
+ modules[i]=(char *) NULL;
+ *number_modules=(unsigned long) i;
+ return(modules);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k M o d u l e P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickModulePath() finds a module with the specified module type and
+% filename.
+%
+% The format of the GetMagickModulePath module is:
+%
+% MagickBooleanType GetMagickModulePath(const char *filename,
+% MagickModuleType module_type,char *path,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the module file name.
+%
+% o module_type: the module type: MagickImageCoderModule or
+% MagickImageFilterModule.
+%
+% o path: the path associated with the filename.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType GetMagickModulePath(const char *filename,
+ MagickModuleType module_type,char *path,ExceptionInfo *exception)
+{
+ char
+ *module_path;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(path != (char *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ (void) CopyMagickString(path,filename,MaxTextExtent);
+ module_path=(char *) NULL;
+ switch (module_type)
+ {
+ case MagickImageCoderModule:
+ default:
+ {
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Searching for coder module file \"%s\" ...",filename);
+ module_path=GetEnvironmentValue("MAGICK_CODER_MODULE_PATH");
+#if defined(MAGICKCORE_CODER_PATH)
+ if (module_path == (char *) NULL)
+ module_path=AcquireString(MAGICKCORE_CODER_PATH);
+#endif
+ break;
+ }
+ case MagickImageFilterModule:
+ {
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Searching for filter module file \"%s\" ...",filename);
+ module_path=GetEnvironmentValue("MAGICK_CODER_FILTER_PATH");
+#if defined(MAGICKCORE_FILTER_PATH)
+ if (module_path == (char *) NULL)
+ module_path=AcquireString(MAGICKCORE_FILTER_PATH);
+#endif
+ break;
+ }
+ }
+ if (module_path != (char *) NULL)
+ {
+ register char
+ *p,
+ *q;
+
+ for (p=module_path-1; p != (char *) NULL; )
+ {
+ (void) CopyMagickString(path,p+1,MaxTextExtent);
+ q=strchr(path,DirectoryListSeparator);
+ if (q != (char *) NULL)
+ *q='\0';
+ q=path+strlen(path)-1;
+ if ((q >= path) && (*q != *DirectorySeparator))
+ (void) ConcatenateMagickString(path,DirectorySeparator,MaxTextExtent);
+ (void) ConcatenateMagickString(path,filename,MaxTextExtent);
+ if (IsPathAccessible(path) != MagickFalse)
+ {
+ module_path=DestroyString(module_path);
+ return(MagickTrue);
+ }
+ p=strchr(p+1,DirectoryListSeparator);
+ }
+ module_path=DestroyString(module_path);
+ }
+#if defined(MAGICKCORE_INSTALLED_SUPPORT)
+ else
+#if defined(MAGICKCORE_CODER_PATH)
+ {
+ const char
+ *directory;
+
+ /*
+ Search hard coded paths.
+ */
+ switch (module_type)
+ {
+ case MagickImageCoderModule:
+ default:
+ {
+ directory=MAGICKCORE_CODER_PATH;
+ break;
+ }
+ case MagickImageFilterModule:
+ {
+ directory=MAGICKCORE_FILTER_PATH;
+ break;
+ }
+ }
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s",directory,filename);
+ if (IsPathAccessible(path) == MagickFalse)
+ {
+ ThrowFileException(exception,ConfigureWarning,
+ "UnableToOpenModuleFile",path);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+ }
+#else
+#if defined(__WINDOWS__)
+ {
+ const char
+ *registery_key;
+
+ unsigned char
+ *key_value;
+
+ /*
+ Locate path via registry key.
+ */
+ switch (module_type)
+ {
+ case MagickImageCoderModule:
+ default:
+ {
+ registery_key="CoderModulesPath";
+ break;
+ }
+ case MagickImageFilterModule:
+ {
+ registery_key="FilterModulesPath";
+ break;
+ }
+ }
+ key_value=NTRegistryKeyLookup(registery_key);
+ if (key_value == (unsigned char *) NULL)
+ {
+ ThrowMagickException(exception,GetMagickModule(),ConfigureError,
+ "RegistryKeyLookupFailed","`%s'",registery_key);
+ return(MagickFalse);
+ }
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",(char *) key_value,
+ DirectorySeparator,filename);
+ key_value=(unsigned char *) RelinquishMagickMemory(key_value);
+ if (IsPathAccessible(path) == MagickFalse)
+ {
+ ThrowFileException(exception,ConfigureWarning,
+ "UnableToOpenModuleFile",path);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+ }
+#endif
+#endif
+#if !defined(MAGICKCORE_CODER_PATH) && !defined(__WINDOWS__)
+# error MAGICKCORE_CODER_PATH or __WINDOWS__ must be defined when MAGICKCORE_INSTALLED_SUPPORT is defined
+#endif
+#else
+ {
+ char
+ *home;
+
+ home=GetEnvironmentValue("MAGICK_HOME");
+ if (home != (char *) NULL)
+ {
+ /*
+ Search MAGICK_HOME.
+ */
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",home,
+ DirectorySeparator,filename);
+#else
+ const char
+ *directory;
+
+ switch (module_type)
+ {
+ case MagickImageCoderModule:
+ default:
+ {
+ directory=MAGICKCORE_CODER_RELATIVE_PATH;
+ break;
+ }
+ case MagickImageFilterModule:
+ {
+ directory=MAGICKCORE_FILTER_RELATIVE_PATH;
+ break;
+ }
+ }
+ (void) FormatMagickString(path,MaxTextExtent,"%s/lib/%s/%s",home,
+ directory,filename);
+#endif
+ home=DestroyString(home);
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickTrue);
+ }
+ }
+ if (*GetClientPath() != '\0')
+ {
+ /*
+ Search based on executable directory.
+ */
+#if !defined(MAGICKCORE_POSIX_SUPPORT)
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",GetClientPath(),
+ DirectorySeparator,filename);
+#else
+ char
+ prefix[MaxTextExtent];
+
+ const char
+ *directory;
+
+ switch (module_type)
+ {
+ case MagickImageCoderModule:
+ default:
+ {
+ directory="modules";
+ break;
+ }
+ case MagickImageFilterModule:
+ {
+ directory="filters";
+ break;
+ }
+ }
+ (void) CopyMagickString(prefix,GetClientPath(),MaxTextExtent);
+ ChopPathComponents(prefix,1);
+ (void) FormatMagickString(path,MaxTextExtent,
+ "%s/lib/%s/modules-Q%d/%s/%s",prefix,MAGICKCORE_LIBRARY_RELATIVE_PATH,
+ MAGICKCORE_QUANTUM_DEPTH,directory,filename);
+#endif
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickTrue);
+ }
+#if defined(__WINDOWS__)
+ {
+ /*
+ Search module path.
+ */
+ if ((NTGetModulePath("CORE_RL_magick_.dll",path) != MagickFalse) ||
+ (NTGetModulePath("CORE_DB_magick_.dll",path) != MagickFalse) ||
+ (NTGetModulePath("Magick.dll",path) != MagickFalse))
+ {
+ (void) ConcatenateMagickString(path,DirectorySeparator,MaxTextExtent);
+ (void) ConcatenateMagickString(path,filename,MaxTextExtent);
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickTrue);
+ }
+ }
+#endif
+ {
+ char
+ *home;
+
+ home=GetEnvironmentValue("HOME");
+ if (home == (char *) NULL)
+ home=GetEnvironmentValue("USERPROFILE");
+ if (home != (char *) NULL)
+ {
+ /*
+ Search $HOME/.magick.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s.magick%s%s",home,
+ DirectorySeparator,DirectorySeparator,filename);
+ home=DestroyString(home);
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickTrue);
+ }
+ }
+ /*
+ Search current directory.
+ */
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickTrue);
+ if (exception->severity < ConfigureError)
+ ThrowFileException(exception,ConfigureWarning,"UnableToOpenModuleFile",
+ path);
+ return(MagickFalse);
+#endif
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e M o d u l e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeModuleList() initializes the module loader.
+%
+% The format of the InitializeModuleList() method is:
+%
+% InitializeModuleList(Exceptioninfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void *DestroyModuleNode(void *module_info)
+{
+ ExceptionInfo
+ *exception;
+
+ register ModuleInfo
+ *p;
+
+ exception=AcquireExceptionInfo();
+ p=(ModuleInfo *) module_info;
+ if (UnregisterModule(p,exception) == MagickFalse)
+ CatchException(exception);
+ if (p->tag != (char *) NULL)
+ p->tag=DestroyString(p->tag);
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ exception=DestroyExceptionInfo(exception);
+ return(RelinquishMagickMemory(p));
+}
+
+static MagickBooleanType InitializeModuleList(
+ ExceptionInfo *magick_unused(exception))
+{
+ if ((module_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_module == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&module_semaphore);
+ if ((module_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_module == MagickFalse))
+ {
+ MagickBooleanType
+ status;
+
+ ModuleInfo
+ *module_info;
+
+ module_list=NewSplayTree(CompareSplayTreeString,
+ (void *(*)(void *)) NULL,DestroyModuleNode);
+ if (module_list == (SplayTreeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ module_info=AcquireModuleInfo((const char *) NULL,"[boot-strap]");
+ module_info->stealth=MagickTrue;
+ status=AddValueToSplayTree(module_list,module_info->tag,module_info);
+ if (status == MagickFalse)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ if (lt_dlinit() != 0)
+ ThrowFatalException(ModuleFatalError,
+ "UnableToInitializeModuleLoader");
+ instantiate_module=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(module_semaphore);
+ }
+ return(module_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n v o k e D y n a m i c I m a g e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InvokeDynamicImageFilter() invokes a dynamic image filter.
+%
+% The format of the InvokeDynamicImageFilter module is:
+%
+% MagickBooleanType InvokeDynamicImageFilter(const char *tag,Image **image,
+% const int argc,const char **argv,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o tag: a character string that represents the name of the particular
+% module.
+%
+% o image: the image.
+%
+% o argc: a pointer to an integer describing the number of elements in the
+% argument vector.
+%
+% o argv: a pointer to a text array containing the command line arguments.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType InvokeDynamicImageFilter(const char *tag,
+ Image **images,const int argc,const char **argv,ExceptionInfo *exception)
+{
+ char
+ name[MaxTextExtent],
+ path[MaxTextExtent];
+
+ ImageFilterHandler
+ *image_filter;
+
+ ModuleHandle
+ handle;
+
+ PolicyRights
+ rights;
+
+ size_t
+ length;
+
+ /*
+ Find the module.
+ */
+ assert(images != (Image **) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ {
+ MagickBooleanType
+ status;
+
+ status=InvokeStaticImageFilter(tag,images,argc,argv,exception);
+ if (status != MagickFalse)
+ return(status);
+ }
+#endif
+ rights=ReadPolicyRights;
+ if (IsRightsAuthorized(FilterPolicyDomain,rights,tag) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",tag);
+ return(MagickFalse);
+ }
+ TagToFilterModuleName(tag,name);
+ length=GetMagickModulePath(name,MagickImageFilterModule,path,exception);
+ if (length == 0)
+ return(MagickFalse);
+ /*
+ Open the module.
+ */
+ handle=(ModuleHandle) lt_dlopen(path);
+ if (handle == (ModuleHandle) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "UnableToLoadModule","`%s': %s",name,lt_dlerror());
+ return(MagickFalse);
+ }
+ /*
+ Locate the module.
+ */
+#if !defined(MAGICKCORE_NAMESPACE_PREFIX)
+ (void) FormatMagickString(name,MaxTextExtent,"%sImage",tag);
+#else
+ (void) FormatMagickString(name,MaxTextExtent,"%s%sImage",
+ MAGICKCORE_NAMESPACE_PREFIX,tag);
+#endif
+ /*
+ Execute the module.
+ */
+ image_filter=(ImageFilterHandler *) lt_dlsym(handle,name);
+ if (image_filter == (ImageFilterHandler *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "UnableToLoadModule","`%s': %s",name,lt_dlerror());
+ else
+ {
+ unsigned long
+ signature;
+
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Invoking \"%s\" dynamic image filter",tag);
+ signature=image_filter(images,argc,argv,exception);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),"\"%s\" completes",
+ tag);
+ if (signature != MagickImageFilterSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "ImageFilterSignatureMismatch","`%s': %8lx != %8lx",tag,signature,
+ MagickImageFilterSignature);
+ return(MagickFalse);
+ }
+ }
+ /*
+ Close the module.
+ */
+ if (lt_dlclose(handle) != 0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleWarning,
+ "UnableToCloseModule","`%s': %s",name,lt_dlerror());
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t M o d u l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListModuleInfo() lists the module info to a file.
+%
+% The format of the ListModuleInfo module is:
+%
+% MagickBooleanType ListModuleInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListModuleInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const ModuleInfo
+ **module_info;
+
+ register long
+ i;
+
+ unsigned long
+ number_modules;
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ module_info=GetModuleInfoList("*",&number_modules,exception);
+ if (module_info == (const ModuleInfo **) NULL)
+ return(MagickFalse);
+ if (module_info[0]->path != (char *) NULL)
+ {
+ char
+ path[MaxTextExtent];
+
+ GetPathComponent(module_info[0]->path,HeadPath,path);
+ (void) fprintf(file,"\nPath: %s\n\n",path);
+ }
+ (void) fprintf(file,"Module\n");
+ (void) fprintf(file,"-------------------------------------------------"
+ "------------------------------\n");
+ for (i=0; i < (long) number_modules; i++)
+ {
+ if (module_info[i]->stealth != MagickFalse)
+ continue;
+ (void) fprintf(file,"%s",module_info[i]->tag);
+ (void) fprintf(file,"\n");
+ }
+ (void) fflush(file);
+ module_info=(const ModuleInfo **)
+ RelinquishMagickMemory((void *) module_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p e n M o d u l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenModule() loads a module, and invokes its registration module. It
+% returns MagickTrue on success, and MagickFalse if there is an error.
+%
+% The format of the OpenModule module is:
+%
+% MagickBooleanType OpenModule(const char *module,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o module: a character string that indicates the module to load.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType OpenModule(const char *module,
+ ExceptionInfo *exception)
+{
+ char
+ filename[MaxTextExtent],
+ module_name[MaxTextExtent],
+ name[MaxTextExtent],
+ path[MaxTextExtent];
+
+ ModuleHandle
+ handle;
+
+ ModuleInfo
+ *module_info;
+
+ register const CoderInfo
+ *p;
+
+ size_t
+ length;
+
+ unsigned long
+ signature;
+
+ /*
+ Assign module name from alias.
+ */
+ assert(module != (const char *) NULL);
+ module_info=(ModuleInfo *) GetModuleInfo(module,exception);
+ if (module_info != (ModuleInfo *) NULL)
+ return(MagickTrue);
+ (void) CopyMagickString(module_name,module,MaxTextExtent);
+ p=GetCoderInfo(module,exception);
+ if (p != (CoderInfo *) NULL)
+ (void) CopyMagickString(module_name,p->name,MaxTextExtent);
+ if (GetValueFromSplayTree(module_list,module_name) != (void *) NULL)
+ return(MagickTrue); /* module already opened, return */
+ /*
+ Locate module.
+ */
+ handle=(ModuleHandle) NULL;
+ TagToCoderModuleName(module_name,filename);
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Searching for module \"%s\" using filename \"%s\"",module_name,filename);
+ *path='\0';
+ length=GetMagickModulePath(filename,MagickImageCoderModule,path,exception);
+ if (length == 0)
+ return(MagickFalse);
+ /*
+ Load module
+ */
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Opening module at path \"%s\"",path);
+ handle=(ModuleHandle) lt_dlopen(path);
+ if (handle == (ModuleHandle) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "UnableToLoadModule","`%s': %s",path,lt_dlerror());
+ return(MagickFalse);
+ }
+ /*
+ Register module.
+ */
+ module_info=AcquireModuleInfo(path,module_name);
+ module_info->handle=handle;
+ if (RegisterModule(module_info,exception) == (ModuleInfo *) NULL)
+ return(MagickFalse);
+ /*
+ Define RegisterFORMATImage method.
+ */
+ TagToModuleName(module_name,"Register%sImage",name);
+ module_info->register_module=(unsigned long (*)(void)) lt_dlsym(handle,name);
+ if (module_info->register_module == (unsigned long (*)(void)) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "UnableToRegisterImageFormat","`%s': %s",module_name,lt_dlerror());
+ return(MagickFalse);
+ }
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Method \"%s\" in module \"%s\" at address %p",name,module_name,
+ (void *) module_info->register_module);
+ /*
+ Define UnregisterFORMATImage method.
+ */
+ TagToModuleName(module_name,"Unregister%sImage",name);
+ module_info->unregister_module=(void (*)(void)) lt_dlsym(handle,name);
+ if (module_info->unregister_module == (void (*)(void)) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "UnableToRegisterImageFormat","`%s': %s",module_name,lt_dlerror());
+ return(MagickFalse);
+ }
+ (void) LogMagickEvent(ModuleEvent,GetMagickModule(),
+ "Method \"%s\" in module \"%s\" at address %p",name,module_name,
+ (void *) module_info->unregister_module);
+ signature=module_info->register_module();
+ if (signature != MagickImageCoderSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "ImageCoderSignatureMismatch","`%s': %8lx != %8lx",module_name,
+ signature,MagickImageCoderSignature);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p e n M o d u l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenModules() loads all available modules.
+%
+% The format of the OpenModules module is:
+%
+% MagickBooleanType OpenModules(ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType OpenModules(ExceptionInfo *exception)
+{
+ char
+ **modules;
+
+ register long
+ i;
+
+ unsigned long
+ number_modules;
+
+ /*
+ Load all modules.
+ */
+ (void) GetMagickInfo((char *) NULL,exception);
+ number_modules=0;
+ modules=GetModuleList("*",&number_modules,exception);
+ if (modules == (char **) NULL)
+ return(MagickFalse);
+ for (i=0; i < (long) number_modules; i++)
+ (void) OpenModule(modules[i],exception);
+ /*
+ Relinquish resources.
+ */
+ for (i=0; i < (long) number_modules; i++)
+ modules[i]=DestroyString(modules[i]);
+ modules=(char **) RelinquishMagickMemory(modules);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e g i s t e r M o d u l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RegisterModule() adds an entry to the module list. It returns a pointer to
+% the registered entry on success.
+%
+% The format of the RegisterModule module is:
+%
+% ModuleInfo *RegisterModule(const ModuleInfo *module_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o info: a pointer to the registered entry is returned.
+%
+% o module_info: a pointer to the ModuleInfo structure to register.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static const ModuleInfo *RegisterModule(const ModuleInfo *module_info,
+ ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ assert(module_info != (ModuleInfo *) NULL);
+ assert(module_info->signature == MagickSignature);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",module_info->tag);
+ if (module_list == (SplayTreeInfo *) NULL)
+ return((const ModuleInfo *) NULL);
+ status=AddValueToSplayTree(module_list,module_info->tag,module_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),ResourceLimitError,
+ "MemoryAllocationFailed","`%s'",module_info->tag);
+ return(module_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T a g T o C o d e r M o d u l e N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TagToCoderModuleName() munges a module tag and obtains the filename of the
+% corresponding module.
+%
+% The format of the TagToCoderModuleName module is:
+%
+% char *TagToCoderModuleName(const char *tag,char *name)
+%
+% A description of each parameter follows:
+%
+% o tag: a character string representing the module tag.
+%
+% o name: return the module name here.
+%
+*/
+static void TagToCoderModuleName(const char *tag,char *name)
+{
+ assert(tag != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",tag);
+ assert(name != (char *) NULL);
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+ (void) FormatMagickString(name,MaxTextExtent,"%s.la",tag);
+ (void) LocaleLower(name);
+#else
+#if defined(__WINDOWS__)
+ if (LocaleNCompare("IM_MOD_",tag,7) == 0)
+ (void) CopyMagickString(name,tag,MaxTextExtent);
+ else
+ {
+#if defined(_DEBUG)
+ (void) FormatMagickString(name,MaxTextExtent,"IM_MOD_DB_%s_.dll",tag);
+#else
+ (void) FormatMagickString(name,MaxTextExtent,"IM_MOD_RL_%s_.dll",tag);
+#endif
+ }
+#endif
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T a g T o F i l t e r M o d u l e N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TagToFilterModuleName() munges a module tag and returns the filename of the
+% corresponding filter module.
+%
+% The format of the TagToFilterModuleName module is:
+%
+% void TagToFilterModuleName(const char *tag,char name)
+%
+% A description of each parameter follows:
+%
+% o tag: a character string representing the module tag.
+%
+% o name: return the filter name here.
+%
+*/
+static void TagToFilterModuleName(const char *tag,char *name)
+{
+ assert(tag != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",tag);
+ assert(name != (char *) NULL);
+#if !defined(MAGICKCORE_LTDL_DELEGATE)
+ (void) FormatMagickString(name,MaxTextExtent,"%s.dll",tag);
+#else
+ (void) FormatMagickString(name,MaxTextExtent,"%s.la",tag);
+ (void) LocaleLower(name);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T a g T o M o d u l e N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TagToModuleName() munges the module tag name and returns an upper-case tag
+% name as the input string, and a user-provided format.
+%
+% The format of the TagToModuleName module is:
+%
+% TagToModuleName(const char *tag,const char *format,char *module)
+%
+% A description of each parameter follows:
+%
+% o tag: the module tag.
+%
+% o format: a sprintf-compatible format string containing %s where the
+% upper-case tag name is to be inserted.
+%
+% o module: pointer to a destination buffer for the formatted result.
+%
+*/
+static void TagToModuleName(const char *tag,const char *format,char *module)
+{
+ char
+ name[MaxTextExtent];
+
+ assert(tag != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",tag);
+ assert(format != (const char *) NULL);
+ assert(module != (char *) NULL);
+ (void) CopyMagickString(name,tag,MaxTextExtent);
+ LocaleUpper(name);
+#if !defined(MAGICKCORE_NAMESPACE_PREFIX)
+ (void) FormatMagickString(module,MaxTextExtent,format,name);
+#else
+ {
+ char
+ prefix_format[MaxTextExtent];
+
+ (void) FormatMagickString(prefix_format,MaxTextExtent,"%s%s",
+ MAGICKCORE_NAMESPACE_PREFIX,format);
+ (void) FormatMagickString(module,MaxTextExtent,prefix_format,name);
+ }
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n r e g i s t e r M o d u l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnregisterModule() unloads a module, and invokes its de-registration module.
+% Returns MagickTrue on success, and MagickFalse if there is an error.
+%
+% The format of the UnregisterModule module is:
+%
+% MagickBooleanType UnregisterModule(const ModuleInfo *module_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o module_info: the module info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType UnregisterModule(const ModuleInfo *module_info,
+ ExceptionInfo *exception)
+{
+ /*
+ Locate and execute UnregisterFORMATImage module.
+ */
+ assert(module_info != (const ModuleInfo *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",module_info->tag);
+ assert(exception != (ExceptionInfo *) NULL);
+ if (module_info->unregister_module == NULL)
+ return(MagickTrue);
+ module_info->unregister_module();
+ if (lt_dlclose((ModuleHandle) module_info->handle) != 0)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleWarning,
+ "UnableToCloseModule","`%s': %s",module_info->tag,lt_dlerror());
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+#else
+MagickExport MagickBooleanType ListModuleInfo(FILE *magick_unused(file),
+ ExceptionInfo *magick_unused(exception))
+{
+ return(MagickTrue);
+}
+
+MagickExport MagickBooleanType InvokeDynamicImageFilter(const char *tag,
+ Image **image,const int argc,const char **argv,ExceptionInfo *exception)
+{
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ {
+ extern unsigned long
+ analyzeImage(Image **,const int,const char **,ExceptionInfo *);
+
+ ImageFilterHandler
+ *image_filter;
+
+ image_filter=(ImageFilterHandler *) NULL;
+ if (LocaleCompare("analyze",tag) == 0)
+ image_filter=analyzeImage;
+ if (image_filter != (ImageFilterHandler *) NULL)
+ {
+ unsigned long
+ signature;
+
+ signature=image_filter(image,argc,argv,exception);
+ if (signature != MagickImageFilterSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "ImageFilterSignatureMismatch","`%s': %8lx != %8lx",tag,signature,
+ MagickImageFilterSignature);
+ return(MagickFalse);
+ }
+ }
+ }
+#endif
+ return(MagickTrue);
+}
+#endif
diff --git a/magick/module.h b/magick/module.h
new file mode 100644
index 0000000..d968345
--- /dev/null
+++ b/magick/module.h
@@ -0,0 +1,94 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore module methods.
+*/
+#ifndef _MAGICKCORE_MODULE_H
+#define _MAGICKCORE_MODULE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <time.h>
+#include "magick/version.h"
+
+#define MagickImageCoderSignature ((unsigned long) \
+ (((MagickLibVersion) << 8) | MAGICKCORE_QUANTUM_DEPTH))
+#define MagickImageFilterSignature ((unsigned long) \
+ (((MagickLibVersion) << 8) | MAGICKCORE_QUANTUM_DEPTH))
+
+typedef enum
+{
+ MagickImageCoderModule,
+ MagickImageFilterModule
+} MagickModuleType;
+
+typedef struct _ModuleInfo
+{
+ char
+ *path,
+ *tag;
+
+ void
+ *handle,
+ (*unregister_module)(void);
+
+ unsigned long
+ (*register_module)(void);
+
+ time_t
+ timestamp;
+
+ MagickBooleanType
+ stealth;
+
+ struct _ModuleInfo
+ *previous,
+ *next; /* deprecated, use GetModuleInfoList() */
+
+ unsigned long
+ signature;
+} ModuleInfo;
+
+typedef ModuleExport unsigned long
+ ImageFilterHandler(Image **,const int,const char **,ExceptionInfo *);
+
+extern MagickExport char
+ **GetModuleList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const ModuleInfo
+ **GetModuleInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ InvokeDynamicImageFilter(const char *,Image **,const int,const char **,
+ ExceptionInfo *),
+ ListModuleInfo(FILE *,ExceptionInfo *),
+ OpenModule(const char *,ExceptionInfo *),
+ OpenModules(ExceptionInfo *);
+
+extern MagickExport ModuleInfo
+ *GetModuleInfo(const char *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyModuleList(void),
+ RegisterStaticModules(void),
+ UnregisterStaticModules(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/monitor-private.h b/magick/monitor-private.h
new file mode 100644
index 0000000..d6c17a5
--- /dev/null
+++ b/magick/monitor-private.h
@@ -0,0 +1,45 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ The ImageMagick progress monitor private methods.
+*/
+#ifndef _MAGICK_MONITOR_PRIVATE_H
+#define _MAGICK_MONITOR_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/image.h>
+
+static inline MagickBooleanType SetImageProgress(const Image *image,
+ const char *tag,const MagickOffsetType offset,const MagickSizeType extent)
+{
+ char
+ message[MaxTextExtent];
+
+ if (image->progress_monitor == (MagickProgressMonitor) NULL)
+ return(MagickTrue);
+ if (QuantumTick(offset,extent) == MagickFalse)
+ return(MagickTrue);
+ (void) FormatMagickString(message,MaxTextExtent,"%s/%s",tag,image->filename);
+ return(image->progress_monitor(message,offset,extent,image->client_data));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/monitor.c b/magick/monitor.c
new file mode 100644
index 0000000..092c919
--- /dev/null
+++ b/magick/monitor.c
@@ -0,0 +1,145 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M OOO N N IIIII TTTTT OOO RRRR %
+% MM MM O O NN N I T O O R R %
+% M M M O O N N N I T O O RRRR %
+% M M O O N NN I T O O R R %
+% M M OOO N N IIIII T OOO R R %
+% %
+% %
+% MagickCore Progress Monitor Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 1995 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/image.h"
+#include "magick/log.h"
+#include "magick/monitor.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e P r o g r e s s M o n i t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageProgressMonitor() sets the image progress monitor to the specified
+% method and returns the previous progress monitor if any. The progress
+% monitor method looks like this:
+%
+% MagickBooleanType MagickProgressMonitor(const char *text,
+% const MagickOffsetType offset,const MagickSizeType span,
+% void *client_data)
+%
+% If the progress monitor returns MagickFalse, the current operation is
+% interrupted.
+%
+% The format of the SetImageProgressMonitor method is:
+%
+% MagickProgressMonitor SetImageProgressMonitor(Image *image,
+% const MagickProgressMonitor progress_monitor,void *client_data)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o progress_monitor: Specifies a pointer to a method to monitor progress of
+% an image operation.
+%
+% o client_data: Specifies a pointer to any client data.
+%
+*/
+MagickExport MagickProgressMonitor SetImageProgressMonitor(Image *image,
+ const MagickProgressMonitor progress_monitor,void *client_data)
+{
+ MagickProgressMonitor
+ previous_monitor;
+
+ previous_monitor=image->progress_monitor;
+ image->progress_monitor=progress_monitor;
+ image->client_data=client_data;
+ return(previous_monitor);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e I n f o P r o g r e s s M o n i t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageInfoProgressMonitor() sets the image_info progress monitor to the
+% specified method and returns the previous progress monitor if any. The
+% progress monitor method looks like this:
+%
+% MagickBooleanType MagickProgressMonitor(const char *text,
+% const MagickOffsetType offset,const MagickSizeType span,
+% void *client_data)
+%
+% If the progress monitor returns MagickFalse, the current operation is
+% interrupted.
+%
+% The format of the SetImageInfoProgressMonitor method is:
+%
+% MagickProgressMonitor SetImageInfoProgressMonitor(ImageInfo *image_info,
+% const MagickProgressMonitor progress_monitor,void *client_data)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o progress_monitor: Specifies a pointer to a method to monitor progress of
+% an image operation.
+%
+% o client_data: Specifies a pointer to any client data.
+%
+*/
+MagickExport MagickProgressMonitor SetImageInfoProgressMonitor(
+ ImageInfo *image_info,const MagickProgressMonitor progress_monitor,
+ void *client_data)
+{
+ MagickProgressMonitor
+ previous_monitor;
+
+ previous_monitor=image_info->progress_monitor;
+ image_info->progress_monitor=progress_monitor;
+ image_info->client_data=client_data;
+ return(previous_monitor);
+}
diff --git a/magick/monitor.h b/magick/monitor.h
new file mode 100644
index 0000000..6b04d0f
--- /dev/null
+++ b/magick/monitor.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore progress monitor methods.
+*/
+#ifndef _MAGICKCORE_MONITOR_H
+#define _MAGICKCORE_MONITOR_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef MagickBooleanType
+ (*MagickProgressMonitor)(const char *,const MagickOffsetType,
+ const MagickSizeType,void *);
+
+MagickExport MagickProgressMonitor
+ SetImageProgressMonitor(Image *,const MagickProgressMonitor,void *),
+ SetImageInfoProgressMonitor(ImageInfo *,const MagickProgressMonitor,void *);
+
+static inline MagickBooleanType QuantumTick(const MagickOffsetType offset,
+ const MagickSizeType span)
+{
+ if (span <= 100)
+ return(MagickTrue);
+ if (offset == (MagickOffsetType) (span-1))
+ return(MagickTrue);
+ if ((offset % (span/100)) == 0)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/montage.c b/magick/montage.c
new file mode 100644
index 0000000..3a387d3
--- /dev/null
+++ b/magick/montage.c
@@ -0,0 +1,893 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M M OOO N N TTTTT AAA GGGG EEEEE %
+% MM MM O O NN N T A A G E %
+% M M M O O N N N T AAAAA G GG EEE %
+% M M O O N NN T A A G G E %
+% M M OOO N N T A A GGG EEEEE %
+% %
+% %
+% MagickCore Methods to Create Image Thumbnails %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/annotate.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/composite.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/quantize.h"
+#include "magick/property.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e M o n t a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneMontageInfo() makes a copy of the given montage info structure. If
+% NULL is specified, a new image info structure is created initialized to
+% default values.
+%
+% The format of the CloneMontageInfo method is:
+%
+% MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
+% const MontageInfo *montage_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o montage_info: the montage info.
+%
+*/
+MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
+ const MontageInfo *montage_info)
+{
+ MontageInfo
+ *clone_info;
+
+ clone_info=(MontageInfo *) AcquireMagickMemory(sizeof(*clone_info));
+ if (clone_info == (MontageInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetMontageInfo(image_info,clone_info);
+ if (montage_info == (MontageInfo *) NULL)
+ return(clone_info);
+ if (montage_info->geometry != (char *) NULL)
+ clone_info->geometry=AcquireString(montage_info->geometry);
+ if (montage_info->tile != (char *) NULL)
+ clone_info->tile=AcquireString(montage_info->tile);
+ if (montage_info->title != (char *) NULL)
+ clone_info->title=AcquireString(montage_info->title);
+ if (montage_info->frame != (char *) NULL)
+ clone_info->frame=AcquireString(montage_info->frame);
+ if (montage_info->texture != (char *) NULL)
+ clone_info->texture=AcquireString(montage_info->texture);
+ if (montage_info->font != (char *) NULL)
+ clone_info->font=AcquireString(montage_info->font);
+ clone_info->pointsize=montage_info->pointsize;
+ clone_info->border_width=montage_info->border_width;
+ clone_info->shadow=montage_info->shadow;
+ clone_info->fill=montage_info->fill;
+ clone_info->stroke=montage_info->stroke;
+ clone_info->background_color=montage_info->background_color;
+ clone_info->border_color=montage_info->border_color;
+ clone_info->matte_color=montage_info->matte_color;
+ clone_info->gravity=montage_info->gravity;
+ (void) CopyMagickString(clone_info->filename,montage_info->filename,
+ MaxTextExtent);
+ clone_info->debug=IsEventLogging();
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y M o n t a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMontageInfo() deallocates memory associated with montage_info.
+%
+% The format of the DestroyMontageInfo method is:
+%
+% MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
+%
+% A description of each parameter follows:
+%
+% o montage_info: Specifies a pointer to an MontageInfo structure.
+%
+%
+*/
+MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
+{
+ if (montage_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(montage_info != (MontageInfo *) NULL);
+ assert(montage_info->signature == MagickSignature);
+ if (montage_info->geometry != (char *) NULL)
+ montage_info->geometry=(char *)
+ RelinquishMagickMemory(montage_info->geometry);
+ if (montage_info->tile != (char *) NULL)
+ montage_info->tile=DestroyString(montage_info->tile);
+ if (montage_info->title != (char *) NULL)
+ montage_info->title=DestroyString(montage_info->title);
+ if (montage_info->frame != (char *) NULL)
+ montage_info->frame=DestroyString(montage_info->frame);
+ if (montage_info->texture != (char *) NULL)
+ montage_info->texture=(char *) RelinquishMagickMemory(
+ montage_info->texture);
+ if (montage_info->font != (char *) NULL)
+ montage_info->font=DestroyString(montage_info->font);
+ montage_info->signature=(~MagickSignature);
+ montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info);
+ return(montage_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M o n t a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMontageInfo() initializes montage_info to default values.
+%
+% The format of the GetMontageInfo method is:
+%
+% void GetMontageInfo(const ImageInfo *image_info,
+% MontageInfo *montage_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: a structure of type ImageInfo.
+%
+% o montage_info: Specifies a pointer to a MontageInfo structure.
+%
+*/
+MagickExport void GetMontageInfo(const ImageInfo *image_info,
+ MontageInfo *montage_info)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(montage_info != (MontageInfo *) NULL);
+ (void) ResetMagickMemory(montage_info,0,sizeof(*montage_info));
+ (void) CopyMagickString(montage_info->filename,image_info->filename,
+ MaxTextExtent);
+ montage_info->geometry=AcquireString(DefaultTileGeometry);
+ if (image_info->font != (char *) NULL)
+ montage_info->font=AcquireString(image_info->font);
+ montage_info->gravity=CenterGravity;
+ montage_info->pointsize=image_info->pointsize;
+ montage_info->fill.opacity=OpaqueOpacity;
+ montage_info->stroke.opacity=(Quantum) TransparentOpacity;
+ montage_info->background_color=image_info->background_color;
+ montage_info->border_color=image_info->border_color;
+ montage_info->matte_color=image_info->matte_color;
+ montage_info->debug=IsEventLogging();
+ montage_info->signature=MagickSignature;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M o n t a g e I m a g e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MontageImageList() is a layout manager that lets you tile one or more
+% thumbnails across an image canvas.
+%
+% The format of the MontageImageList method is:
+%
+% Image *MontageImageList(const ImageInfo *image_info,
+% const MontageInfo *montage_info,Image *images,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o montage_info: Specifies a pointer to a MontageInfo structure.
+%
+% o images: Specifies a pointer to an array of Image structures.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void GetMontageGeometry(char *geometry,const unsigned long number_images,
+ long *x_offset,long *y_offset,unsigned long *tiles_per_column,
+ unsigned long *tiles_per_row)
+{
+ *tiles_per_column=0;
+ *tiles_per_row=0;
+ (void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column);
+ if ((*tiles_per_column == 0) && (*tiles_per_row == 0))
+ *tiles_per_column=(unsigned long) sqrt((double) number_images);
+ if (*tiles_per_column == 0)
+ *tiles_per_column=(unsigned long)
+ ceil((double) number_images/(*tiles_per_row));
+ if (*tiles_per_row == 0)
+ *tiles_per_row=(unsigned long)
+ ceil((double) number_images/(*tiles_per_column));
+}
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline long MagickMin(const long x,const long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int SceneCompare(const void *x,const void *y)
+{
+ Image
+ **image_1,
+ **image_2;
+
+ image_1=(Image **) x;
+ image_2=(Image **) y;
+ return((int) ((*image_1)->scene-(*image_2)->scene));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport Image *MontageImages(const Image *images,
+ const MontageInfo *montage_info,ExceptionInfo *exception)
+{
+ Image
+ *montage_image;
+
+ ImageInfo
+ *image_info;
+
+ image_info=AcquireImageInfo();
+ montage_image=MontageImageList(image_info,montage_info,images,exception);
+ image_info=DestroyImageInfo(image_info);
+ return(montage_image);
+}
+
+MagickExport Image *MontageImageList(const ImageInfo *image_info,
+ const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
+{
+#define MontageImageTag "Montage/Image"
+#define TileImageTag "Tile/Image"
+
+ char
+ tile_geometry[MaxTextExtent],
+ *title;
+
+ const char
+ *value;
+
+ DrawInfo
+ *draw_info;
+
+ FrameInfo
+ frame_info;
+
+ Image
+ *image,
+ **image_list,
+ **master_list,
+ *montage,
+ *texture,
+ *tile_image,
+ *thumbnail;
+
+ ImageInfo
+ *clone_info;
+
+ long
+ tile,
+ x,
+ x_offset,
+ y,
+ y_offset;
+
+ MagickBooleanType
+ concatenate,
+ proceed,
+ status;
+
+ MagickOffsetType
+ tiles;
+
+ MagickStatusType
+ flags;
+
+ MagickProgressMonitor
+ progress_monitor;
+
+ register long
+ i;
+
+ RectangleInfo
+ bounds,
+ geometry,
+ extract_info;
+
+ size_t
+ extent;
+
+ TypeMetric
+ metrics;
+
+ unsigned long
+ bevel_width,
+ border_width,
+ height,
+ images_per_page,
+ max_height,
+ number_images,
+ number_lines,
+ sans,
+ tiles_per_column,
+ tiles_per_page,
+ tiles_per_row,
+ title_offset,
+ total_tiles,
+ width;
+
+ /*
+ Create image tiles.
+ */
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ assert(montage_info != (MontageInfo *) NULL);
+ assert(montage_info->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ number_images=GetImageListLength(images);
+ master_list=ImageListToArray(images,exception);
+ image_list=master_list;
+ image=image_list[0];
+ if (master_list == (Image **) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ thumbnail=NewImageList();
+ for (i=0; i < (long) number_images; i++)
+ {
+ image=CloneImage(image_list[i],0,0,MagickTrue,exception);
+ if (image == (Image *) NULL)
+ break;
+ (void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
+ progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
+ image->client_data);
+ flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
+ thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
+ if (thumbnail == (Image *) NULL)
+ break;
+ image_list[i]=thumbnail;
+ (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
+ proceed=SetImageProgress(image,TileImageTag,i,number_images);
+ if (proceed == MagickFalse)
+ break;
+ image=DestroyImage(image);
+ }
+ if (i < (long) number_images)
+ {
+ if (thumbnail == (Image *) NULL)
+ i--;
+ for (tile=0; (long) tile <= i; tile++)
+ if (image_list[tile] != (Image *) NULL)
+ image_list[tile]=DestroyImage(image_list[tile]);
+ master_list=(Image **) RelinquishMagickMemory(master_list);
+ return((Image *) NULL);
+ }
+ /*
+ Sort image list by increasing tile number.
+ */
+ for (i=0; i < (long) number_images; i++)
+ if (image_list[i]->scene == 0)
+ break;
+ if (i == (long) number_images)
+ qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
+ SceneCompare);
+ /*
+ Determine tiles per row and column.
+ */
+ tiles_per_column=(unsigned long) sqrt((double) number_images);
+ tiles_per_row=(unsigned long) ceil((double) number_images/tiles_per_column);
+ x_offset=0;
+ y_offset=0;
+ if (montage_info->tile != (char *) NULL)
+ GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
+ &tiles_per_column,&tiles_per_row);
+ /*
+ Determine tile sizes.
+ */
+ concatenate=MagickFalse;
+ SetGeometry(image_list[0],&extract_info);
+ extract_info.x=(long) montage_info->border_width;
+ extract_info.y=(long) montage_info->border_width;
+ if (montage_info->geometry != (char *) NULL)
+ {
+ /*
+ Initialize tile geometry.
+ */
+ flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y,
+ &extract_info.width,&extract_info.height);
+ if ((extract_info.x == 0) && (extract_info.y == 0))
+ concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ?
+ MagickTrue : MagickFalse;
+ }
+ border_width=montage_info->border_width;
+ bevel_width=0;
+ if (montage_info->frame != (char *) NULL)
+ {
+ char
+ absolute_geometry[MaxTextExtent];
+
+ (void) ResetMagickMemory(&frame_info,0,sizeof(frame_info));
+ frame_info.width=extract_info.width;
+ frame_info.height=extract_info.height;
+ (void) FormatMagickString(absolute_geometry,MaxTextExtent,"%s!",
+ montage_info->frame);
+ flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel,
+ &frame_info.inner_bevel,&frame_info.width,&frame_info.height);
+ if ((flags & HeightValue) == 0)
+ frame_info.height=frame_info.width;
+ if ((flags & XiValue) == 0)
+ frame_info.outer_bevel=(long) frame_info.width/2;
+ if ((flags & PsiValue) == 0)
+ frame_info.inner_bevel=frame_info.outer_bevel;
+ frame_info.x=(long) frame_info.width;
+ frame_info.y=(long) frame_info.height;
+ bevel_width=(unsigned long) MagickMax(frame_info.inner_bevel,
+ frame_info.outer_bevel);
+ border_width=(unsigned long) MagickMax((long) frame_info.width,
+ (long) frame_info.height);
+ }
+ for (i=0; i < (long) number_images; i++)
+ {
+ if (image_list[i]->columns > extract_info.width)
+ extract_info.width=image_list[i]->columns;
+ if (image_list[i]->rows > extract_info.height)
+ extract_info.height=image_list[i]->rows;
+ }
+ /*
+ Initialize draw attributes.
+ */
+ clone_info=CloneImageInfo(image_info);
+ clone_info->background_color=montage_info->background_color;
+ clone_info->border_color=montage_info->border_color;
+ draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL);
+ if (montage_info->font != (char *) NULL)
+ (void) CloneString(&draw_info->font,montage_info->font);
+ if (montage_info->pointsize != 0.0)
+ draw_info->pointsize=montage_info->pointsize;
+ draw_info->gravity=CenterGravity;
+ draw_info->stroke=montage_info->stroke;
+ draw_info->fill=montage_info->fill;
+ draw_info->text=AcquireString("");
+ (void) GetTypeMetrics(image_list[0],draw_info,&metrics);
+ texture=NewImageList();
+ if (montage_info->texture != (char *) NULL)
+ {
+ (void) CopyMagickString(clone_info->filename,montage_info->texture,
+ MaxTextExtent);
+ texture=ReadImage(clone_info,exception);
+ }
+ /*
+ Determine the number of lines in an next label.
+ */
+ title=InterpretImageProperties(clone_info,image_list[0],montage_info->title);
+ title_offset=0;
+ if (montage_info->title != (char *) NULL)
+ title_offset=(unsigned long) (2*(metrics.ascent-metrics.descent)*
+ MultilineCensus(title)+2*extract_info.y);
+ number_lines=0;
+ for (i=0; i < (long) number_images; i++)
+ {
+ value=GetImageProperty(image_list[i],"label");
+ if (value == (const char *) NULL)
+ continue;
+ if (MultilineCensus(value) > number_lines)
+ number_lines=MultilineCensus(value);
+ }
+ /*
+ Allocate next structure.
+ */
+ tile_image=AcquireImage(NULL);
+ montage=AcquireImage(clone_info);
+ montage->scene=0;
+ images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
+ tiles=0;
+ total_tiles=(unsigned long) number_images;
+ for (i=0; i < (long) images_per_page; i++)
+ {
+ /*
+ Determine bounding box.
+ */
+ tiles_per_page=tiles_per_row*tiles_per_column;
+ x_offset=0;
+ y_offset=0;
+ if (montage_info->tile != (char *) NULL)
+ GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
+ &sans,&sans);
+ tiles_per_page=tiles_per_row*tiles_per_column;
+ y_offset+=(long) title_offset;
+ max_height=0;
+ bounds.width=0;
+ bounds.height=0;
+ width=0;
+ for (tile=0; tile < (long) tiles_per_page; tile++)
+ {
+ if (tile < (long) number_images)
+ {
+ width=concatenate != MagickFalse ? image_list[tile]->columns :
+ extract_info.width;
+ if (image_list[tile]->rows > max_height)
+ max_height=image_list[tile]->rows;
+ }
+ x_offset+=width+(extract_info.x+border_width)*2;
+ if (x_offset > (long) bounds.width)
+ bounds.width=(unsigned long) x_offset;
+ if (((tile+1) == (long) tiles_per_page) ||
+ (((tile+1) % tiles_per_row) == 0))
+ {
+ x_offset=0;
+ if (montage_info->tile != (char *) NULL)
+ GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
+ &sans,&sans);
+ height=concatenate != MagickFalse ? max_height : extract_info.height;
+ y_offset+=(unsigned long) (height+(extract_info.y+border_width)*2+
+ (metrics.ascent-metrics.descent+4)*number_lines+
+ (montage_info->shadow != MagickFalse ? 4 : 0));
+ if (y_offset > (long) bounds.height)
+ bounds.height=(unsigned long) y_offset;
+ max_height=0;
+ }
+ }
+ if (montage_info->shadow != MagickFalse)
+ bounds.width+=4;
+ /*
+ Initialize montage image.
+ */
+ (void) CopyMagickString(montage->filename,montage_info->filename,
+ MaxTextExtent);
+ montage->columns=bounds.width;
+ montage->rows=bounds.height;
+ (void) SetImageBackgroundColor(montage);
+ /*
+ Set montage geometry.
+ */
+ montage->montage=AcquireString((char *) NULL);
+ tile=0;
+ extent=1;
+ while (tile < MagickMin((long) tiles_per_page,(long) number_images))
+ {
+ extent+=strlen(image_list[tile]->filename)+1;
+ tile++;
+ }
+ montage->directory=(char *) AcquireQuantumMemory(extent,
+ sizeof(*montage->directory));
+ if ((montage->montage == (char *) NULL) ||
+ (montage->directory == (char *) NULL))
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ x_offset=0;
+ y_offset=0;
+ if (montage_info->tile != (char *) NULL)
+ GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
+ &sans,&sans);
+ y_offset+=(long) title_offset;
+ (void) FormatMagickString(montage->montage,MaxTextExtent,"%ldx%ld%+ld%+ld",
+ (long) (extract_info.width+(extract_info.x+border_width)*2),
+ (long) (extract_info.height+(extract_info.y+border_width)*2+
+ (metrics.ascent-metrics.descent+4)*number_lines+
+ (montage_info->shadow != MagickFalse ? 4 : 0)),x_offset,y_offset);
+ *montage->directory='\0';
+ tile=0;
+ while (tile < MagickMin((long) tiles_per_page,(long) number_images))
+ {
+ (void) ConcatenateMagickString(montage->directory,
+ image_list[tile]->filename,extent);
+ (void) ConcatenateMagickString(montage->directory,"\n",extent);
+ tile++;
+ }
+ progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
+ NULL,montage->client_data);
+ if (texture != (Image *) NULL)
+ (void) TextureImage(montage,texture);
+ if (montage_info->title != (char *) NULL)
+ {
+ char
+ geometry[MaxTextExtent];
+
+ DrawInfo
+ *clone_info;
+
+ TypeMetric
+ metrics;
+
+ /*
+ Annotate composite image with title.
+ */
+ clone_info=CloneDrawInfo(image_info,draw_info);
+ clone_info->gravity=CenterGravity;
+ clone_info->pointsize*=2.0;
+ (void) GetTypeMetrics(image_list[0],clone_info,&metrics);
+ (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu%+ld%+ld",
+ montage->columns,(unsigned long) (metrics.ascent-metrics.descent),
+ 0L,(long) extract_info.y+4);
+ (void) CloneString(&clone_info->geometry,geometry);
+ (void) CloneString(&clone_info->text,title);
+ (void) AnnotateImage(montage,clone_info);
+ clone_info=DestroyDrawInfo(clone_info);
+ }
+ (void) SetImageProgressMonitor(montage,progress_monitor,
+ montage->client_data);
+ /*
+ Copy tile to the composite.
+ */
+ x_offset=0;
+ y_offset=0;
+ if (montage_info->tile != (char *) NULL)
+ GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
+ &sans,&sans);
+ x_offset+=extract_info.x;
+ y_offset+=(long) title_offset+extract_info.y;
+ max_height=0;
+ for (tile=0; tile < MagickMin((long) tiles_per_page,(long) number_images); tile++)
+ {
+ /*
+ Copy this tile to the composite.
+ */
+ image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
+ progress_monitor=SetImageProgressMonitor(image,
+ (MagickProgressMonitor) NULL,image->client_data);
+ width=concatenate != MagickFalse ? image->columns : extract_info.width;
+ if (image->rows > max_height)
+ max_height=image->rows;
+ height=concatenate != MagickFalse ? max_height : extract_info.height;
+ if (border_width != 0)
+ {
+ Image
+ *border_image;
+
+ RectangleInfo
+ border_info;
+
+ /*
+ Put a border around the image.
+ */
+ border_info.width=border_width;
+ border_info.height=border_width;
+ if (montage_info->frame != (char *) NULL)
+ {
+ border_info.width=(width-image->columns+1)/2;
+ border_info.height=(height-image->rows+1)/2;
+ }
+ border_image=BorderImage(image,&border_info,exception);
+ if (border_image != (Image *) NULL)
+ {
+ image=DestroyImage(image);
+ image=border_image;
+ }
+ if ((montage_info->frame != (char *) NULL) &&
+ (image->compose == DstOutCompositeOp))
+ (void) NegateImageChannel(image,OpacityChannel,MagickFalse);
+ }
+ /*
+ Gravitate as specified by the tile gravity.
+ */
+ tile_image->columns=width;
+ tile_image->rows=height;
+ tile_image->gravity=montage_info->gravity;
+ if (image->gravity != UndefinedGravity)
+ tile_image->gravity=image->gravity;
+ (void) FormatMagickString(tile_geometry,MaxTextExtent,"%lux%lu+0+0",
+ image->columns,image->rows);
+ flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
+ x=(long) (geometry.x+border_width);
+ y=(long) (geometry.y+border_width);
+ if ((montage_info->frame != (char *) NULL) && (bevel_width != 0))
+ {
+ FrameInfo
+ extract_info;
+
+ Image
+ *frame_image;
+
+ /*
+ Put an ornamental border around this tile.
+ */
+ extract_info=frame_info;
+ extract_info.width=width+2*frame_info.width;
+ extract_info.height=height+2*frame_info.height;
+ value=GetImageProperty(image,"label");
+ if (value != (const char *) NULL)
+ extract_info.height+=(unsigned long) ((metrics.ascent-
+ metrics.descent+4)*MultilineCensus(value));
+ frame_image=FrameImage(image,&extract_info,exception);
+ if (frame_image != (Image *) NULL)
+ {
+ image=DestroyImage(image);
+ image=frame_image;
+ }
+ x=0;
+ y=0;
+ }
+ if (LocaleCompare(image->magick,"NULL") != 0)
+ {
+ /*
+ Composite background with tile.
+ */
+ if (montage_info->shadow != MagickFalse)
+ {
+ Image
+ *shadow_image;
+
+ /*
+ Shadow image.
+ */
+ (void) QueryColorDatabase("#000000",&image->background_color,
+ exception);
+ shadow_image=ShadowImage(image,80.0,2.0,5,5,exception);
+ if (shadow_image != (Image *) NULL)
+ {
+ InheritException(&shadow_image->exception,exception);
+ (void) CompositeImage(shadow_image,OverCompositeOp,image,0,0);
+ image=DestroyImage(image);
+ image=shadow_image;
+ }
+ }
+ (void) CompositeImage(montage,OverCompositeOp,image,x_offset+x,
+ y_offset+y);
+ value=GetImageProperty(image,"label");
+ if (value != (const char *) NULL)
+ {
+ char
+ geometry[MaxTextExtent];
+
+ /*
+ Annotate composite tile with label.
+ */
+ (void) FormatMagickString(geometry,MaxTextExtent,
+ "%lux%lu%+ld%+ld",(montage_info->frame ? image->columns :
+ width)-2*border_width,(unsigned long) (metrics.ascent-
+ metrics.descent+4)*MultilineCensus(value),x_offset+
+ border_width,(montage_info->frame ? y_offset+height+
+ border_width+4 : y_offset+extract_info.height+border_width+
+ (montage_info->shadow != MagickFalse ? 4 : 0)));
+ (void) CloneString(&draw_info->geometry,geometry);
+ (void) CloneString(&draw_info->text,value);
+ (void) AnnotateImage(montage,draw_info);
+ }
+ }
+ x_offset+=width+(extract_info.x+border_width)*2;
+ if (((tile+1) == (long) tiles_per_page) ||
+ (((tile+1) % tiles_per_row) == 0))
+ {
+ x_offset=extract_info.x;
+ y_offset+=(unsigned long) (height+(extract_info.y+border_width)*2+
+ (metrics.ascent-metrics.descent+4)*number_lines+
+ (montage_info->shadow != MagickFalse ? 4 : 0));
+ max_height=0;
+ }
+ if ((images->progress_monitor != (MagickProgressMonitor) NULL) &&
+ (QuantumTick(tiles,total_tiles) != MagickFalse))
+ {
+ status=images->progress_monitor(MontageImageTag,tiles,total_tiles,
+ images->client_data);
+ if (status == MagickFalse)
+ break;
+ }
+ image_list[tile]=DestroyImage(image_list[tile]);
+ image=DestroyImage(image);
+ tiles++;
+ }
+ if ((i+1) < (long) images_per_page)
+ {
+ /*
+ Allocate next image structure.
+ */
+ AcquireNextImage(clone_info,montage);
+ if (GetNextImageInList(montage) == (Image *) NULL)
+ {
+ montage=DestroyImageList(montage);
+ return((Image *) NULL);
+ }
+ montage=GetNextImageInList(montage);
+ image_list+=tiles_per_page;
+ number_images-=tiles_per_page;
+ }
+ }
+ tile_image=DestroyImage(tile_image);
+ if (texture != (Image *) NULL)
+ texture=DestroyImage(texture);
+ master_list=(Image **) RelinquishMagickMemory(master_list);
+ draw_info=DestroyDrawInfo(draw_info);
+ clone_info=DestroyImageInfo(clone_info);
+ while (GetPreviousImageInList(montage) != (Image *) NULL)
+ montage=GetPreviousImageInList(montage);
+ return(montage);
+}
diff --git a/magick/montage.h b/magick/montage.h
new file mode 100644
index 0000000..61f5108
--- /dev/null
+++ b/magick/montage.h
@@ -0,0 +1,88 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore montage methods.
+*/
+#ifndef _MAGICKCORE_MONTAGE_H
+#define _MAGICKCORE_MONTAGE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedMode,
+ FrameMode,
+ UnframeMode,
+ ConcatenateMode
+} MontageMode;
+
+typedef struct _MontageInfo
+{
+ char
+ *geometry,
+ *tile,
+ *title,
+ *frame,
+ *texture,
+ *font;
+
+ double
+ pointsize;
+
+ unsigned long
+ border_width;
+
+ MagickBooleanType
+ shadow;
+
+ PixelPacket
+ fill,
+ stroke,
+ background_color,
+ border_color,
+ matte_color;
+
+ GravityType
+ gravity;
+
+ char
+ filename[MaxTextExtent];
+
+ MagickBooleanType
+ debug;
+
+ unsigned long
+ signature;
+} MontageInfo;
+
+extern MagickExport Image
+ *MontageImages(const Image *,const MontageInfo *,ExceptionInfo *),
+ *MontageImageList(const ImageInfo *,const MontageInfo *,const Image *,
+ ExceptionInfo *);
+
+extern MagickExport MontageInfo
+ *CloneMontageInfo(const ImageInfo *,const MontageInfo *),
+ *DestroyMontageInfo(MontageInfo *);
+
+extern MagickExport void
+ GetMontageInfo(const ImageInfo *,MontageInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/nt-base.c b/magick/nt-base.c
new file mode 100644
index 0000000..c46b94a
--- /dev/null
+++ b/magick/nt-base.c
@@ -0,0 +1,2251 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N N TTTTT %
+% NN N T %
+% N N N T %
+% N NN T %
+% N N T %
+% %
+% %
+% Windows NT Utility Methods for MagickCore %
+% %
+% Software Design %
+% John Cristy %
+% December 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#if defined(__WINDOWS__)
+#include "magick/client.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/resource_.h"
+#include "magick/timer.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+# include "ltdl.h"
+#endif
+#include "magick/nt-base.h"
+#if defined(MAGICKCORE_CIPHER_SUPPORT)
+#include <ntsecapi.h>
+#include <wincrypt.h>
+#endif
+
+/*
+ Define declarations.
+*/
+#if !defined(MAP_FAILED)
+#define MAP_FAILED ((void *) -1)
+#endif
+
+/*
+ Static declarations.
+*/
+#if !defined(MAGICKCORE_LTDL_DELEGATE)
+static char
+ *lt_slsearchpath = (char *) NULL;
+#endif
+
+static GhostscriptVectors
+ gs_vectors;
+
+static void
+ *gs_dll_handle = (void *) NULL;
+
+/*
+ External declarations.
+*/
+#if !defined(__WINDOWS__)
+extern "C" BOOL WINAPI
+ DllMain(HINSTANCE handle,DWORD reason,LPVOID lpvReserved);
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D l l M a i n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DllMain() is an entry point to the DLL which is called when processes and
+% threads are initialized and terminated, or upon calls to the Windows
+% LoadLibrary and FreeLibrary functions.
+%
+% The function returns TRUE of it succeeds, or FALSE if initialization fails.
+%
+% The format of the DllMain method is:
+%
+% BOOL WINAPI DllMain(HINSTANCE handle,DWORD reason,LPVOID lpvReserved)
+%
+% A description of each parameter follows:
+%
+% o handle: handle to the DLL module
+%
+% o reason: reason for calling function:
+%
+% DLL_PROCESS_ATTACH - DLL is being loaded into virtual address
+% space of current process.
+% DLL_THREAD_ATTACH - Indicates that the current process is
+% creating a new thread. Called under the
+% context of the new thread.
+% DLL_THREAD_DETACH - Indicates that the thread is exiting.
+% Called under the context of the exiting
+% thread.
+% DLL_PROCESS_DETACH - Indicates that the DLL is being unloaded
+% from the virtual address space of the
+% current process.
+%
+% o lpvReserved: Used for passing additional info during DLL_PROCESS_ATTACH
+% and DLL_PROCESS_DETACH.
+%
+*/
+#if defined(_DLL) && defined( ProvideDllMain )
+BOOL WINAPI DllMain(HINSTANCE handle,DWORD reason,LPVOID lpvReserved)
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ {
+ char
+ *module_path;
+
+ ssize_t
+ count;
+
+ module_path=(char *) AcquireQuantumMemory(MaxTextExtent,
+ sizeof(*module_path));
+ if (module_path == (char *) NULL)
+ return(FALSE);
+ count=(ssize_t) GetModuleFileName(handle,module_path,MaxTextExtent);
+ if (count != 0)
+ {
+ char
+ *path;
+
+ for ( ; count > 0; count--)
+ if (module_path[count] == '\\')
+ {
+ module_path[count+1]='\0';
+ break;
+ }
+ MagickCoreGenesis(module_path,MagickFalse);
+ path=(char *) AcquireQuantumMemory(16UL*MaxTextExtent,sizeof(*path));
+ if (path == (char *) NULL)
+ {
+ module_path=DestroyString(module_path);
+ return(FALSE);
+ }
+ count=(ssize_t) GetEnvironmentVariable("PATH",path,16*MaxTextExtent);
+ if ((count != 0) && (strstr(path,module_path) == (char *) NULL))
+ {
+ if ((strlen(module_path)+count+1) < (16*MaxTextExtent-1))
+ {
+ char
+ *variable;
+
+ variable=(char *) AcquireQuantumMemory(16UL*MaxTextExtent,
+ sizeof(*variable));
+ if (variable == (char *) NULL)
+ {
+ path=DestroyString(path);
+ module_path=DestroyString(module_path);
+ return(FALSE);
+ }
+ (void) FormatMagickString(variable,16*MaxTextExtent,
+ "%s;%s",module_path,path);
+ SetEnvironmentVariable("PATH",variable);
+ variable=DestroyString(variable);
+ }
+ }
+ path=DestroyString(path);
+ }
+ module_path=DestroyString(module_path);
+ break;
+ }
+ case DLL_PROCESS_DETACH:
+ {
+ MagickCoreTerminus();
+ break;
+ }
+ default:
+ break;
+ }
+ return(TRUE);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x i t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Exit() calls TerminateProcess for Win95.
+%
+% The format of the exit method is:
+%
+% int Exit(int status)
+%
+% A description of each parameter follows:
+%
+% o status: an integer value representing the status of the terminating
+% process.
+%
+*/
+MagickExport int Exit(int status)
+{
+ if (IsWindows95())
+ TerminateProcess(GetCurrentProcess(),(unsigned int) status);
+ exit(status);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s W i n d o w s 9 5 %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsWindows95() returns true if the system is Windows 95.
+%
+% The format of the IsWindows95 method is:
+%
+% int IsWindows95()
+%
+*/
+MagickExport int IsWindows95()
+{
+ OSVERSIONINFO
+ version_info;
+
+ version_info.dwOSVersionInfoSize=sizeof(version_info);
+ if (GetVersionEx(&version_info) &&
+ (version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS))
+ return(1);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T C l o s e D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTCloseDirectory() closes the named directory stream and frees the DIR
+% structure.
+%
+% The format of the NTCloseDirectory method is:
+%
+% int NTCloseDirectory(DIR *entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+*/
+MagickExport int NTCloseDirectory(DIR *entry)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(entry != (DIR *) NULL);
+ FindClose(entry->hSearch);
+ entry=(DIR *) RelinquishMagickMemory(entry);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T C l o s e L i b r a r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTCloseLibrary() unloads the module associated with the passed handle.
+%
+% The format of the NTCloseLibrary method is:
+%
+% void NTCloseLibrary(void *handle)
+%
+% A description of each parameter follows:
+%
+% o handle: Specifies a handle to a previously loaded dynamic module.
+%
+*/
+MagickExport int NTCloseLibrary(void *handle)
+{
+ if (IsWindows95())
+ return(FreeLibrary((HINSTANCE) handle));
+ return(!(FreeLibrary((HINSTANCE) handle)));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T C o n t r o l H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTControlHandler() registers a control handler that is activated when, for
+% example, a ctrl-c is received.
+%
+% The format of the NTControlHandler method is:
+%
+% int NTControlHandler(void)
+%
+*/
+
+static BOOL ControlHandler(DWORD type)
+{
+ AsynchronousDestroyMagickResources();
+ return(FALSE);
+}
+
+MagickExport int NTControlHandler(void)
+{
+ return(SetConsoleCtrlHandler((PHANDLER_ROUTINE) ControlHandler,TRUE));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T E l a p s e d T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTElapsedTime() returns the elapsed time (in seconds) since the last call to
+% StartTimer().
+%
+% The format of the ElapsedTime method is:
+%
+% double NTElapsedTime(void)
+%
+*/
+MagickExport double NTElapsedTime(void)
+{
+ union
+ {
+ FILETIME
+ filetime;
+
+ __int64
+ filetime64;
+ } elapsed_time;
+
+ SYSTEMTIME
+ system_time;
+
+ GetSystemTime(&system_time);
+ SystemTimeToFileTime(&system_time,&elapsed_time.filetime);
+ return((double) 1.0e-7*elapsed_time.filetime64);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ N T E r r o r H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTErrorHandler() displays an error reason and then terminates the program.
+%
+% The format of the NTErrorHandler method is:
+%
+% void NTErrorHandler(const ExceptionType error,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o error: Specifies the numeric error category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+MagickExport void NTErrorHandler(const ExceptionType error,const char *reason,
+ const char *description)
+{
+ char
+ buffer[3*MaxTextExtent],
+ *message;
+
+ if (reason == (char *) NULL)
+ {
+ MagickCoreTerminus();
+ exit(0);
+ }
+ message=GetExceptionMessage(errno);
+ if ((description != (char *) NULL) && errno)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s) [%s].\n",
+ GetClientName(),reason,description,message);
+ else
+ if (description != (char *) NULL)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s).\n",
+ GetClientName(),reason,description);
+ else
+ if (errno != 0)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s [%s].\n",
+ GetClientName(),reason,message);
+ else
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s.\n",
+ GetClientName(),reason);
+ message=DestroyString(message);
+ (void) MessageBox(NULL,buffer,"ImageMagick Exception",MB_OK | MB_TASKMODAL |
+ MB_SETFOREGROUND | MB_ICONEXCLAMATION);
+ MagickCoreTerminus();
+ exit(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T E x i t L i b r a r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTExitLibrary() exits the dynamic module loading subsystem.
+%
+% The format of the NTExitLibrary method is:
+%
+% int NTExitLibrary(void)
+%
+*/
+MagickExport int NTExitLibrary(void)
+{
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G a t h e r R a n d o m D a t a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGatherRandomData() gathers random data and returns it.
+%
+% The format of the GatherRandomData method is:
+%
+% MagickBooleanType NTGatherRandomData(const size_t length,
+% unsigned char *random)
+%
+% A description of each parameter follows:
+%
+% length: the length of random data buffer
+%
+% random: the random data is returned here.
+%
+*/
+MagickExport MagickBooleanType NTGatherRandomData(const size_t length,
+ unsigned char *random)
+{
+#if defined(MAGICKCORE_CIPHER_SUPPORT) && defined(_MSC_VER) && (_MSC_VER > 1200)
+ HCRYPTPROV
+ handle;
+
+ int
+ status;
+
+ handle=(HCRYPTPROV) NULL;
+ status=CryptAcquireContext(&handle,NULL,MS_DEF_PROV,PROV_RSA_FULL,
+ (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET));
+ if (status == 0)
+ status=CryptAcquireContext(&handle,NULL,MS_DEF_PROV,PROV_RSA_FULL,
+ (CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET));
+ if (status == 0)
+ return(MagickFalse);
+ status=CryptGenRandom(handle,(DWORD) length,random);
+ if (status == 0)
+ {
+ status=CryptReleaseContext(handle,0);
+ return(MagickFalse);
+ }
+ status=CryptReleaseContext(handle,0);
+ if (status == 0)
+ return(MagickFalse);
+#endif
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G e t E x e c u t i o n P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGetExecutionPath() returns the execution path of a program.
+%
+% The format of the GetExecutionPath method is:
+%
+% MagickBooleanType NTGetExecutionPath(char *path,const size_t extent)
+%
+% A description of each parameter follows:
+%
+% o path: the pathname of the executable that started the process.
+%
+% o extent: the maximum extent of the path.
+%
+*/
+MagickExport MagickBooleanType NTGetExecutionPath(char *path,
+ const size_t extent)
+{
+ GetModuleFileName(0,path,(DWORD) extent);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G e t L a s t E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGetLastError() returns the last error that occurred.
+%
+% The format of the NTGetLastError method is:
+%
+% char *NTGetLastError(void)
+%
+*/
+char *NTGetLastError(void)
+{
+ char
+ *reason;
+
+ int
+ status;
+
+ LPVOID
+ buffer;
+
+ status=FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),
+ MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPTSTR) &buffer,0,NULL);
+ if (!status)
+ reason=AcquireString("An unknown error occurred");
+ else
+ {
+ reason=AcquireString((const char *) buffer);
+ LocalFree(buffer);
+ }
+ return(reason);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G e t L i b r a r y E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Lt_dlerror() returns a pointer to a string describing the last error
+% associated with a lt_dl method. Note that this function is not thread
+% safe so it should only be used under the protection of a lock.
+%
+% The format of the NTGetLibraryError method is:
+%
+% const char *NTGetLibraryError(void)
+%
+*/
+MagickExport const char *NTGetLibraryError(void)
+{
+ static char
+ last_error[MaxTextExtent];
+
+ char
+ *error;
+
+ *last_error='\0';
+ error=NTGetLastError();
+ if (error)
+ (void) CopyMagickString(last_error,error,MaxTextExtent);
+ error=DestroyString(error);
+ return(last_error);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G e t L i b r a r y S y m b o l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGetLibrarySymbol() retrieve the procedure address of the method
+% specified by the passed character string.
+%
+% The format of the NTGetLibrarySymbol method is:
+%
+% void *NTGetLibrarySymbol(void *handle,const char *name)
+%
+% A description of each parameter follows:
+%
+% o handle: Specifies a handle to the previously loaded dynamic module.
+%
+% o name: Specifies the procedure entry point to be returned.
+%
+*/
+void *NTGetLibrarySymbol(void *handle,const char *name)
+{
+ LPFNDLLFUNC1
+ lpfnDllFunc1;
+
+ lpfnDllFunc1=(LPFNDLLFUNC1) GetProcAddress((HINSTANCE) handle,name);
+ if (!lpfnDllFunc1)
+ return((void *) NULL);
+ return((void *) lpfnDllFunc1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G e t M o d u l e P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGetModulePath() returns the path of the specified module.
+%
+% The format of the GetModulePath method is:
+%
+% MagickBooleanType NTGetModulePath(const char *module,char *path)
+%
+% A description of each parameter follows:
+%
+% modith: the module name.
+%
+% path: the module path is returned here.
+%
+*/
+MagickExport MagickBooleanType NTGetModulePath(const char *module,char *path)
+{
+ char
+ module_path[MaxTextExtent];
+
+ HMODULE
+ handle;
+
+ long
+ length;
+
+ *path='\0';
+ handle=GetModuleHandle(module);
+ if (handle == (HMODULE) NULL)
+ return(MagickFalse);
+ length=GetModuleFileName(handle,module_path,MaxTextExtent);
+ if (length != 0)
+ GetPathComponent(module_path,HeadPath,path);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t D L L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptDLL() obtains the path to the latest Ghostscript DLL. The
+% method returns MagickFalse if a value is not obtained.
+%
+% The format of the NTGhostscriptDLL method is:
+%
+% int NTGhostscriptDLL(char *path, int length)
+%
+% A description of each parameter follows:
+%
+% o path: Pointer to buffer in which to return result.
+%
+% o length: Length of buffer
+%
+*/
+
+#define GS_PRODUCT_AFPL "AFPL Ghostscript"
+#define GS_PRODUCT_ALADDIN "Aladdin Ghostscript"
+#define GS_PRODUCT_GNU "GNU Ghostscript"
+#define GS_PRODUCT_GPL "GPL Ghostscript"
+#define GS_MINIMUM_VERSION 550
+
+/*
+ Get Ghostscript versions for given product.
+ Store results starting at pver + 1 + offset.
+ Returns total number of versions in pver.
+*/
+static int NTGhostscriptProductVersions(int *pver,int offset,
+ const char *gs_productfamily)
+{
+ HKEY
+ hkey,
+ hkeyroot;
+
+ DWORD
+ cbData;
+
+ char
+ key[MaxTextExtent],
+ *p;
+
+ int
+ n = 0,
+ ver;
+
+ (void) FormatMagickString(key,MaxTextExtent,"Software\\%s",gs_productfamily);
+ hkeyroot = HKEY_LOCAL_MACHINE;
+ if (RegOpenKeyExA(hkeyroot,key,0,KEY_READ,&hkey) == ERROR_SUCCESS)
+ {
+ /*
+ Now enumerate the keys.
+ */
+ cbData = sizeof(key) / sizeof(char);
+ while (RegEnumKeyA(hkey,n,key,cbData) == ERROR_SUCCESS)
+ {
+ n++;
+ ver = 0;
+ p = key;
+ while (*p && (*p!='.')) {
+ ver = (ver * 10) + (*p - '0')*100;
+ p++;
+ }
+ if (*p == '.')
+ p++;
+ if (*p)
+ {
+ ver+=10*(*p-'0');
+ p++;
+ }
+ if (*p)
+ ver+=(*p - '0');
+ if ((n+offset) < pver[0])
+ pver[n+offset]=ver;
+ }
+ RegCloseKey(hkey);
+ }
+ return(n+offset);
+}
+
+/* Query registry to find which versions of Ghostscript are installed.
+ * Return version numbers in an integer array.
+ * On entry, the first element in the array must be the array size
+ * in elements.
+ * If all is well, TRUE is returned.
+ * On exit, the first element is set to the number of Ghostscript
+ * versions installed, and subsequent elements to the version
+ * numbers of Ghostscript.
+ * e.g. on entry {5, 0, 0, 0, 0}, on exit {3, 550, 600, 596, 0}
+ * Returned version numbers may not be sorted.
+ *
+ * If Ghostscript is not installed at all, return FALSE
+ * and set pver[0] to 0.
+ * If the array is not large enough, return FALSE
+ * and set pver[0] to the number of Ghostscript versions installed.
+ */
+
+static int NTGhostscriptEnumerateVersions(int *pver)
+{
+ int
+ n;
+
+ assert(pver != (int *) NULL);
+ n=NTGhostscriptProductVersions(pver,0,GS_PRODUCT_AFPL);
+ n=NTGhostscriptProductVersions(pver,n,GS_PRODUCT_ALADDIN);
+ n=NTGhostscriptProductVersions(pver,n,GS_PRODUCT_GNU);
+ n=NTGhostscriptProductVersions(pver,n,GS_PRODUCT_GPL);
+ if (n >= pver[0])
+ {
+ pver[0]=n;
+ return(FALSE); /* too small */
+ }
+ if (n == 0)
+ {
+ pver[0] = 0;
+ return(FALSE); /* not installed */
+ }
+ pver[0]=n;
+ return(TRUE);
+}
+
+/*
+ Get a named registry value.
+ Key = hkeyroot\\key, named value = name.
+ name, ptr, plen and return values are the same as in gp_getenv();
+*/
+static int NTGetRegistryValue(HKEY hkeyroot,const char *key,const char *name,
+ char *ptr,int *plen)
+{
+ HKEY
+ hkey;
+
+ DWORD
+ cbData,
+ keytype;
+
+ BYTE
+ b,
+ *bptr = (BYTE *)ptr;
+
+ LONG
+ rc;
+
+ if (RegOpenKeyExA(hkeyroot,key,0,KEY_READ,&hkey) == ERROR_SUCCESS)
+ {
+ keytype = REG_SZ;
+ cbData = *plen;
+ if (bptr == (BYTE *)NULL)
+ bptr=(&b); /* Registry API won't return ERROR_MORE_DATA */
+ /* if ptr is NULL */
+ rc=RegQueryValueExA(hkey,(char *) name,0,&keytype,bptr,&cbData);
+ RegCloseKey(hkey);
+ if (rc == ERROR_SUCCESS)
+ {
+ *plen = cbData;
+ return 0; /* found environment variable and copied it */
+ } else if (rc == ERROR_MORE_DATA) {
+ /* buffer wasn't large enough */
+ *plen = cbData;
+ return -1;
+ }
+ }
+ return(1); /* not found */
+}
+
+static int NTGhostscriptGetProductString(int gs_revision,const char *name,
+ char *ptr,int len,const char *gs_productfamily)
+{
+ /* If using Win32, look in the registry for a value with
+ * the given name. The registry value will be under the key
+ * HKEY_CURRENT_USER\Software\AFPL Ghostscript\N.NN
+ * or if that fails under the key
+ * HKEY_LOCAL_MACHINE\Software\AFPL Ghostscript\N.NN
+ * where "AFPL Ghostscript" is actually gs_productfamily
+ * and N.NN is obtained from gs_revision.
+ */
+
+ char
+ dotversion[MaxTextExtent],
+ key[MaxTextExtent];
+
+ int
+ code,
+ length;
+
+ DWORD version = GetVersion();
+
+ if (((HIWORD(version) & 0x8000) != 0) && ((HIWORD(version) & 0x4000) == 0))
+ {
+ /* Win32s */
+ return FALSE;
+ }
+ (void) FormatMagickString(dotversion,MaxTextExtent,"%d.%02d",(int)
+ (gs_revision/100),(int) (gs_revision % 100));
+ (void) FormatMagickString(key,MaxTextExtent,"Software\\%s\\%s",
+ gs_productfamily,dotversion);
+ length = len;
+ code = NTGetRegistryValue(HKEY_CURRENT_USER, key, name, ptr, &length);
+ if ( code == 0 )
+ return TRUE; /* found it */
+ length = len;
+ code = NTGetRegistryValue(HKEY_LOCAL_MACHINE, key, name, ptr, &length);
+ if ( code == 0 )
+ return TRUE; /* found it */
+ return FALSE;
+}
+
+static int NTGhostscriptGetString(int gs_revision,const char *name,char *ptr,
+ int len)
+{
+ if (NTGhostscriptGetProductString(gs_revision,name,ptr,len,GS_PRODUCT_AFPL))
+ return(TRUE);
+ if (NTGhostscriptGetProductString(gs_revision,name,ptr,len,GS_PRODUCT_ALADDIN))
+ return(TRUE);
+ if (NTGhostscriptGetProductString(gs_revision,name,ptr,len,GS_PRODUCT_GNU))
+ return(TRUE);
+ if (NTGhostscriptGetProductString(gs_revision,name,ptr,len,GS_PRODUCT_GPL))
+ return(TRUE);
+ return(FALSE);
+}
+
+static int NTGetLatestGhostscript( void )
+{
+ int
+ count,
+ i,
+ gsver,
+ *ver;
+
+ DWORD version = GetVersion();
+ if ( ((HIWORD(version) & 0x8000)!=0) && ((HIWORD(version) & 0x4000)==0) )
+ return FALSE; /* win32s */
+
+ count = 1;
+ NTGhostscriptEnumerateVersions(&count);
+ if (count < 1)
+ return FALSE;
+ ver=(int *) AcquireQuantumMemory(count+1UL,sizeof(*ver));
+ if (ver == (int *)NULL)
+ return(FALSE);
+ ver[0]=count+1;
+ if (!NTGhostscriptEnumerateVersions(ver))
+ {
+ ver=(int *) RelinquishMagickMemory(ver);
+ return FALSE;
+ }
+ gsver = 0;
+ for (i=1; i<=ver[0]; i++) {
+ if (ver[i] > gsver)
+ gsver = ver[i];
+ }
+ ver=(int *) RelinquishMagickMemory(ver);
+ return(gsver);
+}
+
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t D L L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptDLL() obtains the path to the latest Ghostscript DLL. The
+% method returns MagickFalse if a value is not obtained.
+%
+% The format of the NTGhostscriptDLL method is:
+%
+% int NTGhostscriptDLL( char *path, int length)
+%
+% A description of each parameter follows:
+%
+% o path: Pointer to path buffer to update
+%
+% o length: Allocation size of path buffer.
+%
+*/
+MagickExport int NTGhostscriptDLL(char *path, int length)
+{
+ int
+ gsver;
+
+ char
+ buf[256];
+
+ *path='\0';
+ gsver = NTGetLatestGhostscript();
+ if ((gsver == FALSE) || (gsver < GS_MINIMUM_VERSION))
+ return FALSE;
+
+ if (!NTGhostscriptGetString(gsver, "GS_DLL", buf, sizeof(buf)))
+ return FALSE;
+
+ (void) CopyMagickString(path,buf,length+1);
+ return(TRUE);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t D L L V e c t o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptDLLVectors() returns a GhostscriptVectors structure containing
+% function vectors to invoke Ghostscript DLL functions. A null pointer is
+% returned if there is an error with loading the DLL or retrieving the
+% function vectors.
+%
+% The format of the NTGhostscriptDLLVectors method is:
+%
+% const GhostscriptVectors *NTGhostscriptDLLVectors(void)
+%
+*/
+MagickExport const GhostscriptVectors *NTGhostscriptDLLVectors( void )
+{
+ if (NTGhostscriptLoadDLL())
+ return(&gs_vectors);
+ return((GhostscriptVectors*) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t E X E %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptEXE() obtains the path to the latest Ghostscript executable.
+% The method returns MagickFalse if a value is not obtained.
+%
+% The format of the NTGhostscriptEXE method is:
+%
+% int NTGhostscriptEXE(char *path, int length)
+%
+% A description of each parameter follows:
+%
+% o path: Pointer to buffer in which to return result.
+%
+% o length: Length of buffer
+%
+*/
+MagickExport int NTGhostscriptEXE(char *path,int length)
+{
+ int
+ gsver;
+
+ char
+ buf[256],
+ *p;
+
+ (void) CopyMagickString(path,"gswin32c.exe",length);
+ gsver=NTGetLatestGhostscript();
+ if ((gsver == FALSE) || (gsver < GS_MINIMUM_VERSION))
+ return(FALSE);
+ if (!NTGhostscriptGetString(gsver, "GS_DLL", buf, sizeof(buf)))
+ return(FALSE);
+ p=strrchr(buf, '\\');
+ if (p) {
+ p++;
+ *p = 0;
+ (void) CopyMagickString(p,"gswin32c.exe",sizeof(buf));
+ (void) CopyMagickString(path,buf,length+1);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t F o n t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptFonts() gets the path to the Ghostscript fonts. The method
+% returns MagickFalse if an error occurs otherwise MagickTrue.
+%
+% The format of the NTGhostscriptFonts method is:
+%
+% int NTGhostscriptFonts(char *path,int length)
+%
+% A description of each parameter follows:
+%
+% o path: Pointer to buffer in which to return result.
+%
+% o length: Length of buffer.
+%
+*/
+MagickExport int NTGhostscriptFonts(char *path,int length)
+{
+ char
+ buffer[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ int
+ version;
+
+ register char
+ *p,
+ *q;
+
+ *path='\0';
+ version=NTGetLatestGhostscript();
+ if ((version == FALSE) || (version < GS_MINIMUM_VERSION))
+ return(FALSE);
+ if (!NTGhostscriptGetString(version,"GS_LIB",buffer,MaxTextExtent))
+ return(FALSE);
+ for (p=buffer-1; p != (char *) NULL; p=strchr(p+1,DirectoryListSeparator))
+ {
+ (void) CopyMagickString(path,p+1,length+1);
+ q=strchr(path,DirectoryListSeparator);
+ if (q != (char *) NULL)
+ *q='\0';
+ FormatMagickString(filename,MaxTextExtent,"%s%sfonts.dir",path,
+ DirectorySeparator);
+ if (IsPathAccessible(filename) != MagickFalse)
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t L o a d D L L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptLoadDLL() attempts to load the Ghostscript DLL and returns
+% MagickTrue if it succeeds.
+%
+% The format of the NTGhostscriptLoadDLL method is:
+%
+% int NTGhostscriptLoadDLL(void)
+%
+*/
+MagickExport int NTGhostscriptLoadDLL(void)
+{
+ char
+ module_path[MaxTextExtent];
+
+ if (gs_dll_handle != (void *) NULL)
+ return(MagickTrue);
+ if (NTGhostscriptDLL(module_path,sizeof(module_path)) == MagickFalse)
+ return(MagickFalse);
+ gs_dll_handle=NTOpenLibrary(module_path);
+ if (gs_dll_handle == (void *) NULL)
+ return(MagickFalse);
+ (void) ResetMagickMemory((void *) &gs_vectors,0,sizeof(GhostscriptVectors));
+ gs_vectors.exit=(int (MagickDLLCall *)
+ (gs_main_instance *)) NTGetLibrarySymbol(gs_dll_handle,"gsapi_exit");
+ gs_vectors.init_with_args=(int (MagickDLLCall *)
+ (gs_main_instance *,int,char **))
+ (NTGetLibrarySymbol(gs_dll_handle,"gsapi_init_with_args"));
+ gs_vectors.new_instance=(int (MagickDLLCall *) (gs_main_instance **,void *))
+ (NTGetLibrarySymbol(gs_dll_handle,"gsapi_new_instance"));
+ gs_vectors.run_string=(int (MagickDLLCall *)
+ (gs_main_instance *,const char *,int,int *))
+ (NTGetLibrarySymbol(gs_dll_handle,"gsapi_run_string"));
+ gs_vectors.delete_instance=(void (MagickDLLCall *)(gs_main_instance *))
+ (NTGetLibrarySymbol(gs_dll_handle,"gsapi_delete_instance"));
+ if ((gs_vectors.exit == NULL) ||
+ (gs_vectors.init_with_args == NULL) ||
+ (gs_vectors.new_instance == NULL) ||
+ (gs_vectors.run_string == NULL) ||
+ (gs_vectors.delete_instance == NULL))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T G h o s t s c r i p t U n L o a d D L L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTGhostscriptUnLoadDLL() unloads the Ghostscript DLL if it is loaded.
+%
+% The format of the NTGhostscriptUnLoadDLL method is:
+%
+% int NTGhostscriptUnLoadDLL(void)
+%
+*/
+MagickExport int NTGhostscriptUnLoadDLL(void)
+{
+ if (gs_dll_handle == (void *) NULL)
+ return(MagickFalse);
+ NTCloseLibrary(gs_dll_handle);
+ gs_dll_handle=(void *) NULL;
+ (void) ResetMagickMemory((void *) &gs_vectors,0,sizeof(GhostscriptVectors));
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T I n i t i a l i z e L i b r a r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTInitializeLibrary() initializes the dynamic module loading subsystem.
+%
+% The format of the NTInitializeLibrary method is:
+%
+% int NTInitializeLibrary(void)
+%
+*/
+MagickExport int NTInitializeLibrary(void)
+{
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ N T M a p M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Mmap() emulates the Unix method of the same name.
+%
+% The format of the NTMapMemory method is:
+%
+% MagickExport void *NTMapMemory(char *address,size_t length,int protection,
+% int access,int file,MagickOffsetType offset)
+%
+*/
+MagickExport void *NTMapMemory(char *address,size_t length,int protection,
+ int flags,int file,MagickOffsetType offset)
+{
+ DWORD
+ access_mode,
+ high_length,
+ high_offset,
+ low_length,
+ low_offset,
+ protection_mode;
+
+ HANDLE
+ file_handle,
+ map_handle;
+
+ void
+ *map;
+
+ access_mode=0;
+ file_handle=INVALID_HANDLE_VALUE;
+ low_length=(DWORD) (length & 0xFFFFFFFFUL);
+ high_length=(DWORD) ((((MagickOffsetType) length) >> 32) & 0xFFFFFFFFUL);
+ map_handle=INVALID_HANDLE_VALUE;
+ map=(void *) NULL;
+ low_offset=(DWORD) (offset & 0xFFFFFFFFUL);
+ high_offset=(DWORD) ((offset >> 32) & 0xFFFFFFFFUL);
+ protection_mode=0;
+ if (protection & PROT_WRITE)
+ {
+ access_mode=FILE_MAP_WRITE;
+ if (!(flags & MAP_PRIVATE))
+ protection_mode=PAGE_READWRITE;
+ else
+ {
+ access_mode=FILE_MAP_COPY;
+ protection_mode=PAGE_WRITECOPY;
+ }
+ }
+ else
+ if (protection & PROT_READ)
+ {
+ access_mode=FILE_MAP_READ;
+ protection_mode=PAGE_READONLY;
+ }
+ if ((file == -1) && (flags & MAP_ANONYMOUS))
+ file_handle=INVALID_HANDLE_VALUE;
+ else
+ file_handle=(HANDLE) _get_osfhandle(file);
+ map_handle=CreateFileMapping(file_handle,0,protection_mode,high_length,
+ low_length,0);
+ if (map_handle)
+ {
+ map=(void *) MapViewOfFile(map_handle,access_mode,high_offset,low_offset,
+ length);
+ CloseHandle(map_handle);
+ }
+ if (map == (void *) NULL)
+ return((void *) MAP_FAILED);
+ return((void *) ((char *) map));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T O p e n D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTOpenDirectory() opens the directory named by filename and associates a
+% directory stream with it.
+%
+% The format of the NTOpenDirectory method is:
+%
+% DIR *NTOpenDirectory(const char *path)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+*/
+MagickExport DIR *NTOpenDirectory(const char *path)
+{
+ char
+ file_specification[MaxTextExtent];
+
+ DIR
+ *entry;
+
+ size_t
+ length;
+
+ assert(path != (const char *) NULL);
+ length=CopyMagickString(file_specification,path,MaxTextExtent);
+ if (length >= MaxTextExtent)
+ return((DIR *) NULL);
+ length=ConcatenateMagickString(file_specification,DirectorySeparator,
+ MaxTextExtent);
+ if (length >= MaxTextExtent)
+ return((DIR *) NULL);
+ entry=(DIR *) AcquireMagickMemory(sizeof(DIR));
+ if (entry != (DIR *) NULL)
+ {
+ entry->firsttime=TRUE;
+ entry->hSearch=FindFirstFile(file_specification,&entry->Win32FindData);
+ }
+ if (entry->hSearch == INVALID_HANDLE_VALUE)
+ {
+ length=ConcatenateMagickString(file_specification,"\\*.*",MaxTextExtent);
+ if (length >= MaxTextExtent)
+ {
+ entry=(DIR *) RelinquishMagickMemory(entry);
+ return((DIR *) NULL);
+ }
+ entry->hSearch=FindFirstFile(file_specification,&entry->Win32FindData);
+ if (entry->hSearch == INVALID_HANDLE_VALUE)
+ {
+ entry=(DIR *) RelinquishMagickMemory(entry);
+ return((DIR *) NULL);
+ }
+ }
+ return(entry);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T O p e n L i b r a r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTOpenLibrary() loads a dynamic module into memory and returns a handle that
+% can be used to access the various procedures in the module.
+%
+% The format of the NTOpenLibrary method is:
+%
+% void *NTOpenLibrary(const char *filename)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies a pointer to string representing dynamic module that
+% is to be loaded.
+%
+*/
+
+static const char *GetSearchPath( void )
+{
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+ return(lt_dlgetsearchpath());
+#else
+ return(lt_slsearchpath);
+#endif
+}
+
+MagickExport void *NTOpenLibrary(const char *filename)
+{
+#define MaxPathElements 31
+
+ char
+ buffer[MaxTextExtent];
+
+ int
+ index;
+
+ register const char
+ *p,
+ *q;
+
+ register int
+ i;
+
+ UINT
+ mode;
+
+ void
+ *handle;
+
+ mode=SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+ handle=(void *) LoadLibraryEx(filename,NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
+ if ((handle != (void *) NULL) || (GetSearchPath() == (char *) NULL))
+ {
+ SetErrorMode(mode);
+ return(handle);
+ }
+ p=(char *) GetSearchPath();
+ index=0;
+ while (index < MaxPathElements)
+ {
+ q=strchr(p,DirectoryListSeparator);
+ if (q == (char *) NULL)
+ {
+ (void) CopyMagickString(buffer,p,MaxTextExtent);
+ (void) ConcatenateMagickString(buffer,"\\",MaxTextExtent);
+ (void) ConcatenateMagickString(buffer,filename,MaxTextExtent);
+ handle=(void *) LoadLibraryEx(buffer,NULL,
+ LOAD_WITH_ALTERED_SEARCH_PATH);
+ break;
+ }
+ i=q-p;
+ (void) CopyMagickString(buffer,p,i+1);
+ (void) ConcatenateMagickString(buffer,"\\",MaxTextExtent);
+ (void) ConcatenateMagickString(buffer,filename,MaxTextExtent);
+ handle=(void *) LoadLibraryEx(buffer,NULL,LOAD_WITH_ALTERED_SEARCH_PATH);
+ if (handle != (void *) NULL)
+ break;
+ p=q+1;
+ }
+ SetErrorMode(mode);
+ return(handle);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T R e a d D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTReadDirectory() returns a pointer to a structure representing the
+% directory entry at the current position in the directory stream to which
+% entry refers.
+%
+% The format of the NTReadDirectory
+%
+% NTReadDirectory(entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+*/
+MagickExport struct dirent *NTReadDirectory(DIR *entry)
+{
+ int
+ status;
+
+ size_t
+ length;
+
+ if (entry == (DIR *) NULL)
+ return((struct dirent *) NULL);
+ if (!entry->firsttime)
+ {
+ status=FindNextFile(entry->hSearch,&entry->Win32FindData);
+ if (status == 0)
+ return((struct dirent *) NULL);
+ }
+ length=CopyMagickString(entry->file_info.d_name,
+ entry->Win32FindData.cFileName,sizeof(entry->file_info.d_name));
+ if (length >= sizeof(entry->file_info.d_name))
+ return((struct dirent *) NULL);
+ entry->firsttime=FALSE;
+ entry->file_info.d_namlen=(int) strlen(entry->file_info.d_name);
+ return(&entry->file_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T R e g i s t r y K e y L o o k u p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTRegistryKeyLookup() returns ImageMagick installation path settings
+% stored in the Windows Registry. Path settings are specific to the
+% installed ImageMagick version so that multiple Image Magick installations
+% may coexist.
+%
+% Values are stored in the registry under a base path path similar to
+% "HKEY_LOCAL_MACHINE/SOFTWARE\ImageMagick\5.5.7\Q:16". The provided subkey
+% is appended to this base path to form the full key.
+%
+% The format of the NTRegistryKeyLookup method is:
+%
+% unsigned char *NTRegistryKeyLookup(const char *subkey)
+%
+% A description of each parameter follows:
+%
+% o subkey: Specifies a string that identifies the registry object.
+% Currently supported sub-keys include: "BinPath", "ConfigurePath",
+% "LibPath", "CoderModulesPath", "FilterModulesPath", "SharePath".
+%
+*/
+MagickExport unsigned char *NTRegistryKeyLookup(const char *subkey)
+{
+ char
+ package_key[MaxTextExtent];
+
+ DWORD
+ size,
+ type;
+
+ HKEY
+ registry_key;
+
+ LONG
+ status;
+
+ unsigned char
+ *value;
+
+ /*
+ Look-up base key.
+ */
+ (void) FormatMagickString(package_key,MaxTextExtent,"SOFTWARE\\%s\\%s\\Q:%d",
+ MagickPackageName,MagickLibVersionText,MAGICKCORE_QUANTUM_DEPTH);
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),"%s",package_key);
+ registry_key=(HKEY) INVALID_HANDLE_VALUE;
+ status=RegOpenKeyExA(HKEY_LOCAL_MACHINE,package_key,0,KEY_READ,®istry_key);
+ if (status != ERROR_SUCCESS)
+ {
+ registry_key=(HKEY) INVALID_HANDLE_VALUE;
+ return((unsigned char *) NULL);
+ }
+ /*
+ Look-up sub key.
+ */
+ size=32;
+ value=(unsigned char *) AcquireQuantumMemory(size,sizeof(*value));
+ if (value == (unsigned char *) NULL)
+ {
+ RegCloseKey(registry_key);
+ return((unsigned char *) NULL);
+ }
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),"%s",subkey);
+ status=RegQueryValueExA(registry_key,subkey,0,&type,value,&size);
+ if ((status == ERROR_MORE_DATA) && (type == REG_SZ))
+ {
+ value=(unsigned char *) ResizeQuantumMemory(value,size,sizeof(*value));
+ if (value == (BYTE *) NULL)
+ {
+ RegCloseKey(registry_key);
+ return((unsigned char *) NULL);
+ }
+ status=RegQueryValueExA(registry_key,subkey,0,&type,value,&size);
+ }
+ RegCloseKey(registry_key);
+ if ((type != REG_SZ) || (status != ERROR_SUCCESS))
+ value=(unsigned char *) RelinquishMagickMemory(value);
+ return((unsigned char *) value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T R e p o r t E v e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTReportEvent() reports an event.
+%
+% The format of the NTReportEvent method is:
+%
+% MagickBooleanType NTReportEvent(const char *event,
+% const MagickBooleanType error)
+%
+% A description of each parameter follows:
+%
+% o event: the event.
+%
+% o error: MagickTrue the event is an error.
+%
+*/
+MagickExport MagickBooleanType NTReportEvent(const char *event,
+ const MagickBooleanType error)
+{
+ const char
+ *events[1];
+
+ HANDLE
+ handle;
+
+ WORD
+ type;
+
+ handle=RegisterEventSource(NULL,MAGICKCORE_PACKAGE_NAME);
+ if (handle == NULL)
+ return(MagickFalse);
+ events[0]=event;
+ type=error ? EVENTLOG_ERROR_TYPE : EVENTLOG_WARNING_TYPE;
+ ReportEvent(handle,type,0,0,NULL,1,0,events,NULL);
+ DeregisterEventSource(handle);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T R e s o u r c e T o B l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTResourceToBlob() returns a blob containing the contents of the resource
+% in the current executable specified by the id parameter. This currently
+% used to retrieve MGK files tha have been embedded into the various command
+% line utilities.
+%
+% The format of the NTResourceToBlob method is:
+%
+% unsigned char *NTResourceToBlob(const char *id)
+%
+% A description of each parameter follows:
+%
+% o id: Specifies a string that identifies the resource.
+%
+*/
+MagickExport unsigned char *NTResourceToBlob(const char *id)
+{
+ char
+ path[MaxTextExtent];
+
+ DWORD
+ length;
+
+ HGLOBAL
+ global;
+
+ HMODULE
+ handle;
+
+ HRSRC
+ resource;
+
+ unsigned char
+ *blob,
+ *value;
+
+ assert(id != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",id);
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",GetClientPath(),
+ DirectorySeparator,GetClientName());
+ if (IsPathAccessible(path) != MagickFalse)
+ handle=GetModuleHandle(path);
+ else
+ handle=GetModuleHandle(0);
+ if (!handle)
+ return((unsigned char *) NULL);
+ resource=FindResource(handle,id,"IMAGEMAGICK");
+ if (!resource)
+ return((unsigned char *) NULL);
+ global=LoadResource(handle,resource);
+ if (!global)
+ return((unsigned char *) NULL);
+ length=SizeofResource(handle,resource);
+ value=(unsigned char *) LockResource(global);
+ if (!value)
+ {
+ FreeResource(global);
+ return((unsigned char *) NULL);
+ }
+ blob=(unsigned char *) AcquireQuantumMemory(length+MaxTextExtent,
+ sizeof(*blob));
+ if (blob != (unsigned char *) NULL)
+ {
+ (void) CopyMagickMemory(blob,value,length);
+ blob[length]='\0';
+ }
+ UnlockResource(global);
+ FreeResource(global);
+ return(blob);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T S e e k D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTSeekDirectory() sets the position of the next NTReadDirectory() operation
+% on the directory stream.
+%
+% The format of the NTSeekDirectory method is:
+%
+% void NTSeekDirectory(DIR *entry,long position)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+% o position: specifies the position associated with the directory
+% stream.
+%
+*/
+MagickExport void NTSeekDirectory(DIR *entry,long position)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(entry != (DIR *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T S e t S e a r c h P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTSetSearchPath() sets the current locations that the subsystem should
+% look at to find dynamically loadable modules.
+%
+% The format of the NTSetSearchPath method is:
+%
+% int NTSetSearchPath(const char *path)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies a pointer to string representing the search path
+% for DLL's that can be dynamically loaded.
+%
+*/
+MagickExport int NTSetSearchPath(const char *path)
+{
+#if defined(MAGICKCORE_LTDL_DELEGATE)
+ lt_dlsetsearchpath(path);
+#else
+ if (lt_slsearchpath != (char *) NULL)
+ lt_slsearchpath=DestroyString(lt_slsearchpath);
+ if (path != (char *) NULL)
+ lt_slsearchpath=AcquireString(path);
+#endif
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ N T S y n c M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTSyncMemory() emulates the Unix method of the same name.
+%
+% The format of the NTSyncMemory method is:
+%
+% int NTSyncMemory(void *address,size_t length,int flags)
+%
+% A description of each parameter follows:
+%
+% o address: the address of the binary large object.
+%
+% o length: the length of the binary large object.
+%
+% o flags: Option flags (ignored for Windows).
+%
+*/
+MagickExport int NTSyncMemory(void *address,size_t length,int flags)
+{
+ if (FlushViewOfFile(address,length) == MagickFalse)
+ return(-1);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T S y s t e m C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTSystemCommand() executes the specified command and waits until it
+% terminates. The returned value is the exit status of the command.
+%
+% The format of the NTSystemComman method is:
+%
+% int NTSystemComman(const char *command)
+%
+% A description of each parameter follows:
+%
+% o command: This string is the command to execute.
+%
+*/
+MagickExport int NTSystemCommand(const char *command)
+{
+ char
+ local_command[MaxTextExtent];
+
+ DWORD
+ child_status;
+
+ int
+ status;
+
+ MagickBooleanType
+ background_process;
+
+ PROCESS_INFORMATION
+ process_info;
+
+ STARTUPINFO
+ startup_info;
+
+ if (command == (char *) NULL)
+ return(-1);
+ GetStartupInfo(&startup_info);
+ startup_info.dwFlags=STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow=SW_SHOWMINNOACTIVE;
+ (void) CopyMagickString(local_command,command,MaxTextExtent);
+ background_process=command[strlen(command)-1] == '&' ? MagickTrue :
+ MagickFalse;
+ if (background_process)
+ local_command[strlen(command)-1]='\0';
+ if (command[strlen(command)-1] == '|')
+ local_command[strlen(command)-1]='\0';
+ else
+ startup_info.wShowWindow=SW_SHOWDEFAULT;
+ status=CreateProcess((LPCTSTR) NULL,local_command,
+ (LPSECURITY_ATTRIBUTES) NULL,(LPSECURITY_ATTRIBUTES) NULL,(BOOL) FALSE,
+ (DWORD) NORMAL_PRIORITY_CLASS,(LPVOID) NULL,(LPCSTR) NULL,&startup_info,
+ &process_info);
+ if (status == 0)
+ return(-1);
+ if (background_process)
+ return(status == 0);
+ status=WaitForSingleObject(process_info.hProcess,INFINITE);
+ if (status != WAIT_OBJECT_0)
+ return(status);
+ status=GetExitCodeProcess(process_info.hProcess,&child_status);
+ if (status == 0)
+ return(-1);
+ CloseHandle(process_info.hProcess);
+ CloseHandle(process_info.hThread);
+ return((int) child_status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T S y s t e m C o n i f i g u r a t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTSystemConfiguration() provides a way for the application to determine
+% values for system limits or options at runtime.
+%
+% The format of the exit method is:
+%
+% long NTSystemConfiguration(int name)
+%
+% A description of each parameter follows:
+%
+% o name: _SC_PAGE_SIZE or _SC_PHYS_PAGES.
+%
+*/
+MagickExport long NTSystemConfiguration(int name)
+{
+ switch (name)
+ {
+ case _SC_PAGESIZE:
+ {
+ SYSTEM_INFO
+ system_info;
+
+ GetSystemInfo(&system_info);
+ return(system_info.dwPageSize);
+ }
+ case _SC_PHYS_PAGES:
+ {
+ HMODULE
+ handle;
+
+ LPFNDLLFUNC2
+ module;
+
+ NTMEMORYSTATUSEX
+ status;
+
+ SYSTEM_INFO
+ system_info;
+
+ handle=GetModuleHandle("kernel32.dll");
+ if (handle == (HMODULE) NULL)
+ return(0L);
+ GetSystemInfo(&system_info);
+ module=(LPFNDLLFUNC2) NTGetLibrarySymbol(handle,"GlobalMemoryStatusEx");
+ if (module == (LPFNDLLFUNC2) NULL)
+ {
+ MEMORYSTATUS
+ status;
+
+ GlobalMemoryStatus(&status);
+ return((long) status.dwTotalPhys/system_info.dwPageSize);
+ }
+ status.dwLength=sizeof(status);
+ if (module(&status) == 0)
+ return(0L);
+ return((long) status.ullTotalPhys/system_info.dwPageSize);
+ }
+ case _SC_OPEN_MAX:
+ return(2048);
+ default:
+ break;
+ }
+ return(-1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T T e l l D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTTellDirectory() returns the current location associated with the named
+% directory stream.
+%
+% The format of the NTTellDirectory method is:
+%
+% long NTTellDirectory(DIR *entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+*/
+MagickExport long NTTellDirectory(DIR *entry)
+{
+ assert(entry != (DIR *) NULL);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T T r u n c a t e F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTTruncateFile() truncates a file to a specified length.
+%
+% The format of the NTTruncateFile method is:
+%
+% int NTTruncateFile(int file,off_t length)
+%
+% A description of each parameter follows:
+%
+% o file: the file.
+%
+% o length: the file length.
+%
+*/
+MagickExport int NTTruncateFile(int file,off_t length)
+{
+ DWORD
+ file_pointer;
+
+ long
+ file_handle,
+ high,
+ low;
+
+ file_handle=_get_osfhandle(file);
+ if (file_handle == -1L)
+ return(-1);
+ low=(long) (length & 0xffffffffUL);
+ high=(long) ((((MagickOffsetType) length) >> 32) & 0xffffffffUL);
+ file_pointer=SetFilePointer((HANDLE) file_handle,low,&high,FILE_BEGIN);
+ if ((file_pointer == 0xFFFFFFFF) && (GetLastError() != NO_ERROR))
+ return(-1);
+ if (SetEndOfFile((HANDLE) file_handle) == 0)
+ return(-1);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ N T U n m a p M e m o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTUnmapMemory() emulates the Unix munmap method.
+%
+% The format of the NTUnmapMemory method is:
+%
+% int NTUnmapMemory(void *map,size_t length)
+%
+% A description of each parameter follows:
+%
+% o map: the address of the binary large object.
+%
+% o length: the length of the binary large object.
+%
+*/
+MagickExport int NTUnmapMemory(void *map,size_t length)
+{
+ if (UnmapViewOfFile(map) == 0)
+ return(-1);
+ return(0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T U s e r T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTUserTime() returns the total time the process has been scheduled (e.g.
+% seconds) since the last call to StartTimer().
+%
+% The format of the UserTime method is:
+%
+% double NTUserTime(void)
+%
+*/
+MagickExport double NTUserTime(void)
+{
+ DWORD
+ status;
+
+ FILETIME
+ create_time,
+ exit_time;
+
+ OSVERSIONINFO
+ OsVersionInfo;
+
+ union
+ {
+ FILETIME
+ filetime;
+
+ __int64
+ filetime64;
+ } kernel_time;
+
+ union
+ {
+ FILETIME
+ filetime;
+
+ __int64
+ filetime64;
+ } user_time;
+
+ OsVersionInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
+ GetVersionEx(&OsVersionInfo);
+ if (OsVersionInfo.dwPlatformId != VER_PLATFORM_WIN32_NT)
+ return(NTElapsedTime());
+ status=GetProcessTimes(GetCurrentProcess(),&create_time,&exit_time,
+ &kernel_time.filetime,&user_time.filetime);
+ if (status != TRUE)
+ return(0.0);
+ return((double) 1.0e-7*(kernel_time.filetime64+user_time.filetime64));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N T W a r n i n g H a n d l e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTWarningHandler() displays a warning reason.
+%
+% The format of the NTWarningHandler method is:
+%
+% void NTWarningHandler(const ExceptionType warning,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o warning: Specifies the numeric warning category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+MagickExport void NTWarningHandler(const ExceptionType warning,
+ const char *reason,const char *description)
+{
+ char
+ buffer[2*MaxTextExtent];
+
+ if (reason == (char *) NULL)
+ return;
+ if (description == (char *) NULL)
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s.\n",GetClientName(),
+ reason);
+ else
+ (void) FormatMagickString(buffer,MaxTextExtent,"%s: %s (%s).\n",
+ GetClientName(),reason,description);
+ (void) MessageBox(NULL,buffer,"ImageMagick Warning",MB_OK | MB_TASKMODAL |
+ MB_SETFOREGROUND | MB_ICONINFORMATION);
+}
+#endif
diff --git a/magick/nt-base.h b/magick/nt-base.h
new file mode 100644
index 0000000..25e4ef0
--- /dev/null
+++ b/magick/nt-base.h
@@ -0,0 +1,379 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore Windows NT utility methods.
+*/
+#ifndef _MAGICKCORE_NT_BASE_H
+#define _MAGICKCORE_NT_BASE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/delegate.h"
+#include "magick/delegate-private.h"
+#include "magick/exception.h"
+
+#define WIN32_LEAN_AND_MEAN
+#define VC_EXTRALEAN
+#define _CRT_SECURE_NO_DEPRECATE 1
+#include <windows.h>
+#include <wchar.h>
+#include <winuser.h>
+#include <wingdi.h>
+#include <io.h>
+#include <process.h>
+#include <errno.h>
+#if defined(_DEBUG) && !defined(__MINGW32__)
+#include <crtdbg.h>
+#endif
+
+#define PROT_READ 0x01
+#define PROT_WRITE 0x02
+#define MAP_SHARED 0x01
+#define MAP_PRIVATE 0x02
+#define MAP_ANONYMOUS 0x20
+#define F_OK 0
+#define R_OK 4
+#define W_OK 2
+#define RW_OK 6
+#define _SC_PAGESIZE 1
+#define _SC_PHYS_PAGES 2
+#define _SC_OPEN_MAX 3
+#if !defined(SSIZE_MAX)
+#define SSIZE_MAX 0x7fffffffL
+#endif
+
+/*
+ _MSC_VER values:
+ 1100 MSVC 5.0
+ 1200 MSVC 6.0
+ 1300 MSVC 7.0 Visual C++ .NET 2002
+ 1310 Visual c++ .NET 2003
+ 1400 Visual C++ 2005
+ 1500 Visual C++ 2008
+*/
+
+#if !defined(chsize)
+# if defined(__BORLANDC__)
+# define chsize(file,length) chsize(file,length)
+# else
+# define chsize(file,length) _chsize(file,length)
+# endif
+#endif
+
+#if !defined(access)
+# define access(path,mode) _access(path,mode)
+#endif
+#if !defined(chdir)
+# define chdir _chdir
+#endif
+#if !defined(close)
+# define close _close
+#endif
+#if !defined(closedir)
+# define closedir(directory) NTCloseDirectory(directory)
+#endif
+#if !defined(fdopen)
+# define fdopen _fdopen
+#endif
+#if !defined(fileno)
+# define fileno _fileno
+#endif
+#if !defined(fseek)
+# define fseeko _fseeki64
+#endif
+#if !defined(fstat) && !defined(__BORLANDC__)
+#if defined(__WINDOWS__) && !defined(Windows95) && \
+ !(defined(_MSC_VER) && (_MSC_VER < 1400)) && (__MSVCRT_VERSION__ < 0x800)
+# define fstat _fstati64
+#else
+# define fstat _fstat
+#endif
+#endif
+#if !defined(fsync)
+# define fsync _commit
+#endif
+#if !defined(ftell)
+# define ftello _ftelli64
+#endif
+#if !defined(ftruncate)
+# define ftruncate(file,length) NTTruncateFile(file,length)
+#endif
+#if !defined(getcwd)
+# define getcwd _getcwd
+#endif
+#if !defined(getpid)
+# define getpid _getpid
+#endif
+#if !defined(hypot)
+# define hypot _hypot
+#endif
+#if !defined(inline)
+# define inline __inline
+#endif
+#if !defined(isatty)
+# define isatty _isatty
+#endif
+#if !defined(MAGICKCORE_LTDL_DELEGATE)
+#if !defined(lt_dlclose)
+# define lt_dlclose(handle) NTCloseLibrary(handle)
+#endif
+#if !defined(lt_dlerror)
+# define lt_dlerror() NTGetLibraryError()
+#endif
+#if !defined(lt_dlexit)
+# define lt_dlexit() NTExitLibrary()
+#endif
+#if !defined(lt_dlinit)
+# define lt_dlinit() NTInitializeLibrary()
+#endif
+#if !defined(lt_dlopen)
+# define lt_dlopen(filename) NTOpenLibrary(filename)
+#endif
+#if !defined(lt_dlsetsearchpath)
+# define lt_dlsetsearchpath(path) NTSetSearchPath(path)
+#endif
+#if !defined(lt_dlsym)
+# define lt_dlsym(handle,name) NTGetLibrarySymbol(handle,name)
+#endif
+#endif
+#if !defined(mkdir)
+# define mkdir _mkdir
+#endif
+#if !defined(mmap)
+# define mmap(address,length,protection,access,file,offset) \
+ NTMapMemory(address,length,protection,access,file,offset)
+#endif
+#if !defined(msync)
+# define msync(address,length,flags) NTSyncMemory(address,length,flags)
+#endif
+#if !defined(munmap)
+# define munmap(address,length) NTUnmapMemory(address,length)
+#endif
+#if !defined(opendir)
+# define opendir(directory) NTOpenDirectory(directory)
+#endif
+#if !defined(open)
+# define open _open
+#endif
+#if !defined(pclose)
+# define pclose _pclose
+#endif
+#if !defined(popen)
+# define popen _popen
+#endif
+#if !defined(read)
+# define read _read
+#endif
+#if !defined(readdir)
+# define readdir(directory) NTReadDirectory(directory)
+#endif
+#if !defined(seekdir)
+# define seekdir(directory,offset) NTSeekDirectory(directory,offset)
+#endif
+#if !defined(setmode)
+# define setmode _setmode
+#endif
+#if !defined(stat) && !defined(__BORLANDC__)
+#if defined(__WINDOWS__) && !defined(Windows95) && \
+ !(defined(_MSC_VER) && (_MSC_VER < 1400)) && (__MSVCRT_VERSION__ < 0x800)
+# define stat _stati64
+#else
+# define stat _stat
+#endif
+#endif
+#if !defined(strcasecmp)
+# define strcasecmp _strcmpi
+#endif
+#if !defined(strncasecmp)
+# define strncasecmp _strnicmp
+#endif
+#if !defined(sysconf)
+# define sysconf(name) NTSystemConfiguration(name)
+#endif
+#if !defined(telldir)
+# define telldir(directory) NTTellDirectory(directory)
+#endif
+#if !defined(tempnam)
+# define tempnam _tempnam
+#endif
+#if !defined(vsnprintf)
+#if !defined(_MSC_VER) || (defined(_MSC_VER) && (_MSC_VER < 1500))
+#define vsnprintf _vsnprintf
+#endif
+#endif
+#if !defined(write)
+# define write _write
+#endif
+#if !defined(wstat) && !defined(__BORLANDC__)
+#if defined(__WINDOWS__) && !defined(Windows95) && \
+ !(defined(_MSC_VER) && (_MSC_VER < 1400)) && (__MSVCRT_VERSION__ < 0x800)
+# define wstat _wstati64
+#else
+# define wstat _wstat
+#endif
+#endif
+
+#if defined(_MT) && defined(__WINDOWS__)
+# define SAFE_GLOBAL __declspec(thread)
+#else
+# define SAFE_GLOBAL
+#endif
+
+#if defined(__BORLANDC__)
+#undef _O_RANDOM
+#define _O_RANDOM 0
+#undef _O_SEQUENTIAL
+#define _O_SEQUENTIAL 0
+#undef _O_SHORT_LIVED
+#define _O_SHORT_LIVED 0
+#undef _O_TEMPORARY
+#define _O_TEMPORARY 0
+#endif
+
+#if !defined(XS_VERSION)
+struct dirent
+{
+ char
+ d_name[2048];
+
+ int
+ d_namlen;
+};
+
+typedef struct _DIR
+{
+ HANDLE
+ hSearch;
+
+ WIN32_FIND_DATA
+ Win32FindData;
+
+ BOOL
+ firsttime;
+
+ struct dirent
+ file_info;
+} DIR;
+
+typedef struct _NTMEMORYSTATUSEX
+{
+ DWORD
+ dwLength,
+ dwMemoryLoad;
+
+ DWORDLONG
+ ullTotalPhys,
+ ullAvailPhys,
+ ullTotalPageFile,
+ ullAvailPageFile,
+ ullTotalVirtual,
+ ullAvailVirtual,
+ ullAvailExtendedVirtual;
+} NTMEMORYSTATUSEX;
+
+typedef UINT
+ (CALLBACK *LPFNDLLFUNC1)(DWORD,UINT);
+
+typedef UINT
+ (CALLBACK *LPFNDLLFUNC2)(NTMEMORYSTATUSEX *);
+
+#endif
+
+#if !defined(ssize_t) && !defined(__MINGW32__)
+typedef long ssize_t;
+#endif
+
+#if defined(MAGICKCORE_BZLIB_DELEGATE)
+# if defined(_WIN32)
+# define BZ_IMPORT 1
+# endif
+#endif
+
+extern MagickExport char
+ *NTGetLastError(void);
+
+extern MagickExport const GhostscriptVectors
+ *NTGhostscriptDLLVectors(void);
+
+#if !defined(MAGICKCORE_LTDL_DELEGATE)
+extern MagickExport const char
+ *NTGetLibraryError(void);
+#endif
+
+#if !defined(XS_VERSION)
+extern MagickExport const char
+ *NTGetLibraryError(void);
+
+extern MagickExport DIR
+ *NTOpenDirectory(const char *);
+
+extern MagickExport double
+ NTElapsedTime(void),
+ NTUserTime(void);
+
+extern MagickExport int
+ Exit(int),
+ IsWindows95(),
+ NTCloseDirectory(DIR *),
+ NTCloseLibrary(void *),
+ NTControlHandler(void),
+ NTExitLibrary(void),
+ NTTruncateFile(int,off_t),
+ NTGhostscriptDLL(char *,int),
+ NTGhostscriptEXE(char *,int),
+ NTGhostscriptFonts(char *,int),
+ NTGhostscriptLoadDLL(void),
+ NTGhostscriptUnLoadDLL(void),
+ NTInitializeLibrary(void),
+ NTSetSearchPath(const char *),
+ NTSyncMemory(void *,size_t,int),
+ NTUnmapMemory(void *,size_t),
+ NTSystemCommand(const char *);
+
+extern MagickExport long
+ NTSystemConfiguration(int),
+ NTTellDirectory(DIR *);
+
+extern MagickExport MagickBooleanType
+ NTGatherRandomData(const size_t,unsigned char *),
+ NTGetExecutionPath(char *,const size_t),
+ NTGetModulePath(const char *,char *),
+ NTReportEvent(const char *,const MagickBooleanType),
+ NTReportException(const char *,const MagickBooleanType);
+
+extern MagickExport struct dirent
+ *NTReadDirectory(DIR *);
+
+extern MagickExport unsigned char
+ *NTRegistryKeyLookup(const char *),
+ *NTResourceToBlob(const char *);
+
+extern MagickExport void
+ NTErrorHandler(const ExceptionType,const char *,const char *),
+ *NTGetLibrarySymbol(void *,const char *),
+ *NTMapMemory(char *,size_t,int,int,int,MagickOffsetType),
+ *NTOpenLibrary(const char *),
+ NTSeekDirectory(DIR *,long),
+ NTWarningHandler(const ExceptionType,const char *,const char *);
+
+#endif /* !XS_VERSION */
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif /* !C++ */
+
+#endif /* !_MAGICKCORE_NT_BASE_H */
diff --git a/magick/nt-feature.c b/magick/nt-feature.c
new file mode 100644
index 0000000..8fa1755
--- /dev/null
+++ b/magick/nt-feature.c
@@ -0,0 +1,672 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% %
+% N N TTTTT %
+% NN N T %
+% N N N T %
+% N NN T %
+% N N T %
+% %
+% %
+% Windows NT Feature Methods for MagickCore %
+% %
+% Software Design %
+% John Cristy %
+% December 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#if defined(__WINDOWS__) || defined(__CYGWIN__)
+#define WIN32_LEAN_AND_MEAN
+#define VC_EXTRALEAN
+#include <windows.h>
+#include "magick/cache.h"
+#include "magick/colorspace.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image-private.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/quantum.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/splay-tree.h"
+#include "magick/utility.h"
+#include "magick/nt-feature.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C r o p I m a g e T o H B i t m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CropImageToHBITMAP() extracts a specified region of the image and returns
+% it as a Windows HBITMAP. While the same functionality can be accomplished by
+% invoking CropImage() followed by ImageToHBITMAP(), this method is more
+% efficient since it copies pixels directly to the HBITMAP.
+%
+% The format of the CropImageToHBITMAP method is:
+%
+% HBITMAP CropImageToHBITMAP(Image* image,const RectangleInfo *geometry,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: Define the region of the image to crop with members
+% x, y, width, and height.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport void *CropImageToHBITMAP(Image *image,
+ const RectangleInfo *geometry,ExceptionInfo *exception)
+{
+#define CropImageTag "Crop/Image"
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ RectangleInfo
+ page;
+
+ register const PixelPacket
+ *p;
+
+ BITMAP
+ bitmap;
+
+ HBITMAP
+ bitmapH;
+
+ HANDLE
+ bitmap_bitsH;
+
+ register RGBQUAD
+ *q;
+
+ RGBQUAD
+ *bitmap_bits;
+
+ /*
+ Check crop geometry.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (((geometry->x+(long) geometry->width) < 0) ||
+ ((geometry->y+(long) geometry->height) < 0) ||
+ (geometry->x >= (long) image->columns) ||
+ (geometry->y >= (long) image->rows))
+ ThrowImageException(OptionError,"GeometryDoesNotContainImage");
+ page=(*geometry);
+ if ((page.x+(long) page.width) > (long) image->columns)
+ page.width=image->columns-page.x;
+ if ((page.y+(long) page.height) > (long) image->rows)
+ page.height=image->rows-page.y;
+ if (page.x < 0)
+ {
+ page.width+=page.x;
+ page.x=0;
+ }
+ if (page.y < 0)
+ {
+ page.height+=page.y;
+ page.y=0;
+ }
+
+ if ((page.width == 0) || (page.height == 0))
+ ThrowImageException(OptionError,"GeometryDimensionsAreZero");
+ /*
+ Initialize crop image attributes.
+ */
+ bitmap.bmType = 0;
+ bitmap.bmWidth = page.width;
+ bitmap.bmHeight = page.height;
+ bitmap.bmWidthBytes = bitmap.bmWidth * 4;
+ bitmap.bmPlanes = 1;
+ bitmap.bmBitsPixel = 32;
+ bitmap.bmBits = NULL;
+
+ bitmap_bitsH=(HANDLE) GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,page.width*
+ page.height*bitmap.bmBitsPixel);
+ if (bitmap_bitsH == NULL)
+ return(NULL);
+ bitmap_bits=(RGBQUAD *) GlobalLock((HGLOBAL) bitmap_bitsH);
+ if ( bitmap.bmBits == NULL )
+ bitmap.bmBits = bitmap_bits;
+ if (image->colorspace != RGBColorspace)
+ TransformImageColorspace(image,RGBColorspace);
+ /*
+ Extract crop image.
+ */
+ q=bitmap_bits;
+ for (y=0; y < (long) page.height; y++)
+ {
+ p=GetVirtualPixels(image,page.x,page.y+y,page.width,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+
+#if MAGICKCORE_QUANTUM_DEPTH == 8
+ /* Form of PixelPacket is identical to RGBQUAD when MAGICKCORE_QUANTUM_DEPTH==8 */
+ CopyMagickMemory((void*)q,(const void*)p,page.width*sizeof(PixelPacket));
+ q += page.width;
+
+#else /* 16 or 32 bit Quantum */
+ {
+ long
+ x;
+
+ /* Transfer pixels, scaling to Quantum */
+ for( x=page.width ; x> 0 ; x-- )
+ {
+ q->rgbRed = ScaleQuantumToChar(p->red);
+ q->rgbGreen = ScaleQuantumToChar(p->green);
+ q->rgbBlue = ScaleQuantumToChar(p->blue);
+ q->rgbReserved = 0;
+ ++q;
+ ++p;
+ }
+ }
+#endif
+ proceed=SetImageProgress(image,CropImageTag,y,page.height);
+ if (proceed == MagickFalse)
+ break;
+ }
+ if (y < (long) page.height)
+ {
+ GlobalUnlock((HGLOBAL) bitmap_bitsH);
+ GlobalFree((HGLOBAL) bitmap_bitsH);
+ return((void *) NULL);
+ }
+ bitmap.bmBits=bitmap_bits;
+ bitmapH=CreateBitmapIndirect(&bitmap);
+ GlobalUnlock((HGLOBAL) bitmap_bitsH);
+ GlobalFree((HGLOBAL) bitmap_bitsH);
+ return((void *) bitmapH);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k C o n f l i c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickConflict() returns true if the image format conflicts with a logical
+% drive (.e.g. X:).
+%
+% The format of the IsMagickConflict method is:
+%
+% MagickBooleanType IsMagickConflict(const char *magick)
+%
+% A description of each parameter follows:
+%
+% o magick: Specifies the image format.
+%
+*/
+MagickExport MagickBooleanType NTIsMagickConflict(const char *magick)
+{
+ MagickBooleanType
+ status;
+
+ assert(magick != (char *) NULL);
+ if (strlen(magick) > 1)
+ return(MagickFalse);
+ status=(GetLogicalDrives() & (1 << ((toupper((int) (*magick)))-'A'))) != 0 ?
+ MagickTrue : MagickFalse;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ N T G e t T y pe L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NTLoadTypeLists() loads a Windows TrueType fonts.
+%
+% The format of the NTLoadTypeLists method is:
+%
+% MagickBooleanType NTLoadTypeLists(SplayTreeInfo *type_list)
+%
+% A description of each parameter follows:
+%
+% o type_list: A linked list of fonts.
+%
+*/
+MagickExport MagickBooleanType NTLoadTypeLists(SplayTreeInfo *type_list,
+ ExceptionInfo *exception)
+{
+ HKEY
+ reg_key = (HKEY) INVALID_HANDLE_VALUE;
+
+ LONG
+ res;
+
+
+ int
+ list_entries = 0;
+
+ char
+ buffer[MaxTextExtent],
+ system_root[MaxTextExtent],
+ font_root[MaxTextExtent];
+
+ DWORD
+ type,
+ system_root_length;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Try to find the right Windows*\CurrentVersion key, the SystemRoot and
+ then the Fonts key
+ */
+ res = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, ®_key);
+ if (res == ERROR_SUCCESS) {
+ system_root_length=sizeof(system_root)-1;
+ res = RegQueryValueExA(reg_key,"SystemRoot",NULL, &type,
+ (BYTE*) system_root, &system_root_length);
+ }
+ if (res != ERROR_SUCCESS) {
+ res = RegOpenKeyExA (HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion", 0, KEY_READ, ®_key);
+ if (res == ERROR_SUCCESS) {
+ system_root_length=sizeof(system_root)-1;
+ res = RegQueryValueExA(reg_key,"SystemRoot",NULL, &type,
+ (BYTE*)system_root, &system_root_length);
+ }
+ }
+ if (res == ERROR_SUCCESS)
+ res = RegOpenKeyExA (reg_key, "Fonts",0, KEY_READ, ®_key);
+ if (res != ERROR_SUCCESS)
+ return(MagickFalse);
+ *font_root='\0';
+ (void) CopyMagickString(buffer,system_root,MaxTextExtent);
+ (void) ConcatenateMagickString(buffer,"\\fonts\\arial.ttf",MaxTextExtent);
+ if (IsPathAccessible(buffer) != MagickFalse)
+ {
+ (void) CopyMagickString(font_root,system_root,MaxTextExtent);
+ (void) ConcatenateMagickString(font_root,"\\fonts\\",MaxTextExtent);
+ }
+ else
+ {
+ (void) CopyMagickString(font_root,system_root,MaxTextExtent);
+ (void) ConcatenateMagickString(font_root,"\\",MaxTextExtent);
+ }
+
+ {
+ TypeInfo
+ *type_info;
+
+ DWORD
+ registry_index = 0,
+ type,
+ value_data_size,
+ value_name_length;
+
+ char
+ value_data[MaxTextExtent],
+ value_name[MaxTextExtent];
+
+ res = ERROR_SUCCESS;
+
+ while (res != ERROR_NO_MORE_ITEMS)
+ {
+ char
+ *family_extent,
+ token[MaxTextExtent],
+ *pos,
+ *q;
+
+ value_name_length = sizeof(value_name) - 1;
+ value_data_size = sizeof(value_data) - 1;
+ res = RegEnumValueA ( reg_key, registry_index, value_name,
+ &value_name_length, 0, &type, (BYTE*)value_data, &value_data_size);
+ registry_index++;
+ if (res != ERROR_SUCCESS)
+ continue;
+ if ( (pos = strstr(value_name, " (TrueType)")) == (char*) NULL )
+ continue;
+ *pos='\0'; /* Remove (TrueType) from string */
+
+ type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info));
+ if (type_info == (TypeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(type_info,0,sizeof(TypeInfo));
+
+ type_info->path=ConstantString("Windows Fonts");
+ type_info->signature=MagickSignature;
+
+ /* Name */
+ (void) CopyMagickString(buffer,value_name,MaxTextExtent);
+ for(pos = buffer; *pos != 0 ; pos++)
+ if (*pos == ' ')
+ *pos = '-';
+ type_info->name=ConstantString(buffer);
+
+ /* Fullname */
+ type_info->description=ConstantString(value_name);
+
+ /* Format */
+ type_info->format=ConstantString("truetype");
+
+ /* Glyphs */
+ if (strchr(value_data,'\\') != (char *) NULL)
+ (void) CopyMagickString(buffer,value_data,MaxTextExtent);
+ else
+ {
+ (void) CopyMagickString(buffer,font_root,MaxTextExtent);
+ (void) ConcatenateMagickString(buffer,value_data,MaxTextExtent);
+ }
+
+ LocaleLower(buffer);
+ type_info->glyphs=ConstantString(buffer);
+
+ type_info->stretch=NormalStretch;
+ type_info->style=NormalStyle;
+ type_info->weight=400;
+
+ /* Some fonts are known to require special encodings */
+ if ( (LocaleCompare(type_info->name, "Symbol") == 0 ) ||
+ (LocaleCompare(type_info->name, "Wingdings") == 0 ) ||
+ (LocaleCompare(type_info->name, "Wingdings-2") == 0 ) ||
+ (LocaleCompare(type_info->name, "Wingdings-3") == 0 ) )
+ type_info->encoding=ConstantString("AppleRoman");
+
+ family_extent=value_name;
+
+ for (q=value_name; *q != '\0'; )
+ {
+ GetMagickToken(q,(const char **) &q,token);
+ if (*token == '\0')
+ break;
+
+ if (LocaleCompare(token,"Italic") == 0)
+ {
+ type_info->style=ItalicStyle;
+ }
+
+ else if (LocaleCompare(token,"Oblique") == 0)
+ {
+ type_info->style=ObliqueStyle;
+ }
+
+ else if (LocaleCompare(token,"Bold") == 0)
+ {
+ type_info->weight=700;
+ }
+
+ else if (LocaleCompare(token,"Thin") == 0)
+ {
+ type_info->weight=100;
+ }
+
+ else if ( (LocaleCompare(token,"ExtraLight") == 0) ||
+ (LocaleCompare(token,"UltraLight") == 0) )
+ {
+ type_info->weight=200;
+ }
+
+ else if (LocaleCompare(token,"Light") == 0)
+ {
+ type_info->weight=300;
+ }
+
+ else if ( (LocaleCompare(token,"Normal") == 0) ||
+ (LocaleCompare(token,"Regular") == 0) )
+ {
+ type_info->weight=400;
+ }
+
+ else if (LocaleCompare(token,"Medium") == 0)
+ {
+ type_info->weight=500;
+ }
+
+ else if ( (LocaleCompare(token,"SemiBold") == 0) ||
+ (LocaleCompare(token,"DemiBold") == 0) )
+ {
+ type_info->weight=600;
+ }
+
+ else if ( (LocaleCompare(token,"ExtraBold") == 0) ||
+ (LocaleCompare(token,"UltraBold") == 0) )
+ {
+ type_info->weight=800;
+ }
+
+ else if ( (LocaleCompare(token,"Heavy") == 0) ||
+ (LocaleCompare(token,"Black") == 0) )
+ {
+ type_info->weight=900;
+ }
+
+ else if (LocaleCompare(token,"Condensed") == 0)
+ {
+ type_info->stretch = CondensedStretch;
+ }
+
+ else if (LocaleCompare(token,"Expanded") == 0)
+ {
+ type_info->stretch = ExpandedStretch;
+ }
+
+ else if (LocaleCompare(token,"ExtraCondensed") == 0)
+ {
+ type_info->stretch = ExtraCondensedStretch;
+ }
+
+ else if (LocaleCompare(token,"ExtraExpanded") == 0)
+ {
+ type_info->stretch = ExtraExpandedStretch;
+ }
+
+ else if (LocaleCompare(token,"SemiCondensed") == 0)
+ {
+ type_info->stretch = SemiCondensedStretch;
+ }
+
+ else if (LocaleCompare(token,"SemiExpanded") == 0)
+ {
+ type_info->stretch = SemiExpandedStretch;
+ }
+
+ else if (LocaleCompare(token,"UltraCondensed") == 0)
+ {
+ type_info->stretch = UltraCondensedStretch;
+ }
+
+ else if (LocaleCompare(token,"UltraExpanded") == 0)
+ {
+ type_info->stretch = UltraExpandedStretch;
+ }
+
+ else
+ {
+ family_extent=q;
+ }
+ }
+
+ (void) CopyMagickString(buffer,value_name,family_extent-value_name+1);
+ StripString(buffer);
+ type_info->family=ConstantString(buffer);
+
+ list_entries++;
+ status=AddValueToSplayTree(type_list,ConstantString(type_info->name),
+ type_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",type_info->name);
+ }
+ }
+ RegCloseKey ( reg_key );
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m a g e T o H B i t m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImageToHBITMAP() creates a Windows HBITMAP from an image.
+%
+% The format of the ImageToHBITMAP method is:
+%
+% HBITMAP ImageToHBITMAP(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image to convert.
+%
+*/
+MagickExport void *ImageToHBITMAP(Image *image)
+{
+ BITMAP
+ bitmap;
+
+ ExceptionInfo
+ *exception;
+
+ HANDLE
+ bitmap_bitsH;
+
+ HBITMAP
+ bitmapH;
+
+ long
+ y;
+
+ register long
+ x;
+
+ register const PixelPacket
+ *p;
+
+ register RGBQUAD
+ *q;
+
+ RGBQUAD
+ *bitmap_bits;
+
+ size_t
+ length;
+
+ (void) ResetMagickMemory(&bitmap,0,sizeof(bitmap));
+ bitmap.bmType=0;
+ bitmap.bmWidth=image->columns;
+ bitmap.bmHeight=image->rows;
+ bitmap.bmWidthBytes=4*bitmap.bmWidth;
+ bitmap.bmPlanes=1;
+ bitmap.bmBitsPixel=32;
+ bitmap.bmBits=NULL;
+ length=bitmap.bmWidthBytes*bitmap.bmHeight;
+ bitmap_bitsH=(HANDLE) GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,length);
+ if (bitmap_bitsH == NULL)
+ {
+ char
+ *message;
+
+ message=GetExceptionMessage(errno);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
+ message=DestroyString(message);
+ return(NULL);
+ }
+ bitmap_bits=(RGBQUAD *) GlobalLock((HGLOBAL) bitmap_bitsH);
+ q=bitmap_bits;
+ if (bitmap.bmBits == NULL)
+ bitmap.bmBits=bitmap_bits;
+ (void) TransformImageColorspace(image,RGBColorspace);
+ exception=(&image->exception);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->rgbRed=ScaleQuantumToChar(p->red);
+ q->rgbGreen=ScaleQuantumToChar(p->green);
+ q->rgbBlue=ScaleQuantumToChar(p->blue);
+ q->rgbReserved=0;
+ p++;
+ q++;
+ }
+ }
+ bitmap.bmBits=bitmap_bits;
+ bitmapH=CreateBitmapIndirect(&bitmap);
+ if (bitmapH == NULL)
+ {
+ char
+ *message;
+
+ message=GetExceptionMessage(errno);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",message);
+ message=DestroyString(message);
+ }
+ GlobalUnlock((HGLOBAL) bitmap_bitsH);
+ GlobalFree((HGLOBAL) bitmap_bitsH);
+ return((void *) bitmapH);
+}
+
+#endif
diff --git a/magick/nt-feature.h b/magick/nt-feature.h
new file mode 100644
index 0000000..b571659
--- /dev/null
+++ b/magick/nt-feature.h
@@ -0,0 +1,43 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore Windows NT utility methods.
+*/
+#ifndef _MAGICKCORE_NT_FEATURE_H
+#define _MAGICKCORE_NT_FEATURE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/splay-tree.h"
+
+extern MagickExport void
+ *CropImageToHBITMAP(Image *,const RectangleInfo *,ExceptionInfo *),
+ *ImageToHBITMAP(Image *);
+
+#if !defined(XS_VERSION)
+
+extern MagickExport MagickBooleanType
+ NTIsMagickConflict(const char *),
+ NTLoadTypeLists(SplayTreeInfo *,ExceptionInfo *);
+
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/option.c b/magick/option.c
new file mode 100644
index 0000000..899879c
--- /dev/null
+++ b/magick/option.c
@@ -0,0 +1,2262 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% OOO PPPP TTTTT IIIII OOO N N %
+% O O P P T I O O NN N %
+% O O PPPP T I O O N N N %
+% O O P T I O O N NN %
+% OOO P T IIIII OOO N N %
+% %
+% %
+% MagickCore Option Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/compare.h"
+#include "magick/constitute.h"
+#include "magick/distort.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/layer.h"
+#include "magick/mime-private.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/policy.h"
+#include "magick/property.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/resource_.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+
+/*
+ ImageMagick options.
+*/
+static const OptionInfo
+ AlignOptions[] =
+ {
+ { "Undefined", (long) UndefinedAlign, MagickTrue },
+ { "Center", (long) CenterAlign, MagickFalse },
+ { "End", (long) RightAlign, MagickFalse },
+ { "Left", (long) LeftAlign, MagickFalse },
+ { "Middle", (long) CenterAlign, MagickFalse },
+ { "Right", (long) RightAlign, MagickFalse },
+ { "Start", (long) LeftAlign, MagickFalse },
+ { (char *) NULL, (long) UndefinedAlign, MagickFalse }
+ },
+ AlphaOptions[] =
+ {
+ { "Undefined", (long) UndefinedAlphaChannel, MagickTrue },
+ { "Activate", (long) ActivateAlphaChannel, MagickFalse },
+ { "Background", (long) BackgroundAlphaChannel, MagickFalse },
+ { "Copy", (long) CopyAlphaChannel, MagickFalse },
+ { "Deactivate", (long) DeactivateAlphaChannel, MagickFalse },
+ { "Extract", (long) ExtractAlphaChannel, MagickFalse },
+ { "Off", (long) DeactivateAlphaChannel, MagickFalse },
+ { "On", (long) ActivateAlphaChannel, MagickFalse },
+ { "Opaque", (long) OpaqueAlphaChannel, MagickFalse },
+ { "Set", (long) SetAlphaChannel, MagickFalse },
+ { "Shape", (long) ShapeAlphaChannel, MagickFalse },
+ { "Reset", (long) SetAlphaChannel, MagickTrue }, /* deprecated */
+ { "Transparent", (long) TransparentAlphaChannel, MagickFalse },
+ { (char *) NULL, (long) UndefinedAlphaChannel, MagickFalse }
+ },
+ BooleanOptions[] =
+ {
+ { "False", 0L, MagickFalse },
+ { "True", 1L, MagickFalse },
+ { "0", 0L, MagickFalse },
+ { "1", 1L, MagickFalse },
+ { (char *) NULL, 0L, MagickFalse }
+ },
+ ChannelOptions[] =
+ {
+ { "Undefined", (long) UndefinedChannel, MagickTrue },
+ { "All", (long) AllChannels, MagickFalse },
+ { "Alpha", (long) OpacityChannel, MagickFalse },
+ { "Black", (long) BlackChannel, MagickFalse },
+ { "Blue", (long) BlueChannel, MagickFalse },
+ { "Cyan", (long) CyanChannel, MagickFalse },
+ { "Default", (long) DefaultChannels, MagickFalse },
+ { "Gray", (long) GrayChannel, MagickFalse },
+ { "Green", (long) GreenChannel, MagickFalse },
+ { "Hue", (long) RedChannel, MagickFalse },
+ { "Index", (long) IndexChannel, MagickFalse },
+ { "Lightness", (long) BlueChannel, MagickFalse },
+ { "Luminance", (long) BlueChannel, MagickFalse },
+ { "Luminosity", (long) BlueChannel, MagickFalse }, /* deprecated */
+ { "Magenta", (long) MagentaChannel, MagickFalse },
+ { "Matte", (long) OpacityChannel, MagickFalse },
+ { "Opacity", (long) OpacityChannel, MagickFalse },
+ { "Red", (long) RedChannel, MagickFalse },
+ { "Saturation", (long) GreenChannel, MagickFalse },
+ { "Yellow", (long) YellowChannel, MagickFalse },
+ { "Sync", (long) SyncChannels, MagickFalse }, /* special channel flag */
+ { (char *) NULL, (long) UndefinedChannel, MagickFalse }
+ },
+ ClassOptions[] =
+ {
+ { "Undefined", (long) UndefinedClass, MagickTrue },
+ { "DirectClass", (long) DirectClass, MagickFalse },
+ { "PseudoClass", (long) PseudoClass, MagickFalse },
+ { (char *) NULL, (long) UndefinedClass, MagickFalse }
+ },
+ ClipPathOptions[] =
+ {
+ { "Undefined", (long) UndefinedPathUnits, MagickTrue },
+ { "ObjectBoundingBox", (long) ObjectBoundingBox, MagickFalse },
+ { "UserSpace", (long) UserSpace, MagickFalse },
+ { "UserSpaceOnUse", (long) UserSpaceOnUse, MagickFalse },
+ { (char *) NULL, (long) UndefinedPathUnits, MagickFalse }
+ },
+ CommandOptions[] =
+ {
+ { "+adjoin", 0L, MagickFalse },
+ { "-adjoin", 0L, MagickFalse },
+ { "+adaptive-sharpen", 1L, MagickFalse },
+ { "-adaptive-sharpen", 1L, MagickFalse },
+ { "+adaptive-threshold", 1L, MagickFalse },
+ { "-adaptive-threshold", 1L, MagickFalse },
+ { "+affine", 0L, MagickFalse },
+ { "-affine", 1L, MagickFalse },
+ { "+affinity", 0L, MagickFalse },
+ { "-affinity", 1L, MagickFalse },
+ { "+alpha", 1L, MagickFalse },
+ { "-alpha", 1L, MagickFalse },
+ { "+annotate", 0L, MagickFalse },
+ { "-annotate", 2L, MagickFalse },
+ { "+antialias", 0L, MagickFalse },
+ { "-antialias", 0L, MagickFalse },
+ { "+append", 0L, MagickFalse },
+ { "-append", 0L, MagickFalse },
+ { "+authenticate", 0L, MagickFalse },
+ { "-authenticate", 1L, MagickFalse },
+ { "+auto-gamma", 0L, MagickTrue }, /* under development */
+ { "-auto-gamma", 0L, MagickTrue }, /* under development */
+ { "+auto-level", 0L, MagickTrue }, /* under development */
+ { "-auto-level", 0L, MagickTrue }, /* under development */
+ { "+auto-orient", 0L, MagickFalse },
+ { "-auto-orient", 0L, MagickFalse },
+ { "+average", 0L, MagickFalse },
+ { "-average", 0L, MagickFalse },
+ { "+backdrop", 0L, MagickFalse },
+ { "-backdrop", 1L, MagickFalse },
+ { "+background", 0L, MagickFalse },
+ { "-background", 1L, MagickFalse },
+ { "+bench", 0L, MagickTrue },
+ { "-bench", 1L, MagickTrue },
+ { "+bias", 0L, MagickFalse },
+ { "-bias", 1L, MagickFalse },
+ { "+black-threshold", 0L, MagickFalse },
+ { "-black-threshold", 1L, MagickFalse },
+ { "+blend", 0L, MagickFalse },
+ { "-blend", 1L, MagickFalse },
+ { "+blue-primary", 0L, MagickFalse },
+ { "-blue-primary", 1L, MagickFalse },
+ { "+blue-shift", 1L, MagickFalse },
+ { "-blue-shift", 1L, MagickFalse },
+ { "+blur", 0L, MagickFalse },
+ { "-blur", 1L, MagickFalse },
+ { "+border", 0L, MagickFalse },
+ { "-border", 1L, MagickFalse },
+ { "+bordercolor", 0L, MagickFalse },
+ { "-bordercolor", 1L, MagickFalse },
+ { "+borderwidth", 0L, MagickFalse },
+ { "-borderwidth", 1L, MagickFalse },
+ { "+box", 0L, MagickFalse },
+ { "-box", 1L, MagickFalse },
+ { "+cache", 0L, MagickFalse },
+ { "-cache", 1L, MagickFalse },
+ { "+cdl", 1L, MagickFalse },
+ { "-cdl", 1L, MagickFalse },
+ { "+channel", 0L, MagickFalse },
+ { "-channel", 1L, MagickFalse },
+ { "+charcoal", 0L, MagickFalse },
+ { "-charcoal", 0L, MagickFalse },
+ { "+chop", 0L, MagickFalse },
+ { "-chop", 1L, MagickFalse },
+ { "+clip", 0L, MagickFalse },
+ { "-clip", 0L, MagickFalse },
+ { "+clip-mask", 0L, MagickFalse },
+ { "-clip-mask", 1L, MagickFalse },
+ { "+clip-path", 0L, MagickFalse },
+ { "-clip-path", 1L, MagickFalse },
+ { "+clone", 0L, MagickFalse },
+ { "-clone", 1L, MagickFalse },
+ { "+clut", 0L, MagickFalse },
+ { "-clut", 0L, MagickFalse },
+ { "+coalesce", 0L, MagickFalse },
+ { "-coalesce", 0L, MagickFalse },
+ { "+colorize", 0L, MagickFalse },
+ { "-colorize", 1L, MagickFalse },
+ { "+colormap", 0L, MagickFalse },
+ { "-colormap", 1L, MagickFalse },
+ { "+colors", 0L, MagickFalse },
+ { "-colors", 1L, MagickFalse },
+ { "+colorspace", 0L, MagickFalse },
+ { "-colorspace", 1L, MagickFalse },
+ { "+combine", 0L, MagickFalse },
+ { "-combine", 0L, MagickFalse },
+ { "+comment", 0L, MagickFalse },
+ { "-comment", 1L, MagickFalse },
+ { "+compose", 0L, MagickFalse },
+ { "-compose", 1L, MagickFalse },
+ { "+composite", 0L, MagickFalse },
+ { "-composite", 0L, MagickFalse },
+ { "+compress", 0L, MagickFalse },
+ { "-compress", 1L, MagickFalse },
+ { "+contrast", 0L, MagickFalse },
+ { "-contrast", 0L, MagickFalse },
+ { "+contrast-stretch", 0L, MagickFalse },
+ { "-contrast-stretch", 1L, MagickFalse },
+ { "+convolve", 0L, MagickFalse },
+ { "-convolve", 1L, MagickFalse },
+ { "+crop", 0L, MagickFalse },
+ { "-crop", 1L, MagickFalse },
+ { "+cycle", 0L, MagickFalse },
+ { "-cycle", 1L, MagickFalse },
+ { "+debug", 0L, MagickFalse },
+ { "-debug", 1L, MagickFalse },
+ { "+decipher", 1L, MagickFalse },
+ { "-decipher", 1L, MagickFalse },
+ { "+deconstruct", 0L, MagickFalse },
+ { "-deconstruct", 0L, MagickFalse },
+ { "+define", 1L, MagickFalse },
+ { "-define", 1L, MagickFalse },
+ { "+delay", 0L, MagickFalse },
+ { "-delay", 1L, MagickFalse },
+ { "+delete", 0L, MagickFalse },
+ { "-delete", 1L, MagickFalse },
+ { "+density", 0L, MagickFalse },
+ { "-density", 1L, MagickFalse },
+ { "+depth", 0L, MagickFalse },
+ { "-depth", 1L, MagickFalse },
+ { "+descend", 0L, MagickFalse },
+ { "-descend", 1L, MagickFalse },
+ { "+deskew", 0L, MagickFalse },
+ { "-deskew", 1L, MagickFalse },
+ { "+despeckle", 0L, MagickFalse },
+ { "-despeckle", 0L, MagickFalse },
+ { "+displace", 0L, MagickFalse },
+ { "-displace", 1L, MagickFalse },
+ { "+display", 0L, MagickFalse },
+ { "-display", 1L, MagickFalse },
+ { "+dispose", 0L, MagickFalse },
+ { "-dispose", 1L, MagickFalse },
+ { "+dissolve", 0L, MagickFalse },
+ { "-dissolve", 1L, MagickFalse },
+ { "+distort", 2L, MagickFalse },
+ { "-distort", 2L, MagickFalse },
+ { "+dither", 0L, MagickFalse },
+ { "-dither", 1L, MagickFalse },
+ { "+draw", 0L, MagickFalse },
+ { "-draw", 1L, MagickFalse },
+ { "+edge", 0L, MagickFalse },
+ { "-edge", 1L, MagickFalse },
+ { "+emboss", 0L, MagickFalse },
+ { "-emboss", 1L, MagickFalse },
+ { "+encipher", 1L, MagickFalse },
+ { "-encipher", 1L, MagickFalse },
+ { "+encoding", 0L, MagickFalse },
+ { "-encoding", 1L, MagickFalse },
+ { "+endian", 0L, MagickFalse },
+ { "-endian", 1L, MagickFalse },
+ { "+enhance", 0L, MagickFalse },
+ { "-enhance", 0L, MagickFalse },
+ { "+equalize", 0L, MagickFalse },
+ { "-equalize", 0L, MagickFalse },
+ { "+evaluate", 0L, MagickFalse },
+ { "-evaluate", 2L, MagickFalse },
+ { "+extent", 0L, MagickFalse },
+ { "-extent", 1L, MagickFalse },
+ { "+extract", 0L, MagickFalse },
+ { "-extract", 1L, MagickFalse },
+ { "+family", 0L, MagickFalse },
+ { "-family", 1L, MagickFalse },
+ { "+fill", 0L, MagickFalse },
+ { "-fill", 1L, MagickFalse },
+ { "+filter", 0L, MagickFalse },
+ { "-filter", 1L, MagickFalse },
+ { "+flatten", 0L, MagickFalse },
+ { "-flatten", 0L, MagickFalse },
+ { "+flip", 0L, MagickFalse },
+ { "-flip", 0L, MagickFalse },
+ { "+floodfill", 0L, MagickFalse },
+ { "-floodfill", 2L, MagickFalse },
+ { "+flop", 0L, MagickFalse },
+ { "-flop", 0L, MagickFalse },
+ { "+font", 0L, MagickFalse },
+ { "-font", 1L, MagickFalse },
+ { "+foreground", 0L, MagickFalse },
+ { "-foreground", 1L, MagickFalse },
+ { "+format", 0L, MagickFalse },
+ { "-format", 1L, MagickFalse },
+ { "+frame", 0L, MagickFalse },
+ { "-frame", 1L, MagickFalse },
+ { "+fuzz", 0L, MagickFalse },
+ { "-fuzz", 1L, MagickFalse },
+ { "+fx", 0L, MagickFalse },
+ { "-fx", 1L, MagickFalse },
+ { "+gamma", 0L, MagickFalse },
+ { "-gamma", 1L, MagickFalse },
+ { "+gaussian", 0L, MagickFalse },
+ { "-gaussian", 1L, MagickFalse },
+ { "+gaussian-blur", 0L, MagickFalse },
+ { "-gaussian-blur", 1L, MagickFalse },
+ { "+geometry", 0L, MagickFalse },
+ { "-geometry", 1L, MagickFalse },
+ { "+gravity", 0L, MagickFalse },
+ { "-gravity", 1L, MagickFalse },
+ { "+green-primary", 0L, MagickFalse },
+ { "-green-primary", 1L, MagickFalse },
+ { "+hald-clut", 0L, MagickFalse },
+ { "-hald-clut", 0L, MagickFalse },
+ { "+help", 0L, MagickFalse },
+ { "-help", 0L, MagickFalse },
+ { "+highlight-color", 1L, MagickFalse },
+ { "-highlight-color", 1L, MagickFalse },
+ { "+iconGeometry", 0L, MagickFalse },
+ { "-iconGeometry", 1L, MagickFalse },
+ { "+iconic", 0L, MagickFalse },
+ { "-iconic", 1L, MagickFalse },
+ { "+identify", 0L, MagickFalse },
+ { "-identify", 0L, MagickFalse },
+ { "+immutable", 0L, MagickFalse },
+ { "-immutable", 0L, MagickFalse },
+ { "+implode", 0L, MagickFalse },
+ { "-implode", 1L, MagickFalse },
+ { "+insert", 0L, MagickFalse },
+ { "-insert", 1L, MagickFalse },
+ { "+intent", 0L, MagickFalse },
+ { "-intent", 1L, MagickFalse },
+ { "+interlace", 0L, MagickFalse },
+ { "-interlace", 1L, MagickFalse },
+ { "+interpolate", 0L, MagickFalse },
+ { "-interpolate", 1L, MagickFalse },
+ { "+interword-spacing", 0L, MagickFalse },
+ { "-interword-spacing", 1L, MagickFalse },
+ { "+kerning", 0L, MagickFalse },
+ { "-kerning", 1L, MagickFalse },
+ { "+label", 0L, MagickFalse },
+ { "-label", 1L, MagickFalse },
+ { "+lat", 0L, MagickFalse },
+ { "-lat", 1L, MagickFalse },
+ { "+layers", 0L, MagickFalse },
+ { "-layers", 1L, MagickFalse },
+ { "+level", 1L, MagickFalse },
+ { "-level", 1L, MagickFalse },
+ { "+level-colors", 1L, MagickFalse },
+ { "-level-colors", 1L, MagickFalse },
+ { "+limit", 0L, MagickFalse },
+ { "-limit", 2L, MagickFalse },
+ { "+linear-stretch", 0L, MagickFalse },
+ { "-linear-stretch", 1L, MagickFalse },
+ { "+linewidth", 0L, MagickFalse },
+ { "-linewidth", 1L, MagickFalse },
+ { "+liquid-rescale", 0L, MagickFalse },
+ { "-liquid-rescale", 1L, MagickFalse },
+ { "+list", 0L, MagickFalse },
+ { "-list", 1L, MagickFalse },
+ { "+log", 0L, MagickFalse },
+ { "-log", 1L, MagickFalse },
+ { "+loop", 0L, MagickFalse },
+ { "-loop", 1L, MagickFalse },
+ { "+lowlight-color", 1L, MagickFalse },
+ { "-lowlight-color", 1L, MagickFalse },
+ { "+magnify", 0L, MagickFalse },
+ { "-magnify", 1L, MagickFalse },
+ { "+map", 0L, MagickFalse },
+ { "-map", 1L, MagickFalse },
+ { "+mask", 0L, MagickFalse },
+ { "-mask", 1L, MagickFalse },
+ { "+matte", 0L, MagickFalse },
+ { "-matte", 0L, MagickFalse },
+ { "+mattecolor", 0L, MagickFalse },
+ { "-mattecolor", 1L, MagickFalse },
+ { "+median", 0L, MagickFalse },
+ { "-median", 1L, MagickFalse },
+ { "+metric", 0L, MagickFalse },
+ { "-metric", 1L, MagickFalse },
+ { "+mode", 0L, MagickFalse },
+ { "-mode", 1L, MagickFalse },
+ { "+modulate", 0L, MagickFalse },
+ { "-modulate", 1L, MagickFalse },
+ { "+monitor", 0L, MagickFalse },
+ { "-monitor", 0L, MagickFalse },
+ { "+monochrome", 0L, MagickFalse },
+ { "-monochrome", 0L, MagickFalse },
+ { "+morph", 0L, MagickFalse },
+ { "-morph", 1L, MagickFalse },
+ { "+mosaic", 0L, MagickFalse },
+ { "-mosaic", 0L, MagickFalse },
+ { "+motion-blur", 0L, MagickFalse },
+ { "-motion-blur", 1L, MagickFalse },
+ { "+name", 0L, MagickFalse },
+ { "-name", 1L, MagickFalse },
+ { "+negate", 0L, MagickFalse },
+ { "-negate", 0L, MagickFalse },
+ { "+noise", 1L, MagickFalse },
+ { "-noise", 1L, MagickFalse },
+ { "+noop", 0L, MagickFalse },
+ { "-noop", 0L, MagickFalse },
+ { "+normalize", 0L, MagickFalse },
+ { "-normalize", 0L, MagickFalse },
+ { "+opaque", 1L, MagickFalse },
+ { "-opaque", 1L, MagickFalse },
+ { "+ordered-dither", 0L, MagickFalse },
+ { "-ordered-dither", 1L, MagickFalse },
+ { "+orient", 0L, MagickFalse },
+ { "-orient", 1L, MagickFalse },
+ { "+origin", 0L, MagickFalse },
+ { "-origin", 1L, MagickFalse },
+ { "+page", 0L, MagickFalse },
+ { "-page", 1L, MagickFalse },
+ { "+paint", 0L, MagickFalse },
+ { "-paint", 1L, MagickFalse },
+ { "+path", 0L, MagickFalse },
+ { "-path", 1L, MagickFalse },
+ { "+pause", 0L, MagickFalse },
+ { "-pause", 1L, MagickFalse },
+ { "+passphrase", 0L, MagickFalse },
+ { "-passphrase", 1L, MagickFalse },
+ { "+pen", 0L, MagickFalse },
+ { "-pen", 1L, MagickFalse },
+ { "+ping", 0L, MagickFalse },
+ { "-ping", 0L, MagickFalse },
+ { "+pointsize", 0L, MagickFalse },
+ { "-pointsize", 1L, MagickFalse },
+ { "+polaroid", 0L, MagickFalse },
+ { "-polaroid", 1L, MagickFalse },
+ { "+posterize", 0L, MagickFalse },
+ { "-posterize", 1L, MagickFalse },
+ { "+preview", 0L, MagickFalse },
+ { "-preview", 1L, MagickFalse },
+ { "+process", 0L, MagickFalse },
+ { "-process", 1L, MagickFalse },
+ { "+profile", 1L, MagickFalse },
+ { "-profile", 1L, MagickFalse },
+ { "+quality", 0L, MagickFalse },
+ { "-quality", 1L, MagickFalse },
+ { "+quiet", 0L, MagickFalse },
+ { "-quiet", 0L, MagickFalse },
+ { "+radial-blur", 0L, MagickFalse },
+ { "-radial-blur", 1L, MagickFalse },
+ { "+raise", 0L, MagickFalse },
+ { "-raise", 1L, MagickFalse },
+ { "+random-threshold", 0L, MagickFalse },
+ { "-random-threshold", 1L, MagickFalse },
+ { "+recolor", 0L, MagickFalse },
+ { "-recolor", 1L, MagickFalse },
+ { "+red-primary", 0L, MagickFalse },
+ { "-red-primary", 1L, MagickFalse },
+ { "+regard-warnings", 0L, MagickFalse },
+ { "-regard-warnings", 0L, MagickFalse },
+ { "+region", 0L, MagickFalse },
+ { "-region", 1L, MagickFalse },
+ { "+remote", 0L, MagickFalse },
+ { "-remote", 1L, MagickFalse },
+ { "+render", 0L, MagickFalse },
+ { "-render", 0L, MagickFalse },
+ { "+repage", 0L, MagickFalse },
+ { "-repage", 1L, MagickFalse },
+ { "+resample", 0L, MagickFalse },
+ { "-resample", 1L, MagickFalse },
+ { "+resize", 0L, MagickFalse },
+ { "-resize", 1L, MagickFalse },
+ { "+respect-parenthesis", 0L, MagickFalse },
+ { "-respect-parenthesis", 0L, MagickFalse },
+ { "+reverse", 0L, MagickFalse },
+ { "-reverse", 0L, MagickFalse },
+ { "+roll", 0L, MagickFalse },
+ { "-roll", 1L, MagickFalse },
+ { "+rotate", 0L, MagickFalse },
+ { "-rotate", 1L, MagickFalse },
+ { "+sample", 0L, MagickFalse },
+ { "-sample", 1L, MagickFalse },
+ { "+sampling-factor", 0L, MagickFalse },
+ { "-sampling-factor", 1L, MagickFalse },
+ { "+sans", 0L, MagickFalse },
+ { "-sans", 1L, MagickFalse },
+ { "+sans0", 0L, MagickFalse },
+ { "-sans0", 0L, MagickFalse },
+ { "+sans2", 2L, MagickFalse },
+ { "-sans2", 2L, MagickFalse },
+ { "+scale", 0L, MagickFalse },
+ { "-scale", 1L, MagickFalse },
+ { "+scene", 0L, MagickFalse },
+ { "-scene", 1L, MagickFalse },
+ { "+scenes", 0L, MagickFalse },
+ { "-scenes", 1L, MagickFalse },
+ { "+screen", 0L, MagickFalse },
+ { "-screen", 1L, MagickFalse },
+ { "+seed", 0L, MagickFalse },
+ { "-seed", 1L, MagickFalse },
+ { "+segment", 0L, MagickFalse },
+ { "-segment", 1L, MagickFalse },
+ { "+separate", 0L, MagickFalse },
+ { "-separate", 0L, MagickFalse },
+ { "+sepia-tone", 0L, MagickFalse },
+ { "-sepia-tone", 1L, MagickFalse },
+ { "+set", 1L, MagickFalse },
+ { "-set", 2L, MagickFalse },
+ { "+shade", 0L, MagickFalse },
+ { "-shade", 1L, MagickFalse },
+ { "+shadow", 0L, MagickFalse },
+ { "-shadow", 1L, MagickFalse },
+ { "+shared-memory", 0L, MagickFalse },
+ { "-shared-memory", 1L, MagickFalse },
+ { "+sharpen", 0L, MagickFalse },
+ { "-sharpen", 1L, MagickFalse },
+ { "+shave", 0L, MagickFalse },
+ { "-shave", 1L, MagickFalse },
+ { "+shear", 0L, MagickFalse },
+ { "-shear", 1L, MagickFalse },
+ { "+sigmoidal-contrast", 0L, MagickFalse },
+ { "-sigmoidal-contrast", 1L, MagickFalse },
+ { "+silent", 0L, MagickFalse },
+ { "-silent", 1L, MagickFalse },
+ { "+size", 0L, MagickFalse },
+ { "-size", 1L, MagickFalse },
+ { "+sketch", 0L, MagickFalse },
+ { "-sketch", 1L, MagickFalse },
+ { "+snaps", 0L, MagickFalse },
+ { "-snaps", 1L, MagickFalse },
+ { "+solarize", 0L, MagickFalse },
+ { "-solarize", 1L, MagickFalse },
+ { "+splice", 0L, MagickFalse },
+ { "-splice", 1L, MagickFalse },
+ { "+sparse-color", 2L, MagickFalse },
+ { "-sparse-color", 2L, MagickFalse },
+ { "+spread", 0L, MagickFalse },
+ { "-spread", 1L, MagickFalse },
+ { "+stegano", 0L, MagickFalse },
+ { "-stegano", 1L, MagickFalse },
+ { "+stereo", 0L, MagickFalse },
+ { "-stereo", 1L, MagickFalse },
+ { "+stretch", 0L, MagickFalse },
+ { "-stretch", 1L, MagickFalse },
+ { "+strip", 0L, MagickFalse },
+ { "-strip", 0L, MagickFalse },
+ { "+stroke", 0L, MagickFalse },
+ { "-stroke", 1L, MagickFalse },
+ { "+strokewidth", 0L, MagickFalse },
+ { "-strokewidth", 1L, MagickFalse },
+ { "+style", 0L, MagickFalse },
+ { "-style", 1L, MagickFalse },
+ { "+swap", 0L, MagickFalse },
+ { "-swap", 1L, MagickFalse },
+ { "+swirl", 0L, MagickFalse },
+ { "-swirl", 1L, MagickFalse },
+ { "+text-font", 0L, MagickFalse },
+ { "-text-font", 1L, MagickFalse },
+ { "+texture", 0L, MagickFalse },
+ { "-texture", 1L, MagickFalse },
+ { "+threshold", 0L, MagickFalse },
+ { "-threshold", 1L, MagickFalse },
+ { "+thumbnail", 0L, MagickFalse },
+ { "-thumbnail", 1L, MagickFalse },
+ { "+thumnail", 0L, MagickFalse },
+ { "-thumnail", 1L, MagickFalse },
+ { "+tile", 0L, MagickFalse },
+ { "-tile", 1L, MagickFalse },
+ { "+tile-offset", 0L, MagickFalse },
+ { "-tile-offset", 1L, MagickFalse },
+ { "+tint", 0L, MagickFalse },
+ { "-tint", 1L, MagickFalse },
+ { "+title", 0L, MagickFalse },
+ { "-title", 1L, MagickFalse },
+ { "+transform", 0L, MagickFalse },
+ { "-transform", 0L, MagickFalse },
+ { "+transparent", 1L, MagickFalse },
+ { "-transparent", 1L, MagickFalse },
+ { "+transparent-color", 1L, MagickFalse },
+ { "-transparent-color", 1L, MagickFalse },
+ { "+transpose", 0L, MagickFalse },
+ { "-transpose", 0L, MagickFalse },
+ { "+transverse", 0L, MagickFalse },
+ { "-transverse", 0L, MagickFalse },
+ { "+treedepth", 0L, MagickFalse },
+ { "-treedepth", 1L, MagickFalse },
+ { "+trim", 0L, MagickFalse },
+ { "-trim", 0L, MagickFalse },
+ { "+type", 0L, MagickFalse },
+ { "-type", 1L, MagickFalse },
+ { "+undercolor", 0L, MagickFalse },
+ { "-undercolor", 1L, MagickFalse },
+ { "+unique-colors", 0L, MagickFalse },
+ { "-unique-colors", 0L, MagickFalse },
+ { "+units", 0L, MagickFalse },
+ { "-units", 1L, MagickFalse },
+ { "+unsharp", 0L, MagickFalse },
+ { "-unsharp", 1L, MagickFalse },
+ { "+update", 0L, MagickFalse },
+ { "-update", 1L, MagickFalse },
+ { "+use-pixmap", 0L, MagickFalse },
+ { "-use-pixmap", 1L, MagickFalse },
+ { "+verbose", 0L, MagickFalse },
+ { "-verbose", 0L, MagickFalse },
+ { "+version", 0L, MagickFalse },
+ { "-version", 1L, MagickFalse },
+ { "+view", 0L, MagickFalse },
+ { "-view", 1L, MagickFalse },
+ { "+vignette", 0L, MagickFalse },
+ { "-vignette", 1L, MagickFalse },
+ { "+virtual-pixel", 0L, MagickFalse },
+ { "-virtual-pixel", 1L, MagickFalse },
+ { "+visual", 0L, MagickFalse },
+ { "-visual", 1L, MagickFalse },
+ { "+watermark", 0L, MagickFalse },
+ { "-watermark", 1L, MagickFalse },
+ { "+wave", 0L, MagickFalse },
+ { "-wave", 1L, MagickFalse },
+ { "+weight", 0L, MagickFalse },
+ { "-weight", 1L, MagickFalse },
+ { "+white-point", 0L, MagickFalse },
+ { "-white-point", 1L, MagickFalse },
+ { "+white-threshold", 0L, MagickFalse },
+ { "-white-threshold", 1L, MagickFalse },
+ { "+window", 0L, MagickFalse },
+ { "-window", 1L, MagickFalse },
+ { "+window-group", 0L, MagickFalse },
+ { "-window-group", 1L, MagickFalse },
+ { "+write", 0L, MagickFalse },
+ { "-write", 1L, MagickFalse },
+ { (char *) NULL, (long) 0L, MagickFalse }
+ },
+ ComposeOptions[] =
+ {
+ { "Undefined", (long) UndefinedCompositeOp, MagickTrue },
+ { "Add", (long) AddCompositeOp, MagickFalse },
+ { "Atop", (long) AtopCompositeOp, MagickFalse },
+ { "Blend", (long) BlendCompositeOp, MagickFalse },
+ { "Blur", (long) BlurCompositeOp, MagickFalse },
+ { "Bumpmap", (long) BumpmapCompositeOp, MagickFalse },
+ { "ChangeMask", (long) ChangeMaskCompositeOp, MagickFalse },
+ { "Clear", (long) ClearCompositeOp, MagickFalse },
+ { "ColorBurn", (long) ColorBurnCompositeOp, MagickFalse },
+ { "ColorDodge", (long) ColorDodgeCompositeOp, MagickFalse },
+ { "Colorize", (long) ColorizeCompositeOp, MagickFalse },
+ { "CopyBlack", (long) CopyBlackCompositeOp, MagickFalse },
+ { "CopyBlue", (long) CopyBlueCompositeOp, MagickFalse },
+ { "CopyCyan", (long) CopyCyanCompositeOp, MagickFalse },
+ { "CopyGreen", (long) CopyGreenCompositeOp, MagickFalse },
+ { "Copy", (long) CopyCompositeOp, MagickFalse },
+ { "CopyMagenta", (long) CopyMagentaCompositeOp, MagickFalse },
+ { "CopyOpacity", (long) CopyOpacityCompositeOp, MagickFalse },
+ { "CopyRed", (long) CopyRedCompositeOp, MagickFalse },
+ { "CopyYellow", (long) CopyYellowCompositeOp, MagickFalse },
+ { "Darken", (long) DarkenCompositeOp, MagickFalse },
+ { "Divide", (long) DivideCompositeOp, MagickFalse },
+ { "Dst", (long) DstCompositeOp, MagickFalse },
+ { "Difference", (long) DifferenceCompositeOp, MagickFalse },
+ { "Displace", (long) DisplaceCompositeOp, MagickFalse },
+ { "Dissolve", (long) DissolveCompositeOp, MagickFalse },
+ { "Distort", (long) DistortCompositeOp, MagickFalse },
+ { "DstAtop", (long) DstAtopCompositeOp, MagickFalse },
+ { "DstIn", (long) DstInCompositeOp, MagickFalse },
+ { "DstOut", (long) DstOutCompositeOp, MagickFalse },
+ { "DstOver", (long) DstOverCompositeOp, MagickFalse },
+ { "Dst", (long) DstCompositeOp, MagickFalse },
+ { "Exclusion", (long) ExclusionCompositeOp, MagickFalse },
+ { "HardLight", (long) HardLightCompositeOp, MagickFalse },
+ { "Hue", (long) HueCompositeOp, MagickFalse },
+ { "In", (long) InCompositeOp, MagickFalse },
+ { "Lighten", (long) LightenCompositeOp, MagickFalse },
+ { "LinearBurn", (long) LinearBurnCompositeOp, MagickFalse },
+ { "LinearDodge", (long) LinearDodgeCompositeOp, MagickFalse },
+ { "LinearLight", (long) LinearLightCompositeOp, MagickFalse },
+ { "Luminize", (long) LuminizeCompositeOp, MagickFalse },
+ { "Mathematics", (long) MathematicsCompositeOp, MagickFalse },
+ { "Minus", (long) MinusCompositeOp, MagickFalse },
+ { "Modulate", (long) ModulateCompositeOp, MagickFalse },
+ { "Multiply", (long) MultiplyCompositeOp, MagickFalse },
+ { "None", (long) NoCompositeOp, MagickFalse },
+ { "Out", (long) OutCompositeOp, MagickFalse },
+ { "Overlay", (long) OverlayCompositeOp, MagickFalse },
+ { "Over", (long) OverCompositeOp, MagickFalse },
+ { "PegtopLight", (long) PegtopLightCompositeOp, MagickFalse },
+ { "PinLight", (long) PinLightCompositeOp, MagickFalse },
+ { "Plus", (long) PlusCompositeOp, MagickFalse },
+ { "Replace", (long) ReplaceCompositeOp, MagickFalse },
+ { "Saturate", (long) SaturateCompositeOp, MagickFalse },
+ { "Screen", (long) ScreenCompositeOp, MagickFalse },
+ { "SoftLight", (long) SoftLightCompositeOp, MagickFalse },
+ { "Src", (long) SrcCompositeOp, MagickFalse },
+ { "SrcAtop", (long) SrcAtopCompositeOp, MagickFalse },
+ { "SrcIn", (long) SrcInCompositeOp, MagickFalse },
+ { "SrcOut", (long) SrcOutCompositeOp, MagickFalse },
+ { "SrcOver", (long) SrcOverCompositeOp, MagickFalse },
+ { "Src", (long) SrcCompositeOp, MagickFalse },
+ { "Subtract", (long) SubtractCompositeOp, MagickFalse },
+ { "Threshold", (long) ThresholdCompositeOp, MagickTrue }, /* depreciate */
+ { "VividLight", (long) VividLightCompositeOp, MagickFalse },
+ { "Xor", (long) XorCompositeOp, MagickFalse },
+ { (char *) NULL, (long) UndefinedCompositeOp, MagickFalse }
+ },
+ CompressOptions[] =
+ {
+ { "Undefined", (long) UndefinedCompression, MagickTrue },
+ { "B44", (long) B44Compression, MagickFalse },
+ { "B44A", (long) B44ACompression, MagickFalse },
+ { "BZip", (long) BZipCompression, MagickFalse },
+ { "DXT1", (long) DXT1Compression, MagickFalse },
+ { "DXT3", (long) DXT3Compression, MagickFalse },
+ { "DXT5", (long) DXT5Compression, MagickFalse },
+ { "Fax", (long) FaxCompression, MagickFalse },
+ { "Group4", (long) Group4Compression, MagickFalse },
+ { "JPEG", (long) JPEGCompression, MagickFalse },
+ { "JPEG2000", (long) JPEG2000Compression, MagickFalse },
+ { "Lossless", (long) LosslessJPEGCompression, MagickFalse },
+ { "LosslessJPEG", (long) LosslessJPEGCompression, MagickFalse },
+ { "LZW", (long) LZWCompression, MagickFalse },
+ { "None", (long) NoCompression, MagickFalse },
+ { "Piz", (long) PizCompression, MagickFalse },
+ { "Pxr24", (long) Pxr24Compression, MagickFalse },
+ { "RLE", (long) RLECompression, MagickFalse },
+ { "Zip", (long) ZipCompression, MagickFalse },
+ { "RunlengthEncoded", (long) RLECompression, MagickFalse },
+ { "ZipS", (long) ZipSCompression, MagickFalse },
+ { (char *) NULL, (long) UndefinedCompression, MagickFalse }
+ },
+ ColorspaceOptions[] =
+ {
+ { "Undefined", (long) UndefinedColorspace, MagickTrue },
+ { "CMY", (long) CMYColorspace, MagickFalse },
+ { "CMYK", (long) CMYKColorspace, MagickFalse },
+ { "Gray", (long) GRAYColorspace, MagickFalse },
+ { "HSB", (long) HSBColorspace, MagickFalse },
+ { "HSL", (long) HSLColorspace, MagickFalse },
+ { "HWB", (long) HWBColorspace, MagickFalse },
+ { "Lab", (long) LabColorspace, MagickFalse },
+ { "Log", (long) LogColorspace, MagickFalse },
+ { "OHTA", (long) OHTAColorspace, MagickFalse },
+ { "Rec601Luma", (long) Rec601LumaColorspace, MagickFalse },
+ { "Rec601YCbCr", (long) Rec601YCbCrColorspace, MagickFalse },
+ { "Rec709Luma", (long) Rec709LumaColorspace, MagickFalse },
+ { "Rec709YCbCr", (long) Rec709YCbCrColorspace, MagickFalse },
+ { "RGB", (long) RGBColorspace, MagickFalse },
+ { "sRGB", (long) sRGBColorspace, MagickFalse },
+ { "Transparent", (long) TransparentColorspace, MagickFalse },
+ { "XYZ", (long) XYZColorspace, MagickFalse },
+ { "YCbCr", (long) YCbCrColorspace, MagickFalse },
+ { "YCC", (long) YCCColorspace, MagickFalse },
+ { "YIQ", (long) YIQColorspace, MagickFalse },
+ { "YPbPr", (long) YPbPrColorspace, MagickFalse },
+ { "YUV", (long) YUVColorspace, MagickFalse },
+ { (char *) NULL, (long) UndefinedColorspace, MagickFalse }
+ },
+ DataTypeOptions[] =
+ {
+ { "Undefined", (long) UndefinedData, MagickTrue },
+ { "Byte", (long) ByteData, MagickFalse },
+ { "Long", (long) LongData, MagickFalse },
+ { "Short", (long) ShortData, MagickFalse },
+ { "String", (long) StringData, MagickFalse },
+ { (char *) NULL, (long) UndefinedData, MagickFalse }
+ },
+ DecorateOptions[] =
+ {
+ { "Undefined", (long) UndefinedDecoration, MagickTrue },
+ { "LineThrough", (long) LineThroughDecoration, MagickFalse },
+ { "None", (long) NoDecoration, MagickFalse },
+ { "Overline", (long) OverlineDecoration, MagickFalse },
+ { "Underline", (long) UnderlineDecoration, MagickFalse },
+ { (char *) NULL, (long) UndefinedDecoration, MagickFalse }
+ },
+ DisposeOptions[] =
+ {
+ { "Background", (long) BackgroundDispose, MagickFalse },
+ { "None", (long) NoneDispose, MagickFalse },
+ { "Previous", (long) PreviousDispose, MagickFalse },
+ { "Undefined", (long) UndefinedDispose, MagickFalse },
+ { "0", (long) UndefinedDispose, MagickFalse },
+ { "1", (long) NoneDispose, MagickFalse },
+ { "2", (long) BackgroundDispose, MagickFalse },
+ { "3", (long) PreviousDispose, MagickFalse },
+ { (char *) NULL, (long) UndefinedDispose, MagickFalse }
+ },
+ DistortOptions[] =
+ {
+ { "Undefined", (long) UndefinedDistortion, MagickTrue },
+ { "Affine", (long) AffineDistortion, MagickFalse },
+ { "AffineProjection", (long) AffineProjectionDistortion, MagickFalse },
+ { "ScaleRotateTranslate", (long) ScaleRotateTranslateDistortion, MagickFalse },
+ { "SRT", (long) ScaleRotateTranslateDistortion, MagickFalse },
+ { "Perspective", (long) PerspectiveDistortion, MagickFalse },
+ { "PerspectiveProjection", (long) PerspectiveProjectionDistortion, MagickFalse },
+ { "Bilinear", (long) BilinearForwardDistortion, MagickTrue },
+ { "BilinearForward", (long) BilinearForwardDistortion, MagickFalse },
+ { "BilinearReverse", (long) BilinearReverseDistortion, MagickFalse },
+ { "Polynomial", (long) PolynomialDistortion, MagickFalse },
+ { "Arc", (long) ArcDistortion, MagickFalse },
+ { "Polar", (long) PolarDistortion, MagickFalse },
+ { "DePolar", (long) DePolarDistortion, MagickFalse },
+ { "Barrel", (long) BarrelDistortion, MagickFalse },
+ { "BarrelInverse", (long) BarrelInverseDistortion, MagickFalse },
+ { "Shepards", (long) ShepardsDistortion, MagickFalse },
+ { (char *) NULL, (long) UndefinedDistortion, MagickFalse }
+ },
+ DitherOptions[] =
+ {
+ { "Undefined", (long) UndefinedDitherMethod, MagickTrue },
+ { "None", (long) NoDitherMethod, MagickFalse },
+ { "FloydSteinberg", (long) FloydSteinbergDitherMethod, MagickFalse },
+ { "Riemersma", (long) RiemersmaDitherMethod, MagickFalse },
+ { (char *) NULL, (long) UndefinedEndian, MagickFalse }
+ },
+ EndianOptions[] =
+ {
+ { "Undefined", (long) UndefinedEndian, MagickTrue },
+ { "LSB", (long) LSBEndian, MagickFalse },
+ { "MSB", (long) MSBEndian, MagickFalse },
+ { (char *) NULL, (long) UndefinedEndian, MagickFalse }
+ },
+ EvaluateOptions[] =
+ {
+ { "Undefined", (long) UndefinedEvaluateOperator, MagickTrue },
+ { "Add", (long) AddEvaluateOperator, MagickFalse },
+ { "AddModulus", (long) AddModulusEvaluateOperator, MagickFalse },
+ { "And", (long) AndEvaluateOperator, MagickFalse },
+ { "Cos", (long) CosineEvaluateOperator, MagickFalse },
+ { "Cosine", (long) CosineEvaluateOperator, MagickFalse },
+ { "Divide", (long) DivideEvaluateOperator, MagickFalse },
+ { "GaussianNoise", (long) GaussianNoiseEvaluateOperator, MagickFalse },
+ { "ImpulseNoise", (long) ImpulseNoiseEvaluateOperator, MagickFalse },
+ { "LaplacianNoise", (long) LaplacianNoiseEvaluateOperator, MagickFalse },
+ { "LeftShift", (long) LeftShiftEvaluateOperator, MagickFalse },
+ { "Log", (long) LogEvaluateOperator, MagickFalse },
+ { "Max", (long) MaxEvaluateOperator, MagickFalse },
+ { "Min", (long) MinEvaluateOperator, MagickFalse },
+ { "MultiplicativeNoise", (long) MultiplicativeNoiseEvaluateOperator, MagickFalse },
+ { "Multiply", (long) MultiplyEvaluateOperator, MagickFalse },
+ { "Or", (long) OrEvaluateOperator, MagickFalse },
+ { "PoissonNoise", (long) PoissonNoiseEvaluateOperator, MagickFalse },
+ { "Pow", (long) PowEvaluateOperator, MagickFalse },
+ { "RightShift", (long) RightShiftEvaluateOperator, MagickFalse },
+ { "Set", (long) SetEvaluateOperator, MagickFalse },
+ { "Sin", (long) SineEvaluateOperator, MagickFalse },
+ { "Sine", (long) SineEvaluateOperator, MagickFalse },
+ { "Subtract", (long) SubtractEvaluateOperator, MagickFalse },
+ { "Threshold", (long) ThresholdEvaluateOperator, MagickFalse },
+ { "ThresholdBlack", (long) ThresholdBlackEvaluateOperator, MagickFalse },
+ { "ThresholdWhite", (long) ThresholdWhiteEvaluateOperator, MagickFalse },
+ { "UniformNoise", (long) UniformNoiseEvaluateOperator, MagickFalse },
+ { "Xor", (long) XorEvaluateOperator, MagickFalse },
+ { (char *) NULL, (long) UndefinedEvaluateOperator, MagickFalse }
+ },
+ FillRuleOptions[] =
+ {
+ { "Undefined", (long) UndefinedRule, MagickTrue },
+ { "Evenodd", (long) EvenOddRule, MagickFalse },
+ { "NonZero", (long) NonZeroRule, MagickFalse },
+ { (char *) NULL, (long) UndefinedRule, MagickFalse }
+ },
+ FilterOptions[] =
+ {
+ { "Undefined", (long) UndefinedFilter, MagickTrue },
+ { "Bartlett", (long) BartlettFilter, MagickFalse },
+ { "Bessel", (long) BesselFilter, MagickFalse },
+ { "Blackman", (long) BlackmanFilter, MagickFalse },
+ { "Bohman", (long) BohmanFilter, MagickFalse },
+ { "Box", (long) BoxFilter, MagickFalse },
+ { "Catrom", (long) CatromFilter, MagickFalse },
+ { "Cubic", (long) CubicFilter, MagickFalse },
+ { "Gaussian", (long) GaussianFilter, MagickFalse },
+ { "Hamming", (long) HammingFilter, MagickFalse },
+ { "Hanning", (long) HanningFilter, MagickFalse },
+ { "Hermite", (long) HermiteFilter, MagickFalse },
+ { "Kaiser", (long) KaiserFilter, MagickFalse },
+ { "Lagrange", (long) LagrangeFilter, MagickFalse },
+ { "Lanczos", (long) LanczosFilter, MagickFalse },
+ { "Mitchell", (long) MitchellFilter, MagickFalse },
+ { "Parzen", (long) ParzenFilter, MagickFalse },
+ { "Point", (long) PointFilter, MagickFalse },
+ { "Quadratic", (long) QuadraticFilter, MagickFalse },
+ { "Sinc", (long) SincFilter, MagickFalse },
+ { "Triangle", (long) TriangleFilter, MagickFalse },
+ { "Welsh", (long) WelshFilter, MagickFalse },
+ { (char *) NULL, (long) UndefinedFilter, MagickFalse }
+ },
+ FunctionOptions[] =
+ {
+ { "Undefined", (long) UndefinedFunction, MagickTrue },
+ { "Polynomial", (long) PolynomialFunction, MagickFalse },
+ { "Sinusoid", (long) SinusoidFunction, MagickFalse },
+ { "ArcSin", (long) ArcsinFunction, MagickFalse },
+ { "ArcTan", (long) ArctanFunction, MagickFalse },
+ { (char *) NULL, (long) UndefinedFunction, MagickFalse }
+ },
+ GravityOptions[] =
+ {
+ { "Undefined", (long) UndefinedGravity, MagickTrue },
+ { "None", (long) UndefinedGravity, MagickFalse },
+ { "Center", (long) CenterGravity, MagickFalse },
+ { "East", (long) EastGravity, MagickFalse },
+ { "Forget", (long) ForgetGravity, MagickFalse },
+ { "NorthEast", (long) NorthEastGravity, MagickFalse },
+ { "North", (long) NorthGravity, MagickFalse },
+ { "NorthWest", (long) NorthWestGravity, MagickFalse },
+ { "SouthEast", (long) SouthEastGravity, MagickFalse },
+ { "South", (long) SouthGravity, MagickFalse },
+ { "SouthWest", (long) SouthWestGravity, MagickFalse },
+ { "West", (long) WestGravity, MagickFalse },
+ { "Static", (long) StaticGravity, MagickFalse },
+ { (char *) NULL, UndefinedGravity, MagickFalse }
+ },
+ ImageListOptions[] =
+ {
+ { "append", MagickTrue, MagickFalse },
+ { "affinity", MagickTrue, MagickFalse },
+ { "average", MagickTrue, MagickFalse },
+ { "clut", MagickTrue, MagickFalse },
+ { "coalesce", MagickTrue, MagickFalse },
+ { "combine", MagickTrue, MagickFalse },
+ { "composite", MagickTrue, MagickFalse },
+ { "crop", MagickTrue, MagickFalse },
+ { "debug", MagickTrue, MagickFalse },
+ { "deconstruct", MagickTrue, MagickFalse },
+ { "delete", MagickTrue, MagickFalse },
+ { "flatten", MagickTrue, MagickFalse },
+ { "fx", MagickTrue, MagickFalse },
+ { "hald-clut", MagickTrue, MagickFalse },
+ { "ift", MagickTrue, MagickFalse },
+ { "identify", MagickTrue, MagickFalse },
+ { "insert", MagickTrue, MagickFalse },
+ { "layers", MagickTrue, MagickFalse },
+ { "limit", MagickTrue, MagickFalse },
+ { "map", MagickTrue, MagickFalse },
+ { "morph", MagickTrue, MagickFalse },
+ { "mosaic", MagickTrue, MagickFalse },
+ { "optimize", MagickTrue, MagickFalse },
+ { "process", MagickTrue, MagickFalse },
+ { "quiet", MagickTrue, MagickFalse },
+ { "separate", MagickTrue, MagickFalse },
+ { "swap", MagickTrue, MagickFalse },
+ { "write", MagickTrue, MagickFalse },
+ { (char *) NULL, MagickFalse, MagickFalse }
+ },
+ IntentOptions[] =
+ {
+ { "Undefined", (long) UndefinedIntent, MagickTrue },
+ { "Absolute", (long) AbsoluteIntent, MagickFalse },
+ { "Perceptual", (long) PerceptualIntent, MagickFalse },
+ { "Relative", (long) RelativeIntent, MagickFalse },
+ { "Saturation", (long) SaturationIntent, MagickFalse },
+ { (char *) NULL, (long) UndefinedIntent, MagickFalse }
+ },
+ InterlaceOptions[] =
+ {
+ { "Undefined", (long) UndefinedInterlace, MagickTrue },
+ { "Line", (long) LineInterlace, MagickFalse },
+ { "None", (long) NoInterlace, MagickFalse },
+ { "Plane", (long) PlaneInterlace, MagickFalse },
+ { "Partition", (long) PartitionInterlace, MagickFalse },
+ { "GIF", (long) GIFInterlace, MagickFalse },
+ { "JPEG", (long) JPEGInterlace, MagickFalse },
+ { "PNG", (long) PNGInterlace, MagickFalse },
+ { (char *) NULL, (long) UndefinedInterlace, MagickFalse }
+ },
+ InterpolateOptions[] =
+ {
+ { "Undefined", (long) UndefinedInterpolatePixel, MagickTrue },
+ { "Average", (long) AverageInterpolatePixel, MagickFalse },
+ { "Bicubic", (long) BicubicInterpolatePixel, MagickFalse },
+ { "Bilinear", (long) BilinearInterpolatePixel, MagickFalse },
+ { "filter", (long) FilterInterpolatePixel, MagickFalse },
+ { "Integer", (long) IntegerInterpolatePixel, MagickFalse },
+ { "Mesh", (long) MeshInterpolatePixel, MagickFalse },
+ { "NearestNeighbor", (long) NearestNeighborInterpolatePixel, MagickFalse },
+ { "Spline", (long) SplineInterpolatePixel, MagickFalse },
+ { (char *) NULL, (long) UndefinedInterpolatePixel, MagickFalse }
+ },
+ LayerOptions[] =
+ {
+ { "Undefined", (long) UndefinedLayer, MagickTrue },
+ { "Coalesce", (long) CoalesceLayer, MagickFalse },
+ { "CompareAny", (long) CompareAnyLayer, MagickFalse },
+ { "CompareClear", (long) CompareClearLayer, MagickFalse },
+ { "CompareOverlay", (long) CompareOverlayLayer, MagickFalse },
+ { "Dispose", (long) DisposeLayer, MagickFalse },
+ { "Optimize", (long) OptimizeLayer, MagickFalse },
+ { "OptimizeFrame", (long) OptimizeImageLayer, MagickFalse },
+ { "OptimizePlus", (long) OptimizePlusLayer, MagickFalse },
+ { "OptimizeTransparency", (long) OptimizeTransLayer, MagickFalse },
+ { "RemoveDups", (long) RemoveDupsLayer, MagickFalse },
+ { "RemoveZero", (long) RemoveZeroLayer, MagickFalse },
+ { "Composite", (long) CompositeLayer, MagickFalse },
+ { "Merge", (long) MergeLayer, MagickFalse },
+ { "Flatten", (long) FlattenLayer, MagickFalse },
+ { "Mosaic", (long) MosaicLayer, MagickFalse },
+ { "TrimBounds", (long) TrimBoundsLayer, MagickFalse },
+ { (char *) NULL, (long) UndefinedLayer, MagickFalse }
+ },
+ LineCapOptions[] =
+ {
+ { "Undefined", (long) UndefinedCap, MagickTrue },
+ { "Butt", (long) ButtCap, MagickFalse },
+ { "Round", (long) RoundCap, MagickFalse },
+ { "Square", (long) SquareCap, MagickFalse },
+ { (char *) NULL, (long) UndefinedCap, MagickFalse }
+ },
+ LineJoinOptions[] =
+ {
+ { "Undefined", (long) UndefinedJoin, MagickTrue },
+ { "Bevel", (long) BevelJoin, MagickFalse },
+ { "Miter", (long) MiterJoin, MagickFalse },
+ { "Round", (long) RoundJoin, MagickFalse },
+ { (char *) NULL, (long) UndefinedJoin, MagickFalse }
+ },
+ ListOptions[] =
+ {
+ { "Align", (long) MagickAlignOptions, MagickFalse },
+ { "Alpha", (long) MagickAlphaOptions, MagickFalse },
+ { "Boolean", (long) MagickBooleanOptions, MagickFalse },
+ { "Channel", (long) MagickChannelOptions, MagickFalse },
+ { "Class", (long) MagickClassOptions, MagickFalse },
+ { "ClipPath", (long) MagickClipPathOptions, MagickFalse },
+ { "Coder", (long) MagickCoderOptions, MagickFalse },
+ { "Color", (long) MagickColorOptions, MagickFalse },
+ { "Colorspace", (long) MagickColorspaceOptions, MagickFalse },
+ { "Command", (long) MagickCommandOptions, MagickFalse },
+ { "Compose", (long) MagickComposeOptions, MagickFalse },
+ { "Compress", (long) MagickCompressOptions, MagickFalse },
+ { "Configure", (long) MagickConfigureOptions, MagickFalse },
+ { "DataType", (long) MagickDataTypeOptions, MagickFalse },
+ { "Debug", (long) MagickDebugOptions, MagickFalse },
+ { "Decoration", (long) MagickDecorateOptions, MagickFalse },
+ { "Delegate", (long) MagickDelegateOptions, MagickFalse },
+ { "Dispose", (long) MagickDisposeOptions, MagickFalse },
+ { "Distort", (long) MagickDistortOptions, MagickFalse },
+ { "Dither", (long) MagickDitherOptions, MagickFalse },
+ { "Endian", (long) MagickEndianOptions, MagickFalse },
+ { "Evaluate", (long) MagickEvaluateOptions, MagickFalse },
+ { "FillRule", (long) MagickFillRuleOptions, MagickFalse },
+ { "Filter", (long) MagickFilterOptions, MagickFalse },
+ { "Font", (long) MagickFontOptions, MagickFalse },
+ { "Format", (long) MagickFormatOptions, MagickFalse },
+ { "Function", (long) MagickFunctionOptions, MagickFalse },
+ { "Gravity", (long) MagickGravityOptions, MagickFalse },
+ { "ImageList", (long) MagickImageListOptions, MagickFalse },
+ { "Intent", (long) MagickIntentOptions, MagickFalse },
+ { "Interlace", (long) MagickInterlaceOptions, MagickFalse },
+ { "Interpolate", (long) MagickInterpolateOptions, MagickFalse },
+ { "Layers", (long) MagickLayerOptions, MagickFalse },
+ { "LineCap", (long) MagickLineCapOptions, MagickFalse },
+ { "LineJoin", (long) MagickLineJoinOptions, MagickFalse },
+ { "List", (long) MagickListOptions, MagickFalse },
+ { "Locale", (long) MagickLocaleOptions, MagickFalse },
+ { "LogEvent", (long) MagickLogEventOptions, MagickFalse },
+ { "Log", (long) MagickLogOptions, MagickFalse },
+ { "Magic", (long) MagickMagicOptions, MagickFalse },
+ { "Method", (long) MagickMethodOptions, MagickFalse },
+ { "Metric", (long) MagickMetricOptions, MagickFalse },
+ { "Mime", (long) MagickMimeOptions, MagickFalse },
+ { "Mode", (long) MagickModeOptions, MagickFalse },
+ { "Module", (long) MagickModuleOptions, MagickFalse },
+ { "Noise", (long) MagickNoiseOptions, MagickFalse },
+ { "Orientation", (long) MagickOrientationOptions, MagickFalse },
+ { "Policy", (long) MagickPolicyOptions, MagickFalse },
+ { "PolicyDomain", (long) MagickPolicyDomainOptions, MagickFalse },
+ { "PolicyRights", (long) MagickPolicyRightsOptions, MagickFalse },
+ { "Preview", (long) MagickPreviewOptions, MagickFalse },
+ { "Primitive", (long) MagickPrimitiveOptions, MagickFalse },
+ { "QuantumFormat", (long) MagickQuantumFormatOptions, MagickFalse },
+ { "Resource", (long) MagickResourceOptions, MagickFalse },
+ { "SparseColor", (long) MagickSparseColorOptions, MagickFalse },
+ { "Storage", (long) MagickStorageOptions, MagickFalse },
+ { "Stretch", (long) MagickStretchOptions, MagickFalse },
+ { "Style", (long) MagickStyleOptions, MagickFalse },
+ { "Threshold", (long) MagickThresholdOptions, MagickFalse },
+ { "Type", (long) MagickTypeOptions, MagickFalse },
+ { "Units", (long) MagickResolutionOptions, MagickFalse },
+ { "Undefined", (long) MagickUndefinedOptions, MagickTrue },
+ { "Validate", (long) MagickValidateOptions, MagickFalse },
+ { "VirtualPixel", (long) MagickVirtualPixelOptions, MagickFalse },
+ { (char *) NULL, (long) MagickUndefinedOptions, MagickFalse }
+ },
+ LogEventOptions[] =
+ {
+ { "Undefined", (long) UndefinedEvents, MagickTrue },
+ { "All", (long) (AllEvents &~ TraceEvent), MagickFalse },
+ { "Annotate", (long) AnnotateEvent, MagickFalse },
+ { "Blob", (long) BlobEvent, MagickFalse },
+ { "Cache", (long) CacheEvent, MagickFalse },
+ { "Coder", (long) CoderEvent, MagickFalse },
+ { "Configure", (long) ConfigureEvent, MagickFalse },
+ { "Deprecate", (long) DeprecateEvent, MagickFalse },
+ { "Draw", (long) DrawEvent, MagickFalse },
+ { "Exception", (long) ExceptionEvent, MagickFalse },
+ { "Locale", (long) LocaleEvent, MagickFalse },
+ { "Module", (long) ModuleEvent, MagickFalse },
+ { "None", (long) NoEvents, MagickFalse },
+ { "Policy", (long) PolicyEvent, MagickFalse },
+ { "Resource", (long) ResourceEvent, MagickFalse },
+ { "Trace", (long) TraceEvent, MagickFalse },
+ { "Transform", (long) TransformEvent, MagickFalse },
+ { "User", (long) UserEvent, MagickFalse },
+ { "Wand", (long) WandEvent, MagickFalse },
+ { "X11", (long) X11Event, MagickFalse },
+ { (char *) NULL, (long) UndefinedEvents, MagickFalse }
+ },
+ MetricOptions[] =
+ {
+ { "Undefined", (long) UndefinedMetric, MagickTrue },
+ { "AE", (long) AbsoluteErrorMetric, MagickFalse },
+ { "MAE", (long) MeanAbsoluteErrorMetric, MagickFalse },
+ { "MEPP", (long) MeanErrorPerPixelMetric, MagickFalse },
+ { "MSE", (long) MeanSquaredErrorMetric, MagickFalse },
+ { "PAE", (long) PeakAbsoluteErrorMetric, MagickFalse },
+ { "PSNR", (long) PeakSignalToNoiseRatioMetric, MagickFalse },
+ { "RMSE", (long) RootMeanSquaredErrorMetric, MagickFalse },
+ { (char *) NULL, (long) UndefinedMetric, MagickFalse }
+ },
+ MethodOptions[] =
+ {
+ { "Undefined", (long) UndefinedMethod, MagickTrue },
+ { "FillToBorder", (long) FillToBorderMethod, MagickFalse },
+ { "Floodfill", (long) FloodfillMethod, MagickFalse },
+ { "Point", (long) PointMethod, MagickFalse },
+ { "Replace", (long) ReplaceMethod, MagickFalse },
+ { "Reset", (long) ResetMethod, MagickFalse },
+ { (char *) NULL, (long) UndefinedMethod, MagickFalse }
+ },
+ ModeOptions[] =
+ {
+ { "Undefined", (long) UndefinedMode, MagickTrue },
+ { "Concatenate", (long) ConcatenateMode, MagickFalse },
+ { "Frame", (long) FrameMode, MagickFalse },
+ { "Unframe", (long) UnframeMode, MagickFalse },
+ { (char *) NULL, (long) UndefinedMode, MagickFalse }
+ },
+ NoiseOptions[] =
+ {
+ { "Undefined", (long) UndefinedNoise, MagickTrue },
+ { "Gaussian", (long) (long) GaussianNoise, MagickFalse },
+ { "Impulse", (long) ImpulseNoise, MagickFalse },
+ { "Laplacian", (long) LaplacianNoise, MagickFalse },
+ { "Multiplicative", (long) MultiplicativeGaussianNoise, MagickFalse },
+ { "Poisson", (long) PoissonNoise, MagickFalse },
+ { "Random", (long) RandomNoise, MagickFalse },
+ { "Uniform", (long) UniformNoise, MagickFalse },
+ { (char *) NULL, (long) UndefinedNoise, MagickFalse }
+ },
+ OrientationOptions[] =
+ {
+ { "Undefined", (long) UndefinedOrientation, MagickTrue },
+ { "TopLeft", (long) TopLeftOrientation, MagickFalse },
+ { "TopRight", (long) TopRightOrientation, MagickFalse },
+ { "BottomRight", (long) BottomRightOrientation, MagickFalse },
+ { "BottomLeft", (long) BottomLeftOrientation, MagickFalse },
+ { "LeftTop", (long) LeftTopOrientation, MagickFalse },
+ { "RightTop", (long) RightTopOrientation, MagickFalse },
+ { "RightBottom", (long) RightBottomOrientation, MagickFalse },
+ { "LeftBottom", (long) LeftBottomOrientation, MagickFalse }
+ },
+ PolicyDomainOptions[] =
+ {
+ { "Undefined", (long) UndefinedPolicyDomain, MagickTrue },
+ { "Coder", (long) CoderPolicyDomain, MagickFalse },
+ { "Delegate", (long) DelegatePolicyDomain, MagickFalse },
+ { "Filter", (long) FilterPolicyDomain, MagickFalse },
+ { "Path", (long) PathPolicyDomain, MagickFalse },
+ { "Resource", (long) ResourcePolicyDomain, MagickFalse }
+ },
+ PolicyRightsOptions[] =
+ {
+ { "Undefined", (long) UndefinedPolicyRights, MagickTrue },
+ { "None", (long) NoPolicyRights, MagickFalse },
+ { "Read", (long) ReadPolicyRights, MagickFalse },
+ { "Write", (long) WritePolicyRights, MagickFalse },
+ { "Execute", (long) ExecutePolicyRights, MagickFalse }
+ },
+ PreviewOptions[] =
+ {
+ { "Undefined", (long) UndefinedPreview, MagickTrue },
+ { "AddNoise", (long) AddNoisePreview, MagickFalse },
+ { "Blur", (long) BlurPreview, MagickFalse },
+ { "Brightness", (long) BrightnessPreview, MagickFalse },
+ { "Charcoal", (long) CharcoalDrawingPreview, MagickFalse },
+ { "Despeckle", (long) DespecklePreview, MagickFalse },
+ { "Dull", (long) DullPreview, MagickFalse },
+ { "EdgeDetect", (long) EdgeDetectPreview, MagickFalse },
+ { "Gamma", (long) GammaPreview, MagickFalse },
+ { "Grayscale", (long) GrayscalePreview, MagickFalse },
+ { "Hue", (long) HuePreview, MagickFalse },
+ { "Implode", (long) ImplodePreview, MagickFalse },
+ { "JPEG", (long) JPEGPreview, MagickFalse },
+ { "OilPaint", (long) OilPaintPreview, MagickFalse },
+ { "Quantize", (long) QuantizePreview, MagickFalse },
+ { "Raise", (long) RaisePreview, MagickFalse },
+ { "ReduceNoise", (long) ReduceNoisePreview, MagickFalse },
+ { "Roll", (long) RollPreview, MagickFalse },
+ { "Rotate", (long) RotatePreview, MagickFalse },
+ { "Saturation", (long) SaturationPreview, MagickFalse },
+ { "Segment", (long) SegmentPreview, MagickFalse },
+ { "Shade", (long) ShadePreview, MagickFalse },
+ { "Sharpen", (long) SharpenPreview, MagickFalse },
+ { "Shear", (long) ShearPreview, MagickFalse },
+ { "Solarize", (long) SolarizePreview, MagickFalse },
+ { "Spiff", (long) SpiffPreview, MagickFalse },
+ { "Spread", (long) SpreadPreview, MagickFalse },
+ { "Swirl", (long) SwirlPreview, MagickFalse },
+ { "Threshold", (long) ThresholdPreview, MagickFalse },
+ { "Wave", (long) WavePreview, MagickFalse },
+ { (char *) NULL, (long) UndefinedPreview, MagickFalse }
+ },
+ PrimitiveOptions[] =
+ {
+ { "Undefined", (long) UndefinedPrimitive, MagickTrue },
+ { "Arc", (long) ArcPrimitive, MagickFalse },
+ { "Bezier", (long) BezierPrimitive, MagickFalse },
+ { "Circle", (long) CirclePrimitive, MagickFalse },
+ { "Color", (long) ColorPrimitive, MagickFalse },
+ { "Ellipse", (long) EllipsePrimitive, MagickFalse },
+ { "Image", (long) ImagePrimitive, MagickFalse },
+ { "Line", (long) LinePrimitive, MagickFalse },
+ { "Matte", (long) MattePrimitive, MagickFalse },
+ { "Path", (long) PathPrimitive, MagickFalse },
+ { "Point", (long) PointPrimitive, MagickFalse },
+ { "Polygon", (long) PolygonPrimitive, MagickFalse },
+ { "Polyline", (long) PolylinePrimitive, MagickFalse },
+ { "Rectangle", (long) RectanglePrimitive, MagickFalse },
+ { "roundRectangle", (long) RoundRectanglePrimitive, MagickFalse },
+ { "Text", (long) TextPrimitive, MagickFalse },
+ { (char *) NULL, (long) UndefinedPrimitive, MagickFalse }
+ },
+ QuantumFormatOptions[] =
+ {
+ { "Undefined", (long) UndefinedQuantumFormat, MagickTrue },
+ { "FloatingPoint", (long) FloatingPointQuantumFormat, MagickFalse },
+ { "Signed", (long) SignedQuantumFormat, MagickFalse },
+ { "Unsigned", (long) UnsignedQuantumFormat, MagickFalse },
+ { (char *) NULL, (long) FloatingPointQuantumFormat, MagickFalse }
+ },
+ ResolutionOptions[] =
+ {
+ { "Undefined", (long) UndefinedResolution, MagickTrue },
+ { "PixelsPerInch", (long) PixelsPerInchResolution, MagickFalse },
+ { "PixelsPerCentimeter", (long) PixelsPerCentimeterResolution, MagickFalse },
+ { (char *) NULL, (long) UndefinedResolution, MagickFalse }
+ },
+ ResourceOptions[] =
+ {
+ { "Undefined", (long) UndefinedResource, MagickTrue },
+ { "Area", (long) AreaResource, MagickFalse },
+ { "Disk", (long) DiskResource, MagickFalse },
+ { "File", (long) FileResource, MagickFalse },
+ { "Map", (long) MapResource, MagickFalse },
+ { "Memory", (long) MemoryResource, MagickFalse },
+ { "Thread", (long) ThreadResource, MagickFalse },
+ { "Time", (long) TimeResource, MagickFalse },
+ { (char *) NULL, (long) UndefinedResource, MagickFalse }
+ },
+ SparseColorOptions[] =
+ {
+ { "Undefined", (long) UndefinedDistortion, MagickTrue },
+ { "Barycentric", (long) BarycentricColorInterpolate, MagickFalse },
+ { "Bilinear", (long) BilinearColorInterpolate, MagickFalse },
+ { "Shepards", (long) ShepardsColorInterpolate, MagickFalse },
+ { "Voronoi", (long) VoronoiColorInterpolate, MagickFalse },
+ { (char *) NULL, (long) UndefinedResource, MagickFalse }
+ },
+ StorageOptions[] =
+ {
+ { "Undefined", (long) UndefinedPixel, MagickTrue },
+ { "Char", (long) CharPixel, MagickFalse },
+ { "Double", (long) DoublePixel, MagickFalse },
+ { "Float", (long) FloatPixel, MagickFalse },
+ { "Integer", (long) IntegerPixel, MagickFalse },
+ { "Long", (long) LongPixel, MagickFalse },
+ { "Quantum", (long) QuantumPixel, MagickFalse },
+ { "Short", (long) ShortPixel, MagickFalse },
+ { (char *) NULL, (long) UndefinedResource, MagickFalse }
+ },
+ StretchOptions[] =
+ {
+ { "Undefined", (long) UndefinedStretch, MagickTrue },
+ { "Any", (long) AnyStretch, MagickFalse },
+ { "Condensed", (long) CondensedStretch, MagickFalse },
+ { "Expanded", (long) ExpandedStretch, MagickFalse },
+ { "ExtraCondensed", (long) ExtraCondensedStretch, MagickFalse },
+ { "ExtraExpanded", (long) ExtraExpandedStretch, MagickFalse },
+ { "Normal", (long) NormalStretch, MagickFalse },
+ { "SemiCondensed", (long) SemiCondensedStretch, MagickFalse },
+ { "SemiExpanded", (long) SemiExpandedStretch, MagickFalse },
+ { "UltraCondensed", (long) UltraCondensedStretch, MagickFalse },
+ { "UltraExpanded", (long) UltraExpandedStretch, MagickFalse },
+ { (char *) NULL, (long) UndefinedStretch, MagickFalse }
+ },
+ StyleOptions[] =
+ {
+ { "Undefined", (long) UndefinedStyle, MagickTrue },
+ { "Any", (long) AnyStyle, MagickFalse },
+ { "Italic", (long) ItalicStyle, MagickFalse },
+ { "Normal", (long) NormalStyle, MagickFalse },
+ { "Oblique", (long) ObliqueStyle, MagickFalse },
+ { (char *) NULL, (long) UndefinedStyle, MagickFalse }
+ },
+ TypeOptions[] =
+ {
+ { "Undefined", (long) UndefinedType, MagickTrue },
+ { "Bilevel", (long) BilevelType, MagickFalse },
+ { "ColorSeparation", (long) ColorSeparationType, MagickFalse },
+ { "ColorSeparationMatte", (long) ColorSeparationMatteType, MagickFalse },
+ { "Grayscale", (long) GrayscaleType, MagickFalse },
+ { "GrayscaleMatte", (long) GrayscaleMatteType, MagickFalse },
+ { "Optimize", (long) OptimizeType, MagickFalse },
+ { "Palette", (long) PaletteType, MagickFalse },
+ { "PaletteBilevelMatte", (long) PaletteBilevelMatteType, MagickFalse },
+ { "PaletteMatte", (long) PaletteMatteType, MagickFalse },
+ { "TrueColorMatte", (long) TrueColorMatteType, MagickFalse },
+ { "TrueColor", (long) TrueColorType, MagickFalse },
+ { (char *) NULL, (long) UndefinedType, MagickFalse }
+ },
+ ValidateOptions[] =
+ {
+ { "Undefined", (long) UndefinedValidate, MagickTrue },
+ { "All", (long) AllValidate, MagickFalse },
+ { "Compare", (long) CompareValidate, MagickFalse },
+ { "Composite", (long) CompositeValidate, MagickFalse },
+ { "Convert", (long) ConvertValidate, MagickFalse },
+ { "FormatsInMemory", (long) FormatsInMemoryValidate, MagickFalse },
+ { "FormatsOnDisk", (long) FormatsOnDiskValidate, MagickFalse },
+ { "Identify", (long) IdentifyValidate, MagickFalse },
+ { "ImportExport", (long) ImportExportValidate, MagickFalse },
+ { "Montage", (long) MontageValidate, MagickFalse },
+ { "Stream", (long) StreamValidate, MagickFalse },
+ { "None", (long) NoValidate, MagickFalse },
+ { (char *) NULL, (long) UndefinedValidate, MagickFalse }
+ },
+ VirtualPixelOptions[] =
+ {
+ { "Undefined", (long) UndefinedVirtualPixelMethod, MagickTrue },
+ { "Background", (long) BackgroundVirtualPixelMethod, MagickFalse },
+ { "Black", (long) BlackVirtualPixelMethod, MagickFalse },
+ { "Constant", (long) BackgroundVirtualPixelMethod, MagickTrue }, /* deprecated */
+ { "CheckerTile", (long) CheckerTileVirtualPixelMethod, MagickFalse },
+ { "Dither", (long) DitherVirtualPixelMethod, MagickFalse },
+ { "Edge", (long) EdgeVirtualPixelMethod, MagickFalse },
+ { "Gray", (long) GrayVirtualPixelMethod, MagickFalse },
+ { "HorizontalTile", (long) HorizontalTileVirtualPixelMethod, MagickFalse },
+ { "HorizontalTileEdge", (long) HorizontalTileEdgeVirtualPixelMethod, MagickFalse },
+ { "Mirror", (long) MirrorVirtualPixelMethod, MagickFalse },
+ { "Random", (long) RandomVirtualPixelMethod, MagickFalse },
+ { "Tile", (long) TileVirtualPixelMethod, MagickFalse },
+ { "Transparent", (long) TransparentVirtualPixelMethod, MagickFalse },
+ { "VerticalTile", (long) VerticalTileVirtualPixelMethod, MagickFalse },
+ { "VerticalTileEdge", (long) VerticalTileEdgeVirtualPixelMethod, MagickFalse },
+ { "White", (long) WhiteVirtualPixelMethod, MagickFalse },
+ { (char *) NULL, (long) UndefinedVirtualPixelMethod, MagickFalse }
+ };
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageOptions() clones one or more image options.
+%
+% The format of the CloneImageOptions method is:
+%
+% MagickBooleanType CloneImageOptions(ImageInfo *image_info,
+% const ImageInfo *clone_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o clone_info: the clone image info.
+%
+*/
+MagickExport MagickBooleanType CloneImageOptions(ImageInfo *image_info,
+ const ImageInfo *clone_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(clone_info != (const ImageInfo *) NULL);
+ assert(clone_info->signature == MagickSignature);
+ if (clone_info->options != (void *) NULL)
+ image_info->options=CloneSplayTree((SplayTreeInfo *) clone_info->options,
+ (void *(*)(void *)) ConstantString,(void *(*)(void *)) ConstantString);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e f i n e I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageOption() associates a key/value pair with an image option.
+%
+% The format of the DefineImageOption method is:
+%
+% MagickBooleanType DefineImageOption(ImageInfo *image_info,
+% const char *option)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o option: the image option.
+%
+*/
+MagickExport MagickBooleanType DefineImageOption(ImageInfo *image_info,
+ const char *option)
+{
+ char
+ key[MaxTextExtent],
+ value[MaxTextExtent];
+
+ register char
+ *p;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(option != (const char *) NULL);
+ (void) CopyMagickString(key,option,MaxTextExtent);
+ for (p=key; *p != '\0'; p++)
+ if (*p == '=')
+ break;
+ *value='\0';
+ if (*p == '=')
+ (void) CopyMagickString(value,p+1,MaxTextExtent);
+ *p='\0';
+ return(SetImageOption(image_info,key,value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageOption() deletes an key from the image map.
+%
+% The format of the DeleteImageOption method is:
+%
+% MagickBooleanType DeleteImageOption(ImageInfo *image_info,
+% const char *key)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o option: the image option.
+%
+*/
+MagickExport MagickBooleanType DeleteImageOption(ImageInfo *image_info,
+ const char *option)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return(MagickFalse);
+ return(DeleteNodeFromSplayTree((SplayTreeInfo *) image_info->options,option));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageOptions() releases memory associated with image option values.
+%
+% The format of the DestroyDefines method is:
+%
+% void DestroyImageOptions(ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport void DestroyImageOptions(ImageInfo *image_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options != (void *) NULL)
+ image_info->options=DestroySplayTree((SplayTreeInfo *) image_info->options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageOption() gets a value associated with an image option.
+%
+% The format of the GetImageOption method is:
+%
+% const char *GetImageOption(const ImageInfo *image_info,
+% const char *key)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o key: the key.
+%
+*/
+MagickExport const char *GetImageOption(const ImageInfo *image_info,
+ const char *key)
+{
+ const char
+ *option;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return((const char *) NULL);
+ option=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image_info->options,key);
+ return(option);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickOptions() returns a list of values.
+%
+% The format of the GetMagickOptions method is:
+%
+% const char **GetMagickOptions(const MagickOption value)
+%
+% A description of each parameter follows:
+%
+% o value: the value.
+%
+*/
+
+static const OptionInfo *GetOptionInfo(const MagickOption option)
+{
+ switch (option)
+ {
+ case MagickAlignOptions: return(AlignOptions);
+ case MagickAlphaOptions: return(AlphaOptions);
+ case MagickBooleanOptions: return(BooleanOptions);
+ case MagickChannelOptions: return(ChannelOptions);
+ case MagickClassOptions: return(ClassOptions);
+ case MagickClipPathOptions: return(ClipPathOptions);
+ case MagickColorspaceOptions: return(ColorspaceOptions);
+ case MagickCommandOptions: return(CommandOptions);
+ case MagickComposeOptions: return(ComposeOptions);
+ case MagickCompressOptions: return(CompressOptions);
+ case MagickDataTypeOptions: return(DataTypeOptions);
+ case MagickDebugOptions: return(LogEventOptions);
+ case MagickDecorateOptions: return(DecorateOptions);
+ case MagickDisposeOptions: return(DisposeOptions);
+ case MagickDistortOptions: return(DistortOptions);
+ case MagickDitherOptions: return(DitherOptions);
+ case MagickEndianOptions: return(EndianOptions);
+ case MagickEvaluateOptions: return(EvaluateOptions);
+ case MagickFillRuleOptions: return(FillRuleOptions);
+ case MagickFilterOptions: return(FilterOptions);
+ case MagickFunctionOptions: return(FunctionOptions);
+ case MagickGravityOptions: return(GravityOptions);
+ case MagickImageListOptions: return(ImageListOptions);
+ case MagickIntentOptions: return(IntentOptions);
+ case MagickInterlaceOptions: return(InterlaceOptions);
+ case MagickInterpolateOptions: return(InterpolateOptions);
+ case MagickLayerOptions: return(LayerOptions);
+ case MagickLineCapOptions: return(LineCapOptions);
+ case MagickLineJoinOptions: return(LineJoinOptions);
+ case MagickListOptions: return(ListOptions);
+ case MagickLogEventOptions: return(LogEventOptions);
+ case MagickMetricOptions: return(MetricOptions);
+ case MagickMethodOptions: return(MethodOptions);
+ case MagickModeOptions: return(ModeOptions);
+ case MagickNoiseOptions: return(NoiseOptions);
+ case MagickOrientationOptions: return(OrientationOptions);
+ case MagickPolicyDomainOptions: return(PolicyDomainOptions);
+ case MagickPolicyRightsOptions: return(PolicyRightsOptions);
+ case MagickPreviewOptions: return(PreviewOptions);
+ case MagickPrimitiveOptions: return(PrimitiveOptions);
+ case MagickQuantumFormatOptions: return(QuantumFormatOptions);
+ case MagickResolutionOptions: return(ResolutionOptions);
+ case MagickResourceOptions: return(ResourceOptions);
+ case MagickSparseColorOptions: return(SparseColorOptions);
+ case MagickStorageOptions: return(StorageOptions);
+ case MagickStretchOptions: return(StretchOptions);
+ case MagickStyleOptions: return(StyleOptions);
+ case MagickTypeOptions: return(TypeOptions);
+ case MagickValidateOptions: return(ValidateOptions);
+ case MagickVirtualPixelOptions: return(VirtualPixelOptions);
+ default: break;
+ }
+ return((const OptionInfo *) NULL);
+}
+
+MagickExport char **GetMagickOptions(const MagickOption value)
+{
+ char
+ **values;
+
+ const OptionInfo
+ *option_info;
+
+ register long
+ i;
+
+ option_info=GetOptionInfo(value);
+ if (option_info == (const OptionInfo *) NULL)
+ return((char **) NULL);
+ for (i=0; option_info[i].mnemonic != (const char *) NULL; i++) ;
+ values=(char **) AcquireQuantumMemory((size_t) i+1UL,sizeof(*values));
+ if (values == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ for (i=0; option_info[i].mnemonic != (const char *) NULL; i++)
+ values[i]=AcquireString(option_info[i].mnemonic);
+ values[i]=(char *) NULL;
+ return(values);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageOption() gets the next image option value.
+%
+% The format of the GetNextImageOption method is:
+%
+% char *GetNextImageOption(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport char *GetNextImageOption(const ImageInfo *image_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return((char *) NULL);
+ return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image_info->options));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickOption() returns MagickTrue if the option begins with a - or + and
+% the first character that follows is alphanumeric.
+%
+% The format of the IsMagickOption method is:
+%
+% MagickBooleanType IsMagickOption(const char *option)
+%
+% A description of each parameter follows:
+%
+% o option: the option.
+%
+*/
+MagickExport MagickBooleanType IsMagickOption(const char *option)
+{
+ assert(option != (const char *) NULL);
+ if ((*option != '-') && (*option != '+'))
+ return(MagickFalse);
+ if (strlen(option) == 1)
+ return(MagickFalse);
+ option++;
+ if (isalpha((int) ((unsigned char) *option)) == 0)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k O p t i o n T o M n e m o n i c %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickOptionToMnemonic() returns an enumerated value as a mnemonic.
+%
+% The format of the MagickOptionToMnemonic method is:
+%
+% const char *MagickOptionToMnemonic(const MagickOption option,
+% const long type)
+%
+% A description of each parameter follows:
+%
+% o option: the option.
+%
+% o type: one or more values separated by commas.
+%
+*/
+MagickExport const char *MagickOptionToMnemonic(const MagickOption option,
+ const long type)
+{
+ const OptionInfo
+ *option_info;
+
+ register long
+ i;
+
+ option_info=GetOptionInfo(option);
+ if (option_info == (const OptionInfo *) NULL)
+ return((const char *) NULL);
+ for (i=0; option_info[i].mnemonic != (const char *) NULL; i++)
+ if (type == option_info[i].type)
+ break;
+ if (option_info[i].mnemonic == (const char *) NULL)
+ return("undefined");
+ return(option_info[i].mnemonic);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t M a g i c k O p t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListMagickOptions() lists the contents of enumerated option type(s).
+%
+% The format of the ListMagickOptions method is:
+%
+% MagickBooleanType ListMagickOptions(FILE *file,const MagickOption option,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o file: list options to this file handle.
+%
+% o option: list these options.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListMagickOptions(FILE *file,
+ const MagickOption option,ExceptionInfo *magick_unused(exception))
+{
+ const OptionInfo
+ *option_info;
+
+ register long
+ i;
+
+ if (file == (FILE *) NULL)
+ file=stdout;
+ option_info=GetOptionInfo(option);
+ if (option_info == (const OptionInfo *) NULL)
+ return(MagickFalse);
+ for (i=0; option_info[i].mnemonic != (char *) NULL; i++)
+ {
+ if (option_info[i].stealth != MagickFalse)
+ continue;
+ (void) fprintf(file,"%s\n",option_info[i].mnemonic);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e C h a n n e l O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseChannelOption() parses a string and returns an enumerated channel
+% type(s).
+%
+% The format of the ParseChannelOption method is:
+%
+% long ParseChannelOption(const char *channels)
+%
+% A description of each parameter follows:
+%
+% o options: One or more values separated by commas.
+%
+*/
+MagickExport long ParseChannelOption(const char *channels)
+{
+ long
+ channel;
+
+ register long
+ i;
+
+ channel=ParseMagickOption(MagickChannelOptions,MagickTrue,channels);
+ if (channel >= 0)
+ return(channel);
+ channel=0;
+ for (i=0; i < (long) strlen(channels); i++)
+ {
+ switch (channels[i])
+ {
+ case 'A':
+ case 'a':
+ {
+ channel|=OpacityChannel;
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ channel|=BlueChannel;
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ channel|=CyanChannel;
+ break;
+ }
+ case 'g':
+ case 'G':
+ {
+ channel|=GreenChannel;
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ channel|=IndexChannel;
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ channel|=BlackChannel;
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ channel|=MagentaChannel;
+ break;
+ }
+ case 'o':
+ case 'O':
+ {
+ channel|=OpacityChannel;
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ channel|=RedChannel;
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ channel|=YellowChannel;
+ break;
+ }
+ case ',':
+ {
+ /*
+ More channel flags follow shorthand. For example "RGB,sync"
+ Gather the additional channel flags and merge with shorthand
+ */
+ long
+ more_channel;
+ more_channel=ParseMagickOption(MagickChannelOptions,MagickTrue,
+ channels+i+1);
+ if (more_channel < 0)
+ return(more_channel);
+ channel |= more_channel;
+ return(channel);
+ }
+ default:
+ return(-1);
+ }
+ }
+ return(channel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P a r s e M a g i c k O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ParseMagickOption() parses a string and returns an enumerated option type(s).
+%
+% The format of the ParseMagickOption method is:
+%
+% long ParseMagickOption(const MagickOption option,
+% const MagickBooleanType list,const char *options)
+%
+% A description of each parameter follows:
+%
+% o option: Index to the option table to lookup
+%
+% o list: A option other than zero permits more than one option separated by
+% a comma or pipe.
+%
+% o options: One or more options separated by commas.
+%
+*/
+MagickExport long ParseMagickOption(const MagickOption option,
+ const MagickBooleanType list,const char *options)
+{
+ char
+ token[MaxTextExtent];
+
+ const OptionInfo
+ *option_info;
+
+ int
+ sentinel;
+
+ long
+ option_types;
+
+ MagickBooleanType
+ negate;
+
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ register long
+ i;
+
+ option_info=GetOptionInfo(option);
+ if (option_info == (const OptionInfo *) NULL)
+ return(-1);
+ option_types=0;
+ sentinel=',';
+ if (strchr(options,'|') != (char *) NULL)
+ sentinel='|';
+ for (p=options; p != (char *) NULL; p=strchr(p,sentinel))
+ {
+ while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == sentinel)) &&
+ (*p != '\0'))
+ p++;
+ negate=(*p == '!') ? MagickTrue : MagickFalse;
+ if (negate != MagickFalse)
+ p++;
+ q=token;
+ while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != sentinel)) &&
+ (*p != '\0'))
+ {
+ if ((q-token) >= MaxTextExtent)
+ break;
+ *q++=(*p++);
+ }
+ *q='\0';
+ for (i=0; option_info[i].mnemonic != (char *) NULL; i++)
+ if (LocaleCompare(token,option_info[i].mnemonic) == 0)
+ {
+ if (*token == '!')
+ option_types=option_types &~ option_info[i].type;
+ else
+ option_types=option_types | option_info[i].type;
+ break;
+ }
+ if ((option_info[i].mnemonic == (char *) NULL) &&
+ ((strchr(token+1,'-') != (char *) NULL) ||
+ (strchr(token+1,'_') != (char *) NULL)))
+ {
+ while ((q=strchr(token+1,'-')) != (char *) NULL)
+ (void) CopyMagickString(q,q+1,MaxTextExtent-strlen(q));
+ while ((q=strchr(token+1,'_')) != (char *) NULL)
+ (void) CopyMagickString(q,q+1,MaxTextExtent-strlen(q));
+ for (i=0; option_info[i].mnemonic != (char *) NULL; i++)
+ if (LocaleCompare(token,option_info[i].mnemonic) == 0)
+ {
+ if (*token == '!')
+ option_types=option_types &~ option_info[i].type;
+ else
+ option_types=option_types | option_info[i].type;
+ break;
+ }
+ }
+ if (option_info[i].mnemonic == (char *) NULL)
+ return(-1);
+ if (list == MagickFalse)
+ break;
+ }
+ return(option_types);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageOption() removes an option from the image and returns its value.
+%
+% The format of the RemoveImageOption method is:
+%
+% char *RemoveImageOption(ImageInfo *image_info,const char *option)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o option: the image option.
+%
+*/
+MagickExport char *RemoveImageOption(ImageInfo *image_info,const char *option)
+{
+ char
+ *value;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return((char *) NULL);
+ value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *)
+ image_info->options,option);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageOptions() resets the image_info option. That is, it deletes
+% all options associated with the image_info structure.
+%
+% The format of the ResetImageOptions method is:
+%
+% ResetImageOptions(ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport void ResetImageOptions(const ImageInfo *image_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return;
+ ResetSplayTree((SplayTreeInfo *) image_info->options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e O p t i o n I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageOptionIterator() resets the image_info values iterator. Use it
+% in conjunction with GetNextImageOption() to iterate over all the values
+% associated with an image option.
+%
+% The format of the ResetImageOptionIterator method is:
+%
+% ResetImageOptionIterator(ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport void ResetImageOptionIterator(const ImageInfo *image_info)
+{
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (image_info->options == (void *) NULL)
+ return;
+ ResetSplayTreeIterator((SplayTreeInfo *) image_info->options);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e O p t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageOption() associates an value with an image option.
+%
+% The format of the SetImageOption method is:
+%
+% MagickBooleanType SetImageOption(ImageInfo *image_info,
+% const char *option,const char *value)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o option: the image option.
+%
+% o values: the image option values.
+%
+*/
+MagickExport MagickBooleanType SetImageOption(ImageInfo *image_info,
+ const char *option,const char *value)
+{
+ MagickBooleanType
+ status;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ if (LocaleCompare(option,"size") == 0)
+ (void) CloneString(&image_info->size,value);
+ if (image_info->options == (void *) NULL)
+ image_info->options=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,RelinquishMagickMemory);
+ status=AddValueToSplayTree((SplayTreeInfo *) image_info->options,
+ ConstantString(option),ConstantString(value));
+ return(status);
+}
diff --git a/magick/option.h b/magick/option.h
new file mode 100644
index 0000000..1f5f932
--- /dev/null
+++ b/magick/option.h
@@ -0,0 +1,152 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore option methods.
+*/
+#ifndef _MAGICKCORE_OPTION_H
+#define _MAGICKCORE_OPTION_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ MagickUndefinedOptions = -1,
+ MagickAlignOptions = 0,
+ MagickAlphaOptions,
+ MagickBooleanOptions,
+ MagickChannelOptions,
+ MagickClassOptions,
+ MagickClipPathOptions,
+ MagickCoderOptions,
+ MagickColorOptions,
+ MagickColorspaceOptions,
+ MagickCommandOptions,
+ MagickComposeOptions,
+ MagickCompressOptions,
+ MagickConfigureOptions,
+ MagickDataTypeOptions,
+ MagickDebugOptions,
+ MagickDecorateOptions,
+ MagickDelegateOptions,
+ MagickDisposeOptions,
+ MagickDistortOptions,
+ MagickDitherOptions,
+ MagickEndianOptions,
+ MagickEvaluateOptions,
+ MagickFillRuleOptions,
+ MagickFilterOptions,
+ MagickFontOptions,
+ MagickFontsOptions,
+ MagickFormatOptions,
+ MagickFunctionOptions,
+ MagickGravityOptions,
+ MagickImageListOptions,
+ MagickIntentOptions,
+ MagickInterlaceOptions,
+ MagickInterpolateOptions,
+ MagickLayerOptions,
+ MagickLineCapOptions,
+ MagickLineJoinOptions,
+ MagickListOptions,
+ MagickLocaleOptions,
+ MagickLogEventOptions,
+ MagickLogOptions,
+ MagickMagicOptions,
+ MagickMethodOptions,
+ MagickMetricOptions,
+ MagickMimeOptions,
+ MagickModeOptions,
+ MagickModuleOptions,
+ MagickNoiseOptions,
+ MagickOrientationOptions,
+ MagickPolicyOptions,
+ MagickPolicyDomainOptions,
+ MagickPolicyRightsOptions,
+ MagickPreviewOptions,
+ MagickPrimitiveOptions,
+ MagickQuantumFormatOptions,
+ MagickResolutionOptions,
+ MagickResourceOptions,
+ MagickSparseColorOptions,
+ MagickStorageOptions,
+ MagickStretchOptions,
+ MagickStyleOptions,
+ MagickThresholdOptions,
+ MagickTypeOptions,
+ MagickValidateOptions,
+ MagickVirtualPixelOptions
+} MagickOption;
+
+typedef enum
+{
+ UndefinedValidate,
+ NoValidate = 0x00000,
+ CompareValidate = 0x00001,
+ CompositeValidate = 0x00002,
+ ConvertValidate = 0x00004,
+ FormatsInMemoryValidate = 0x00008,
+ FormatsOnDiskValidate = 0x00010,
+ IdentifyValidate = 0x00020,
+ ImportExportValidate = 0x00040,
+ MontageValidate = 0x00080,
+ StreamValidate = 0x00100,
+ AllValidate = 0x7fffffff
+} ValidateType;
+
+typedef struct _OptionInfo
+{
+ const char
+ *mnemonic;
+
+ long
+ type;
+
+ MagickBooleanType
+ stealth;
+} OptionInfo;
+
+extern MagickExport char
+ **GetMagickOptions(const MagickOption),
+ *GetNextImageOption(const ImageInfo *),
+ *RemoveImageOption(ImageInfo *,const char *);
+
+extern MagickExport const char
+ *GetImageOption(const ImageInfo *,const char *),
+ *MagickOptionToMnemonic(const MagickOption,const long);
+
+extern MagickExport long
+ ParseChannelOption(const char *),
+ ParseMagickOption(const MagickOption,const MagickBooleanType,const char *);
+
+extern MagickExport MagickBooleanType
+ CloneImageOptions(ImageInfo *,const ImageInfo *),
+ DefineImageOption(ImageInfo *,const char *),
+ DeleteImageOption(ImageInfo *,const char *),
+ IsMagickOption(const char *),
+ ListMagickOptions(FILE *,const MagickOption,ExceptionInfo *),
+ SetImageOption(ImageInfo *,const char *,const char *);
+
+extern MagickExport void
+ DestroyImageOptions(ImageInfo *),
+ ResetImageOptions(const ImageInfo *),
+ ResetImageOptionIterator(const ImageInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/paint.c b/magick/paint.c
new file mode 100644
index 0000000..89e71aa
--- /dev/null
+++ b/magick/paint.c
@@ -0,0 +1,1123 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% PPPP AAA IIIII N N TTTTT %
+% P P A A I NN N T %
+% PPPP AAAAA I N N N T %
+% P A A I N NN T %
+% P A A IIIII N N T %
+% %
+% %
+% Methods to Paint on an Image %
+% %
+% Software Design %
+% John Cristy %
+% July 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/draw.h"
+#include "magick/draw-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/paint.h"
+#include "magick/pixel-private.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+
+static inline double MagickMax(const double x,const double y)
+{
+ return( x > y ? x : y);
+}
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F l o o d f i l l P a i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FloodfillPaintImage() changes the color value of any pixel that matches
+% target and is an immediate neighbor. If the method FillToBorderMethod is
+% specified, the color value is changed for any neighbor pixel that does not
+% match the bordercolor member of image.
+%
+% By default target must match a particular pixel color exactly.
+% However, in many cases two colors may differ by a small amount. The
+% fuzz member of image defines how much tolerance is acceptable to
+% consider two colors as the same. For example, set fuzz to 10 and the
+% color red at intensities of 100 and 102 respectively are now
+% interpreted as the same color for the purposes of the floodfill.
+%
+% The format of the FloodfillPaintImage method is:
+%
+% MagickBooleanType FloodfillPaintImage(Image *image,
+% const ChannelType channel,const DrawInfo *draw_info,
+% const MagickPixelPacket target,const long x_offset,const long y_offset,
+% const MagickBooleanType invert)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel(s).
+%
+% o draw_info: the draw info.
+%
+% o target: the RGB value of the target color.
+%
+% o x_offset,y_offset: the starting location of the operation.
+%
+% o invert: paint any pixel that does not match the target color.
+%
+*/
+MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
+ const ChannelType channel,const DrawInfo *draw_info,
+ const MagickPixelPacket *target,const long x_offset,const long y_offset,
+ const MagickBooleanType invert)
+{
+#define MaxStacksize (1UL << 15)
+#define PushSegmentStack(up,left,right,delta) \
+{ \
+ if (s >= (segment_stack+MaxStacksize)) \
+ ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
+ else \
+ { \
+ if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (long) image->rows)) \
+ { \
+ s->x1=(double) (left); \
+ s->y1=(double) (up); \
+ s->x2=(double) (right); \
+ s->y2=(double) (delta); \
+ s++; \
+ } \
+ } \
+}
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *floodplane_image;
+
+ long
+ offset,
+ start,
+ x,
+ x1,
+ x2,
+ y;
+
+ MagickBooleanType
+ skip;
+
+ MagickPixelPacket
+ fill,
+ pixel;
+
+ PixelPacket
+ fill_color;
+
+ register SegmentInfo
+ *s;
+
+ SegmentInfo
+ *segment_stack;
+
+ /*
+ Check boundary conditions.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(draw_info != (DrawInfo *) NULL);
+ assert(draw_info->signature == MagickSignature);
+ if ((x_offset < 0) || (x_offset >= (long) image->columns))
+ return(MagickFalse);
+ if ((y_offset < 0) || (y_offset >= (long) image->rows))
+ return(MagickFalse);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ /*
+ Set floodfill state.
+ */
+ floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (floodplane_image == (Image *) NULL)
+ return(MagickFalse);
+ (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
+ segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
+ sizeof(*segment_stack));
+ if (segment_stack == (SegmentInfo *) NULL)
+ {
+ floodplane_image=DestroyImage(floodplane_image);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ /*
+ Push initial segment on stack.
+ */
+ exception=(&image->exception);
+ x=x_offset;
+ y=y_offset;
+ start=0;
+ s=segment_stack;
+ PushSegmentStack(y,x,x,1);
+ PushSegmentStack(y+1,x,x,-1);
+ GetMagickPixelPacket(image,&fill);
+ GetMagickPixelPacket(image,&pixel);
+ while (s > segment_stack)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Pop segment off stack.
+ */
+ s--;
+ x1=(long) s->x1;
+ x2=(long) s->x2;
+ offset=(long) s->y2;
+ y=(long) s->y1+offset;
+ /*
+ Recolor neighboring pixels.
+ */
+ p=GetVirtualPixels(image,0,y,(unsigned long) (x1+1),1,exception);
+ q=GetAuthenticPixels(floodplane_image,0,y,(unsigned long) (x1+1),1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ p+=x1;
+ q+=x1;
+ for (x=x1; x >= 0; x--)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if (IsMagickColorSimilar(&pixel,target) == invert)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ p--;
+ q--;
+ }
+ if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
+ break;
+ skip=x >= x1 ? MagickTrue : MagickFalse;
+ if (skip == MagickFalse)
+ {
+ start=x+1;
+ if (start < x1)
+ PushSegmentStack(y,start,x1-1,-offset);
+ x=x1+1;
+ }
+ do
+ {
+ if (skip == MagickFalse)
+ {
+ if (x < (long) image->columns)
+ {
+ p=GetVirtualPixels(image,x,y,image->columns-x,1,exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) ||
+ (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for ( ; x < (long) image->columns; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if (IsMagickColorSimilar(&pixel,target) == invert)
+ break;
+ q->opacity=(Quantum) TransparentOpacity;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
+ break;
+ }
+ PushSegmentStack(y,start,x-1,offset);
+ if (x > (x2+1))
+ PushSegmentStack(y,x2+1,x-1,-offset);
+ }
+ skip=MagickFalse;
+ x++;
+ if (x <= x2)
+ {
+ p=GetVirtualPixels(image,x,y,(unsigned long) (x2-x+1),1,exception);
+ q=GetAuthenticPixels(floodplane_image,x,y,(unsigned long) (x2-x+1),1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for ( ; x <= x2; x++)
+ {
+ if (q->opacity == (Quantum) TransparentOpacity)
+ break;
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if (IsMagickColorSimilar(&pixel,target) != invert)
+ break;
+ p++;
+ q++;
+ }
+ }
+ start=x;
+ } while (x <= x2);
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Tile fill color onto floodplane.
+ */
+ p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,exception);
+ q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (p->opacity != OpaqueOpacity)
+ {
+ (void) GetFillColor(draw_info,x,y,&fill_color);
+ SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
+ if (image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&fill);
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(fill.red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(fill.green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(fill.blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(fill.opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=RoundToQuantum(fill.index);
+ }
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
+ floodplane_image=DestroyImage(floodplane_image);
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G r a d i e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GradientImage() applies a continuously smooth color transitions along a
+% vector from one color to another.
+%
+% Note, the interface of this method will change in the future to support
+% more than one transistion.
+%
+% The format of the GradientImage method is:
+%
+% MagickBooleanType GradientImage(Image *image,const GradientType type,
+% const SpreadMethod method,const PixelPacket *start_color,
+% const PixelPacket *stop_color)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o type: the gradient type: linear or radial.
+%
+% o spread: the gradient spread meathod: pad, reflect, or repeat.
+%
+% o start_color: the start color.
+%
+% o stop_color: the stop color.
+%
+% This provides a good example of making use of the DrawGradientImage
+% function and the gradient structure in draw_info.
+*/
+MagickExport MagickBooleanType GradientImage(Image *image,
+ const GradientType type,const SpreadMethod method,
+ const PixelPacket *start_color,const PixelPacket *stop_color)
+{
+ DrawInfo
+ *draw_info;
+
+ GradientInfo
+ *gradient;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ /*
+ Set gradient start-stop end points.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(start_color != (const PixelPacket *) NULL);
+ assert(stop_color != (const PixelPacket *) NULL);
+ draw_info=AcquireDrawInfo();
+ gradient=(&draw_info->gradient);
+ gradient->type=type;
+ gradient->bounding_box.width=image->columns;
+ gradient->bounding_box.height=image->rows;
+ gradient->gradient_vector.x2=(double) image->columns-1.0;
+ gradient->gradient_vector.y2=(double) image->rows-1.0;
+ if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
+ gradient->gradient_vector.x2=0.0;
+ gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
+ gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
+ gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
+ gradient->spread=method;
+ /*
+ Define the gradient to fill between the stops.
+ */
+ gradient->number_stops=2;
+ gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
+ sizeof(*gradient->stops));
+ if (gradient->stops == (StopInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
+ sizeof(*gradient->stops));
+ for (i=0; i < (long) gradient->number_stops; i++)
+ GetMagickPixelPacket(image,&gradient->stops[i].color);
+ SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
+ &gradient->stops[0].color);
+ gradient->stops[0].offset=0.0;
+ SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
+ &gradient->stops[1].color);
+ gradient->stops[1].offset=1.0;
+ /*
+ Draw a gradient on the image.
+ */
+ status=DrawGradientImage(image,draw_info);
+ draw_info=DestroyDrawInfo(draw_info);
+ if ((start_color->opacity == OpaqueOpacity) &&
+ (stop_color->opacity == OpaqueOpacity))
+ image->matte=MagickFalse;
+ if ((IsGrayPixel(start_color) != MagickFalse) &&
+ (IsGrayPixel(stop_color) != MagickFalse))
+ image->type=GrayscaleType;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O i l P a i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OilPaintImage() applies a special effect filter that simulates an oil
+% painting. Each pixel is replaced by the most frequent color occurring
+% in a circular region defined by radius.
+%
+% The format of the OilPaintImage method is:
+%
+% Image *OilPaintImage(const Image *image,const double radius,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o radius: the radius of the circular neighborhood.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static unsigned long **DestroyHistogramThreadSet(unsigned long **histogram)
+{
+ register long
+ i;
+
+ assert(histogram != (unsigned long **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (histogram[i] != (unsigned long *) NULL)
+ histogram[i]=(unsigned long *) RelinquishMagickMemory(histogram[i]);
+ histogram=(unsigned long **) RelinquishAlignedMemory(histogram);
+ return(histogram);
+}
+
+static unsigned long **AcquireHistogramThreadSet(const size_t count)
+{
+ register long
+ i;
+
+ unsigned long
+ **histogram,
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ histogram=(unsigned long **) AcquireAlignedMemory(number_threads,
+ sizeof(*histogram));
+ if (histogram == (unsigned long **) NULL)
+ return((unsigned long **) NULL);
+ (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ histogram[i]=(unsigned long *) AcquireQuantumMemory(count,
+ sizeof(**histogram));
+ if (histogram[i] == (unsigned long *) NULL)
+ return(DestroyHistogramThreadSet(histogram));
+ }
+ return(histogram);
+}
+
+MagickExport Image *OilPaintImage(const Image *image,const double radius,
+ ExceptionInfo *exception)
+{
+#define NumberPaintBins 256
+#define OilPaintImageTag "OilPaint/Image"
+
+ Image
+ *paint_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ unsigned long
+ **histograms,
+ width;
+
+ CacheView
+ *image_view,
+ *paint_view;
+
+ /*
+ Initialize painted image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=GetOptimalKernelWidth2D(radius,0.5);
+ if ((image->columns < width) || (image->rows < width))
+ ThrowImageException(OptionError,"ImageSmallerThanRadius");
+ paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (paint_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&paint_image->exception);
+ paint_image=DestroyImage(paint_image);
+ return((Image *) NULL);
+ }
+ histograms=AcquireHistogramThreadSet(NumberPaintBins);
+ if (histograms == (unsigned long **) NULL)
+ {
+ paint_image=DestroyImage(paint_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Oil paint image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ paint_view=AcquireCacheView(paint_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict paint_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ register unsigned long
+ *histogram;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
+ 2L),image->columns+width,width,exception);
+ q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
+ histogram=histograms[GetOpenMPThreadId()];
+ for (x=0; x < (long) image->columns; x++)
+ {
+ long
+ j,
+ k,
+ v;
+
+ register long
+ i,
+ u;
+
+ unsigned long
+ count;
+
+ /*
+ Assign most frequent color.
+ */
+ i=0;
+ j=0;
+ count=0;
+ (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
+ for (v=0; v < (long) width; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ k=(long) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
+ histogram[k]++;
+ if (histogram[k] > count)
+ {
+ j=i+u;
+ count=histogram[k];
+ }
+ }
+ i+=image->columns+width;
+ }
+ *q=(*(p+j));
+ if (image->colorspace == CMYKColorspace)
+ paint_indexes[x]=indexes[x+j];
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_OilPaintImage)
+#endif
+ proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ paint_view=DestroyCacheView(paint_view);
+ image_view=DestroyCacheView(image_view);
+ histograms=DestroyHistogramThreadSet(histograms);
+ if (status == MagickFalse)
+ paint_image=DestroyImage(paint_image);
+ return(paint_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p a q u e P a i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpaquePaintImage() changes any pixel that matches color with the color
+% defined by fill.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the OpaquePaintImage method is:
+%
+% MagickBooleanType OpaquePaintImage(Image *image,
+% const PixelPacket *target,const PixelPacket *fill,
+% const MagickBooleanType invert)
+% MagickBooleanType OpaquePaintImageChannel(Image *image,
+% const ChannelType channel,const PixelPacket *target,
+% const PixelPacket *fill,const MagickBooleanType invert)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel(s).
+%
+% o target: the RGB value of the target color.
+%
+% o fill: the replacement color.
+%
+% o invert: paint any pixel that does not match the target color.
+%
+*/
+
+MagickExport MagickBooleanType OpaquePaintImage(Image *image,
+ const MagickPixelPacket *target,const MagickPixelPacket *fill,
+ const MagickBooleanType invert)
+{
+ return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
+}
+
+MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
+ const ChannelType channel,const MagickPixelPacket *target,
+ const MagickPixelPacket *fill,const MagickBooleanType invert)
+{
+#define OpaquePaintImageTag "Opaque/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(target != (MagickPixelPacket *) NULL);
+ assert(fill != (MagickPixelPacket *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Make image color opaque.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ if (IsMagickColorSimilar(&pixel,target) != invert)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=RoundToQuantum(fill->red);
+ if ((channel & GreenChannel) != 0)
+ q->green=RoundToQuantum(fill->green);
+ if ((channel & BlueChannel) != 0)
+ q->blue=RoundToQuantum(fill->blue);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=RoundToQuantum(fill->opacity);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=RoundToQuantum(fill->index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_OpaquePaintImageChannel)
+#endif
+ proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s p a r e n t P a i n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransparentPaintImage() changes the opacity value associated with any pixel
+% that matches color to the value defined by opacity.
+%
+% By default color must match a particular pixel color exactly. However,
+% in many cases two colors may differ by a small amount. Fuzz defines
+% how much tolerance is acceptable to consider two colors as the same.
+% For example, set fuzz to 10 and the color red at intensities of 100 and
+% 102 respectively are now interpreted as the same color.
+%
+% The format of the TransparentPaintImage method is:
+%
+% MagickBooleanType TransparentPaintImage(Image *image,
+% const MagickPixelPacket *target,const Quantum opacity,
+% const MagickBooleanType invert)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o target: the target color.
+%
+% o opacity: the replacement opacity value.
+%
+% o invert: paint any pixel that does not match the target color.
+%
+*/
+MagickExport MagickBooleanType TransparentPaintImage(Image *image,
+ const MagickPixelPacket *target,const Quantum opacity,
+ const MagickBooleanType invert)
+{
+#define TransparentPaintImageTag "Transparent/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(target != (MagickPixelPacket *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
+ /*
+ Make image color transparent.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ GetMagickPixelPacket(image,&zero);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ if (IsMagickColorSimilar(&pixel,target) != invert)
+ q->opacity=opacity;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransparentPaintImage)
+#endif
+ proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s p a r e n t P a i n t I m a g e C h r o m a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransparentPaintImageChroma() changes the opacity value associated with any
+% pixel that matches color to the value defined by opacity.
+%
+% As there is one fuzz value for the all the channels, the
+% TransparentPaintImage() API is not suitable for the operations like chroma,
+% where the tolerance for similarity of two color component (RGB) can be
+% different, Thus we define this method take two target pixels (one
+% low and one hight) and all the pixels of an image which are lying between
+% these two pixels are made transparent.
+%
+% The format of the TransparentPaintImage method is:
+%
+% MagickBooleanType TransparentPaintImage(Image *image,
+% const MagickPixelPacket *low,const MagickPixelPacket *hight,
+% const Quantum opacity,const MagickBooleanType invert)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o low: the low target color.
+%
+% o high: the high target color.
+%
+% o opacity: the replacement opacity value.
+%
+% o invert: paint any pixel that does not match the target color.
+%
+*/
+MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
+ const MagickPixelPacket *low,const MagickPixelPacket *high,
+ const Quantum opacity,const MagickBooleanType invert)
+{
+#define TransparentPaintImageTag "Transparent/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ assert(high != (MagickPixelPacket *) NULL);
+ assert(low != (MagickPixelPacket *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ if (image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(image,ResetAlphaChannel);
+ /*
+ Make image color transparent.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ match;
+
+ MagickPixelPacket
+ pixel;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ GetMagickPixelPacket(image,&pixel);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,q,indexes+x,&pixel);
+ match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
+ (pixel.green >= low->green) && (pixel.green <= high->green) &&
+ (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ?
+ MagickTrue : MagickFalse;
+ if (match != invert)
+ q->opacity=opacity;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransparentPaintImageChroma)
+#endif
+ proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
diff --git a/magick/paint.h b/magick/paint.h
new file mode 100644
index 0000000..c011d72
--- /dev/null
+++ b/magick/paint.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image paint methods.
+*/
+#ifndef _MAGICKCORE_PAINT_H
+#define _MAGICKCORE_PAINT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/color.h"
+#include "magick/draw.h"
+
+extern MagickExport Image
+ *OilPaintImage(const Image *,const double,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ FloodfillPaintImage(Image *,const ChannelType,const DrawInfo *,
+ const MagickPixelPacket *,const long,const long,const MagickBooleanType),
+ GradientImage(Image *,const GradientType,const SpreadMethod,
+ const PixelPacket *,const PixelPacket *),
+ OpaquePaintImage(Image *,const MagickPixelPacket *,const MagickPixelPacket *,
+ const MagickBooleanType),
+ OpaquePaintImageChannel(Image *,const ChannelType,const MagickPixelPacket *,
+ const MagickPixelPacket *,const MagickBooleanType),
+ TransparentPaintImage(Image *,const MagickPixelPacket *,
+ const Quantum,const MagickBooleanType),
+ TransparentPaintImageChroma(Image *,const MagickPixelPacket *,
+ const MagickPixelPacket *,const Quantum,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/pixel-private.h b/magick/pixel-private.h
new file mode 100644
index 0000000..5772646
--- /dev/null
+++ b/magick/pixel-private.h
@@ -0,0 +1,103 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image pixel private methods.
+*/
+#ifndef _MAGICKCORE_PIXEL_PRIVATE_H
+#define _MAGICKCORE_PIXEL_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/exception-private.h>
+#include <magick/image.h>
+#include <magick/color.h>
+#include <magick/image-private.h>
+#include <magick/quantum-private.h>
+
+static inline MagickPixelPacket *CloneMagickPixelPacket(
+ const MagickPixelPacket *pixel)
+{
+ MagickPixelPacket
+ *clone_pixel;
+
+ clone_pixel=(MagickPixelPacket *) AcquireMagickMemory(sizeof(*clone_pixel));
+ if (clone_pixel == (MagickPixelPacket *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ *clone_pixel=(*pixel);
+ return(clone_pixel);
+}
+
+static inline MagickBooleanType IsGrayPixel(const PixelPacket *pixel)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ if ((pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return(MagickTrue);
+#else
+ if ((fabs(pixel->red-pixel->green) <= MagickEpsilon) &&
+ (fabs(pixel->green-pixel->blue) <= MagickEpsilon))
+ return(MagickTrue);
+#endif
+ return(MagickFalse);
+}
+
+static inline MagickBooleanType IsMonochromePixel(const PixelPacket *pixel)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ if (((pixel->red == 0) || (pixel->red == (Quantum) QuantumRange)) &&
+ (pixel->red == pixel->green) && (pixel->green == pixel->blue))
+ return(MagickTrue);
+#else
+ if (((fabs(pixel->red) <= MagickEpsilon) ||
+ (fabs(pixel->red-QuantumRange) <= MagickEpsilon)) &&
+ (fabs(pixel->red-pixel->green) <= MagickEpsilon) &&
+ (fabs(pixel->green-pixel->blue) <= MagickEpsilon))
+ return(MagickTrue);
+#endif
+ return(MagickFalse);
+}
+
+static inline void SetMagickPixelPacket(const Image *image,
+ const PixelPacket *color,const IndexPacket *index,MagickPixelPacket *pixel)
+{
+ pixel->red=(MagickRealType) color->red;
+ pixel->green=(MagickRealType) color->green;
+ pixel->blue=(MagickRealType) color->blue;
+ pixel->opacity=(MagickRealType) color->opacity;
+ if (((image->colorspace == CMYKColorspace) ||
+ (image->storage_class == PseudoClass)) &&
+ (index != (const IndexPacket *) NULL))
+ pixel->index=(MagickRealType) *index;
+}
+
+static inline void SetPixelPacket(const Image *image,
+ const MagickPixelPacket *pixel,PixelPacket *color,IndexPacket *index)
+{
+ color->red=RoundToQuantum(pixel->red);
+ color->green=RoundToQuantum(pixel->green);
+ color->blue=RoundToQuantum(pixel->blue);
+ color->opacity=RoundToQuantum(pixel->opacity);
+ if (((image->colorspace == CMYKColorspace) ||
+ (image->storage_class == PseudoClass)) &&
+ (index != (const IndexPacket *) NULL))
+ *index=RoundToQuantum(pixel->index);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/pixel.c b/magick/pixel.c
new file mode 100644
index 0000000..1142e2c
--- /dev/null
+++ b/magick/pixel.c
@@ -0,0 +1,3374 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% PPPP IIIII X X EEEEE L %
+% P P I X X E L %
+% PPPP I X EEE L %
+% P I X X E L %
+% P IIIII X X EEEEE LLLLL %
+% %
+% MagickCore Methods to Import/Export Pixels %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/cache.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/statistic.h"
+#include "magick/stream.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x p o r t I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExportImagePixels() extracts pixel data from an image and returns it to you.
+% The method returns MagickTrue on success otherwise MagickFalse if an error is
+% encountered. The data is returned as char, short int, int, long, float,
+% or double in the order specified by map.
+%
+% Suppose you want to extract the first scanline of a 640x480 image as
+% character data in red-green-blue order:
+%
+% ExportImagePixels(image,0,0,640,1,"RGB",CharPixel,pixels,exception);
+%
+% The format of the ExportImagePixels method is:
+%
+% MagickBooleanType ExportImagePixels(const Image *image,
+% const long x_offset,const long y_offset,const unsigned long columns,
+% const unsigned long rows,const char *map,const StorageType type,
+% void *pixels,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x_offset,y_offset,columns,rows: These values define the perimeter
+% of a region of pixels you want to extract.
+%
+% o map: This string reflects the expected ordering of the pixel array.
+% It can be any combination or order of R = red, G = green, B = blue,
+% A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
+% Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
+% P = pad.
+%
+% o type: Define the data type of the pixels. Float and double types are
+% normalized to [0..1] otherwise [0..QuantumRange]. Choose from these
+% types: CharPixel, DoublePixel, FloatPixel, IntegerPixel, LongPixel,
+% QuantumPixel, or ShortPixel.
+%
+% o pixels: This array of values contain the pixel components as defined by
+% map and type. You must preallocate this array where the expected
+% length varies depending on the values of width, height, map, and type.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ExportImagePixels(const Image *image,
+ const long x_offset,const long y_offset,const unsigned long columns,
+ const unsigned long rows,const char *map,const StorageType type,void *pixels,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ QuantumType
+ *quantum_map;
+
+ register long
+ i,
+ x;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ size_t
+ length;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ length=strlen(map);
+ quantum_map=(QuantumType *) AcquireQuantumMemory(length,sizeof(*quantum_map));
+ if (quantum_map == (QuantumType *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ for (i=0; i < (long) length; i++)
+ {
+ switch (map[i])
+ {
+ case 'A':
+ case 'a':
+ {
+ quantum_map[i]=AlphaQuantum;
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ quantum_map[i]=BlueQuantum;
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ quantum_map[i]=CyanQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",map);
+ return(MagickFalse);
+ }
+ case 'g':
+ case 'G':
+ {
+ quantum_map[i]=GreenQuantum;
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ quantum_map[i]=IndexQuantum;
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ quantum_map[i]=BlackQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",map);
+ return(MagickFalse);
+ }
+ case 'M':
+ case 'm':
+ {
+ quantum_map[i]=MagentaQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",map);
+ return(MagickFalse);
+ }
+ case 'o':
+ case 'O':
+ {
+ quantum_map[i]=OpacityQuantum;
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ quantum_map[i]=UndefinedQuantum;
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ quantum_map[i]=RedQuantum;
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ quantum_map[i]=YellowQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",map);
+ return(MagickFalse);
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnrecognizedPixelMap","`%s'",map);
+ return(MagickFalse);
+ }
+ }
+ }
+ switch (type)
+ {
+ case CharPixel:
+ {
+ register unsigned char
+ *q;
+
+ q=(unsigned char *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar((Quantum) 0);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar((Quantum) 0);
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToChar(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToChar(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToChar(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToChar(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToChar(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case DoublePixel:
+ {
+ register double
+ *q;
+
+ q=(double *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->blue);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->blue);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->red);
+ *q++=(double) (QuantumScale*((Quantum) (QuantumRange-
+ p->opacity)));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->blue);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->red);
+ *q++=0.0;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->red);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->red);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->blue);
+ *q++=(double) (QuantumScale*((Quantum) (QuantumRange-
+ p->opacity)));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(double) (QuantumScale*p->red);
+ *q++=(double) (QuantumScale*p->green);
+ *q++=(double) (QuantumScale*p->blue);
+ *q++=0.0;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(double) (QuantumScale*p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(double) (QuantumScale*p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(double) (QuantumScale*p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(double) (QuantumScale*((Quantum) (QuantumRange-
+ p->opacity)));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(double) (QuantumScale*p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(double) (QuantumScale*indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(double) (QuantumScale*PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case FloatPixel:
+ {
+ register float
+ *q;
+
+ q=(float *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->blue);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->blue);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->red);
+ *q++=(float) (QuantumScale*(Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->blue);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->red);
+ *q++=0.0;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->red);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->red);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->blue);
+ *q++=(float) (QuantumScale*((Quantum) (QuantumRange-p->opacity)));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(float) (QuantumScale*p->red);
+ *q++=(float) (QuantumScale*p->green);
+ *q++=(float) (QuantumScale*p->blue);
+ *q++=0.0;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(float) (QuantumScale*p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(float) (QuantumScale*p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(float) (QuantumScale*p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(float) (QuantumScale*((Quantum) (QuantumRange-p->opacity)));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(float) (QuantumScale*p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(float) (QuantumScale*indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(float) (QuantumScale*PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case IntegerPixel:
+ {
+ register unsigned int
+ *q;
+
+ q=(unsigned int *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong((Quantum) (QuantumRange-
+ p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=0U;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int)
+ ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong((Quantum)
+ (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=0U;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong((Quantum) (QuantumRange-
+ p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(unsigned int) ScaleQuantumToLong(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(unsigned int)
+ ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case LongPixel:
+ {
+ register unsigned long
+ *q;
+
+ q=(unsigned long *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=0;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=0;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToLong(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToLong(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToLong(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToLong(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToLong(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case QuantumPixel:
+ {
+ register Quantum
+ *q;
+
+ q=(Quantum *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ *q++=(Quantum) (QuantumRange-p->opacity);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ *q++=(Quantum) 0;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=PixelIntensityToQuantum(p);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ *q++=(Quantum) (QuantumRange-p->opacity);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ *q++=(Quantum) 0;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=(Quantum) 0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=p->red;
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=p->green;
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=p->blue;
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(Quantum) (QuantumRange-p->opacity);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=p->opacity;
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=indexes[x];
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=(Quantum) 0;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ case ShortPixel:
+ {
+ register unsigned short
+ *q;
+
+ q=(unsigned short *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=0;
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=0;
+ p++;
+ }
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ p=GetVirtualPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToShort(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToShort(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToShort(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToShort(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToShort(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnrecognizedPixelMap","`%s'",map);
+ break;
+ }
+ }
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k P i x e l P a c k e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickPixelPacket() initializes the MagickPixelPacket structure.
+%
+% The format of the GetMagickPixelPacket method is:
+%
+% GetMagickPixelPacket(const Image *image,MagickPixelPacket *pixel)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o pixel: Specifies a pointer to a PixelPacket structure.
+%
+*/
+MagickExport void GetMagickPixelPacket(const Image *image,
+ MagickPixelPacket *pixel)
+{
+ pixel->storage_class=DirectClass;
+ pixel->colorspace=RGBColorspace;
+ pixel->matte=MagickFalse;
+ pixel->fuzz=0.0;
+ pixel->depth=MAGICKCORE_QUANTUM_DEPTH;
+ pixel->red=0.0;
+ pixel->green=0.0;
+ pixel->blue=0.0;
+ pixel->opacity=(MagickRealType) OpaqueOpacity;
+ pixel->index=0.0;
+ if (image == (const Image *) NULL)
+ return;
+ pixel->storage_class=image->storage_class;
+ pixel->colorspace=image->colorspace;
+ pixel->matte=image->matte;
+ pixel->depth=image->depth;
+ pixel->fuzz=image->fuzz;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m p o r t I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImportImagePixels() accepts pixel data and stores in the image at the
+% location you specify. The method returns MagickTrue on success otherwise
+% MagickFalse if an error is encountered. The pixel data can be either char,
+% short int, int, long, float, or double in the order specified by map.
+%
+% Suppose your want to upload the first scanline of a 640x480 image from
+% character data in red-green-blue order:
+%
+% ImportImagePixels(image,0,0,640,1,"RGB",CharPixel,pixels);
+%
+% The format of the ImportImagePixels method is:
+%
+% MagickBooleanType ImportImagePixels(Image *image,const long x_offset,
+% const long y_offset,const unsigned long columns,
+% const unsigned long rows,const char *map,const StorageType type,
+% const void *pixels)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x_offset,y_offset,columns,rows: These values define the perimeter
+% of a region of pixels you want to define.
+%
+% o map: This string reflects the expected ordering of the pixel array.
+% It can be any combination or order of R = red, G = green, B = blue,
+% A = alpha (0 is transparent), O = opacity (0 is opaque), C = cyan,
+% Y = yellow, M = magenta, K = black, I = intensity (for grayscale),
+% P = pad.
+%
+% o type: Define the data type of the pixels. Float and double types are
+% normalized to [0..1] otherwise [0..QuantumRange]. Choose from these
+% types: CharPixel, ShortPixel, IntegerPixel, LongPixel, FloatPixel, or
+% DoublePixel.
+%
+% o pixels: This array of values contain the pixel components as defined by
+% map and type. You must preallocate this array where the expected
+% length varies depending on the values of width, height, map, and type.
+%
+*/
+MagickExport MagickBooleanType ImportImagePixels(Image *image,
+ const long x_offset,const long y_offset,const unsigned long columns,
+ const unsigned long rows,const char *map,const StorageType type,
+ const void *pixels)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ PixelPacket
+ *q;
+
+ QuantumType
+ *quantum_map;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ i,
+ x;
+
+ size_t
+ length;
+
+ /*
+ Allocate image structure.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ length=strlen(map);
+ quantum_map=(QuantumType *) AcquireQuantumMemory(length,sizeof(*quantum_map));
+ if (quantum_map == (QuantumType *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ for (i=0; i < (long) length; i++)
+ {
+ switch (map[i])
+ {
+ case 'a':
+ case 'A':
+ {
+ quantum_map[i]=AlphaQuantum;
+ image->matte=MagickTrue;
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ quantum_map[i]=BlueQuantum;
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ quantum_map[i]=CyanQuantum;
+ (void) SetImageColorspace(image,CMYKColorspace);
+ break;
+ }
+ case 'g':
+ case 'G':
+ {
+ quantum_map[i]=GreenQuantum;
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ quantum_map[i]=BlackQuantum;
+ (void) SetImageColorspace(image,CMYKColorspace);
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ quantum_map[i]=IndexQuantum;
+ break;
+ }
+ case 'm':
+ case 'M':
+ {
+ quantum_map[i]=MagentaQuantum;
+ (void) SetImageColorspace(image,CMYKColorspace);
+ break;
+ }
+ case 'O':
+ case 'o':
+ {
+ quantum_map[i]=OpacityQuantum;
+ image->matte=MagickTrue;
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ quantum_map[i]=UndefinedQuantum;
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ quantum_map[i]=RedQuantum;
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ quantum_map[i]=YellowQuantum;
+ (void) SetImageColorspace(image,CMYKColorspace);
+ break;
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ OptionError,"UnrecognizedPixelMap","`%s'",map);
+ return(MagickFalse);
+ }
+ }
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Transfer the pixels from the pixel datarray to the image.
+ */
+ exception=(&image->exception);
+ switch (type)
+ {
+ case CharPixel:
+ {
+ register const unsigned char
+ *p;
+
+ p=(const unsigned char *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->red=ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->red=ScaleCharToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRO") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->red=ScaleCharToQuantum(*p++);
+ q->opacity=ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->red=ScaleCharToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleCharToQuantum(*p++);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->blue=ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->blue=ScaleCharToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBO") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->blue=ScaleCharToQuantum(*p++);
+ q->opacity=ScaleCharToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleCharToQuantum(*p++);
+ q->green=ScaleCharToQuantum(*p++);
+ q->blue=ScaleCharToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=ScaleCharToQuantum(*p);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=ScaleCharToQuantum(*p);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=ScaleCharToQuantum(*p);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-ScaleCharToQuantum(*p);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=ScaleCharToQuantum(*p);
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=ScaleCharToQuantum(*p);
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=ScaleCharToQuantum(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case DoublePixel:
+ {
+ register const double
+ *p;
+
+ p=(const double *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum((MagickRealType)
+ QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ q->green=q->red;
+ q->blue=q->red;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum((MagickRealType)
+ QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum(
+ (MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case FloatPixel:
+ {
+ register const float
+ *p;
+
+ p=(const float *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum((MagickRealType)
+ QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ q->green=q->red;
+ q->blue=q->red;
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum((MagickRealType)
+ QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-RoundToQuantum(
+ (MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=RoundToQuantum((MagickRealType) QuantumRange*(*p));
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case IntegerPixel:
+ {
+ register const unsigned int
+ *p;
+
+ p=(const unsigned int *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=ScaleLongToQuantum(*p);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=ScaleLongToQuantum(*p);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=ScaleLongToQuantum(*p);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=ScaleLongToQuantum(*p);
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=ScaleLongToQuantum(*p);
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=ScaleLongToQuantum(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case LongPixel:
+ {
+ register const unsigned long
+ *p;
+
+ p=(const unsigned long *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->red=ScaleLongToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleLongToQuantum(*p++);
+ q->green=ScaleLongToQuantum(*p++);
+ q->blue=ScaleLongToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=ScaleLongToQuantum(*p);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=ScaleLongToQuantum(*p);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=ScaleLongToQuantum(*p);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-ScaleLongToQuantum(*p);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=ScaleLongToQuantum(*p);
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=ScaleLongToQuantum(*p);
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=ScaleLongToQuantum(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case QuantumPixel:
+ {
+ register const Quantum
+ *p;
+
+ p=(const Quantum *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=(*p++);
+ q->green=(*p++);
+ q->red=(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=(*p++);
+ q->green=(*p++);
+ q->red=(*p++);
+ q->opacity=(Quantum) QuantumRange-(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=(*p++);
+ q->green=(*p++);
+ q->red=(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=(*p++);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=(*p++);
+ q->green=(*p++);
+ q->blue=(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=(*p++);
+ q->green=(*p++);
+ q->blue=(*p++);
+ q->opacity=(Quantum) QuantumRange-(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=(*p++);
+ q->green=(*p++);
+ q->blue=(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=(*p);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=(*p);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=(*p);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-(*p);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=(*p);
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=(*p);
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case ShortPixel:
+ {
+ register const unsigned short
+ *p;
+
+ p=(const unsigned short *) pixels;
+ if (LocaleCompare(map,"BGR") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->red=ScaleShortToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->red=ScaleShortToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleShortToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"BGRP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->blue=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->red=ScaleShortToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"I") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleShortToQuantum(*p++);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGB") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->blue=ScaleShortToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBA") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->blue=ScaleShortToQuantum(*p++);
+ q->opacity=(Quantum) QuantumRange-ScaleShortToQuantum(*p++);
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ if (LocaleCompare(map,"RGBP") == 0)
+ {
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) columns; x++)
+ {
+ q->red=ScaleShortToQuantum(*p++);
+ q->green=ScaleShortToQuantum(*p++);
+ q->blue=ScaleShortToQuantum(*p++);
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ for (y=0; y < (long) rows; y++)
+ {
+ q=GetAuthenticPixels(image,x_offset,y_offset+y,columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) columns; x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ q->red=ScaleShortToQuantum(*p);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ q->green=ScaleShortToQuantum(*p);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ q->blue=ScaleShortToQuantum(*p);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ q->opacity=(Quantum) QuantumRange-ScaleShortToQuantum(*p);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ q->opacity=ScaleShortToQuantum(*p);
+ break;
+ }
+ case BlackQuantum:
+ {
+ indexes[x]=ScaleShortToQuantum(*p);
+ break;
+ }
+ case IndexQuantum:
+ {
+ q->red=ScaleShortToQuantum(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ break;
+ }
+ default:
+ break;
+ }
+ p++;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ OptionError,"UnrecognizedPixelMap","`%s'",map);
+ break;
+ }
+ }
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ return(MagickTrue);
+}
diff --git a/magick/pixel.h b/magick/pixel.h
new file mode 100644
index 0000000..ed567cf
--- /dev/null
+++ b/magick/pixel.h
@@ -0,0 +1,95 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image constitute methods.
+*/
+#ifndef _MAGICKCORE_PIXEL_H
+#define _MAGICKCORE_PIXEL_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/colorspace.h>
+#include <magick/constitute.h>
+
+typedef struct _LongPixelPacket
+{
+ unsigned long
+ red,
+ green,
+ blue,
+ opacity,
+ index;
+} LongPixelPacket;
+
+typedef struct _MagickPixelPacket
+{
+ ClassType
+ storage_class;
+
+ ColorspaceType
+ colorspace;
+
+ MagickBooleanType
+ matte;
+
+ double
+ fuzz;
+
+ unsigned long
+ depth;
+
+ MagickRealType
+ red,
+ green,
+ blue,
+ opacity,
+ index;
+} MagickPixelPacket;
+
+typedef Quantum IndexPacket;
+
+typedef struct _PixelPacket
+{
+#if defined(MAGICKCORE_WORDS_BIGENDIAN)
+ Quantum
+ red,
+ green,
+ blue,
+ opacity;
+#else
+ Quantum
+ blue,
+ green,
+ red,
+ opacity;
+#endif
+} PixelPacket;
+
+extern MagickExport MagickBooleanType
+ ExportImagePixels(const Image *,const long,const long,const unsigned long,
+ const unsigned long,const char *,const StorageType,void *,ExceptionInfo *),
+ ImportImagePixels(Image *,const long,const long,const unsigned long,
+ const unsigned long,const char *,const StorageType,const void *);
+
+extern MagickExport void
+ GetMagickPixelPacket(const Image *,MagickPixelPacket *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/policy.c b/magick/policy.c
new file mode 100644
index 0000000..e4cc4d8
--- /dev/null
+++ b/magick/policy.c
@@ -0,0 +1,936 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% PPPP OOO L IIIII CCCC Y Y %
+% P P O O L I C Y Y %
+% PPPP O O L I C Y %
+% P O O L I C Y %
+% P OOO LLLLL IIIII CCCC Y %
+% %
+% %
+% MagickCore Policy Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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/client.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/policy.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define PolicyFilename "policy.xml"
+
+/*
+ Declare policy map.
+*/
+static const char
+ *PolicyMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<policymap>"
+ "</policymap>";
+
+/*
+ Domaindef declarations.
+*/
+struct _PolicyInfo
+{
+ char
+ *path,
+ *name;
+
+ PolicyDomain
+ domain;
+
+ PolicyRights
+ rights;
+
+ char
+ *pattern,
+ *value;
+
+ MagickBooleanType
+ stealth,
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Static declarations.
+*/
+static LinkedListInfo
+ *policy_list = (LinkedListInfo *) NULL;
+
+static SemaphoreInfo
+ *policy_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_policy = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializePolicyList(ExceptionInfo *),
+ LoadPolicyLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P o l i c y L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPolicyList() deallocates memory associated with the policy list.
+%
+% The format of the DestroyPolicyList method is:
+%
+% DestroyPolicyList(void)
+%
+*/
+
+static void *DestroyPolicyElement(void *policy_info)
+{
+ register PolicyInfo
+ *p;
+
+ p=(PolicyInfo *) policy_info;
+ if (p->value != (char *) NULL)
+ p->value=DestroyString(p->value);
+ if (p->pattern != (char *) NULL)
+ p->pattern=DestroyString(p->pattern);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ p=(PolicyInfo *) RelinquishMagickMemory(p);
+ return((void *) NULL);
+}
+
+MagickExport void DestroyPolicyList(void)
+{
+ AcquireSemaphoreInfo(&policy_semaphore);
+ if (policy_list != (LinkedListInfo *) NULL)
+ policy_list=DestroyLinkedList(policy_list,DestroyPolicyElement);
+ instantiate_policy=MagickFalse;
+ RelinquishSemaphoreInfo(policy_semaphore);
+ DestroySemaphoreInfo(&policy_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t P o l i c y I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPolicyInfo() searches the policy list for the specified name and if found
+% returns attributes for that policy.
+%
+% The format of the GetPolicyInfo method is:
+%
+% PolicyInfo *GetPolicyInfo(const char *name,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the policy name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static PolicyInfo *GetPolicyInfo(const char *name,ExceptionInfo *exception)
+{
+ char
+ policyname[MaxTextExtent];
+
+ register PolicyInfo
+ *p;
+
+ register char
+ *q;
+
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((policy_list == (LinkedListInfo *) NULL) ||
+ (instantiate_policy == MagickFalse))
+ if (InitializePolicyList(exception) == MagickFalse)
+ return((PolicyInfo *) NULL);
+ if ((policy_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(policy_list) != MagickFalse))
+ return((PolicyInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ return((PolicyInfo *) GetValueFromLinkedList(policy_list,0));
+ /*
+ Strip names of whitespace.
+ */
+ (void) CopyMagickString(policyname,name,MaxTextExtent);
+ for (q=policyname; *q != '\0'; q++)
+ {
+ if (isspace((int) ((unsigned char) *q)) == 0)
+ continue;
+ (void) CopyMagickString(q,q+1,MaxTextExtent);
+ q--;
+ }
+ /*
+ Search for policy tag.
+ */
+ AcquireSemaphoreInfo(&policy_semaphore);
+ ResetLinkedListIterator(policy_list);
+ p=(PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ while (p != (PolicyInfo *) NULL)
+ {
+ if (LocaleCompare(policyname,p->name) == 0)
+ break;
+ p=(PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ }
+ if (p == (PolicyInfo *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "UnrecognizedPolicy","`%s'",name);
+ else
+ (void) InsertValueInLinkedList(policy_list,0,
+ RemoveElementByValueFromLinkedList(policy_list,p));
+ RelinquishSemaphoreInfo(policy_semaphore);
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P o l i c y I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPolicyInfoList() returns any policies that match the specified pattern.
+%
+% The format of the GetPolicyInfoList function is:
+%
+% const PolicyInfo **GetPolicyInfoList(const char *pattern,
+% unsigned long *number_policies,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_policies: returns the number of policies in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const PolicyInfo **GetPolicyInfoList(const char *pattern,
+ unsigned long *number_policies,ExceptionInfo *exception)
+{
+ const PolicyInfo
+ **policies;
+
+ register const PolicyInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate policy list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_policies != (unsigned long *) NULL);
+ *number_policies=0;
+ p=GetPolicyInfo("*",exception);
+ if (p == (const PolicyInfo *) NULL)
+ return((const PolicyInfo **) NULL);
+ policies=(const PolicyInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(policy_list)+1UL,sizeof(*policies));
+ if (policies == (const PolicyInfo **) NULL)
+ return((const PolicyInfo **) NULL);
+ /*
+ Generate policy list.
+ */
+ AcquireSemaphoreInfo(&policy_semaphore);
+ ResetLinkedListIterator(policy_list);
+ p=(const PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ for (i=0; p != (const PolicyInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ policies[i++]=p;
+ p=(const PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ }
+ RelinquishSemaphoreInfo(policy_semaphore);
+ policies[i]=(PolicyInfo *) NULL;
+ *number_policies=(unsigned long) i;
+ return(policies);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P o l i c y L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPolicyList() returns any policies that match the specified pattern.
+%
+% The format of the GetPolicyList function is:
+%
+% char **GetPolicyList(const char *pattern,unsigned long *number_policies,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: a pointer to a text string containing a pattern.
+%
+% o number_policies: returns the number of policies in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport char **GetPolicyList(const char *pattern,
+ unsigned long *number_policies,ExceptionInfo *exception)
+{
+ char
+ **policies;
+
+ register const PolicyInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate policy list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_policies != (unsigned long *) NULL);
+ *number_policies=0;
+ p=GetPolicyInfo("*",exception);
+ if (p == (const PolicyInfo *) NULL)
+ return((char **) NULL);
+ policies=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfElementsInLinkedList(policy_list)+1UL,sizeof(*policies));
+ if (policies == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Generate policy list.
+ */
+ AcquireSemaphoreInfo(&policy_semaphore);
+ ResetLinkedListIterator(policy_list);
+ p=(const PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ for (i=0; p != (const PolicyInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ policies[i++]=ConstantString(p->name);
+ p=(const PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ }
+ RelinquishSemaphoreInfo(policy_semaphore);
+ policies[i]=(char *) NULL;
+ *number_policies=(unsigned long) i;
+ return(policies);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P o l i c y V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPolicyValue() returns the value associated with the named policy.
+%
+% The format of the GetPolicyValue method is:
+%
+% char *GetPolicyValue(const char *name)
+%
+% A description of each parameter follows:
+%
+% o policy_info: The policy info.
+%
+*/
+MagickExport char *GetPolicyValue(const char *name)
+{
+ const char
+ *value;
+
+ const PolicyInfo
+ *policy_info;
+
+ ExceptionInfo
+ *exception;
+
+ assert(name != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
+ exception=AcquireExceptionInfo();
+ policy_info=GetPolicyInfo(name,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (policy_info == (PolicyInfo *) NULL)
+ return((char *) NULL);
+ value=policy_info->value;
+ if ((value == (const char *) NULL) || (*value == '\0'))
+ return((char *) NULL);
+ return(ConstantString(value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e P o l i c y L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializePolicyList() initializes the policy list.
+%
+% The format of the InitializePolicyList method is:
+%
+% MagickBooleanType InitializePolicyList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType InitializePolicyList(ExceptionInfo *exception)
+{
+ if ((policy_list == (LinkedListInfo *) NULL) &&
+ (instantiate_policy == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&policy_semaphore);
+ if ((policy_list == (LinkedListInfo *) NULL) &&
+ (instantiate_policy == MagickFalse))
+ {
+ (void) LoadPolicyLists(PolicyFilename,exception);
+ instantiate_policy=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(policy_semaphore);
+ }
+ return(policy_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s R i g h t s A u t h o r i z e d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsRightsAuthorized() returns MagickTrue if the policy authorizes the
+% requested rights for the specified domain.
+%
+% The format of the IsRightsAuthorized method is:
+%
+% MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
+% const PolicyRights rights,const char *pattern)
+%
+% A description of each parameter follows:
+%
+% o domain: the policy domain.
+%
+% o rights: the policy rights.
+%
+% o pattern: the coder, delegate, filter, or path pattern.
+%
+*/
+MagickExport MagickBooleanType IsRightsAuthorized(const PolicyDomain domain,
+ const PolicyRights rights,const char *pattern)
+{
+ const PolicyInfo
+ *policy_info;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ authorized;
+
+ register PolicyInfo
+ *p;
+
+ (void) LogMagickEvent(PolicyEvent,GetMagickModule(),
+ "Domain: %s; rights=%s; pattern=\"%s\" ...",
+ MagickOptionToMnemonic(MagickPolicyDomainOptions,domain),
+ MagickOptionToMnemonic(MagickPolicyRightsOptions,rights),pattern);
+ exception=AcquireExceptionInfo();
+ policy_info=GetPolicyInfo("*",exception);
+ exception=DestroyExceptionInfo(exception);
+ if (policy_info == (PolicyInfo *) NULL)
+ return(MagickTrue);
+ authorized=MagickTrue;
+ AcquireSemaphoreInfo(&policy_semaphore);
+ ResetLinkedListIterator(policy_list);
+ p=(PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ while ((p != (PolicyInfo *) NULL) && (authorized != MagickFalse))
+ {
+ if ((p->domain == domain) &&
+ (GlobExpression(pattern,p->pattern,MagickFalse) != MagickFalse))
+ {
+ if (((rights & ReadPolicyRights) != 0) &&
+ ((p->rights & ReadPolicyRights) == 0))
+ authorized=MagickFalse;
+ if (((rights & WritePolicyRights) != 0) &&
+ ((p->rights & WritePolicyRights) == 0))
+ authorized=MagickFalse;
+ if (((rights & ExecutePolicyRights) != 0) &&
+ ((p->rights & ExecutePolicyRights) == 0))
+ authorized=MagickFalse;
+ }
+ p=(PolicyInfo *) GetNextValueInLinkedList(policy_list);
+ }
+ RelinquishSemaphoreInfo(policy_semaphore);
+ return(authorized);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t P o l i c y I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListPolicyInfo() lists policies to the specified file.
+%
+% The format of the ListPolicyInfo method is:
+%
+% MagickBooleanType ListPolicyInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: List policy names to this file handle.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListPolicyInfo(FILE *file,
+ ExceptionInfo *exception)
+{
+ const char
+ *path,
+ *domain;
+
+ const PolicyInfo
+ **policy_info;
+
+ register long
+ i;
+
+ unsigned long
+ number_policies;
+
+ /*
+ List name and attributes of each policy in the list.
+ */
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ policy_info=GetPolicyInfoList("*",&number_policies,exception);
+ if (policy_info == (const PolicyInfo **) NULL)
+ return(MagickFalse);
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_policies; i++)
+ {
+ if (policy_info[i]->stealth != MagickFalse)
+ continue;
+ if (((path == (const char *) NULL) ||
+ (LocaleCompare(path,policy_info[i]->path) != 0)) &&
+ (policy_info[i]->path != (char *) NULL))
+ (void) fprintf(file,"\nPath: %s\n",policy_info[i]->path);
+ path=policy_info[i]->path;
+ domain=MagickOptionToMnemonic(MagickPolicyDomainOptions,
+ policy_info[i]->domain);
+ (void) fprintf(file," Policy: %s\n",domain);
+ if (policy_info[i]->domain == ResourcePolicyDomain)
+ {
+ if (policy_info[i]->name != (char *) NULL)
+ (void) fprintf(file," name: %s\n",policy_info[i]->name);
+ if (policy_info[i]->value != (char *) NULL)
+ (void) fprintf(file," value: %s\n",policy_info[i]->value);
+ }
+ else
+ {
+ (void) fprintf(file," rights: ");
+ if (policy_info[i]->rights == NoPolicyRights)
+ (void) fprintf(file,"None ");
+ if ((policy_info[i]->rights & ReadPolicyRights) != 0)
+ (void) fprintf(file,"Read ");
+ if ((policy_info[i]->rights & WritePolicyRights) != 0)
+ (void) fprintf(file,"Write ");
+ if ((policy_info[i]->rights & ExecutePolicyRights) != 0)
+ (void) fprintf(file,"Execute ");
+ (void) fprintf(file,"\n");
+ if (policy_info[i]->pattern != (char *) NULL)
+ (void) fprintf(file," pattern: %s\n",policy_info[i]->pattern);
+ }
+ }
+ policy_info=(const PolicyInfo **) RelinquishMagickMemory((void *)
+ policy_info);
+ (void) fflush(file);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d P o l i c y L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadPolicyList() loads the policy configuration file which provides a mapping
+% between policy attributes and a policy domain.
+%
+% The format of the LoadPolicyList method is:
+%
+% MagickBooleanType LoadPolicyList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The policy list in XML format.
+%
+% o filename: The policy list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadPolicyList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ keyword[MaxTextExtent],
+ *token;
+
+ PolicyInfo
+ *policy_info;
+
+ const char
+ *q;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Load the policy map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading policy file \"%s\" ...",filename);
+ if (xml == (char *) NULL)
+ return(MagickFalse);
+ if (policy_list == (LinkedListInfo *) NULL)
+ {
+ policy_list=NewLinkedList(0);
+ if (policy_list == (LinkedListInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ policy_info=(PolicyInfo *) NULL;
+ token=AcquireString(xml);
+ for (q=(const char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Docdomain element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ xml=FileToString(path,~0,exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadPolicyList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<policy") == 0)
+ {
+ /*
+ Policy element.
+ */
+ policy_info=(PolicyInfo *) AcquireMagickMemory(sizeof(*policy_info));
+ if (policy_info == (PolicyInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(policy_info,0,sizeof(*policy_info));
+ policy_info->path=ConstantString(filename);
+ policy_info->signature=MagickSignature;
+ continue;
+ }
+ if (policy_info == (PolicyInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AppendValueToLinkedList(policy_list,policy_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ policy_info->name);
+ policy_info=(PolicyInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'D':
+ case 'd':
+ {
+ if (LocaleCompare((char *) keyword,"domain") == 0)
+ {
+ policy_info->domain=(PolicyDomain) ParseMagickOption(
+ MagickPolicyDomainOptions,MagickTrue,token);
+ break;
+ }
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ policy_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ if (LocaleCompare((char *) keyword,"pattern") == 0)
+ {
+ policy_info->pattern=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ if (LocaleCompare((char *) keyword,"rights") == 0)
+ {
+ policy_info->rights=(PolicyRights) ParseMagickOption(
+ MagickPolicyRightsOptions,MagickTrue,token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ policy_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ break;
+ }
+ case 'V':
+ case 'v':
+ {
+ if (LocaleCompare((char *) keyword,"value") == 0)
+ {
+ policy_info->value=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d P o l i c y L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadPolicyList() loads one or more policy configuration file which provides a
+% mapping between policy attributes and a policy name.
+%
+% The format of the LoadPolicyLists method is:
+%
+% MagickBooleanType LoadPolicyLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadPolicyLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadPolicyList(PolicyMap,"built-in",0,exception));
+#else
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ status|=LoadPolicyList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ if ((policy_list == (LinkedListInfo *) NULL) ||
+ (IsLinkedListEmpty(policy_list) != MagickFalse))
+ status|=LoadPolicyList(PolicyMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/policy.h b/magick/policy.h
new file mode 100644
index 0000000..d8ad7bd
--- /dev/null
+++ b/magick/policy.h
@@ -0,0 +1,68 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image color methods.
+*/
+#ifndef _MAGICKCORE_POLICY_H
+#define _MAGICKCORE_POLICY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/pixel.h>
+#include <magick/exception.h>
+
+typedef enum
+{
+ UndefinedPolicyDomain,
+ CoderPolicyDomain,
+ DelegatePolicyDomain,
+ FilterPolicyDomain,
+ PathPolicyDomain,
+ ResourcePolicyDomain
+} PolicyDomain;
+
+typedef enum
+{
+ UndefinedPolicyRights = 0x00,
+ NoPolicyRights = 0x00,
+ ReadPolicyRights = 0x01,
+ WritePolicyRights = 0x02,
+ ExecutePolicyRights = 0x04
+} PolicyRights;
+
+typedef struct _PolicyInfo
+ PolicyInfo;
+
+extern MagickExport char
+ *GetPolicyValue(const char *name),
+ **GetPolicyList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport const PolicyInfo
+ **GetPolicyInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ IsRightsAuthorized(const PolicyDomain,const PolicyRights,const char *),
+ ListPolicyInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport void
+ DestroyPolicyList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/prepress.c b/magick/prepress.c
new file mode 100644
index 0000000..139382a
--- /dev/null
+++ b/magick/prepress.c
@@ -0,0 +1,153 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% PPPP RRRR EEEEE PPPP RRRR EEEEE SSSSS SSSSS %
+% P P R R E P P R R E SS SS %
+% PPPP RRRR EEE PPPP RRRR EEE SSS SSS %
+% P R R E P R R E SS SS %
+% P R R EEEEE P R R EEEEE SSSSS SSSSS %
+% %
+% %
+% MagickCore Prepress Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 2001 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache-view.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/image.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/prepress.h"
+#include "magick/registry.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e T o t a l I n k D e n s i t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageTotalInkDensity() returns the total ink density for a CMYK image.
+% Total Ink Density (TID) is determined by adding the CMYK values in the
+% darkest shadow area in an image.
+%
+% The format of the GetImageTotalInkDensity method is:
+%
+% double GetImageTotalInkDensity(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport double GetImageTotalInkDensity(Image *image)
+{
+ double
+ total_ink_density;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ImageError,"ColorSeparatedImageRequired","`%s'",image->filename);
+ return(0.0);
+ }
+ status=MagickTrue;
+ total_ink_density=0.0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ double
+ density;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ density=(double) p->red+p->green+p->blue+indexes[x];
+ if (density > total_ink_density)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_GetImageTotalInkDensity)
+#endif
+ {
+ if (density > total_ink_density)
+ total_ink_density=density;
+ }
+ p++;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ total_ink_density=0.0;
+ return(total_ink_density);
+}
diff --git a/magick/prepress.h b/magick/prepress.h
new file mode 100644
index 0000000..023e16a
--- /dev/null
+++ b/magick/prepress.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore prepress methods.
+*/
+#ifndef _MAGICKCORE_PREPRESS_H
+#define _MAGICKCORE_PREPRESS_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport double
+ GetImageTotalInkDensity(Image *image);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/profile.c b/magick/profile.c
new file mode 100644
index 0000000..cafda02
--- /dev/null
+++ b/magick/profile.c
@@ -0,0 +1,1943 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% PPPP RRRR OOO FFFFF IIIII L EEEEE %
+% P P R R O O F I L E %
+% PPPP RRRR O O FFF I L EEE %
+% P R R O O F I L E %
+% P R R OOO F IIIII LLLLL EEEEE %
+% %
+% %
+% MagickCore Image Profile Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+#if defined(MAGICKCORE_HAVE_LCMS_LCMS_H)
+#include <lcms/lcms.h>
+#else
+#include "lcms.h"
+#endif
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e P r o f i l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageProfiles() clones one or more image profiles.
+%
+% The format of the CloneImageProfiles method is:
+%
+% MagickBooleanType CloneImageProfiles(Image *image,
+% const Image *clone_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clone_image: the clone image.
+%
+*/
+MagickExport MagickBooleanType CloneImageProfiles(Image *image,
+ const Image *clone_image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(clone_image != (const Image *) NULL);
+ assert(clone_image->signature == MagickSignature);
+ image->color_profile.length=clone_image->color_profile.length;
+ image->color_profile.info=clone_image->color_profile.info;
+ image->iptc_profile.length=clone_image->iptc_profile.length;
+ image->iptc_profile.info=clone_image->iptc_profile.info;
+ if (clone_image->profiles != (void *) NULL)
+ image->profiles=CloneSplayTree((SplayTreeInfo *) clone_image->profiles,
+ (void *(*)(void *)) ConstantString,(void *(*)(void *)) CloneStringInfo);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e P r o f i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageProfile() deletes a profile from the image by its name.
+%
+% The format of the DeleteImageProfile method is:
+%
+% MagickBooleanTyupe DeleteImageProfile(Image *image,const char *name)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o name: the profile name.
+%
+*/
+MagickExport MagickBooleanType DeleteImageProfile(Image *image,
+ const char *name)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ return(MagickFalse);
+ if (LocaleCompare(name,"icc") == 0)
+ {
+ /*
+ Continue to support deprecated color profile for now.
+ */
+ image->color_profile.length=0;
+ image->color_profile.info=(unsigned char *) NULL;
+ }
+ if (LocaleCompare(name,"iptc") == 0)
+ {
+ /*
+ Continue to support deprecated IPTC profile for now.
+ */
+ image->iptc_profile.length=0;
+ image->iptc_profile.info=(unsigned char *) NULL;
+ }
+ return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->profiles,name));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e P r o f i l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageProfiles() releases memory associated with an image profile map.
+%
+% The format of the DestroyProfiles method is:
+%
+% void DestroyImageProfiles(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyImageProfiles(Image *image)
+{
+ if (image->profiles != (SplayTreeInfo *) NULL)
+ image->profiles=DestroySplayTree((SplayTreeInfo *) image->profiles);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e P r o f i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageProfile() gets a profile associated with an image by name.
+%
+% The format of the GetImageProfile method is:
+%
+% const StringInfo *GetImageProfile(const Image *image,const char *name)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o name: the profile name.
+%
+*/
+MagickExport const StringInfo *GetImageProfile(const Image *image,
+ const char *name)
+{
+ char
+ key[MaxTextExtent];
+
+ const StringInfo
+ *profile;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ return((StringInfo *) NULL);
+ (void) CopyMagickString(key,name,MaxTextExtent);
+ profile=(const StringInfo *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->profiles,key);
+ return(profile);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e P r o f i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageProfile() gets the next profile name for an image.
+%
+% The format of the GetNextImageProfile method is:
+%
+% char *GetNextImageProfile(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o hash_info: the hash info.
+%
+*/
+MagickExport char *GetNextImageProfile(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ return((char *) NULL);
+ return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->profiles));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r o f i l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ProfileImage() associates, applies, or removes an ICM, IPTC, or generic
+% profile with / to / from an image. If the profile is NULL, it is removed
+% from the image otherwise added or applied. Use a name of '*' and a profile
+% of NULL to remove all profiles from the image.
+%
+% ICC and ICM profiles are handled as follows: If the image does not have
+% an associated color profile, the one you provide is associated with the
+% image and the image pixels are not transformed. Otherwise, the colorspace
+% transform defined by the existing and new profile are applied to the image
+% pixels and the new profile is associated with the image.
+%
+% The format of the ProfileImage method is:
+%
+% MagickBooleanType ProfileImage(Image *image,const char *name,
+% const void *datum,const size_t length,const MagickBooleanType clone)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o name: Name of profile to add or remove: ICC, IPTC, or generic profile.
+%
+% o datum: the profile data.
+%
+% o length: the length of the profile.
+%
+% o clone: should be MagickFalse.
+%
+*/
+
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+
+static unsigned short **DestroyPixelThreadSet(unsigned short **pixels)
+{
+ register long
+ i;
+
+ assert(pixels != (unsigned short **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (pixels[i] != (unsigned short *) NULL)
+ pixels[i]=(unsigned short *) RelinquishMagickMemory(pixels[i]);
+ pixels=(unsigned short **) RelinquishAlignedMemory(pixels);
+ return(pixels);
+}
+
+static unsigned short **AcquirePixelThreadSet(const size_t columns,
+ const size_t channels)
+{
+ register long
+ i;
+
+ unsigned short
+ **pixels;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ pixels=(unsigned short **) AcquireAlignedMemory(number_threads,
+ sizeof(*pixels));
+ if (pixels == (unsigned short **) NULL)
+ return((unsigned short **) NULL);
+ (void) ResetMagickMemory(pixels,0,number_threads*sizeof(*pixels));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ pixels[i]=(unsigned short *) AcquireQuantumMemory(columns,channels*
+ sizeof(**pixels));
+ if (pixels[i] == (unsigned short *) NULL)
+ return(DestroyPixelThreadSet(pixels));
+ }
+ return(pixels);
+}
+
+static cmsHTRANSFORM *DestroyTransformThreadSet(cmsHTRANSFORM *transform)
+{
+ register long
+ i;
+
+ assert(transform != (cmsHTRANSFORM *) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (transform[i] != (cmsHTRANSFORM) NULL)
+ cmsDeleteTransform(transform[i]);
+ transform=(cmsHTRANSFORM *) RelinquishAlignedMemory(transform);
+ return(transform);
+}
+
+static cmsHTRANSFORM *AcquireTransformThreadSet(
+ const cmsHPROFILE source_profile,const DWORD source_type,
+ const cmsHPROFILE target_profile,const DWORD target_type,const int intent,
+ const DWORD flags)
+{
+ cmsHTRANSFORM
+ *transform;
+
+ register long
+ i;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ transform=(cmsHTRANSFORM *) AcquireAlignedMemory(number_threads,
+ sizeof(*transform));
+ if (transform == (cmsHTRANSFORM *) NULL)
+ return((cmsHTRANSFORM *) NULL);
+ (void) ResetMagickMemory(transform,0,number_threads*sizeof(*transform));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ transform[i]=cmsCreateTransform(source_profile,source_type,target_profile,
+ target_type,intent,flags);
+ if (transform[i] == (cmsHTRANSFORM) NULL)
+ return(DestroyTransformThreadSet(transform));
+ }
+ return(transform);
+}
+#endif
+
+static MagickBooleanType SetAdobeRGB1998ImageProfile(Image *image)
+{
+ static unsigned char
+ AdobeRGB1998Profile[] =
+ {
+ 0x00, 0x00, 0x02, 0x30, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00,
+ 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59,
+ 0x5a, 0x20, 0x07, 0xd0, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x13, 0x00,
+ 0x33, 0x00, 0x3b, 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4c,
+ 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xd3, 0x2d, 0x41, 0x44, 0x42, 0x45, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a,
+ 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
+ 0x32, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00,
+ 0x00, 0x6b, 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x9c, 0x00,
+ 0x00, 0x00, 0x14, 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x01, 0xb0,
+ 0x00, 0x00, 0x00, 0x14, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01,
+ 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00,
+ 0x01, 0xd4, 0x00, 0x00, 0x00, 0x0e, 0x62, 0x54, 0x52, 0x43, 0x00,
+ 0x00, 0x01, 0xe4, 0x00, 0x00, 0x00, 0x0e, 0x72, 0x58, 0x59, 0x5a,
+ 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x00, 0x14, 0x67, 0x58, 0x59,
+ 0x5a, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x14, 0x62, 0x58,
+ 0x59, 0x5a, 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x14, 0x74,
+ 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x70, 0x79,
+ 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x30, 0x30, 0x20,
+ 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x73, 0x20, 0x49, 0x6e, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72,
+ 0x61, 0x74, 0x65, 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x41, 0x64, 0x6f,
+ 0x62, 0x65, 0x20, 0x52, 0x47, 0x42, 0x20, 0x28, 0x31, 0x39, 0x39,
+ 0x38, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x16, 0xcc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x02, 0x33, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00,
+ 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x33, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x9c, 0x18, 0x00, 0x00, 0x4f, 0xa5, 0x00,
+ 0x00, 0x04, 0xfc, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x34, 0x8d, 0x00, 0x00, 0xa0, 0x2c, 0x00, 0x00, 0x0f,
+ 0x95, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x26, 0x31, 0x00, 0x00, 0x10, 0x2f, 0x00, 0x00, 0xbe, 0x9c
+ };
+
+ StringInfo
+ *profile;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (GetImageProfile(image,"icm") != (const StringInfo *) NULL)
+ return(MagickFalse);
+ profile=AcquireStringInfo(sizeof(AdobeRGB1998Profile));
+ SetStringInfoDatum(profile,AdobeRGB1998Profile);
+ status=SetImageProfile(image,"icm",profile);
+ profile=DestroyStringInfo(profile);
+ return(status);
+}
+
+static MagickBooleanType SetsRGBImageProfile(Image *image)
+{
+ static unsigned char
+ sRGBProfile[] =
+ {
+ 0x00, 0x00, 0x0c, 0x48, 0x4c, 0x69, 0x6e, 0x6f, 0x02, 0x10, 0x00,
+ 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59,
+ 0x5a, 0x20, 0x07, 0xce, 0x00, 0x02, 0x00, 0x09, 0x00, 0x06, 0x00,
+ 0x31, 0x00, 0x00, 0x61, 0x63, 0x73, 0x70, 0x4d, 0x53, 0x46, 0x54,
+ 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47,
+ 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf6, 0xd6, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xd3, 0x2d, 0x48, 0x50, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00,
+ 0x33, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00,
+ 0x00, 0x6c, 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0xf0, 0x00,
+ 0x00, 0x00, 0x14, 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, 0x02, 0x04,
+ 0x00, 0x00, 0x00, 0x14, 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x02,
+ 0x18, 0x00, 0x00, 0x00, 0x14, 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00,
+ 0x02, 0x2c, 0x00, 0x00, 0x00, 0x14, 0x62, 0x58, 0x59, 0x5a, 0x00,
+ 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x14, 0x64, 0x6d, 0x6e, 0x64,
+ 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70, 0x64, 0x6d, 0x64,
+ 0x64, 0x00, 0x00, 0x02, 0xc4, 0x00, 0x00, 0x00, 0x88, 0x76, 0x75,
+ 0x65, 0x64, 0x00, 0x00, 0x03, 0x4c, 0x00, 0x00, 0x00, 0x86, 0x76,
+ 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xd4, 0x00, 0x00, 0x00, 0x24,
+ 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00, 0x00,
+ 0x14, 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x04, 0x0c, 0x00, 0x00,
+ 0x00, 0x24, 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x04, 0x30, 0x00,
+ 0x00, 0x00, 0x0c, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3c,
+ 0x00, 0x00, 0x08, 0x0c, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04,
+ 0x3c, 0x00, 0x00, 0x08, 0x0c, 0x62, 0x54, 0x52, 0x43, 0x00, 0x00,
+ 0x04, 0x3c, 0x00, 0x00, 0x08, 0x0c, 0x74, 0x65, 0x78, 0x74, 0x00,
+ 0x00, 0x00, 0x00, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68,
+ 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x38, 0x20,
+ 0x48, 0x65, 0x77, 0x6c, 0x65, 0x74, 0x74, 0x2d, 0x50, 0x61, 0x63,
+ 0x6b, 0x61, 0x72, 0x64, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x6e,
+ 0x79, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x12, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45,
+ 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
+ 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39,
+ 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xcc, 0x58,
+ 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa2, 0x00, 0x00,
+ 0x38, 0xf5, 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5a, 0x20, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x99, 0x00, 0x00, 0xb7, 0x85,
+ 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0x0f, 0x84, 0x00, 0x00,
+ 0xb6, 0xcf, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
+ 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e,
+ 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43, 0x20, 0x36, 0x31,
+ 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44, 0x65, 0x66,
+ 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63, 0x6f,
+ 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20,
+ 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43, 0x20,
+ 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44,
+ 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20,
+ 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x20, 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73,
+ 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x52, 0x65,
+ 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65,
+ 0x77, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, 0x43, 0x36,
+ 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x52, 0x65,
+ 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65,
+ 0x77, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, 0x43, 0x36,
+ 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
+ 0xa4, 0xfe, 0x00, 0x14, 0x5f, 0x2e, 0x00, 0x10, 0xcf, 0x14, 0x00,
+ 0x03, 0xed, 0xcc, 0x00, 0x04, 0x13, 0x0b, 0x00, 0x03, 0x5c, 0x9e,
+ 0x00, 0x00, 0x00, 0x01, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x4c, 0x09, 0x56, 0x00, 0x50, 0x00, 0x00, 0x00, 0x57,
+ 0x1f, 0xe7, 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x8f, 0x00, 0x00, 0x00, 0x02, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00,
+ 0x00, 0x00, 0x43, 0x52, 0x54, 0x20, 0x63, 0x75, 0x72, 0x76, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1e, 0x00,
+ 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3b,
+ 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54, 0x00,
+ 0x59, 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72,
+ 0x00, 0x77, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00,
+ 0x90, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, 0x00, 0xa9,
+ 0x00, 0xae, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00,
+ 0xc6, 0x00, 0xcb, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0,
+ 0x00, 0xe5, 0x00, 0xeb, 0x00, 0xf0, 0x00, 0xf6, 0x00, 0xfb, 0x01,
+ 0x01, 0x01, 0x07, 0x01, 0x0d, 0x01, 0x13, 0x01, 0x19, 0x01, 0x1f,
+ 0x01, 0x25, 0x01, 0x2b, 0x01, 0x32, 0x01, 0x38, 0x01, 0x3e, 0x01,
+ 0x45, 0x01, 0x4c, 0x01, 0x52, 0x01, 0x59, 0x01, 0x60, 0x01, 0x67,
+ 0x01, 0x6e, 0x01, 0x75, 0x01, 0x7c, 0x01, 0x83, 0x01, 0x8b, 0x01,
+ 0x92, 0x01, 0x9a, 0x01, 0xa1, 0x01, 0xa9, 0x01, 0xb1, 0x01, 0xb9,
+ 0x01, 0xc1, 0x01, 0xc9, 0x01, 0xd1, 0x01, 0xd9, 0x01, 0xe1, 0x01,
+ 0xe9, 0x01, 0xf2, 0x01, 0xfa, 0x02, 0x03, 0x02, 0x0c, 0x02, 0x14,
+ 0x02, 0x1d, 0x02, 0x26, 0x02, 0x2f, 0x02, 0x38, 0x02, 0x41, 0x02,
+ 0x4b, 0x02, 0x54, 0x02, 0x5d, 0x02, 0x67, 0x02, 0x71, 0x02, 0x7a,
+ 0x02, 0x84, 0x02, 0x8e, 0x02, 0x98, 0x02, 0xa2, 0x02, 0xac, 0x02,
+ 0xb6, 0x02, 0xc1, 0x02, 0xcb, 0x02, 0xd5, 0x02, 0xe0, 0x02, 0xeb,
+ 0x02, 0xf5, 0x03, 0x00, 0x03, 0x0b, 0x03, 0x16, 0x03, 0x21, 0x03,
+ 0x2d, 0x03, 0x38, 0x03, 0x43, 0x03, 0x4f, 0x03, 0x5a, 0x03, 0x66,
+ 0x03, 0x72, 0x03, 0x7e, 0x03, 0x8a, 0x03, 0x96, 0x03, 0xa2, 0x03,
+ 0xae, 0x03, 0xba, 0x03, 0xc7, 0x03, 0xd3, 0x03, 0xe0, 0x03, 0xec,
+ 0x03, 0xf9, 0x04, 0x06, 0x04, 0x13, 0x04, 0x20, 0x04, 0x2d, 0x04,
+ 0x3b, 0x04, 0x48, 0x04, 0x55, 0x04, 0x63, 0x04, 0x71, 0x04, 0x7e,
+ 0x04, 0x8c, 0x04, 0x9a, 0x04, 0xa8, 0x04, 0xb6, 0x04, 0xc4, 0x04,
+ 0xd3, 0x04, 0xe1, 0x04, 0xf0, 0x04, 0xfe, 0x05, 0x0d, 0x05, 0x1c,
+ 0x05, 0x2b, 0x05, 0x3a, 0x05, 0x49, 0x05, 0x58, 0x05, 0x67, 0x05,
+ 0x77, 0x05, 0x86, 0x05, 0x96, 0x05, 0xa6, 0x05, 0xb5, 0x05, 0xc5,
+ 0x05, 0xd5, 0x05, 0xe5, 0x05, 0xf6, 0x06, 0x06, 0x06, 0x16, 0x06,
+ 0x27, 0x06, 0x37, 0x06, 0x48, 0x06, 0x59, 0x06, 0x6a, 0x06, 0x7b,
+ 0x06, 0x8c, 0x06, 0x9d, 0x06, 0xaf, 0x06, 0xc0, 0x06, 0xd1, 0x06,
+ 0xe3, 0x06, 0xf5, 0x07, 0x07, 0x07, 0x19, 0x07, 0x2b, 0x07, 0x3d,
+ 0x07, 0x4f, 0x07, 0x61, 0x07, 0x74, 0x07, 0x86, 0x07, 0x99, 0x07,
+ 0xac, 0x07, 0xbf, 0x07, 0xd2, 0x07, 0xe5, 0x07, 0xf8, 0x08, 0x0b,
+ 0x08, 0x1f, 0x08, 0x32, 0x08, 0x46, 0x08, 0x5a, 0x08, 0x6e, 0x08,
+ 0x82, 0x08, 0x96, 0x08, 0xaa, 0x08, 0xbe, 0x08, 0xd2, 0x08, 0xe7,
+ 0x08, 0xfb, 0x09, 0x10, 0x09, 0x25, 0x09, 0x3a, 0x09, 0x4f, 0x09,
+ 0x64, 0x09, 0x79, 0x09, 0x8f, 0x09, 0xa4, 0x09, 0xba, 0x09, 0xcf,
+ 0x09, 0xe5, 0x09, 0xfb, 0x0a, 0x11, 0x0a, 0x27, 0x0a, 0x3d, 0x0a,
+ 0x54, 0x0a, 0x6a, 0x0a, 0x81, 0x0a, 0x98, 0x0a, 0xae, 0x0a, 0xc5,
+ 0x0a, 0xdc, 0x0a, 0xf3, 0x0b, 0x0b, 0x0b, 0x22, 0x0b, 0x39, 0x0b,
+ 0x51, 0x0b, 0x69, 0x0b, 0x80, 0x0b, 0x98, 0x0b, 0xb0, 0x0b, 0xc8,
+ 0x0b, 0xe1, 0x0b, 0xf9, 0x0c, 0x12, 0x0c, 0x2a, 0x0c, 0x43, 0x0c,
+ 0x5c, 0x0c, 0x75, 0x0c, 0x8e, 0x0c, 0xa7, 0x0c, 0xc0, 0x0c, 0xd9,
+ 0x0c, 0xf3, 0x0d, 0x0d, 0x0d, 0x26, 0x0d, 0x40, 0x0d, 0x5a, 0x0d,
+ 0x74, 0x0d, 0x8e, 0x0d, 0xa9, 0x0d, 0xc3, 0x0d, 0xde, 0x0d, 0xf8,
+ 0x0e, 0x13, 0x0e, 0x2e, 0x0e, 0x49, 0x0e, 0x64, 0x0e, 0x7f, 0x0e,
+ 0x9b, 0x0e, 0xb6, 0x0e, 0xd2, 0x0e, 0xee, 0x0f, 0x09, 0x0f, 0x25,
+ 0x0f, 0x41, 0x0f, 0x5e, 0x0f, 0x7a, 0x0f, 0x96, 0x0f, 0xb3, 0x0f,
+ 0xcf, 0x0f, 0xec, 0x10, 0x09, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61,
+ 0x10, 0x7e, 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11,
+ 0x13, 0x11, 0x31, 0x11, 0x4f, 0x11, 0x6d, 0x11, 0x8c, 0x11, 0xaa,
+ 0x11, 0xc9, 0x11, 0xe8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, 0x12,
+ 0x64, 0x12, 0x84, 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x03,
+ 0x13, 0x23, 0x13, 0x43, 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13,
+ 0xc5, 0x13, 0xe5, 0x14, 0x06, 0x14, 0x27, 0x14, 0x49, 0x14, 0x6a,
+ 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce, 0x14, 0xf0, 0x15, 0x12, 0x15,
+ 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b, 0x15, 0xbd, 0x15, 0xe0,
+ 0x16, 0x03, 0x16, 0x26, 0x16, 0x49, 0x16, 0x6c, 0x16, 0x8f, 0x16,
+ 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, 0x41, 0x17, 0x65,
+ 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b, 0x18,
+ 0x40, 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa,
+ 0x19, 0x20, 0x19, 0x45, 0x19, 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19,
+ 0xdd, 0x1a, 0x04, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, 0x1a, 0x9e,
+ 0x1a, 0xc5, 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b,
+ 0x8a, 0x1b, 0xb2, 0x1b, 0xda, 0x1c, 0x02, 0x1c, 0x2a, 0x1c, 0x52,
+ 0x1c, 0x7b, 0x1c, 0xa3, 0x1c, 0xcc, 0x1c, 0xf5, 0x1d, 0x1e, 0x1d,
+ 0x47, 0x1d, 0x70, 0x1d, 0x99, 0x1d, 0xc3, 0x1d, 0xec, 0x1e, 0x16,
+ 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94, 0x1e, 0xbe, 0x1e, 0xe9, 0x1f,
+ 0x13, 0x1f, 0x3e, 0x1f, 0x69, 0x1f, 0x94, 0x1f, 0xbf, 0x1f, 0xea,
+ 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, 0x98, 0x20, 0xc4, 0x20,
+ 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1, 0x21, 0xce,
+ 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf, 0x22,
+ 0xdd, 0x23, 0x0a, 0x23, 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2,
+ 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, 0x24, 0xab, 0x24,
+ 0xda, 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7,
+ 0x25, 0xf7, 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26,
+ 0xe8, 0x27, 0x18, 0x27, 0x49, 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc,
+ 0x28, 0x0d, 0x28, 0x3f, 0x28, 0x71, 0x28, 0xa2, 0x28, 0xd4, 0x29,
+ 0x06, 0x29, 0x38, 0x29, 0x6b, 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x02,
+ 0x2a, 0x35, 0x2a, 0x68, 0x2a, 0x9b, 0x2a, 0xcf, 0x2b, 0x02, 0x2b,
+ 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, 0xd1, 0x2c, 0x05, 0x2c, 0x39,
+ 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0x0c, 0x2d, 0x41, 0x2d,
+ 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c, 0x2e, 0x82,
+ 0x2e, 0xb7, 0x2e, 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91, 0x2f,
+ 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, 0x30, 0xa4, 0x30, 0xdb,
+ 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32,
+ 0x2a, 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0x0d, 0x33, 0x46,
+ 0x33, 0x7f, 0x33, 0xb8, 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34,
+ 0x9e, 0x34, 0xd8, 0x35, 0x13, 0x35, 0x4d, 0x35, 0x87, 0x35, 0xc2,
+ 0x35, 0xfd, 0x36, 0x37, 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37,
+ 0x24, 0x37, 0x60, 0x37, 0x9c, 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50,
+ 0x38, 0x8c, 0x38, 0xc8, 0x39, 0x05, 0x39, 0x42, 0x39, 0x7f, 0x39,
+ 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74, 0x3a, 0xb2, 0x3a, 0xef,
+ 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8, 0x3c, 0x27, 0x3c,
+ 0x65, 0x3c, 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61, 0x3d, 0xa1,
+ 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, 0x3e, 0xa0, 0x3e, 0xe0, 0x3f,
+ 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, 0x64,
+ 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41,
+ 0xee, 0x42, 0x30, 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a,
+ 0x43, 0x7d, 0x43, 0xc0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8a, 0x44,
+ 0xce, 0x45, 0x12, 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22,
+ 0x46, 0x67, 0x46, 0xab, 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47,
+ 0xc0, 0x48, 0x05, 0x48, 0x4b, 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d,
+ 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0, 0x4a, 0x37, 0x4a, 0x7d, 0x4a,
+ 0xc4, 0x4b, 0x0c, 0x4b, 0x53, 0x4b, 0x9a, 0x4b, 0xe2, 0x4c, 0x2a,
+ 0x4c, 0x72, 0x4c, 0xba, 0x4d, 0x02, 0x4d, 0x4a, 0x4d, 0x93, 0x4d,
+ 0xdc, 0x4e, 0x25, 0x4e, 0x6e, 0x4e, 0xb7, 0x4f, 0x00, 0x4f, 0x49,
+ 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, 0xbb, 0x51,
+ 0x06, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c,
+ 0x52, 0xc7, 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54,
+ 0x42, 0x54, 0x8f, 0x54, 0xdb, 0x55, 0x28, 0x55, 0x75, 0x55, 0xc2,
+ 0x56, 0x0f, 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, 0x57,
+ 0x92, 0x57, 0xe0, 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a,
+ 0x59, 0x69, 0x59, 0xb8, 0x5a, 0x07, 0x5a, 0x56, 0x5a, 0xa6, 0x5a,
+ 0xf5, 0x5b, 0x45, 0x5b, 0x95, 0x5b, 0xe5, 0x5c, 0x35, 0x5c, 0x86,
+ 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78, 0x5d, 0xc9, 0x5e, 0x1a, 0x5e,
+ 0x6c, 0x5e, 0xbd, 0x5f, 0x0f, 0x5f, 0x61, 0x5f, 0xb3, 0x60, 0x05,
+ 0x60, 0x57, 0x60, 0xaa, 0x60, 0xfc, 0x61, 0x4f, 0x61, 0xa2, 0x61,
+ 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, 0x43, 0x63, 0x97,
+ 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d, 0x65,
+ 0x92, 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d,
+ 0x67, 0x93, 0x67, 0xe9, 0x68, 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69,
+ 0x43, 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, 0x6a, 0xf7,
+ 0x6b, 0x4f, 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d,
+ 0x08, 0x6d, 0x60, 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4,
+ 0x6f, 0x1e, 0x6f, 0x78, 0x6f, 0xd1, 0x70, 0x2b, 0x70, 0x86, 0x70,
+ 0xe0, 0x71, 0x3a, 0x71, 0x95, 0x71, 0xf0, 0x72, 0x4b, 0x72, 0xa6,
+ 0x73, 0x01, 0x73, 0x5d, 0x73, 0xb8, 0x74, 0x14, 0x74, 0x70, 0x74,
+ 0xcc, 0x75, 0x28, 0x75, 0x85, 0x75, 0xe1, 0x76, 0x3e, 0x76, 0x9b,
+ 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, 0x11, 0x78, 0x6e, 0x78,
+ 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46, 0x7a, 0xa5,
+ 0x7b, 0x04, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81, 0x7c,
+ 0xe1, 0x7d, 0x41, 0x7d, 0xa1, 0x7e, 0x01, 0x7e, 0x62, 0x7e, 0xc2,
+ 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, 0x80, 0xa8, 0x81,
+ 0x0a, 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4,
+ 0x83, 0x57, 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85,
+ 0x47, 0x85, 0xab, 0x86, 0x0e, 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b,
+ 0x87, 0x9f, 0x88, 0x04, 0x88, 0x69, 0x88, 0xce, 0x89, 0x33, 0x89,
+ 0x99, 0x89, 0xfe, 0x8a, 0x64, 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96,
+ 0x8b, 0xfc, 0x8c, 0x63, 0x8c, 0xca, 0x8d, 0x31, 0x8d, 0x98, 0x8d,
+ 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, 0x36, 0x8f, 0x9e, 0x90, 0x06,
+ 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8, 0x92, 0x11, 0x92,
+ 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20, 0x94, 0x8a,
+ 0x94, 0xf4, 0x95, 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f, 0x97,
+ 0x0a, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, 0x98, 0xb8, 0x99, 0x24,
+ 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b,
+ 0xaf, 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2,
+ 0x9e, 0x40, 0x9e, 0xae, 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0,
+ 0x69, 0xa0, 0xd8, 0xa1, 0x47, 0xa1, 0xb6, 0xa2, 0x26, 0xa2, 0x96,
+ 0xa3, 0x06, 0xa3, 0x76, 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5,
+ 0x38, 0xa5, 0xa9, 0xa6, 0x1a, 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e,
+ 0xa7, 0xe0, 0xa8, 0x52, 0xa8, 0xc4, 0xa9, 0x37, 0xa9, 0xa9, 0xaa,
+ 0x1c, 0xaa, 0x8f, 0xab, 0x02, 0xab, 0x75, 0xab, 0xe9, 0xac, 0x5c,
+ 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d, 0xae, 0xa1, 0xaf,
+ 0x16, 0xaf, 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea, 0xb1, 0x60,
+ 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, 0xb3, 0x38, 0xb3, 0xae, 0xb4,
+ 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x01, 0xb6, 0x79,
+ 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9,
+ 0x4a, 0xb9, 0xc2, 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7,
+ 0xbc, 0x21, 0xbc, 0x9b, 0xbd, 0x15, 0xbd, 0x8f, 0xbe, 0x0a, 0xbe,
+ 0x84, 0xbe, 0xff, 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec,
+ 0xc1, 0x67, 0xc1, 0xe3, 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3,
+ 0xd4, 0xc4, 0x51, 0xc4, 0xce, 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46,
+ 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf, 0xc8, 0x3d, 0xc8, 0xbc, 0xc9,
+ 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7, 0xcb, 0x36, 0xcb, 0xb6,
+ 0xcc, 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5, 0xce, 0x36, 0xce,
+ 0xb6, 0xcf, 0x37, 0xcf, 0xb8, 0xd0, 0x39, 0xd0, 0xba, 0xd1, 0x3c,
+ 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, 0xc6, 0xd4,
+ 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8,
+ 0xd7, 0x5c, 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9,
+ 0xf1, 0xda, 0x76, 0xda, 0xfb, 0xdb, 0x80, 0xdc, 0x05, 0xdc, 0x8a,
+ 0xdd, 0x10, 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, 0xdf,
+ 0xaf, 0xe0, 0x36, 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53,
+ 0xe2, 0xdb, 0xe3, 0x63, 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5,
+ 0x84, 0xe6, 0x0d, 0xe6, 0x96, 0xe7, 0x1f, 0xe7, 0xa9, 0xe8, 0x32,
+ 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0, 0xea, 0x5b, 0xea, 0xe5, 0xeb,
+ 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11, 0xed, 0x9c, 0xee, 0x28,
+ 0xee, 0xb4, 0xef, 0x40, 0xef, 0xcc, 0xf0, 0x58, 0xf0, 0xe5, 0xf1,
+ 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, 0xa7, 0xf4, 0x34,
+ 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb, 0xf7,
+ 0x8a, 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57,
+ 0xfa, 0xe7, 0xfb, 0x77, 0xfc, 0x07, 0xfc, 0x98, 0xfd, 0x29, 0xfd,
+ 0xba, 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff
+ };
+
+ StringInfo
+ *profile;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (GetImageProfile(image,"icm") != (const StringInfo *) NULL)
+ return(MagickFalse);
+ profile=AcquireStringInfo(sizeof(sRGBProfile));
+ SetStringInfoDatum(profile,sRGBProfile);
+ status=SetImageProfile(image,"icm",profile);
+ profile=DestroyStringInfo(profile);
+ return(status);
+}
+#if defined(MAGICKCORE_LCMS_DELEGATE)
+#if defined(LCMS_VERSION) && (LCMS_VERSION > 1010)
+static int LCMSErrorHandler(int severity,const char *message)
+{
+ (void) LogMagickEvent(TransformEvent,GetMagickModule(),"lcms: #%d, %s",
+ severity,message != (char *) NULL ? message : "no message");
+ return(1);
+}
+#endif
+#endif
+
+MagickExport MagickBooleanType ProfileImage(Image *image,const char *name,
+ const void *datum,const size_t length,
+ const MagickBooleanType magick_unused(clone))
+{
+#define ProfileImageTag "Profile/Image"
+#define ThrowProfileException(severity,tag,context) \
+{ \
+ (void) cmsCloseProfile(source_profile); \
+ (void) cmsCloseProfile(target_profile); \
+ ThrowBinaryException(severity,tag,context); \
+}
+
+ MagickBooleanType
+ status;
+
+ StringInfo
+ *profile;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(name != (const char *) NULL);
+ if ((datum == (const void *) NULL) || (length == 0))
+ {
+ char
+ **arguments,
+ *names;
+
+ int
+ number_arguments;
+
+ register long
+ i;
+
+ /*
+ Delete image profile(s).
+ */
+ names=ConstantString(name);
+ (void) SubstituteString(&names,","," ");
+ arguments=StringToArgv(names,&number_arguments);
+ names=DestroyString(names);
+ if (arguments == (char **) NULL)
+ return(MagickTrue);
+ ResetImageProfileIterator(image);
+ for (name=GetNextImageProfile(image); name != (const char *) NULL; )
+ {
+ for (i=1; i < number_arguments; i++)
+ {
+ if ((*arguments[i] == '!') &&
+ (LocaleCompare(name,arguments[i]+1) == 0))
+ break;
+ if (GlobExpression(name,arguments[i],MagickTrue) != MagickFalse)
+ {
+ (void) DeleteImageProfile(image,name);
+ ResetImageProfileIterator(image);
+ break;
+ }
+ }
+ name=GetNextImageProfile(image);
+ }
+ for (i=0; i < number_arguments; i++)
+ arguments[i]=DestroyString(arguments[i]);
+ arguments=(char **) RelinquishMagickMemory(arguments);
+ return(MagickTrue);
+ }
+ /*
+ Add a ICC, IPTC, or generic profile to the image.
+ */
+ profile=AcquireStringInfo((size_t) length);
+ SetStringInfoDatum(profile,(unsigned char *) datum);
+ if ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0))
+ {
+ const StringInfo
+ *icc_profile;
+
+ icc_profile=GetImageProfile(image,"icc");
+ if ((icc_profile != (const StringInfo *) NULL) &&
+ (CompareStringInfo(icc_profile,profile) == 0))
+ {
+ const char
+ *value;
+
+ value=GetImageProperty(image,"exif:ColorSpace");
+ if (LocaleCompare(value,"1") != 0)
+ (void) SetsRGBImageProfile(image);
+ value=GetImageProperty(image,"exif:InteroperabilityIndex");
+ if (LocaleCompare(value,"R98.") != 0)
+ (void) SetsRGBImageProfile(image);
+ value=GetImageProperty(image,"exif:InteroperabilityIndex");
+ if (LocaleCompare(value,"R03.") != 0)
+ (void) SetAdobeRGB1998ImageProfile(image);
+ icc_profile=GetImageProfile(image,"icc");
+ }
+ if ((icc_profile != (const StringInfo *) NULL) &&
+ (CompareStringInfo(icc_profile,profile) == 0))
+ {
+ profile=DestroyStringInfo(profile);
+ return(MagickTrue);
+ }
+#if !defined(MAGICKCORE_LCMS_DELEGATE)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ MissingDelegateWarning,"DelegateLibrarySupportNotBuiltIn","`%s' (LCMS)",
+ image->filename);
+#else
+ if (icc_profile == (StringInfo *) NULL)
+ (void) ThrowMagickException(&image->exception,GetMagickModule(),
+ ImageWarning,"AssociateProfile","`%s'",name);
+ else
+ {
+ CacheView
+ *image_view;
+
+ ColorspaceType
+ source_colorspace,
+ target_colorspace;
+
+ cmsHPROFILE
+ source_profile,
+ target_profile;
+
+ cmsHTRANSFORM
+ *transform;
+
+ DWORD
+ flags,
+ source_type,
+ target_type;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ intent;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ size_t
+ length,
+ source_channels,
+ target_channels;
+
+ unsigned short
+ **source_pixels,
+ **target_pixels;
+
+ /*
+ Transform pixel colors as defined by the color profiles.
+ */
+#if defined(LCMS_VERSION) && (LCMS_VERSION > 1010)
+ cmsSetErrorHandler(LCMSErrorHandler);
+#else
+ (void) cmsErrorAction(LCMS_ERROR_SHOW);
+#endif
+ source_profile=cmsOpenProfileFromMem(GetStringInfoDatum(icc_profile),
+ (DWORD) GetStringInfoLength(icc_profile));
+ target_profile=cmsOpenProfileFromMem(GetStringInfoDatum(profile),
+ (DWORD) GetStringInfoLength(profile));
+ if ((source_profile == (cmsHPROFILE) NULL) ||
+ (target_profile == (cmsHPROFILE) NULL))
+ ThrowBinaryException(ResourceLimitError,
+ "ColorspaceColorProfileMismatch",name);
+ switch (cmsGetColorSpace(source_profile))
+ {
+ case icSigCmykData:
+ {
+ source_colorspace=CMYKColorspace;
+ source_type=(DWORD) TYPE_CMYK_16;
+ source_channels=4;
+ break;
+ }
+ case icSigGrayData:
+ {
+ source_colorspace=GRAYColorspace;
+ source_type=(DWORD) TYPE_GRAY_16;
+ source_channels=1;
+ break;
+ }
+ case icSigLabData:
+ {
+ source_colorspace=LabColorspace;
+ source_type=(DWORD) TYPE_Lab_16;
+ source_channels=3;
+ break;
+ }
+ case icSigLuvData:
+ {
+ source_colorspace=YUVColorspace;
+ source_type=(DWORD) TYPE_YUV_16;
+ source_channels=3;
+ break;
+ }
+ case icSigRgbData:
+ {
+ source_colorspace=RGBColorspace;
+ source_type=(DWORD) TYPE_RGB_16;
+ source_channels=3;
+ break;
+ }
+ case icSigXYZData:
+ {
+ source_colorspace=XYZColorspace;
+ source_type=(DWORD) TYPE_XYZ_16;
+ source_channels=3;
+ break;
+ }
+ case icSigYCbCrData:
+ {
+ source_colorspace=YCbCrColorspace;
+ source_type=(DWORD) TYPE_YCbCr_16;
+ source_channels=3;
+ break;
+ }
+ default:
+ {
+ source_colorspace=UndefinedColorspace;
+ source_type=(DWORD) TYPE_RGB_16;
+ source_channels=3;
+ break;
+ }
+ }
+ switch (cmsGetColorSpace(target_profile))
+ {
+ case icSigCmykData:
+ {
+ target_colorspace=CMYKColorspace;
+ target_type=(DWORD) TYPE_CMYK_16;
+ target_channels=4;
+ break;
+ }
+ case icSigLabData:
+ {
+ target_colorspace=LabColorspace;
+ target_type=(DWORD) TYPE_Lab_16;
+ target_channels=3;
+ break;
+ }
+ case icSigGrayData:
+ {
+ target_colorspace=GRAYColorspace;
+ target_type=(DWORD) TYPE_GRAY_16;
+ target_channels=1;
+ break;
+ }
+ case icSigLuvData:
+ {
+ target_colorspace=YUVColorspace;
+ target_type=(DWORD) TYPE_YUV_16;
+ target_channels=3;
+ break;
+ }
+ case icSigRgbData:
+ {
+ target_colorspace=RGBColorspace;
+ target_type=(DWORD) TYPE_RGB_16;
+ target_channels=3;
+ break;
+ }
+ case icSigXYZData:
+ {
+ target_colorspace=XYZColorspace;
+ target_type=(DWORD) TYPE_XYZ_16;
+ target_channels=3;
+ break;
+ }
+ case icSigYCbCrData:
+ {
+ target_colorspace=YCbCrColorspace;
+ target_type=(DWORD) TYPE_YCbCr_16;
+ target_channels=3;
+ break;
+ }
+ default:
+ {
+ target_colorspace=UndefinedColorspace;
+ target_type=(DWORD) TYPE_RGB_16;
+ target_channels=3;
+ break;
+ }
+ }
+ exception=(&image->exception);
+ if ((source_colorspace == UndefinedColorspace) ||
+ (target_colorspace == UndefinedColorspace))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ if ((source_colorspace == GRAYColorspace) &&
+ (IsGrayImage(image,exception) == MagickFalse))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ if ((source_colorspace == CMYKColorspace) &&
+ (image->colorspace != CMYKColorspace))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ if ((source_colorspace == XYZColorspace) &&
+ (image->colorspace != XYZColorspace))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ if ((source_colorspace == YCbCrColorspace) &&
+ (image->colorspace != YCbCrColorspace))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ if ((source_colorspace != CMYKColorspace) &&
+ (source_colorspace != GRAYColorspace) &&
+ (source_colorspace != LabColorspace) &&
+ (source_colorspace != XYZColorspace) &&
+ (source_colorspace != YCbCrColorspace) &&
+ (image->colorspace != RGBColorspace))
+ ThrowProfileException(ImageError,"ColorspaceColorProfileMismatch",
+ name);
+ switch (image->rendering_intent)
+ {
+ case AbsoluteIntent: intent=INTENT_ABSOLUTE_COLORIMETRIC; break;
+ case PerceptualIntent: intent=INTENT_PERCEPTUAL; break;
+ case RelativeIntent: intent=INTENT_RELATIVE_COLORIMETRIC; break;
+ case SaturationIntent: intent=INTENT_SATURATION; break;
+ default: intent=INTENT_PERCEPTUAL; break;
+ }
+ flags=cmsFLAGS_HIGHRESPRECALC;
+#if defined(cmsFLAGS_BLACKPOINTCOMPENSATION)
+ if (image->black_point_compensation != MagickFalse)
+ flags|=cmsFLAGS_BLACKPOINTCOMPENSATION;
+#endif
+ transform=AcquireTransformThreadSet(source_profile,source_type,
+ target_profile,target_type,intent,flags);
+ (void) cmsCloseProfile(source_profile);
+ if (transform == (cmsHTRANSFORM *) NULL)
+ ThrowBinaryException(ImageError,"UnableToCreateColorTransform",
+ name);
+ /*
+ Transform image as dictated by the source and target image profiles.
+ */
+ length=(size_t) image->columns;
+ source_pixels=AcquirePixelThreadSet(image->columns,source_channels);
+ target_pixels=AcquirePixelThreadSet(image->columns,target_channels);
+ if ((source_pixels == (unsigned short **) NULL) ||
+ (target_pixels == (unsigned short **) NULL))
+ {
+ transform=DestroyTransformThreadSet(transform);
+ (void) cmsCloseProfile(target_profile);
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ {
+ target_pixels=DestroyPixelThreadSet(target_pixels);
+ source_pixels=DestroyPixelThreadSet(source_pixels);
+ transform=DestroyTransformThreadSet(transform);
+ (void) cmsCloseProfile(target_profile);
+ return(MagickFalse);
+ }
+ if (target_colorspace == CMYKColorspace)
+ (void) SetImageColorspace(image,target_colorspace);
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ register unsigned short
+ *p;
+
+ 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);
+ id=GetOpenMPThreadId();
+ p=source_pixels[id];
+ for (x=0; x < (long) image->columns; x++)
+ {
+ *p++=ScaleQuantumToShort(q->red);
+ if (source_channels > 1)
+ {
+ *p++=ScaleQuantumToShort(q->green);
+ *p++=ScaleQuantumToShort(q->blue);
+ }
+ if (source_channels > 3)
+ *p++=ScaleQuantumToShort(indexes[x]);
+ q++;
+ }
+ cmsDoTransform(transform[id],source_pixels[id],target_pixels[id],
+ (unsigned int) image->columns);
+ p=target_pixels[id];
+ q-=image->columns;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=ScaleShortToQuantum(*p);
+ q->green=q->red;
+ q->blue=q->red;
+ p++;
+ if (target_channels > 1)
+ {
+ q->green=ScaleShortToQuantum(*p);
+ p++;
+ q->blue=ScaleShortToQuantum(*p);
+ p++;
+ }
+ if (target_channels > 3)
+ {
+ indexes[x]=ScaleShortToQuantum(*p);
+ p++;
+ }
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ProfileImage)
+#endif
+ proceed=SetImageProgress(image,ProfileImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ (void) SetImageColorspace(image,target_colorspace);
+ switch (cmsGetColorSpace(target_profile))
+ {
+ case icSigRgbData:
+ {
+ image->type=image->matte == MagickFalse ? TrueColorType :
+ TrueColorMatteType;
+ break;
+ }
+ case icSigCmykData:
+ {
+ image->type=image->matte == MagickFalse ? ColorSeparationType :
+ ColorSeparationMatteType;
+ break;
+ }
+ case icSigGrayData:
+ {
+ image->type=image->matte == MagickFalse ? GrayscaleType :
+ GrayscaleMatteType;
+ break;
+ }
+ default:
+ break;
+ }
+ target_pixels=DestroyPixelThreadSet(target_pixels);
+ source_pixels=DestroyPixelThreadSet(source_pixels);
+ transform=DestroyTransformThreadSet(transform);
+ (void) cmsCloseProfile(target_profile);
+ }
+#endif
+ }
+ status=SetImageProfile(image,name,profile);
+ profile=DestroyStringInfo(profile);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e P r o f i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageProfile() removes a named profile from the image and returns its
+% value.
+%
+% The format of the RemoveImageProfile method is:
+%
+% void *RemoveImageProfile(Image *image,const char *name)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o name: the profile name.
+%
+*/
+MagickExport StringInfo *RemoveImageProfile(Image *image,const char *name)
+{
+ StringInfo
+ *profile;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ return((StringInfo *) NULL);
+ if (LocaleCompare(name,"icc") == 0)
+ {
+ /*
+ Continue to support deprecated color profile for now.
+ */
+ image->color_profile.length=0;
+ image->color_profile.info=(unsigned char *) NULL;
+ }
+ if (LocaleCompare(name,"iptc") == 0)
+ {
+ /*
+ Continue to support deprecated IPTC profile for now.
+ */
+ image->iptc_profile.length=0;
+ image->iptc_profile.info=(unsigned char *) NULL;
+ }
+ profile=(StringInfo *) RemoveNodeFromSplayTree((SplayTreeInfo *)
+ image->profiles,name);
+ return(profile);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t P r o f i l e I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageProfileIterator() resets the image profile iterator. Use it in
+% conjunction with GetNextImageProfile() to iterate over all the profiles
+% associated with an image.
+%
+% The format of the ResetImageProfileIterator method is:
+%
+% ResetImageProfileIterator(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void ResetImageProfileIterator(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ return;
+ ResetSplayTreeIterator((SplayTreeInfo *) image->profiles);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e P r o f i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageProfile() adds a named profile to the image. If a profile with the
+% same name already exists, it is replaced. This method differs from the
+% ProfileImage() method in that it does not apply CMS color profiles.
+%
+% The format of the SetImageProfile method is:
+%
+% MagickBooleanType SetImageProfile(Image *image,const char *name,
+% const StringInfo *profile)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o name: the profile name, for example icc, exif, and 8bim (8bim is the
+% Photoshop wrapper for iptc profiles).
+%
+% o profile: A StringInfo structure that contains the named profile.
+%
+*/
+
+static void *DestroyProfile(void *profile)
+{
+ return((void *) DestroyStringInfo((StringInfo *) profile));
+}
+
+static inline const unsigned char *ReadResourceByte(const unsigned char *p,
+ unsigned char *quantum)
+{
+ *quantum=(*p++);
+ return(p);
+}
+
+static inline const unsigned char *ReadResourceBytes(const unsigned char *p,
+ const ssize_t count,unsigned char *quantum)
+{
+ register ssize_t
+ i;
+
+ for (i=0; i < count; i++)
+ *quantum++=(*p++);
+ return(p);
+}
+
+static inline const unsigned char *ReadResourceLong(const unsigned char *p,
+ unsigned long *quantum)
+{
+ *quantum=(unsigned long) (*p++ << 24);
+ *quantum|=(unsigned long) (*p++ << 16);
+ *quantum|=(unsigned long) (*p++ << 8);
+ *quantum|=(unsigned long) (*p++ << 0);
+ return(p);
+}
+
+static inline const unsigned char *ReadResourceShort(const unsigned char *p,
+ unsigned short *quantum)
+{
+ *quantum=(unsigned short) (*p++ << 8);
+ *quantum|=(unsigned short) (*p++ << 0);
+ return(p);
+}
+
+static MagickBooleanType GetProfilesFromResourceBlock(Image *image,
+ const StringInfo *resource_block)
+{
+ const unsigned char
+ *datum;
+
+ register const unsigned char
+ *p;
+
+ size_t
+ length;
+
+ StringInfo
+ *profile;
+
+ unsigned char
+ length_byte;
+
+ unsigned long
+ count;
+
+ unsigned short
+ id;
+
+ datum=GetStringInfoDatum(resource_block);
+ length=GetStringInfoLength(resource_block);
+ for (p=datum; p < (datum+length-16); )
+ {
+ if (LocaleNCompare((char *) p,"8BIM",4) != 0)
+ break;
+ p+=4;
+ p=ReadResourceShort(p,&id);
+ p=ReadResourceByte(p,&length_byte);
+ p+=length_byte;
+ if (((length_byte+1) & 0x01) != 0)
+ p++;
+ if (p > (datum+length-4))
+ break;
+ p=ReadResourceLong(p,&count);
+ if ((p > (datum+length-count)) || (count > length))
+ break;
+ switch (id)
+ {
+ case 0x03ed:
+ {
+ unsigned short
+ resolution;
+
+ /*
+ Resolution.
+ */
+ p=ReadResourceShort(p,&resolution)+6;
+ image->x_resolution=(double) resolution;
+ p=ReadResourceShort(p,&resolution)+6;
+ image->y_resolution=(double) resolution;
+ break;
+ }
+ case 0x0404:
+ {
+ /*
+ IPTC Profile
+ */
+ profile=AcquireStringInfo(count);
+ SetStringInfoDatum(profile,p);
+ (void) SetImageProfile(image,"iptc",profile);
+ profile=DestroyStringInfo(profile);
+ p+=count;
+ break;
+ }
+ case 0x040c:
+ {
+ /*
+ Thumbnail.
+ */
+ p+=count;
+ break;
+ }
+ case 0x040f:
+ {
+ /*
+ ICC Profile.
+ */
+ profile=AcquireStringInfo(count);
+ SetStringInfoDatum(profile,p);
+ (void) SetImageProfile(image,"icc",profile);
+ profile=DestroyStringInfo(profile);
+ p+=count;
+ break;
+ }
+ case 0x0422:
+ {
+ /*
+ EXIF Profile.
+ */
+ profile=AcquireStringInfo(count);
+ SetStringInfoDatum(profile,p);
+ (void) SetImageProfile(image,"exif",profile);
+ profile=DestroyStringInfo(profile);
+ p+=count;
+ break;
+ }
+ case 0x0424:
+ {
+ /*
+ XMP Profile.
+ */
+ profile=AcquireStringInfo(count);
+ SetStringInfoDatum(profile,p);
+ (void) SetImageProfile(image,"xmp",profile);
+ profile=DestroyStringInfo(profile);
+ p+=count;
+ break;
+ }
+ default:
+ {
+ p+=count;
+ break;
+ }
+ }
+ if ((count & 0x01) != 0)
+ p++;
+ }
+ return(MagickTrue);
+}
+
+MagickExport MagickBooleanType SetImageProfile(Image *image,const char *name,
+ const StringInfo *profile)
+{
+ char
+ key[MaxTextExtent],
+ property[MaxTextExtent];
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (image->profiles == (SplayTreeInfo *) NULL)
+ image->profiles=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
+ DestroyProfile);
+ (void) CopyMagickString(key,name,MaxTextExtent);
+ status=AddValueToSplayTree((SplayTreeInfo *) image->profiles,
+ ConstantString(key),CloneStringInfo(profile));
+ if ((status != MagickFalse) &&
+ ((LocaleCompare(name,"icc") == 0) || (LocaleCompare(name,"icm") == 0)))
+ {
+ const StringInfo
+ *icc_profile;
+
+ /*
+ Continue to support deprecated color profile member.
+ */
+ icc_profile=GetImageProfile(image,name);
+ if (icc_profile != (const StringInfo *) NULL)
+ {
+ image->color_profile.length=GetStringInfoLength(icc_profile);
+ image->color_profile.info=GetStringInfoDatum(icc_profile);
+ }
+ }
+ if ((status != MagickFalse) &&
+ ((LocaleCompare(name,"iptc") == 0) || (LocaleCompare(name,"8bim") == 0)))
+ {
+ const StringInfo
+ *iptc_profile;
+
+ /*
+ Continue to support deprecated IPTC profile member.
+ */
+ iptc_profile=GetImageProfile(image,name);
+ if (iptc_profile != (const StringInfo *) NULL)
+ {
+ image->iptc_profile.length=GetStringInfoLength(iptc_profile);
+ image->iptc_profile.info=GetStringInfoDatum(iptc_profile);
+ }
+ (void) GetProfilesFromResourceBlock(image,profile);
+ }
+ /*
+ Inject profile into image properties.
+ */
+ (void) FormatMagickString(property,MaxTextExtent,"%s:sans",name);
+ (void) GetImageProperty(image,property);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y n c I m a g e P r o f i l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncImageProfiles() synchronizes image properties with the image profiles.
+% Currently we only support updating the EXIF resolution and orientation.
+%
+% The format of the SyncImageProfiles method is:
+%
+% MagickBooleanType SyncImageProfiles(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static inline int ReadProfileByte(unsigned char **p,size_t *length)
+{
+ int
+ c;
+
+ if (*length < 1)
+ return(EOF);
+ c=(int) (*(*p)++);
+ (*length)--;
+ return(c);
+}
+
+static inline unsigned short ReadProfileShort(const EndianType endian,
+ unsigned char *buffer)
+{
+ unsigned short
+ value;
+
+ if (endian == MSBEndian)
+ {
+ value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
+ ((unsigned char *) buffer)[1]);
+ return((unsigned short) (value & 0xffff));
+ }
+ value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
+ return((unsigned short) (value & 0xffff));
+}
+
+static inline unsigned long ReadProfileLong(const EndianType endian,
+ unsigned char *buffer)
+{
+ unsigned long
+ value;
+
+ if (endian == MSBEndian)
+ {
+ value=(unsigned long) ((buffer[0] << 24) | (buffer[1] << 16) |
+ (buffer[2] << 8) | buffer[3]);
+ return((unsigned long) (value & 0xffffffff));
+ }
+ value=(unsigned long) ((buffer[3] << 24) | (buffer[2] << 16) |
+ (buffer[1] << 8 ) | (buffer[0]));
+ return((unsigned long) (value & 0xffffffff));
+}
+
+static inline void WriteProfileLong(const EndianType endian,
+ const unsigned long value,unsigned char *p)
+{
+ unsigned char
+ buffer[4];
+
+ if (endian == MSBEndian)
+ {
+ buffer[0]=(unsigned char) (value >> 24);
+ buffer[1]=(unsigned char) (value >> 16);
+ buffer[2]=(unsigned char) (value >> 8);
+ buffer[3]=(unsigned char) value;
+ (void) CopyMagickMemory(p,buffer,4);
+ return;
+ }
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ buffer[2]=(unsigned char) (value >> 16);
+ buffer[3]=(unsigned char) (value >> 24);
+ (void) CopyMagickMemory(p,buffer,4);
+}
+
+static void WriteProfileShort(const EndianType endian,
+ const unsigned short value,unsigned char *p)
+{
+ unsigned char
+ buffer[2];
+
+ if (endian == MSBEndian)
+ {
+ buffer[0]=(unsigned char) (value >> 8);
+ buffer[1]=(unsigned char) value;
+ (void) CopyMagickMemory(p,buffer,2);
+ return;
+ }
+ buffer[0]=(unsigned char) value;
+ buffer[1]=(unsigned char) (value >> 8);
+ (void) CopyMagickMemory(p,buffer,2);
+}
+
+MagickExport MagickBooleanType SyncImageProfiles(Image *image)
+{
+#define MaxDirectoryStack 16
+#define EXIF_DELIMITER "\n"
+#define EXIF_NUM_FORMATS 12
+#define TAG_EXIF_OFFSET 0x8769
+#define TAG_INTEROP_OFFSET 0xa005
+
+ typedef struct _DirectoryInfo
+ {
+ unsigned char
+ *directory;
+
+ unsigned long
+ entry;
+ } DirectoryInfo;
+
+ DirectoryInfo
+ directory_stack[MaxDirectoryStack];
+
+ EndianType
+ endian;
+
+ long
+ id,
+ level;
+
+ size_t
+ length;
+
+ ssize_t
+ offset;
+
+ static int
+ format_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
+
+ StringInfo
+ *profile;
+
+ unsigned char
+ *directory,
+ *exif;
+
+ unsigned long
+ entry,
+ number_entries;
+
+ /*
+ Set EXIF resolution tag.
+ */
+ profile=(StringInfo *) GetImageProfile(image,"EXIF");
+ if (profile == (StringInfo *) NULL)
+ return(MagickTrue);
+ length=GetStringInfoLength(profile);
+ exif=GetStringInfoDatum(profile);
+ while (length != 0)
+ {
+ if (ReadProfileByte(&exif,&length) != 0x45)
+ continue;
+ if (ReadProfileByte(&exif,&length) != 0x78)
+ continue;
+ if (ReadProfileByte(&exif,&length) != 0x69)
+ continue;
+ if (ReadProfileByte(&exif,&length) != 0x66)
+ continue;
+ if (ReadProfileByte(&exif,&length) != 0x00)
+ continue;
+ if (ReadProfileByte(&exif,&length) != 0x00)
+ continue;
+ break;
+ }
+ if (length < 16)
+ return(MagickFalse);
+ id=(int) ReadProfileShort(LSBEndian,exif);
+ endian=LSBEndian;
+ if (id == 0x4949)
+ endian=LSBEndian;
+ else
+ if (id == 0x4D4D)
+ endian=MSBEndian;
+ else
+ return(MagickFalse);
+ if (ReadProfileShort(endian,exif+2) != 0x002a)
+ return(MagickFalse);
+ /*
+ This the offset to the first IFD.
+ */
+ offset=(ssize_t) ReadProfileLong(endian,exif+4);
+ if ((size_t) offset >= length)
+ return(MagickFalse);
+ directory=exif+offset;
+ level=0;
+ entry=0;
+ do
+ {
+ if (level > 0)
+ {
+ level--;
+ directory=directory_stack[level].directory;
+ entry=directory_stack[level].entry;
+ }
+ /*
+ Determine how many entries there are in the current IFD.
+ */
+ number_entries=ReadProfileShort(endian,directory);
+ for ( ; entry < number_entries; entry++)
+ {
+ long
+ components,
+ format,
+ tag_value;
+
+ register unsigned char
+ *p,
+ *q;
+
+ size_t
+ number_bytes;
+
+ q=(unsigned char *) (directory+2+(12*entry));
+ tag_value=(long) ReadProfileShort(endian,q);
+ format=(long) ReadProfileShort(endian,q+2);
+ if ((format-1) >= EXIF_NUM_FORMATS)
+ break;
+ components=(long) ReadProfileLong(endian,q+4);
+ number_bytes=(size_t) components*format_bytes[format];
+ if (number_bytes <= 4)
+ p=q+8;
+ else
+ {
+ ssize_t
+ offset;
+
+ /*
+ The directory entry contains an offset.
+ */
+ offset=(ssize_t) ReadProfileLong(endian,q+8);
+ if ((size_t) (offset+number_bytes) > length)
+ continue;
+ p=(unsigned char *) (exif+offset);
+ }
+ switch (tag_value)
+ {
+ case 0x011a:
+ {
+ (void) WriteProfileLong(endian,(unsigned long)
+ (image->x_resolution+0.5),p);
+ (void) WriteProfileLong(endian,1UL,p+4);
+ break;
+ }
+ case 0x011b:
+ {
+ (void) WriteProfileLong(endian,(unsigned long)
+ (image->y_resolution+0.5),p);
+ (void) WriteProfileLong(endian,1UL,p+4);
+ break;
+ }
+ case 0x0112:
+ {
+ (void) WriteProfileShort(endian,(unsigned short)
+ image->orientation,p);
+ (void) WriteProfileLong(endian,1UL,p+4);
+ break;
+ }
+ case 0x0128:
+ {
+ (void) WriteProfileShort(endian,(unsigned short)
+ (image->units+1),p);
+ break;
+ }
+ default:
+ break;
+ }
+ if ((tag_value == TAG_EXIF_OFFSET) || (tag_value == TAG_INTEROP_OFFSET))
+ {
+ size_t
+ offset;
+
+ offset=(size_t) ReadProfileLong(endian,p);
+ if ((offset < length) && (level < (MaxDirectoryStack-2)))
+ {
+ directory_stack[level].directory=directory;
+ entry++;
+ directory_stack[level].entry=entry;
+ level++;
+ directory_stack[level].directory=exif+offset;
+ directory_stack[level].entry=0;
+ level++;
+ if ((directory+2+(12*number_entries)) > (exif+length))
+ break;
+ offset=(size_t) ReadProfileLong(endian,directory+2+(12*
+ number_entries));
+ if ((offset != 0) && (offset < length) &&
+ (level < (MaxDirectoryStack-2)))
+ {
+ directory_stack[level].directory=exif+offset;
+ directory_stack[level].entry=0;
+ level++;
+ }
+ }
+ break;
+ }
+ }
+ } while (level > 0);
+ return(MagickTrue);
+}
diff --git a/magick/profile.h b/magick/profile.h
new file mode 100644
index 0000000..c18c95f
--- /dev/null
+++ b/magick/profile.h
@@ -0,0 +1,75 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image profile methods.
+*/
+#ifndef _MAGICKCORE_PROFILE_H
+#define _MAGICKCORE_PROFILE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/string_.h"
+
+typedef struct _ProfileInfo
+{
+ char
+ *name;
+
+ size_t
+ length;
+
+ unsigned char
+ *info;
+
+ unsigned long
+ signature;
+} ProfileInfo;
+
+typedef enum
+{
+ UndefinedIntent,
+ SaturationIntent,
+ PerceptualIntent,
+ AbsoluteIntent,
+ RelativeIntent
+} RenderingIntent;
+
+extern MagickExport char
+ *GetNextImageProfile(const Image *);
+
+extern MagickExport const StringInfo
+ *GetImageProfile(const Image *,const char *);
+
+extern MagickExport MagickBooleanType
+ CloneImageProfiles(Image *,const Image *),
+ DeleteImageProfile(Image *,const char *),
+ ProfileImage(Image *,const char *,const void *,const size_t,
+ const MagickBooleanType),
+ SetImageProfile(Image *,const char *,const StringInfo *),
+ SyncImageProfiles(Image *);
+
+extern MagickExport StringInfo
+ *RemoveImageProfile(Image *,const char *);
+
+extern MagickExport void
+ DestroyImageProfiles(Image *),
+ ResetImageProfileIterator(const Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+#endif
diff --git a/magick/property.c b/magick/property.c
new file mode 100644
index 0000000..155b4ae
--- /dev/null
+++ b/magick/property.c
@@ -0,0 +1,3462 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% PPPP RRRR OOO PPPP EEEEE RRRR TTTTT Y Y %
+% P P R R O O P P E R R T Y Y %
+% PPPP RRRR O O PPPP EEE RRRR T Y %
+% P R R O O P E R R T Y %
+% P R R OOO P EEEEE R R T Y %
+% %
+% %
+% MagickCore Property Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/compare.h"
+#include "magick/constitute.h"
+#include "magick/draw.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/fx.h"
+#include "magick/fx-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/montage.h"
+#include "magick/option.h"
+#include "magick/profile.h"
+#include "magick/property.h"
+#include "magick/quantum.h"
+#include "magick/resource_.h"
+#include "magick/splay-tree.h"
+#include "magick/signature-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e I m a g e P r o p e r t i e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneImageProperties() clones one or more image properties.
+%
+% The format of the CloneImageProperties method is:
+%
+% MagickBooleanType CloneImageProperties(Image *image,
+% const Image *clone_image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o clone_image: the clone image.
+%
+*/
+MagickExport MagickBooleanType CloneImageProperties(Image *image,
+ const Image *clone_image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(clone_image != (const Image *) NULL);
+ assert(clone_image->signature == MagickSignature);
+ if (clone_image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ clone_image->filename);
+ (void) CopyMagickString(image->filename,clone_image->filename,MaxTextExtent);
+ (void) CopyMagickString(image->magick_filename,clone_image->magick_filename,
+ MaxTextExtent);
+ image->compression=clone_image->compression;
+ image->quality=clone_image->quality;
+ image->depth=clone_image->depth;
+ image->background_color=clone_image->background_color;
+ image->border_color=clone_image->border_color;
+ image->matte_color=clone_image->matte_color;
+ image->transparent_color=clone_image->transparent_color;
+ image->gamma=clone_image->gamma;
+ image->chromaticity=clone_image->chromaticity;
+ image->rendering_intent=clone_image->rendering_intent;
+ image->black_point_compensation=clone_image->black_point_compensation;
+ image->units=clone_image->units;
+ image->montage=(char *) NULL;
+ image->directory=(char *) NULL;
+ (void) CloneString(&image->geometry,clone_image->geometry);
+ image->offset=clone_image->offset;
+ image->x_resolution=clone_image->x_resolution;
+ image->y_resolution=clone_image->y_resolution;
+ image->page=clone_image->page;
+ image->tile_offset=clone_image->tile_offset;
+ image->extract_info=clone_image->extract_info;
+ image->bias=clone_image->bias;
+ image->filter=clone_image->filter;
+ image->blur=clone_image->blur;
+ image->fuzz=clone_image->fuzz;
+ image->interlace=clone_image->interlace;
+ image->interpolate=clone_image->interpolate;
+ image->endian=clone_image->endian;
+ image->gravity=clone_image->gravity;
+ image->compose=clone_image->compose;
+ image->scene=clone_image->scene;
+ image->orientation=clone_image->orientation;
+ image->dispose=clone_image->dispose;
+ image->delay=clone_image->delay;
+ image->ticks_per_second=clone_image->ticks_per_second;
+ image->iterations=clone_image->iterations;
+ image->total_colors=clone_image->total_colors;
+ image->taint=clone_image->taint;
+ image->progress_monitor=clone_image->progress_monitor;
+ image->client_data=clone_image->client_data;
+ image->start_loop=clone_image->start_loop;
+ image->error=clone_image->error;
+ image->signature=clone_image->signature;
+ if (clone_image->properties != (void *) NULL)
+ {
+ if (image->properties != (void *) NULL)
+ DestroyImageProperties(image);
+ image->properties=CloneSplayTree((SplayTreeInfo *)
+ clone_image->properties,(void *(*)(void *)) ConstantString,
+ (void *(*)(void *)) ConstantString);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e f i n e I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageProperty() associates a key/value pair with an image property.
+%
+% The format of the DefineImageProperty method is:
+%
+% MagickBooleanType DefineImageProperty(Image *image,
+% const char *property)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o property: the image property.
+%
+*/
+MagickExport MagickBooleanType DefineImageProperty(Image *image,
+ const char *property)
+{
+ char
+ key[MaxTextExtent],
+ value[MaxTextExtent];
+
+ register char
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(property != (const char *) NULL);
+ (void) CopyMagickString(key,property,MaxTextExtent-1);
+ for (p=key; *p != '\0'; p++)
+ if (*p == '=')
+ break;
+ *value='\0';
+ if (*p == '=')
+ (void) CopyMagickString(value,p+1,MaxTextExtent);
+ *p='\0';
+ return(SetImageProperty(image,key,value));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageProperty() deletes an image property.
+%
+% The format of the DeleteImageProperty method is:
+%
+% MagickBooleanType DeleteImageProperty(Image *image,const char *property)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o property: the image property.
+%
+*/
+MagickExport MagickBooleanType DeleteImageProperty(Image *image,
+ const char *property)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties == (void *) NULL)
+ return(MagickFalse);
+ return(DeleteNodeFromSplayTree((SplayTreeInfo *) image->properties,property));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e P r o p e r t i e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageProperties() releases memory associated with image property
+% values.
+%
+% The format of the DestroyDefines method is:
+%
+% void DestroyImageProperties(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void DestroyImageProperties(Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties != (void *) NULL)
+ image->properties=(void *) DestroySplayTree((SplayTreeInfo *)
+ image->properties);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatImageProperty() permits formatted property/value pairs to be saved as
+% an image proporty.
+%
+% The format of the FormatImageProperty method is:
+%
+% MagickBooleanType FormatImageProperty(Image *image,const char *property,
+% const char *format,...)
+%
+% A description of each parameter follows.
+%
+% o image: The image.
+%
+% o property: The attribute property.
+%
+% o format: A string describing the format to use to write the remaining
+% arguments.
+%
+*/
+
+MagickExport MagickBooleanType FormatImagePropertyList(Image *image,
+ const char *property,const char *format,va_list operands)
+{
+ char
+ value[MaxTextExtent];
+
+ int
+ n;
+
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(value,MaxTextExtent,format,operands);
+#else
+ n=vsprintf(value,format,operands);
+#endif
+ if (n < 0)
+ value[MaxTextExtent-1]='\0';
+ return(SetImageProperty(image,property,value));
+}
+
+MagickExport MagickBooleanType FormatImageProperty(Image *image,
+ const char *property,const char *format,...)
+{
+ MagickBooleanType
+ status;
+
+ va_list
+ operands;
+
+ va_start(operands,format);
+ status=FormatImagePropertyList(image,property,format,operands);
+ va_end(operands);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageProperty() gets a value associated with an image property.
+%
+% The format of the GetImageProperty method is:
+%
+% const char *GetImageProperty(const Image *image,const char *key)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o key: the key.
+%
+*/
+
+static char
+ *TracePSClippath(const unsigned char *,size_t,const unsigned long,
+ const unsigned long),
+ *TraceSVGClippath(const unsigned char *,size_t,const unsigned long,
+ const unsigned long);
+
+static MagickBooleanType GetIPTCProperty(const Image *image,const char *key)
+{
+ char
+ *attribute,
+ *message;
+
+ const StringInfo
+ *profile;
+
+ long
+ count,
+ dataset,
+ record;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ profile=GetImageProfile(image,"iptc");
+ if (profile == (StringInfo *) NULL)
+ profile=GetImageProfile(image,"8bim");
+ if (profile == (StringInfo *) NULL)
+ return(MagickFalse);
+ count=sscanf(key,"IPTC:%ld:%ld",&dataset,&record);
+ if (count != 2)
+ return(MagickFalse);
+ attribute=(char *) NULL;
+ for (i=0; i < (long) GetStringInfoLength(profile); i+=(long) length)
+ {
+ length=1;
+ if ((long) GetStringInfoDatum(profile)[i] != 0x1c)
+ continue;
+ length=(size_t) (GetStringInfoDatum(profile)[i+3] << 8);
+ length|=GetStringInfoDatum(profile)[i+4];
+ if (((long) GetStringInfoDatum(profile)[i+1] == dataset) &&
+ ((long) GetStringInfoDatum(profile)[i+2] == record))
+ {
+ message=(char *) NULL;
+ if (~length >= 1)
+ message=(char *) AcquireQuantumMemory(length+1UL,sizeof(*message));
+ if (message != (char *) NULL)
+ {
+ (void) CopyMagickString(message,(char *) GetStringInfoDatum(
+ profile)+i+5,length+1);
+ (void) ConcatenateString(&attribute,message);
+ (void) ConcatenateString(&attribute,";");
+ message=DestroyString(message);
+ }
+ }
+ i+=5;
+ }
+ if ((attribute == (char *) NULL) || (*attribute == ';'))
+ {
+ if (attribute != (char *) NULL)
+ attribute=DestroyString(attribute);
+ return(MagickFalse);
+ }
+ attribute[strlen(attribute)-1]='\0';
+ (void) SetImageProperty((Image *) image,key,(const char *) attribute);
+ attribute=DestroyString(attribute);
+ return(MagickTrue);
+}
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline int ReadPropertyByte(const unsigned char **p,size_t *length)
+{
+ int
+ c;
+
+ if (*length < 1)
+ return(EOF);
+ c=(int) (*(*p)++);
+ (*length)--;
+ return(c);
+}
+
+static inline unsigned long ReadPropertyMSBLong(const unsigned char **p,
+ size_t *length)
+{
+ int
+ c;
+
+ register long
+ i;
+
+ unsigned char
+ buffer[4];
+
+ unsigned long
+ value;
+
+ if (*length < 4)
+ return(~0UL);
+ for (i=0; i < 4; i++)
+ {
+ c=(int) (*(*p)++);
+ (*length)--;
+ buffer[i]=(unsigned char) c;
+ }
+ value=(unsigned long) (buffer[0] << 24);
+ value|=buffer[1] << 16;
+ value|=buffer[2] << 8;
+ value|=buffer[3];
+ return(value & 0xffffffff);
+}
+
+static inline unsigned short ReadPropertyMSBShort(const unsigned char **p,
+ size_t *length)
+{
+ int
+ c;
+
+ register long
+ i;
+
+ unsigned char
+ buffer[2];
+
+ unsigned short
+ value;
+
+ if (*length < 2)
+ return((unsigned short) ~0U);
+ for (i=0; i < 2; i++)
+ {
+ c=(int) (*(*p)++);
+ (*length)--;
+ buffer[i]=(unsigned char) c;
+ }
+ value=(unsigned short) (buffer[0] << 8);
+ value|=buffer[1];
+ return((unsigned short) (value & 0xffff));
+}
+
+static MagickBooleanType Get8BIMProperty(const Image *image,const char *key)
+{
+ char
+ *attribute,
+ format[MaxTextExtent],
+ name[MaxTextExtent],
+ *resource;
+
+ const StringInfo
+ *profile;
+
+ const unsigned char
+ *info;
+
+ long
+ id,
+ start,
+ stop,
+ sub_number;
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ ssize_t
+ count;
+
+ size_t
+ length;
+
+ /*
+ There's no newlines in path names, so it's safe as terminator.
+ */
+ profile=GetImageProfile(image,"8bim");
+ if (profile == (StringInfo *) NULL)
+ return(MagickFalse);
+ count=(ssize_t) sscanf(key,"8BIM:%ld,%ld:%[^\n]\n%[^\n]",&start,&stop,name,
+ format);
+ if ((count != 2) && (count != 3) && (count != 4))
+ return(MagickFalse);
+ if (count < 4)
+ (void) CopyMagickString(format,"SVG",MaxTextExtent);
+ if (count < 3)
+ *name='\0';
+ sub_number=1;
+ if (*name == '#')
+ sub_number=atol(&name[1]);
+ sub_number=MagickMax(sub_number,1L);
+ resource=(char *) NULL;
+ status=MagickFalse;
+ length=GetStringInfoLength(profile);
+ info=GetStringInfoDatum(profile);
+ while ((length > 0) && (status == MagickFalse))
+ {
+ if (ReadPropertyByte(&info,&length) != (unsigned char) '8')
+ continue;
+ if (ReadPropertyByte(&info,&length) != (unsigned char) 'B')
+ continue;
+ if (ReadPropertyByte(&info,&length) != (unsigned char) 'I')
+ continue;
+ if (ReadPropertyByte(&info,&length) != (unsigned char) 'M')
+ continue;
+ id=(long) ReadPropertyMSBShort(&info,&length);
+ if (id < start)
+ continue;
+ if (id > stop)
+ continue;
+ if (resource != (char *) NULL)
+ resource=DestroyString(resource);
+ count=(ssize_t) ReadPropertyByte(&info,&length);
+ if ((count != 0) && ((size_t) count <= length))
+ {
+ resource=(char *) NULL;
+ if (~(1UL*count) >= MaxTextExtent)
+ resource=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
+ sizeof(*resource));
+ if (resource != (char *) NULL)
+ {
+ for (i=0; i < (long) count; i++)
+ resource[i]=(char) ReadPropertyByte(&info,&length);
+ resource[count]='\0';
+ }
+ }
+ if ((count & 0x01) == 0)
+ (void) ReadPropertyByte(&info,&length);
+ count=(ssize_t) ReadPropertyMSBLong(&info,&length);
+ if ((*name != '\0') && (*name != '#'))
+ if ((resource == (char *) NULL) || (LocaleCompare(name,resource) != 0))
+ {
+ /*
+ No name match, scroll forward and try next.
+ */
+ info+=count;
+ length-=count;
+ continue;
+ }
+ if ((*name == '#') && (sub_number != 1))
+ {
+ /*
+ No numbered match, scroll forward and try next.
+ */
+ sub_number--;
+ info+=count;
+ length-=count;
+ continue;
+ }
+ /*
+ We have the resource of interest.
+ */
+ attribute=(char *) NULL;
+ if (~(1UL*count) >= MaxTextExtent)
+ attribute=(char *) AcquireQuantumMemory((size_t) count+MaxTextExtent,
+ sizeof(*attribute));
+ if (attribute != (char *) NULL)
+ {
+ (void) CopyMagickMemory(attribute,(char *) info,(size_t) count);
+ attribute[count]='\0';
+ info+=count;
+ length-=count;
+ if ((id <= 1999) || (id >= 2999))
+ (void) SetImageProperty((Image *) image,key,(const char *)
+ attribute);
+ else
+ {
+ char
+ *path;
+
+ if (LocaleCompare(format,"svg") == 0)
+ path=TraceSVGClippath((unsigned char *) attribute,(size_t) count,
+ image->columns,image->rows);
+ else
+ path=TracePSClippath((unsigned char *) attribute,(size_t) count,
+ image->columns,image->rows);
+ (void) SetImageProperty((Image *) image,key,(const char *) path);
+ path=DestroyString(path);
+ }
+ attribute=DestroyString(attribute);
+ status=MagickTrue;
+ }
+ }
+ if (resource != (char *) NULL)
+ resource=DestroyString(resource);
+ return(status);
+}
+
+static inline unsigned short ReadPropertyShort(const EndianType endian,
+ const unsigned char *buffer)
+{
+ unsigned short
+ value;
+
+ if (endian == MSBEndian)
+ {
+ value=(unsigned short) ((((unsigned char *) buffer)[0] << 8) |
+ ((unsigned char *) buffer)[1]);
+ return((unsigned short) (value & 0xffff));
+ }
+ value=(unsigned short) ((buffer[1] << 8) | buffer[0]);
+ return((unsigned short) (value & 0xffff));
+}
+
+static inline unsigned long ReadPropertyLong(const EndianType endian,
+ const unsigned char *buffer)
+{
+ unsigned long
+ value;
+
+ if (endian == MSBEndian)
+ {
+ value=(unsigned long) ((buffer[0] << 24) | (buffer[1] << 16) |
+ (buffer[2] << 8) | buffer[3]);
+ return((unsigned long) (value & 0xffffffff));
+ }
+ value=(unsigned long) ((buffer[3] << 24) | (buffer[2] << 16) |
+ (buffer[1] << 8 ) | (buffer[0]));
+ return((unsigned long) (value & 0xffffffff));
+}
+
+static MagickBooleanType GetEXIFProperty(const Image *image,
+ const char *property)
+{
+#define MaxDirectoryStack 16
+#define EXIF_DELIMITER "\n"
+#define EXIF_NUM_FORMATS 12
+#define EXIF_FMT_BYTE 1
+#define EXIF_FMT_STRING 2
+#define EXIF_FMT_USHORT 3
+#define EXIF_FMT_ULONG 4
+#define EXIF_FMT_URATIONAL 5
+#define EXIF_FMT_SBYTE 6
+#define EXIF_FMT_UNDEFINED 7
+#define EXIF_FMT_SSHORT 8
+#define EXIF_FMT_SLONG 9
+#define EXIF_FMT_SRATIONAL 10
+#define EXIF_FMT_SINGLE 11
+#define EXIF_FMT_DOUBLE 12
+#define TAG_EXIF_OFFSET 0x8769
+#define TAG_GPS_OFFSET 0x8825
+#define TAG_INTEROP_OFFSET 0xa005
+
+#define EXIFMultipleValues(size, format, arg) \
+{ \
+ long \
+ component; \
+ \
+ size_t \
+ length; \
+ \
+ unsigned char \
+ *p1; \
+ \
+ length=0; \
+ p1=p; \
+ for (component=0; component < components; component++) \
+ { \
+ length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
+ format", ",arg); \
+ if (length >= MaxTextExtent - 1) \
+ length=MaxTextExtent-1; \
+ p1+=size; \
+ } \
+ if (length > 1) \
+ buffer[length-2]='\0'; \
+ value=AcquireString(buffer); \
+}
+
+#define EXIFMultipleFractions(size, format, arg1, arg2) \
+{ \
+ long \
+ component; \
+ \
+ size_t \
+ length; \
+ \
+ unsigned char \
+ *p1; \
+ \
+ length=0; \
+ p1=p; \
+ for (component=0; component < components; component++) \
+ { \
+ length+=FormatMagickString(buffer+length,MaxTextExtent-length, \
+ format", ",arg1, arg2); \
+ if (length >= MaxTextExtent - 1) \
+ length=MaxTextExtent-1; \
+ p1+=size; \
+ } \
+ if (length > 1) \
+ buffer[length-2]='\0'; \
+ value=AcquireString(buffer); \
+}
+
+ typedef struct _DirectoryInfo
+ {
+ const unsigned char
+ *directory;
+
+ unsigned long
+ entry,
+ offset;
+ } DirectoryInfo;
+
+ typedef struct _TagInfo
+ {
+ unsigned long
+ tag;
+
+ const char
+ *description;
+ } TagInfo;
+
+ static TagInfo
+ EXIFTag[] =
+ {
+ { 0x001, "exif:InteroperabilityIndex" },
+ { 0x002, "exif:InteroperabilityVersion" },
+ { 0x100, "exif:ImageWidth" },
+ { 0x101, "exif:ImageLength" },
+ { 0x102, "exif:BitsPerSample" },
+ { 0x103, "exif:Compression" },
+ { 0x106, "exif:PhotometricInterpretation" },
+ { 0x10a, "exif:FillOrder" },
+ { 0x10d, "exif:DocumentName" },
+ { 0x10e, "exif:ImageDescription" },
+ { 0x10f, "exif:Make" },
+ { 0x110, "exif:Model" },
+ { 0x111, "exif:StripOffsets" },
+ { 0x112, "exif:Orientation" },
+ { 0x115, "exif:SamplesPerPixel" },
+ { 0x116, "exif:RowsPerStrip" },
+ { 0x117, "exif:StripByteCounts" },
+ { 0x11a, "exif:XResolution" },
+ { 0x11b, "exif:YResolution" },
+ { 0x11c, "exif:PlanarConfiguration" },
+ { 0x11d, "exif:PageName" },
+ { 0x11e, "exif:XPosition" },
+ { 0x11f, "exif:YPosition" },
+ { 0x118, "exif:MinSampleValue" },
+ { 0x119, "exif:MaxSampleValue" },
+ { 0x120, "exif:FreeOffsets" },
+ { 0x121, "exif:FreeByteCounts" },
+ { 0x122, "exif:GrayResponseUnit" },
+ { 0x123, "exif:GrayResponseCurve" },
+ { 0x124, "exif:T4Options" },
+ { 0x125, "exif:T6Options" },
+ { 0x128, "exif:ResolutionUnit" },
+ { 0x12d, "exif:TransferFunction" },
+ { 0x131, "exif:Software" },
+ { 0x132, "exif:DateTime" },
+ { 0x13b, "exif:Artist" },
+ { 0x13e, "exif:WhitePoint" },
+ { 0x13f, "exif:PrimaryChromaticities" },
+ { 0x140, "exif:ColorMap" },
+ { 0x141, "exif:HalfToneHints" },
+ { 0x142, "exif:TileWidth" },
+ { 0x143, "exif:TileLength" },
+ { 0x144, "exif:TileOffsets" },
+ { 0x145, "exif:TileByteCounts" },
+ { 0x14a, "exif:SubIFD" },
+ { 0x14c, "exif:InkSet" },
+ { 0x14d, "exif:InkNames" },
+ { 0x14e, "exif:NumberOfInks" },
+ { 0x150, "exif:DotRange" },
+ { 0x151, "exif:TargetPrinter" },
+ { 0x152, "exif:ExtraSample" },
+ { 0x153, "exif:SampleFormat" },
+ { 0x154, "exif:SMinSampleValue" },
+ { 0x155, "exif:SMaxSampleValue" },
+ { 0x156, "exif:TransferRange" },
+ { 0x157, "exif:ClipPath" },
+ { 0x158, "exif:XClipPathUnits" },
+ { 0x159, "exif:YClipPathUnits" },
+ { 0x15a, "exif:Indexed" },
+ { 0x15b, "exif:JPEGTables" },
+ { 0x15f, "exif:OPIProxy" },
+ { 0x200, "exif:JPEGProc" },
+ { 0x201, "exif:JPEGInterchangeFormat" },
+ { 0x202, "exif:JPEGInterchangeFormatLength" },
+ { 0x203, "exif:JPEGRestartInterval" },
+ { 0x205, "exif:JPEGLosslessPredictors" },
+ { 0x206, "exif:JPEGPointTransforms" },
+ { 0x207, "exif:JPEGQTables" },
+ { 0x208, "exif:JPEGDCTables" },
+ { 0x209, "exif:JPEGACTables" },
+ { 0x211, "exif:YCbCrCoefficients" },
+ { 0x212, "exif:YCbCrSubSampling" },
+ { 0x213, "exif:YCbCrPositioning" },
+ { 0x214, "exif:ReferenceBlackWhite" },
+ { 0x2bc, "exif:ExtensibleMetadataPlatform" },
+ { 0x301, "exif:Gamma" },
+ { 0x302, "exif:ICCProfileDescriptor" },
+ { 0x303, "exif:SRGBRenderingIntent" },
+ { 0x320, "exif:ImageTitle" },
+ { 0x5001, "exif:ResolutionXUnit" },
+ { 0x5002, "exif:ResolutionYUnit" },
+ { 0x5003, "exif:ResolutionXLengthUnit" },
+ { 0x5004, "exif:ResolutionYLengthUnit" },
+ { 0x5005, "exif:PrintFlags" },
+ { 0x5006, "exif:PrintFlagsVersion" },
+ { 0x5007, "exif:PrintFlagsCrop" },
+ { 0x5008, "exif:PrintFlagsBleedWidth" },
+ { 0x5009, "exif:PrintFlagsBleedWidthScale" },
+ { 0x500A, "exif:HalftoneLPI" },
+ { 0x500B, "exif:HalftoneLPIUnit" },
+ { 0x500C, "exif:HalftoneDegree" },
+ { 0x500D, "exif:HalftoneShape" },
+ { 0x500E, "exif:HalftoneMisc" },
+ { 0x500F, "exif:HalftoneScreen" },
+ { 0x5010, "exif:JPEGQuality" },
+ { 0x5011, "exif:GridSize" },
+ { 0x5012, "exif:ThumbnailFormat" },
+ { 0x5013, "exif:ThumbnailWidth" },
+ { 0x5014, "exif:ThumbnailHeight" },
+ { 0x5015, "exif:ThumbnailColorDepth" },
+ { 0x5016, "exif:ThumbnailPlanes" },
+ { 0x5017, "exif:ThumbnailRawBytes" },
+ { 0x5018, "exif:ThumbnailSize" },
+ { 0x5019, "exif:ThumbnailCompressedSize" },
+ { 0x501a, "exif:ColorTransferFunction" },
+ { 0x501b, "exif:ThumbnailData" },
+ { 0x5020, "exif:ThumbnailImageWidth" },
+ { 0x5021, "exif:ThumbnailImageHeight" },
+ { 0x5022, "exif:ThumbnailBitsPerSample" },
+ { 0x5023, "exif:ThumbnailCompression" },
+ { 0x5024, "exif:ThumbnailPhotometricInterp" },
+ { 0x5025, "exif:ThumbnailImageDescription" },
+ { 0x5026, "exif:ThumbnailEquipMake" },
+ { 0x5027, "exif:ThumbnailEquipModel" },
+ { 0x5028, "exif:ThumbnailStripOffsets" },
+ { 0x5029, "exif:ThumbnailOrientation" },
+ { 0x502a, "exif:ThumbnailSamplesPerPixel" },
+ { 0x502b, "exif:ThumbnailRowsPerStrip" },
+ { 0x502c, "exif:ThumbnailStripBytesCount" },
+ { 0x502d, "exif:ThumbnailResolutionX" },
+ { 0x502e, "exif:ThumbnailResolutionY" },
+ { 0x502f, "exif:ThumbnailPlanarConfig" },
+ { 0x5030, "exif:ThumbnailResolutionUnit" },
+ { 0x5031, "exif:ThumbnailTransferFunction" },
+ { 0x5032, "exif:ThumbnailSoftwareUsed" },
+ { 0x5033, "exif:ThumbnailDateTime" },
+ { 0x5034, "exif:ThumbnailArtist" },
+ { 0x5035, "exif:ThumbnailWhitePoint" },
+ { 0x5036, "exif:ThumbnailPrimaryChromaticities" },
+ { 0x5037, "exif:ThumbnailYCbCrCoefficients" },
+ { 0x5038, "exif:ThumbnailYCbCrSubsampling" },
+ { 0x5039, "exif:ThumbnailYCbCrPositioning" },
+ { 0x503A, "exif:ThumbnailRefBlackWhite" },
+ { 0x503B, "exif:ThumbnailCopyRight" },
+ { 0x5090, "exif:LuminanceTable" },
+ { 0x5091, "exif:ChrominanceTable" },
+ { 0x5100, "exif:FrameDelay" },
+ { 0x5101, "exif:LoopCount" },
+ { 0x5110, "exif:PixelUnit" },
+ { 0x5111, "exif:PixelPerUnitX" },
+ { 0x5112, "exif:PixelPerUnitY" },
+ { 0x5113, "exif:PaletteHistogram" },
+ { 0x1000, "exif:RelatedImageFileFormat" },
+ { 0x1001, "exif:RelatedImageLength" },
+ { 0x1002, "exif:RelatedImageWidth" },
+ { 0x800d, "exif:ImageID" },
+ { 0x80e3, "exif:Matteing" },
+ { 0x80e4, "exif:DataType" },
+ { 0x80e5, "exif:ImageDepth" },
+ { 0x80e6, "exif:TileDepth" },
+ { 0x828d, "exif:CFARepeatPatternDim" },
+ { 0x828e, "exif:CFAPattern2" },
+ { 0x828f, "exif:BatteryLevel" },
+ { 0x8298, "exif:Copyright" },
+ { 0x829a, "exif:ExposureTime" },
+ { 0x829d, "exif:FNumber" },
+ { 0x83bb, "exif:IPTC/NAA" },
+ { 0x84e3, "exif:IT8RasterPadding" },
+ { 0x84e5, "exif:IT8ColorTable" },
+ { 0x8649, "exif:ImageResourceInformation" },
+ { 0x8769, "exif:ExifOffset" },
+ { 0x8773, "exif:InterColorProfile" },
+ { 0x8822, "exif:ExposureProgram" },
+ { 0x8824, "exif:SpectralSensitivity" },
+ { 0x8825, "exif:GPSInfo" },
+ { 0x8827, "exif:ISOSpeedRatings" },
+ { 0x8828, "exif:OECF" },
+ { 0x8829, "exif:Interlace" },
+ { 0x882a, "exif:TimeZoneOffset" },
+ { 0x882b, "exif:SelfTimerMode" },
+ { 0x9000, "exif:ExifVersion" },
+ { 0x9003, "exif:DateTimeOriginal" },
+ { 0x9004, "exif:DateTimeDigitized" },
+ { 0x9101, "exif:ComponentsConfiguration" },
+ { 0x9102, "exif:CompressedBitsPerPixel" },
+ { 0x9201, "exif:ShutterSpeedValue" },
+ { 0x9202, "exif:ApertureValue" },
+ { 0x9203, "exif:BrightnessValue" },
+ { 0x9204, "exif:ExposureBiasValue" },
+ { 0x9205, "exif:MaxApertureValue" },
+ { 0x9206, "exif:SubjectDistance" },
+ { 0x9207, "exif:MeteringMode" },
+ { 0x9208, "exif:LightSource" },
+ { 0x9209, "exif:Flash" },
+ { 0x920a, "exif:FocalLength" },
+ { 0x920b, "exif:FlashEnergy" },
+ { 0x920c, "exif:SpatialFrequencyResponse" },
+ { 0x920d, "exif:Noise" },
+ { 0x9211, "exif:ImageNumber" },
+ { 0x9212, "exif:SecurityClassification" },
+ { 0x9213, "exif:ImageHistory" },
+ { 0x9214, "exif:SubjectArea" },
+ { 0x9215, "exif:ExposureIndex" },
+ { 0x9216, "exif:TIFF-EPStandardID" },
+ { 0x927c, "exif:MakerNote" },
+ { 0x9C9b, "exif:WinXP-Title" },
+ { 0x9C9c, "exif:WinXP-Comments" },
+ { 0x9C9d, "exif:WinXP-Author" },
+ { 0x9C9e, "exif:WinXP-Keywords" },
+ { 0x9C9f, "exif:WinXP-Subject" },
+ { 0x9286, "exif:UserComment" },
+ { 0x9290, "exif:SubSecTime" },
+ { 0x9291, "exif:SubSecTimeOriginal" },
+ { 0x9292, "exif:SubSecTimeDigitized" },
+ { 0xa000, "exif:FlashPixVersion" },
+ { 0xa001, "exif:ColorSpace" },
+ { 0xa002, "exif:ExifImageWidth" },
+ { 0xa003, "exif:ExifImageLength" },
+ { 0xa004, "exif:RelatedSoundFile" },
+ { 0xa005, "exif:InteroperabilityOffset" },
+ { 0xa20b, "exif:FlashEnergy" },
+ { 0xa20c, "exif:SpatialFrequencyResponse" },
+ { 0xa20d, "exif:Noise" },
+ { 0xa20e, "exif:FocalPlaneXResolution" },
+ { 0xa20f, "exif:FocalPlaneYResolution" },
+ { 0xa210, "exif:FocalPlaneResolutionUnit" },
+ { 0xa214, "exif:SubjectLocation" },
+ { 0xa215, "exif:ExposureIndex" },
+ { 0xa216, "exif:TIFF/EPStandardID" },
+ { 0xa217, "exif:SensingMethod" },
+ { 0xa300, "exif:FileSource" },
+ { 0xa301, "exif:SceneType" },
+ { 0xa302, "exif:CFAPattern" },
+ { 0xa401, "exif:CustomRendered" },
+ { 0xa402, "exif:ExposureMode" },
+ { 0xa403, "exif:WhiteBalance" },
+ { 0xa404, "exif:DigitalZoomRatio" },
+ { 0xa405, "exif:FocalLengthIn35mmFilm" },
+ { 0xa406, "exif:SceneCaptureType" },
+ { 0xa407, "exif:GainControl" },
+ { 0xa408, "exif:Contrast" },
+ { 0xa409, "exif:Saturation" },
+ { 0xa40a, "exif:Sharpness" },
+ { 0xa40b, "exif:DeviceSettingDescription" },
+ { 0xa40c, "exif:SubjectDistanceRange" },
+ { 0xa420, "exif:ImageUniqueID" },
+ { 0xc4a5, "exif:PrintImageMatching" },
+ { 0x10000, "exif:GPSVersionID" },
+ { 0x10001, "exif:GPSLatitudeRef" },
+ { 0x10002, "exif:GPSLatitude" },
+ { 0x10003, "exif:GPSLongitudeRef" },
+ { 0x10004, "exif:GPSLongitude" },
+ { 0x10005, "exif:GPSAltitudeRef" },
+ { 0x10006, "exif:GPSAltitude" },
+ { 0x10007, "exif:GPSTimeStamp" },
+ { 0x10008, "exif:GPSSatellites" },
+ { 0x10009, "exif:GPSStatus" },
+ { 0x1000a, "exif:GPSMeasureMode" },
+ { 0x1000b, "exif:GPSDop" },
+ { 0x1000c, "exif:GPSSpeedRef" },
+ { 0x1000d, "exif:GPSSpeed" },
+ { 0x1000e, "exif:GPSTrackRef" },
+ { 0x1000f, "exif:GPSTrack" },
+ { 0x10010, "exif:GPSImgDirectionRef" },
+ { 0x10011, "exif:GPSImgDirection" },
+ { 0x10012, "exif:GPSMapDatum" },
+ { 0x10013, "exif:GPSDestLatitudeRef" },
+ { 0x10014, "exif:GPSDestLatitude" },
+ { 0x10015, "exif:GPSDestLongitudeRef" },
+ { 0x10016, "exif:GPSDestLongitude" },
+ { 0x10017, "exif:GPSDestBearingRef" },
+ { 0x10018, "exif:GPSDestBearing" },
+ { 0x10019, "exif:GPSDestDistanceRef" },
+ { 0x1001a, "exif:GPSDestDistance" },
+ { 0x1001b, "exif:GPSProcessingMethod" },
+ { 0x1001c, "exif:GPSAreaInformation" },
+ { 0x1001d, "exif:GPSDateStamp" },
+ { 0x1001e, "exif:GPSDifferential" },
+ { 0x0000, NULL}
+ };
+
+ const StringInfo
+ *profile;
+
+ const unsigned char
+ *directory,
+ *exif;
+
+ DirectoryInfo
+ directory_stack[MaxDirectoryStack];
+
+ EndianType
+ endian;
+
+ long
+ all,
+ id,
+ level,
+ tag_value;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ ssize_t
+ offset;
+
+ static int
+ tag_bytes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
+
+ unsigned long
+ entry,
+ number_entries,
+ tag_offset,
+ tag;
+
+ /*
+ If EXIF data exists, then try to parse the request for a tag.
+ */
+ profile=GetImageProfile(image,"exif");
+ if (profile == (StringInfo *) NULL)
+ return(MagickFalse);
+ if ((property == (const char *) NULL) || (*property == '\0'))
+ return(MagickFalse);
+ while (isspace((int) ((unsigned char) *property)) != 0)
+ property++;
+ all=0;
+ tag=(~0UL);
+ switch (*(property+5))
+ {
+ case '*':
+ {
+ /*
+ Caller has asked for all the tags in the EXIF data.
+ */
+ tag=0;
+ all=1; /* return the data in description=value format */
+ break;
+ }
+ case '!':
+ {
+ tag=0;
+ all=2; /* return the data in tagid=value format */
+ break;
+ }
+ case '#':
+ case '@':
+ {
+ int
+ c;
+
+ size_t
+ n;
+
+ /*
+ Check for a hex based tag specification first.
+ */
+ tag=(*(property+5) == '@') ? 1UL : 0UL;
+ property+=6;
+ n=strlen(property);
+ if (n != 4)
+ return(MagickFalse);
+ /*
+ Parse tag specification as a hex number.
+ */
+ n/=4;
+ do
+ {
+ for (i=(long) n-1L; i >= 0; i--)
+ {
+ c=(*property++);
+ tag<<=4;
+ if ((c >= '0') && (c <= '9'))
+ tag|=(c-'0');
+ else
+ if ((c >= 'A') && (c <= 'F'))
+ tag|=(c-('A'-10));
+ else
+ if ((c >= 'a') && (c <= 'f'))
+ tag|=(c-('a'-10));
+ else
+ return(MagickFalse);
+ }
+ } while (*property != '\0');
+ break;
+ }
+ default:
+ {
+ /*
+ Try to match the text with a tag name instead.
+ */
+ for (i=0; ; i++)
+ {
+ if (EXIFTag[i].tag == 0)
+ break;
+ if (LocaleCompare(EXIFTag[i].description,property) == 0)
+ {
+ tag=(unsigned long) EXIFTag[i].tag;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if (tag == (~0UL))
+ return(MagickFalse);
+ length=GetStringInfoLength(profile);
+ exif=GetStringInfoDatum(profile);
+ while (length != 0)
+ {
+ if (ReadPropertyByte(&exif,&length) != 0x45)
+ continue;
+ if (ReadPropertyByte(&exif,&length) != 0x78)
+ continue;
+ if (ReadPropertyByte(&exif,&length) != 0x69)
+ continue;
+ if (ReadPropertyByte(&exif,&length) != 0x66)
+ continue;
+ if (ReadPropertyByte(&exif,&length) != 0x00)
+ continue;
+ if (ReadPropertyByte(&exif,&length) != 0x00)
+ continue;
+ break;
+ }
+ if (length < 16)
+ return(MagickFalse);
+ id=(long) ReadPropertyShort(LSBEndian,exif);
+ endian=LSBEndian;
+ if (id == 0x4949)
+ endian=LSBEndian;
+ else
+ if (id == 0x4D4D)
+ endian=MSBEndian;
+ else
+ return(MagickFalse);
+ if (ReadPropertyShort(endian,exif+2) != 0x002a)
+ return(MagickFalse);
+ /*
+ This the offset to the first IFD.
+ */
+ offset=(ssize_t) ReadPropertyLong(endian,exif+4);
+ if ((size_t) offset >= length)
+ return(MagickFalse);
+ /*
+ Set the pointer to the first IFD and follow it were it leads.
+ */
+ directory=exif+offset;
+ level=0;
+ entry=0;
+ tag_offset=0;
+ do
+ {
+ /*
+ If there is anything on the stack then pop it off.
+ */
+ if (level > 0)
+ {
+ level--;
+ directory=directory_stack[level].directory;
+ entry=directory_stack[level].entry;
+ tag_offset=directory_stack[level].offset;
+ }
+ /*
+ Determine how many entries there are in the current IFD.
+ */
+ number_entries=ReadPropertyShort(endian,directory);
+ for ( ; entry < number_entries; entry++)
+ {
+ long
+ components;
+
+ register unsigned char
+ *p,
+ *q;
+
+ size_t
+ number_bytes;
+
+ unsigned long
+ format;
+
+ q=(unsigned char *) (directory+2+(12*entry));
+ tag_value=(long) ReadPropertyShort(endian,q)+tag_offset;
+ format=(unsigned long) ReadPropertyShort(endian,q+2);
+ if (format >= (sizeof(tag_bytes)/sizeof(*tag_bytes)))
+ break;
+ components=(long) ReadPropertyLong(endian,q+4);
+ number_bytes=(size_t) components*tag_bytes[format];
+ if (number_bytes <= 4)
+ p=q+8;
+ else
+ {
+ ssize_t
+ offset;
+
+ /*
+ The directory entry contains an offset.
+ */
+ offset=(ssize_t) ReadPropertyLong(endian,q+8);
+ if ((size_t) (offset+number_bytes) > length)
+ continue;
+ p=(unsigned char *) (exif+offset);
+ }
+ if ((all != 0) || (tag == (unsigned long) tag_value))
+ {
+ char
+ buffer[MaxTextExtent],
+ *value;
+
+ switch (format)
+ {
+ case EXIF_FMT_BYTE:
+ case EXIF_FMT_UNDEFINED:
+ {
+ EXIFMultipleValues(1,"%lu",(unsigned long)
+ (*(unsigned char *) p1));
+ break;
+ }
+ case EXIF_FMT_SBYTE:
+ {
+ EXIFMultipleValues(1,"%ld",(long) (*(signed char *) p1));
+ break;
+ }
+ case EXIF_FMT_SSHORT:
+ {
+ EXIFMultipleValues(2,"%hd",ReadPropertyShort(endian,p1));
+ break;
+ }
+ case EXIF_FMT_USHORT:
+ {
+ EXIFMultipleValues(2,"%hu",ReadPropertyShort(endian,p1));
+ break;
+ }
+ case EXIF_FMT_ULONG:
+ {
+ EXIFMultipleValues(4,"%lu",ReadPropertyLong(endian,p1));
+ break;
+ }
+ case EXIF_FMT_SLONG:
+ {
+ EXIFMultipleValues(4,"%ld",ReadPropertyLong(endian,p1));
+ break;
+ }
+ case EXIF_FMT_URATIONAL:
+ {
+ EXIFMultipleFractions(8,"%ld/%ld",ReadPropertyLong(endian,p1),
+ ReadPropertyLong(endian,p1+4));
+ break;
+ }
+ case EXIF_FMT_SRATIONAL:
+ {
+ EXIFMultipleFractions(8,"%ld/%ld",ReadPropertyLong(endian,p1),
+ ReadPropertyLong(endian,p1+4));
+ break;
+ }
+ case EXIF_FMT_SINGLE:
+ {
+ EXIFMultipleValues(4,"%f",(double) *(float *) p1);
+ break;
+ }
+ case EXIF_FMT_DOUBLE:
+ {
+ EXIFMultipleValues(8,"%f",*(double *) p1);
+ break;
+ }
+ default:
+ case EXIF_FMT_STRING:
+ {
+ value=(char *) NULL;
+ if (~(1UL*number_bytes) >= 1)
+ value=(char *) AcquireQuantumMemory((size_t) number_bytes+1UL,
+ sizeof(*value));
+ if (value != (char *) NULL)
+ {
+ register long
+ i;
+
+ for (i=0; i < (long) number_bytes; i++)
+ {
+ value[i]='.';
+ if ((isprint((int) p[i]) != 0) || (p[i] == '\0'))
+ value[i]=(char) p[i];
+ }
+ value[i]='\0';
+ }
+ break;
+ }
+ }
+ if (value != (char *) NULL)
+ {
+ char
+ key[MaxTextExtent];
+
+ register const char
+ *p;
+
+ (void) CopyMagickString(key,property,MaxTextExtent);
+ switch (all)
+ {
+ case 1:
+ {
+ const char
+ *description;
+
+ register long
+ i;
+
+ description="unknown";
+ for (i=0; ; i++)
+ {
+ if (EXIFTag[i].tag == 0)
+ break;
+ if ((long) EXIFTag[i].tag == tag_value)
+ {
+ description=EXIFTag[i].description;
+ break;
+ }
+ }
+ (void) FormatMagickString(key,MaxTextExtent,"%s",
+ description);
+ break;
+ }
+ case 2:
+ {
+ if (tag_value < 0x10000)
+ (void) FormatMagickString(key,MaxTextExtent,"#%04lx",
+ tag_value);
+ else
+ if (tag_value < 0x20000)
+ (void) FormatMagickString(key,MaxTextExtent,"@%04lx",
+ tag_value & 0xffff);
+ else
+ (void) FormatMagickString(key,MaxTextExtent,"unknown");
+ break;
+ }
+ }
+ p=(const char *) NULL;
+ if (image->properties != (void *) NULL)
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,key);
+ if (p == (const char *) NULL)
+ (void) SetImageProperty((Image *) image,key,value);
+ value=DestroyString(value);
+ }
+ }
+ if ((tag_value == TAG_EXIF_OFFSET) ||
+ (tag_value == TAG_INTEROP_OFFSET) ||
+ (tag_value == TAG_GPS_OFFSET))
+ {
+ size_t
+ offset;
+
+ offset=(size_t) ReadPropertyLong(endian,p);
+ if ((offset < length) && (level < (MaxDirectoryStack-2)))
+ {
+ unsigned long
+ tag_offset1;
+
+ tag_offset1=(tag_value == TAG_GPS_OFFSET) ? 0x10000UL : 0UL;
+ directory_stack[level].directory=directory;
+ entry++;
+ directory_stack[level].entry=entry;
+ directory_stack[level].offset=tag_offset;
+ level++;
+ directory_stack[level].directory=exif+offset;
+ directory_stack[level].offset=tag_offset1;
+ directory_stack[level].entry=0;
+ level++;
+ if ((directory+2+(12*number_entries)) > (exif+length))
+ break;
+ offset=(size_t) ReadPropertyLong(endian,directory+2+(12*
+ number_entries));
+ if ((offset != 0) && (offset < length) &&
+ (level < (MaxDirectoryStack-2)))
+ {
+ directory_stack[level].directory=exif+offset;
+ directory_stack[level].entry=0;
+ directory_stack[level].offset=tag_offset1;
+ level++;
+ }
+ }
+ break;
+ }
+ }
+ } while (level > 0);
+ return(MagickTrue);
+}
+
+static MagickBooleanType GetXMPProperty(const Image *image,
+ const char *property)
+{
+ char
+ *xmp_profile;
+
+ const StringInfo
+ *profile;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ register const char
+ *p;
+
+ XMLTreeInfo
+ *child,
+ *description,
+ *node,
+ *rdf,
+ *xmp;
+
+ profile=GetImageProfile(image,"xmp");
+ if (profile == (StringInfo *) NULL)
+ return(MagickFalse);
+ if ((property == (const char *) NULL) || (*property == '\0'))
+ return(MagickFalse);
+ xmp_profile=StringInfoToString(profile);
+ if (xmp_profile == (char *) NULL)
+ return(MagickFalse);
+ for (p=xmp_profile; *p != '\0'; p++)
+ if ((*p == '<') && (*(p+1) == 'x'))
+ break;
+ exception=AcquireExceptionInfo();
+ xmp=NewXMLTree((char *) p,exception);
+ xmp_profile=DestroyString(xmp_profile);
+ exception=DestroyExceptionInfo(exception);
+ if (xmp == (XMLTreeInfo *) NULL)
+ return(MagickFalse);
+ status=MagickFalse;
+ rdf=GetXMLTreeChild(xmp,"rdf:RDF");
+ if (rdf != (XMLTreeInfo *) NULL)
+ {
+ if (image->properties == (void *) NULL)
+ ((Image *) image)->properties=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,RelinquishMagickMemory);
+ description=GetXMLTreeChild(rdf,"rdf:Description");
+ while (description != (XMLTreeInfo *) NULL)
+ {
+ node=GetXMLTreeChild(description,(const char *) NULL);
+ while (node != (XMLTreeInfo *) NULL)
+ {
+ child=GetXMLTreeChild(node,(const char *) NULL);
+ if (child == (XMLTreeInfo *) NULL)
+ (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(GetXMLTreeTag(node)),
+ ConstantString(GetXMLTreeContent(node)));
+ while (child != (XMLTreeInfo *) NULL)
+ {
+ if (LocaleCompare(GetXMLTreeTag(child),"rdf:Seq") != 0)
+ (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(GetXMLTreeTag(child)),
+ ConstantString(GetXMLTreeContent(child)));
+ child=GetXMLTreeSibling(child);
+ }
+ node=GetXMLTreeSibling(node);
+ }
+ description=GetNextXMLTreeTag(description);
+ }
+ }
+ xmp=DestroyXMLTree(xmp);
+ return(status);
+}
+
+static char *TracePSClippath(const unsigned char *blob,size_t length,
+ const unsigned long magick_unused(columns),
+ const unsigned long magick_unused(rows))
+{
+ char
+ *path,
+ *message;
+
+ long
+ knot_count,
+ selector,
+ y;
+
+ MagickBooleanType
+ in_subpath;
+
+ PointInfo
+ first[3],
+ last[3],
+ point[3];
+
+ register long
+ i,
+ x;
+
+ path=AcquireString((char *) NULL);
+ if (path == (char *) NULL)
+ return((char *) NULL);
+ message=AcquireString((char *) NULL);
+ (void) FormatMagickString(message,MaxTextExtent,"/ClipImage\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,"{\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent," /c {curveto} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent," /l {lineto} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent," /m {moveto} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ " /v {currentpoint 6 2 roll curveto} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ " /y {2 copy curveto} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ " /z {closepath} bind def\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent," newpath\n");
+ (void) ConcatenateString(&path,message);
+ /*
+ The clipping path format is defined in "Adobe Photoshop File
+ Formats Specification" version 6.0 downloadable from adobe.com.
+ */
+ (void) ResetMagickMemory(point,0,sizeof(point));
+ (void) ResetMagickMemory(first,0,sizeof(first));
+ (void) ResetMagickMemory(last,0,sizeof(last));
+ knot_count=0;
+ in_subpath=MagickFalse;
+ while (length > 0)
+ {
+ selector=(long) ReadPropertyMSBShort(&blob,&length);
+ switch (selector)
+ {
+ case 0:
+ case 3:
+ {
+ if (knot_count != 0)
+ {
+ blob+=24;
+ length-=24;
+ break;
+ }
+ /*
+ Expected subpath length record.
+ */
+ knot_count=(long) ReadPropertyMSBShort(&blob,&length);
+ blob+=22;
+ length-=22;
+ break;
+ }
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ {
+ if (knot_count == 0)
+ {
+ /*
+ Unexpected subpath knot
+ */
+ blob+=24;
+ length-=24;
+ break;
+ }
+ /*
+ Add sub-path knot
+ */
+ for (i=0; i < 3; i++)
+ {
+ y=(long) ReadPropertyMSBLong(&blob,&length);
+ x=(long) ReadPropertyMSBLong(&blob,&length);
+ point[i].x=(double) x/4096/4096;
+ point[i].y=1.0-(double) y/4096/4096;
+ }
+ if (in_subpath == MagickFalse)
+ {
+ (void) FormatMagickString(message,MaxTextExtent," %g %g m\n",
+ point[1].x,point[1].y);
+ for (i=0; i < 3; i++)
+ {
+ first[i]=point[i];
+ last[i]=point[i];
+ }
+ }
+ else
+ {
+ /*
+ Handle special cases when Bezier curves are used to describe
+ corners and straight lines.
+ */
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
+ (point[0].x == point[1].x) && (point[0].y == point[1].y))
+ (void) FormatMagickString(message,MaxTextExtent," %g %g l\n",
+ point[1].x,point[1].y);
+ else
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g v\n",point[0].x,point[0].y,point[1].x,
+ point[1].y);
+ else
+ if ((point[0].x == point[1].x) && (point[0].y == point[1].y))
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g y\n",last[2].x,last[2].y,point[1].x,
+ point[1].y);
+ else
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g %g %g c\n",last[2].x,last[2].y,point[0].x,
+ point[0].y,point[1].x,point[1].y);
+ for (i=0; i < 3; i++)
+ last[i]=point[i];
+ }
+ (void) ConcatenateString(&path,message);
+ in_subpath=MagickTrue;
+ knot_count--;
+ /*
+ Close the subpath if there are no more knots.
+ */
+ if (knot_count == 0)
+ {
+ /*
+ Same special handling as above except we compare to the
+ first point in the path and close the path.
+ */
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
+ (first[0].x == first[1].x) && (first[0].y == first[1].y))
+ (void) FormatMagickString(message,MaxTextExtent," %g %g l z\n",
+ first[1].x,first[1].y);
+ else
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y))
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g v z\n",first[0].x,first[0].y,first[1].x,
+ first[1].y);
+ else
+ if ((first[0].x == first[1].x) && (first[0].y == first[1].y))
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g y z\n",last[2].x,last[2].y,first[1].x,
+ first[1].y);
+ else
+ (void) FormatMagickString(message,MaxTextExtent,
+ " %g %g %g %g %g %g c z\n",last[2].x,last[2].y,first[0].x,
+ first[0].y,first[1].x,first[1].y);
+ (void) ConcatenateString(&path,message);
+ in_subpath=MagickFalse;
+ }
+ break;
+ }
+ case 6:
+ case 7:
+ case 8:
+ default:
+ {
+ blob+=24;
+ length-=24;
+ break;
+ }
+ }
+ }
+ /*
+ Returns an empty PS path if the path has no knots.
+ */
+ (void) FormatMagickString(message,MaxTextExtent," eoclip\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,"} bind def");
+ (void) ConcatenateString(&path,message);
+ message=DestroyString(message);
+ return(path);
+}
+
+static char *TraceSVGClippath(const unsigned char *blob,size_t length,
+ const unsigned long columns,const unsigned long rows)
+{
+ char
+ *path,
+ *message;
+
+ long
+ knot_count,
+ selector,
+ x,
+ y;
+
+ MagickBooleanType
+ in_subpath;
+
+ PointInfo
+ first[3],
+ last[3],
+ point[3];
+
+ register long
+ i;
+
+ path=AcquireString((char *) NULL);
+ if (path == (char *) NULL)
+ return((char *) NULL);
+ message=AcquireString((char *) NULL);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "<svg width=\"%lu\" height=\"%lu\">\n",columns,rows);
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,"<g>\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "<path style=\"fill:#00000000;stroke:#00000000;");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,
+ "stroke-width:0;stroke-antialiasing:false\" d=\"\n");
+ (void) ConcatenateString(&path,message);
+ (void) ResetMagickMemory(point,0,sizeof(point));
+ (void) ResetMagickMemory(first,0,sizeof(first));
+ (void) ResetMagickMemory(last,0,sizeof(last));
+ knot_count=0;
+ in_subpath=MagickFalse;
+ while (length != 0)
+ {
+ selector=(long) ReadPropertyMSBShort(&blob,&length);
+ switch (selector)
+ {
+ case 0:
+ case 3:
+ {
+ if (knot_count != 0)
+ {
+ blob+=24;
+ length-=24;
+ break;
+ }
+ /*
+ Expected subpath length record.
+ */
+ knot_count=(long) ReadPropertyMSBShort(&blob,&length);
+ blob+=22;
+ length-=22;
+ break;
+ }
+ case 1:
+ case 2:
+ case 4:
+ case 5:
+ {
+ if (knot_count == 0)
+ {
+ /*
+ Unexpected subpath knot.
+ */
+ blob+=24;
+ length-=24;
+ }
+ else
+ {
+ /*
+ Add sub-path knot
+ */
+ for (i=0; i < 3; i++)
+ {
+ y=(long) ReadPropertyMSBLong(&blob,&length);
+ x=(long) ReadPropertyMSBLong(&blob,&length);
+ point[i].x=(double) x*columns/4096/4096;
+ point[i].y=(double) y*rows/4096/4096;
+ }
+ if (in_subpath == MagickFalse)
+ {
+ (void) FormatMagickString(message,MaxTextExtent,"M %g,%g\n",
+ point[1].x,point[1].y);
+ for (i=0; i < 3; i++)
+ {
+ first[i]=point[i];
+ last[i]=point[i];
+ }
+ }
+ else
+ {
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
+ (point[0].x == point[1].x) && (point[0].y == point[1].y))
+ (void) FormatMagickString(message,MaxTextExtent,"L %g,%g\n",
+ point[1].x,point[1].y);
+ else
+ (void) FormatMagickString(message,MaxTextExtent,
+ "C %g,%g %g,%g %g,%g\n",last[2].x,last[2].y,
+ point[0].x,point[0].y,point[1].x,point[1].y);
+ for (i=0; i < 3; i++)
+ last[i]=point[i];
+ }
+ (void) ConcatenateString(&path,message);
+ in_subpath=MagickTrue;
+ knot_count--;
+ /*
+ Close the subpath if there are no more knots.
+ */
+ if (knot_count == 0)
+ {
+ if ((last[1].x == last[2].x) && (last[1].y == last[2].y) &&
+ (first[0].x == first[1].x) && (first[0].y == first[1].y))
+ (void) FormatMagickString(message,MaxTextExtent,"L %g,%g Z\n",
+ first[1].x,first[1].y);
+ else
+ {
+ (void) FormatMagickString(message,MaxTextExtent,
+ "C %g,%g %g,%g %g,%g Z\n",last[2].x,last[2].y,
+ first[0].x,first[0].y,first[1].x,first[1].y);
+ (void) ConcatenateString(&path,message);
+ }
+ in_subpath=MagickFalse;
+ }
+ }
+ break;
+ }
+ case 6:
+ case 7:
+ case 8:
+ default:
+ {
+ blob+=24;
+ length-=24;
+ break;
+ }
+ }
+ }
+ /*
+ Return an empty SVG image if the path does not have knots.
+ */
+ (void) FormatMagickString(message,MaxTextExtent,"\"/>\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,"</g>\n");
+ (void) ConcatenateString(&path,message);
+ (void) FormatMagickString(message,MaxTextExtent,"</svg>\n");
+ (void) ConcatenateString(&path,message);
+ message=DestroyString(message);
+ return(path);
+}
+
+MagickExport const char *GetImageProperty(const Image *image,
+ const char *property)
+{
+ ExceptionInfo
+ *exception;
+
+ FxInfo
+ *fx_info;
+
+ MagickRealType
+ alpha;
+
+ MagickStatusType
+ status;
+
+ register const char
+ *p;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ p=(const char *) NULL;
+ if (property == (const char *) NULL)
+ {
+ ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
+ p=(const char *) GetNextValueInSplayTree((SplayTreeInfo *)
+ image->properties);
+ return(p);
+ }
+ if ((image->properties != (void *) NULL) &&
+ (LocaleNCompare("fx:",property,3) != 0))
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ if (p != (const char *) NULL)
+ return(p);
+ }
+ if (strchr(property,':') == (char *) NULL)
+ return(p);
+ exception=(&((Image *) image)->exception);
+ switch (*property)
+ {
+ case '8':
+ {
+ if (LocaleNCompare("8bim:",property,5) == 0)
+ {
+ if (Get8BIMProperty(image,property) != MagickFalse)
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ return(p);
+ }
+ }
+ break;
+ }
+ case 'E':
+ case 'e':
+ {
+ if (LocaleNCompare("exif:",property,5) == 0)
+ {
+ if (GetEXIFProperty(image,property) != MagickFalse)
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ return(p);
+ }
+ }
+ break;
+ }
+ case 'F':
+ case 'f':
+ {
+ if (LocaleNCompare("fx:",property,3) == 0)
+ {
+ fx_info=AcquireFxInfo(image,property+3);
+ status=FxEvaluateExpression(fx_info,&alpha,exception);
+ fx_info=DestroyFxInfo(fx_info);
+ if (status != MagickFalse)
+ {
+ char
+ value[MaxTextExtent];
+
+ (void) FormatMagickString(value,MaxTextExtent,"%g",(double)
+ alpha);
+ (void) SetImageProperty((Image *) image,property,value);
+ }
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ return(p);
+ }
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ if (LocaleNCompare("iptc:",property,5) == 0)
+ {
+ if (GetIPTCProperty(image,property) != MagickFalse)
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ return(p);
+ }
+ }
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ if (LocaleNCompare("pixel:",property,6) == 0)
+ {
+ MagickPixelPacket
+ pixel;
+
+ GetMagickPixelPacket(image,&pixel);
+ fx_info=AcquireFxInfo(image,property+6);
+ status=FxEvaluateChannelExpression(fx_info,RedChannel,0,0,&alpha,
+ exception);
+ pixel.red=(MagickRealType) QuantumRange*alpha;
+ status|=FxEvaluateChannelExpression(fx_info,GreenChannel,0,0,&alpha,
+ exception);
+ pixel.green=(MagickRealType) QuantumRange*alpha;
+ status|=FxEvaluateChannelExpression(fx_info,BlueChannel,0,0,&alpha,
+ exception);
+ pixel.blue=(MagickRealType) QuantumRange*alpha;
+ status|=FxEvaluateChannelExpression(fx_info,OpacityChannel,0,0,&alpha,
+ exception);
+ pixel.opacity=(MagickRealType) QuantumRange*(1.0-alpha);
+ if (image->colorspace == CMYKColorspace)
+ {
+ status|=FxEvaluateChannelExpression(fx_info,BlackChannel,0,0,
+ &alpha,exception);
+ pixel.index=(MagickRealType) QuantumRange*alpha;
+ }
+ fx_info=DestroyFxInfo(fx_info);
+ if (status != MagickFalse)
+ {
+ char
+ name[MaxTextExtent];
+
+ (void) QueryMagickColorname(image,&pixel,SVGCompliance,name,
+ exception);
+ (void) SetImageProperty((Image *) image,property,name);
+ return(GetImageProperty(image,property));
+ }
+ }
+ break;
+ }
+ case 'X':
+ case 'x':
+ {
+ if (LocaleNCompare("xmp:",property,4) == 0)
+ {
+ if (GetXMPProperty(image,property) != MagickFalse)
+ {
+ p=(const char *) GetValueFromSplayTree((SplayTreeInfo *)
+ image->properties,property);
+ return(p);
+ }
+ }
+ break;
+ }
+ }
+ return(p);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickProperty() gets a value associated with an image property.
+%
+% The format of the GetMagickProperty method is:
+%
+% const char *GetMagickProperty(const ImageInfo *image_info,
+% Image *image,const char *key)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o key: the key.
+%
+*/
+MagickExport const char *GetMagickProperty(const ImageInfo *image_info,
+ Image *image,const char *property)
+{
+ char
+ value[MaxTextExtent],
+ filename[MaxTextExtent];
+
+ *value='\0';
+ switch (*(property))
+ {
+ case 'b':
+ {
+ if (LocaleNCompare("base",property,4) == 0)
+ {
+ GetPathComponent(image->magick_filename,BasePath,filename);
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'c':
+ {
+ if (LocaleNCompare("channels",property,8) == 0)
+ {
+ /*
+ Image channels.
+ */
+ (void) FormatMagickString(value,MaxTextExtent,"%s",
+ MagickOptionToMnemonic(MagickColorspaceOptions,(long)
+ image->colorspace));
+ LocaleLower(value);
+ if (image->matte != MagickFalse)
+ (void) ConcatenateMagickString(value,"a",MaxTextExtent);
+ break;
+ }
+ if (LocaleNCompare("colorspace",property,10) == 0)
+ {
+ ColorspaceType
+ colorspace;
+
+ /*
+ Image storage class and colorspace.
+ */
+ colorspace=image->colorspace;
+ if (IsGrayImage(image,&image->exception) != MagickFalse)
+ colorspace=GRAYColorspace;
+ (void) FormatMagickString(value,MaxTextExtent,"%s",
+ MagickOptionToMnemonic(MagickColorspaceOptions,(long) colorspace));
+ break;
+ }
+ break;
+ }
+ case 'd':
+ {
+ if (LocaleNCompare("depth",property,5) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",image->depth);
+ break;
+ }
+ if (LocaleNCompare("directory",property,9) == 0)
+ {
+ GetPathComponent(image->magick_filename,HeadPath,filename);
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'e':
+ {
+ if (LocaleNCompare("extension",property,9) == 0)
+ {
+ GetPathComponent(image->magick_filename,ExtensionPath,filename);
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'g':
+ {
+ if (LocaleNCompare("group",property,5) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"0x%lx",
+ image_info->group);
+ break;
+ }
+ break;
+ }
+ case 'h':
+ {
+ if (LocaleNCompare("height",property,6) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",
+ image->magick_rows != 0 ? image->magick_rows : 256UL);
+ break;
+ }
+ break;
+ }
+ case 'i':
+ {
+ if (LocaleNCompare("input",property,5) == 0)
+ {
+ (void) CopyMagickString(value,image->filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'k':
+ {
+ if (LocaleNCompare("kurtosis",property,8) == 0)
+ {
+ double
+ kurtosis,
+ skewness;
+
+ (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
+ &skewness,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",kurtosis);
+ break;
+ }
+ break;
+ }
+ case 'm':
+ {
+ if (LocaleNCompare("magick",property,6) == 0)
+ {
+ (void) CopyMagickString(value,image->magick,MaxTextExtent);
+ break;
+ }
+ if (LocaleNCompare("max",property,3) == 0)
+ {
+ double
+ maximum,
+ minimum;
+
+ (void) GetImageChannelRange(image,image_info->channel,&minimum,
+ &maximum,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",maximum);
+ break;
+ }
+ if (LocaleNCompare("mean",property,4) == 0)
+ {
+ double
+ mean,
+ standard_deviation;
+
+ (void) GetImageChannelMean(image,image_info->channel,&mean,
+ &standard_deviation,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",mean);
+ break;
+ }
+ if (LocaleNCompare("min",property,3) == 0)
+ {
+ double
+ maximum,
+ minimum;
+
+ (void) GetImageChannelRange(image,image_info->channel,&minimum,
+ &maximum,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",minimum);
+ break;
+ }
+ break;
+ }
+ case 'n':
+ {
+ if (LocaleNCompare("name",property,4) == 0)
+ {
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'o':
+ {
+ if (LocaleNCompare("output",property,6) == 0)
+ {
+ (void) CopyMagickString(value,image_info->filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'p':
+ {
+ if (LocaleNCompare("page",property,4) == 0)
+ {
+ register const Image
+ *p;
+
+ unsigned long
+ page;
+
+ p=image;
+ for (page=1; GetPreviousImageInList(p) != (Image *) NULL; page++)
+ p=GetPreviousImageInList(p);
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",page);
+ break;
+ }
+ break;
+ }
+ case 's':
+ {
+ if (LocaleNCompare("size",property,4) == 0)
+ {
+ char
+ format[MaxTextExtent];
+
+ (void) FormatMagickSize(GetBlobSize(image),format);
+ (void) FormatMagickString(value,MaxTextExtent,"%s",format);
+ break;
+ }
+ if (LocaleNCompare("scenes",property,6) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",
+ (unsigned long) GetImageListLength(image));
+ break;
+ }
+ if (LocaleNCompare("scene",property,5) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",image->scene);
+ if (image_info->number_scenes != 0)
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",
+ image_info->scene);
+ break;
+ }
+ if (LocaleNCompare("skewness",property,8) == 0)
+ {
+ double
+ kurtosis,
+ skewness;
+
+ (void) GetImageChannelKurtosis(image,image_info->channel,&kurtosis,
+ &skewness,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",skewness);
+ break;
+ }
+ if ((LocaleNCompare("standard-deviation",property,18) == 0) ||
+ (LocaleNCompare("standard_deviation",property,18) == 0))
+ {
+ double
+ mean,
+ standard_deviation;
+
+ (void) GetImageChannelMean(image,image_info->channel,&mean,
+ &standard_deviation,&image->exception);
+ (void) FormatMagickString(value,MaxTextExtent,"%g",
+ standard_deviation);
+ break;
+ }
+ break;
+ }
+ case 'u':
+ {
+ if (LocaleNCompare("unique",property,6) == 0)
+ {
+ (void) CopyMagickString(filename,image_info->unique,MaxTextExtent);
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ case 'w':
+ {
+ if (LocaleNCompare("width",property,5) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",
+ image->magick_columns != 0 ? image->magick_columns : 256UL);
+ break;
+ }
+ break;
+ }
+ case 'x':
+ {
+ if (LocaleNCompare("xresolution",property,11) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%g",
+ image->x_resolution);
+ break;
+ }
+ break;
+ }
+ case 'y':
+ {
+ if (LocaleNCompare("yresolution",property,11) == 0)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%g",
+ image->y_resolution);
+ break;
+ }
+ break;
+ }
+ case 'z':
+ {
+ if (LocaleNCompare("zero",property,4) == 0)
+ {
+ (void) CopyMagickString(filename,image_info->zero,MaxTextExtent);
+ (void) CopyMagickString(value,filename,MaxTextExtent);
+ break;
+ }
+ break;
+ }
+ }
+ if (*value != '\0')
+ {
+ if (image->properties == (void *) NULL)
+ image->properties=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,RelinquishMagickMemory);
+ (void) AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ }
+ return(GetImageProperty(image,property));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageProperty() gets the next image property value.
+%
+% The format of the GetNextImageProperty method is:
+%
+% char *GetNextImageProperty(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport char *GetNextImageProperty(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties == (void *) NULL)
+ return((char *) NULL);
+ return((char *) GetNextKeyInSplayTree((SplayTreeInfo *) image->properties));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n t e r p r e t I m a g e P r o p e r t i e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InterpretImageProperties() replaces any embedded formatting characters with
+% the appropriate image property and returns the interpretted text.
+%
+% The format of the InterpretImageProperties method is:
+%
+% char *InterpretImageProperties(const ImageInfo *image_info,Image *image,
+% const char *embed_text)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+% o embed_text: the address of a character string containing the embedded
+% formatting characters.
+%
+*/
+MagickExport char *InterpretImageProperties(const ImageInfo *image_info,
+ Image *image,const char *embed_text)
+{
+ char
+ filename[MaxTextExtent],
+ *interpret_text,
+ *text;
+
+ const char
+ *value;
+
+ ImageInfo
+ *text_info;
+
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ register long
+ i;
+
+ size_t
+ extent,
+ length;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((embed_text == (const char *) NULL) || (*embed_text == '\0'))
+ return((char *) NULL);
+ text=(char *) embed_text;
+ if ((*text == '@') && ((*(text+1) == '-') ||
+ (IsPathAccessible(text+1) != MagickFalse)))
+ return(FileToString(embed_text+1,~0,&image->exception));
+ /*
+ Translate any embedded format characters.
+ */
+ text_info=CloneImageInfo(image_info);
+ interpret_text=AcquireString(text);
+ extent=MaxTextExtent;
+ p=text;
+ for (q=interpret_text; *p != '\0'; p++)
+ {
+ *q='\0';
+ if ((size_t) (q-interpret_text+MaxTextExtent) >= extent)
+ {
+ extent+=MaxTextExtent;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,extent+
+ MaxTextExtent+1,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ /*
+ Process formatting characters in text.
+ */
+ if ((*p == '\\') && (*(p+1) == 'r'))
+ {
+ *q++='\r';
+ p++;
+ continue;
+ }
+ if ((*p == '\\') && (*(p+1) == 'n'))
+ {
+ *q++='\n';
+ p++;
+ continue;
+ }
+ if (*p == '\\')
+ {
+ p++;
+ *q++=(*p);
+ continue;
+ }
+ if (*p != '%')
+ {
+ *q++=(*p);
+ continue;
+ }
+ p++;
+ switch (*p)
+ {
+ case 'b':
+ {
+ char
+ format[MaxTextExtent];
+
+ MagickSizeType
+ length;
+
+ /*
+ File size.
+ */
+ length=GetBlobSize(image);
+ (void) FormatMagickString(format,MaxTextExtent,"%lu",(unsigned long)
+ length);
+ if (length != (MagickSizeType) ((size_t) length))
+ (void) FormatMagickSize(length,format);
+ q+=ConcatenateMagickString(q,format,extent);
+ break;
+ }
+ case 'c':
+ {
+ /*
+ Image comment.
+ */
+ value=GetImageProperty(image,"comment");
+ if (value == (const char *) NULL)
+ break;
+ length=strlen(value);
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+ extent+MaxTextExtent,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ (void) CopyMagickString(q,value,extent);
+ q+=length;
+ break;
+ }
+ case 'd':
+ case 'e':
+ case 'f':
+ case 't':
+ {
+ /*
+ Label segment is the base of the filename.
+ */
+ if (*image->magick_filename == '\0')
+ break;
+ switch (*p)
+ {
+ case 'd':
+ {
+ /*
+ Directory.
+ */
+ GetPathComponent(image->magick_filename,HeadPath,filename);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ case 'e':
+ {
+ /*
+ Filename extension.
+ */
+ GetPathComponent(image->magick_filename,ExtensionPath,filename);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ case 'f':
+ {
+ /*
+ Filename.
+ */
+ GetPathComponent(image->magick_filename,TailPath,filename);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ case 't':
+ {
+ /*
+ Base filename.
+ */
+ GetPathComponent(image->magick_filename,BasePath,filename);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ }
+ break;
+ }
+ case 'g':
+ {
+ /*
+ Image geometry.
+ */
+ q+=FormatMagickString(q,extent,"%lux%lu%+ld%+ld",image->page.width,
+ image->page.height,image->page.x,image->page.y);
+ break;
+ }
+ case 'h':
+ {
+ /*
+ Image height.
+ */
+ q+=FormatMagickString(q,extent,"%lu",image->rows != 0 ? image->rows :
+ image->magick_rows);
+ break;
+ }
+ case 'i':
+ {
+ /*
+ Image filename.
+ */
+ q+=CopyMagickString(q,image->filename,extent);
+ break;
+ }
+ case 'k':
+ {
+ /*
+ Number of unique colors.
+ */
+ q+=FormatMagickString(q,extent,"%lu",GetNumberColors(image,
+ (FILE *) NULL,&image->exception));
+ break;
+ }
+ case 'l':
+ {
+ /*
+ Image label.
+ */
+ value=GetImageProperty(image,"label");
+ if (value == (const char *) NULL)
+ break;
+ length=strlen(value);
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+ extent+MaxTextExtent,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ q+=CopyMagickString(q,value,extent);
+ break;
+ }
+ case 'm':
+ {
+ /*
+ Image format.
+ */
+ q+=CopyMagickString(q,image->magick,extent);
+ break;
+ }
+ case 'M':
+ {
+ /*
+ Image magick filename.
+ */
+ q+=CopyMagickString(q,image->magick_filename,extent);
+ break;
+ }
+ case 'n':
+ {
+ /*
+ Number of images in the list.
+ */
+ q+=FormatMagickString(q,extent,"%lu",(unsigned long)
+ GetImageListLength(image));
+ break;
+ }
+ case 'o':
+ {
+ /*
+ Image output filename.
+ */
+ q+=CopyMagickString(q,text_info->filename,extent);
+ break;
+ }
+ case 'p':
+ {
+ register const Image
+ *p;
+
+ unsigned long
+ page;
+
+ /*
+ Image page number.
+ */
+ p=image;
+ for (page=1; GetPreviousImageInList(p) != (Image *) NULL; page++)
+ p=GetPreviousImageInList(p);
+ q+=FormatMagickString(q,extent,"%lu",page);
+ break;
+ }
+ case 'q':
+ {
+ /*
+ Image depth.
+ */
+ q+=FormatMagickString(q,extent,"%lu",image->depth);
+ break;
+ }
+ case 'r':
+ {
+ ColorspaceType
+ colorspace;
+
+ /*
+ Image storage class and colorspace.
+ */
+ colorspace=image->colorspace;
+ if (IsGrayImage(image,&image->exception) != MagickFalse)
+ colorspace=GRAYColorspace;
+ q+=FormatMagickString(q,extent,"%s%s%s",MagickOptionToMnemonic(
+ MagickClassOptions,(long) image->storage_class),
+ MagickOptionToMnemonic(MagickColorspaceOptions,(long) colorspace),
+ image->matte != MagickFalse ? "Matte" : "");
+ break;
+ }
+ case 's':
+ {
+ /*
+ Image scene number.
+ */
+ if (text_info->number_scenes == 0)
+ q+=FormatMagickString(q,extent,"%lu",image->scene);
+ else
+ q+=FormatMagickString(q,extent,"%lu",text_info->scene);
+ break;
+ }
+ case 'u':
+ {
+ /*
+ Unique filename.
+ */
+ (void) CopyMagickString(filename,text_info->unique,extent);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ case 'w':
+ {
+ /*
+ Image width.
+ */
+ q+=FormatMagickString(q,extent,"%lu",image->columns != 0 ?
+ image->columns : image->magick_columns);
+ break;
+ }
+ case 'x':
+ {
+ /*
+ Image horizontal resolution.
+ */
+ q+=FormatMagickString(q,extent,"%g %s",image->x_resolution,
+ MagickOptionToMnemonic(MagickResolutionOptions,(long) image->units));
+ break;
+ }
+ case 'y':
+ {
+ /*
+ Image vertical resolution.
+ */
+ q+=FormatMagickString(q,extent,"%g %s",image->y_resolution,
+ MagickOptionToMnemonic(MagickResolutionOptions,(long) image->units));
+ break;
+ }
+ case 'z':
+ {
+ /*
+ Image depth.
+ */
+ q+=FormatMagickString(q,extent,"%lu",image->depth);
+ break;
+ }
+ case 'A':
+ {
+ /*
+ Image alpha channel.
+ */
+ q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
+ MagickBooleanOptions,(long) image->matte));
+ break;
+ }
+ case 'C':
+ {
+ /*
+ Image compression method.
+ */
+ q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
+ MagickCompressOptions,(long) image->compression));
+ break;
+ }
+ case 'D':
+ {
+ /*
+ Image dispose method.
+ */
+ q+=FormatMagickString(q,extent,"%s",MagickOptionToMnemonic(
+ MagickDisposeOptions,(long) image->dispose));
+ break;
+ }
+ case 'G':
+ {
+ q+=FormatMagickString(q,extent,"%lux%lu",image->magick_columns,
+ image->magick_rows);
+ break;
+ }
+ case 'H':
+ {
+ q+=FormatMagickString(q,extent,"%ld",image->page.height);
+ break;
+ }
+ case 'O':
+ {
+ q+=FormatMagickString(q,extent,"%+ld%+ld",image->page.x,image->page.y);
+ break;
+ }
+ case 'P':
+ {
+ q+=FormatMagickString(q,extent,"%lux%lu",image->page.width,
+ image->page.height);
+ break;
+ }
+ case 'Q':
+ {
+ q+=FormatMagickString(q,extent,"%lu",image->quality);
+ break;
+ }
+ case 'S':
+ {
+ /*
+ Image scenes.
+ */
+ if (text_info->number_scenes == 0)
+ q+=CopyMagickString(q,"2147483647",extent);
+ else
+ q+=FormatMagickString(q,extent,"%lu",text_info->scene+
+ text_info->number_scenes);
+ break;
+ }
+ case 'T':
+ {
+ q+=FormatMagickString(q,extent,"%lu",image->delay);
+ break;
+ }
+ case 'W':
+ {
+ q+=FormatMagickString(q,extent,"%ld",image->page.width);
+ break;
+ }
+ case 'X':
+ {
+ q+=FormatMagickString(q,extent,"%+ld",image->page.x);
+ break;
+ }
+ case 'Y':
+ {
+ q+=FormatMagickString(q,extent,"%+ld",image->page.y);
+ break;
+ }
+ case 'Z':
+ {
+ /*
+ Unique filename.
+ */
+ (void) CopyMagickString(filename,text_info->zero,extent);
+ q+=CopyMagickString(q,filename,extent);
+ break;
+ }
+ case '[':
+ {
+ char
+ pattern[MaxTextExtent];
+
+ const char
+ *key,
+ *value;
+
+ long
+ depth;
+
+ /*
+ Image value.
+ */
+ if (strchr(p,']') == (char *) NULL)
+ break;
+ depth=1;
+ p++;
+ for (i=0; (i < (MaxTextExtent-1L)) && (*p != '\0'); i++)
+ {
+ if (*p == '[')
+ depth++;
+ if (*p == ']')
+ depth--;
+ if (depth <= 0)
+ break;
+ pattern[i]=(*p++);
+ }
+ pattern[i]='\0';
+ value=GetImageProperty(image,pattern);
+ if (value != (const char *) NULL)
+ {
+ length=strlen(value);
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+ extent+MaxTextExtent,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ (void) CopyMagickString(q,value,extent);
+ q+=length;
+ break;
+ }
+ else
+ if (IsGlob(pattern) != MagickFalse)
+ {
+ /*
+ Iterate over image properties.
+ */
+ ResetImagePropertyIterator(image);
+ key=GetNextImageProperty(image);
+ while (key != (const char *) NULL)
+ {
+ if (GlobExpression(key,pattern,MagickTrue) != MagickFalse)
+ {
+ value=GetImageProperty(image,key);
+ if (value != (const char *) NULL)
+ {
+ length=strlen(key)+strlen(value)+2;
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(
+ interpret_text,extent+MaxTextExtent,
+ sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ q+=FormatMagickString(q,extent,"%s=%s\n",key,value);
+ }
+ }
+ key=GetNextImageProperty(image);
+ }
+ }
+ value=GetMagickProperty(text_info,image,pattern);
+ if (value != (const char *) NULL)
+ {
+ length=strlen(value);
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+ extent+MaxTextExtent,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ (void) CopyMagickString(q,value,extent);
+ q+=length;
+ }
+ if (image_info == (ImageInfo *) NULL)
+ break;
+ value=GetImageOption(image_info,pattern);
+ if (value != (char *) NULL)
+ {
+ length=strlen(value);
+ if ((size_t) (q-interpret_text+length+1) >= extent)
+ {
+ extent+=length;
+ interpret_text=(char *) ResizeQuantumMemory(interpret_text,
+ extent+MaxTextExtent,sizeof(*interpret_text));
+ if (interpret_text == (char *) NULL)
+ break;
+ q=interpret_text+strlen(interpret_text);
+ }
+ (void) CopyMagickString(q,value,extent);
+ q+=length;
+ }
+ break;
+ }
+ case '@':
+ {
+ RectangleInfo
+ page;
+
+ /*
+ Image bounding box.
+ */
+ page=GetImageBoundingBox(image,&image->exception);
+ q+=FormatMagickString(q,MaxTextExtent,"%lux%lu%+ld%+ld",page.width,
+ page.height,page.x,page.y);
+ break;
+ }
+ case '#':
+ {
+ /*
+ Image signature.
+ */
+ (void) SignatureImage(image);
+ value=GetImageProperty(image,"signature");
+ if (value == (const char *) NULL)
+ break;
+ q+=CopyMagickString(q,value,extent);
+ break;
+ }
+ case '%':
+ {
+ *q++=(*p);
+ break;
+ }
+ default:
+ {
+ *q++='%';
+ *q++=(*p);
+ break;
+ }
+ }
+ }
+ *q='\0';
+ text_info=DestroyImageInfo(text_info);
+ if (text != (const char *) embed_text)
+ text=DestroyString(text);
+ (void) SubstituteString(&interpret_text,"<","<");
+ (void) SubstituteString(&interpret_text,">",">");
+ return(interpret_text);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageProperty() removes a property from the image and returns its
+% value.
+%
+% The format of the RemoveImageProperty method is:
+%
+% char *RemoveImageProperty(Image *image,const char *property)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o property: the image property.
+%
+*/
+MagickExport char *RemoveImageProperty(Image *image,
+ const char *property)
+{
+ char
+ *value;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties == (void *) NULL)
+ return((char *) NULL);
+ value=(char *) RemoveNodeFromSplayTree((SplayTreeInfo *) image->properties,
+ property);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e P r o p e r t y I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImagePropertyIterator() resets the image properties iterator. Use it
+% in conjunction with GetNextImageProperty() to iterate over all the values
+% associated with an image property.
+%
+% The format of the ResetImagePropertyIterator method is:
+%
+% ResetImagePropertyIterator(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport void ResetImagePropertyIterator(const Image *image)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties == (void *) NULL)
+ return;
+ ResetSplayTreeIterator((SplayTreeInfo *) image->properties);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e P r o p e r t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageProperty() associates an value with an image property.
+%
+% The format of the SetImageProperty method is:
+%
+% MagickBooleanType SetImageProperty(Image *image,const char *property,
+% const char *value)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o property: the image property.
+%
+% o values: the image property values.
+%
+*/
+MagickExport MagickBooleanType SetImageProperty(Image *image,
+ const char *property,const char *value)
+{
+ MagickBooleanType
+ status;
+
+ MagickStatusType
+ flags;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image->filename);
+ if (image->properties == (void *) NULL)
+ image->properties=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,RelinquishMagickMemory);
+ if ((value == (const char *) NULL) || (*value == '\0'))
+ return(DeleteImageProperty(image,property));
+ status=MagickTrue;
+ switch (*property)
+ {
+ case 'B':
+ case 'b':
+ {
+ if (LocaleCompare(property,"bias") == 0)
+ {
+ image->bias=StringToDouble(value,QuantumRange);
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ if (LocaleCompare(property,"colorspace") == 0)
+ {
+ long
+ colorspace;
+
+ colorspace=ParseMagickOption(MagickColorspaceOptions,MagickFalse,
+ value);
+ if (colorspace < 0)
+ break;
+ (void) SetImageColorspace(image,(ColorspaceType) colorspace);
+ break;
+ }
+ if (LocaleCompare(property,"compose") == 0)
+ {
+ long
+ compose;
+
+ compose=ParseMagickOption(MagickComposeOptions,MagickFalse,value);
+ if (compose < 0)
+ break;
+ image->compose=(CompositeOperator) compose;
+ break;
+ }
+ if (LocaleCompare(property,"compress") == 0)
+ {
+ long
+ compression;
+
+ compression=ParseMagickOption(MagickCompressOptions,MagickFalse,
+ value);
+ if (compression < 0)
+ break;
+ image->compression=(CompressionType) compression;
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'D':
+ case 'd':
+ {
+ if (LocaleCompare(property,"delay") == 0)
+ {
+ GeometryInfo
+ geometry_info;
+
+ flags=ParseGeometry(value,&geometry_info);
+ if ((flags & GreaterValue) != 0)
+ {
+ if (image->delay > (unsigned long) (geometry_info.rho+0.5))
+ image->delay=(unsigned long) (geometry_info.rho+0.5);
+ }
+ else
+ if ((flags & LessValue) != 0)
+ {
+ if (image->delay < (unsigned long) (geometry_info.rho+0.5))
+ image->ticks_per_second=(long) (geometry_info.sigma+0.5);
+ }
+ else
+ image->delay=(unsigned long) (geometry_info.rho+0.5);
+ if ((flags & SigmaValue) != 0)
+ image->ticks_per_second=(long) (geometry_info.sigma+0.5);
+ break;
+ }
+ if (LocaleCompare(property,"depth") == 0)
+ {
+ image->depth=(unsigned long) atol(value);
+ break;
+ }
+ if (LocaleCompare(property,"dispose") == 0)
+ {
+ long
+ dispose;
+
+ dispose=ParseMagickOption(MagickDisposeOptions,MagickFalse,value);
+ if (dispose < 0)
+ break;
+ image->dispose=(DisposeType) dispose;
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'G':
+ case 'g':
+ {
+ if (LocaleCompare(property,"gravity") == 0)
+ {
+ long
+ gravity;
+
+ gravity=ParseMagickOption(MagickGravityOptions,MagickFalse,value);
+ if (gravity < 0)
+ break;
+ image->gravity=(GravityType) gravity;
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ if (LocaleCompare(property,"intent") == 0)
+ {
+ long
+ rendering_intent;
+
+ rendering_intent=ParseMagickOption(MagickIntentOptions,MagickFalse,
+ value);
+ if (rendering_intent < 0)
+ break;
+ image->rendering_intent=(RenderingIntent) rendering_intent;
+ break;
+ }
+ if (LocaleCompare(property,"interpolate") == 0)
+ {
+ long
+ interpolate;
+
+ interpolate=ParseMagickOption(MagickInterpolateOptions,MagickFalse,
+ value);
+ if (interpolate < 0)
+ break;
+ image->interpolate=(InterpolatePixelMethod) interpolate;
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'L':
+ case 'l':
+ {
+ if (LocaleCompare(property,"loop") == 0)
+ {
+ image->iterations=(unsigned long) atol(value);
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ if (LocaleCompare(property,"page") == 0)
+ {
+ char
+ *geometry;
+
+ geometry=GetPageGeometry(value);
+ flags=ParseAbsoluteGeometry(geometry,&image->page);
+ geometry=DestroyString(geometry);
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ if (LocaleCompare(property,"rendering-intent") == 0)
+ {
+ long
+ rendering_intent;
+
+ rendering_intent=ParseMagickOption(MagickIntentOptions,MagickFalse,
+ value);
+ if (rendering_intent < 0)
+ break;
+ image->rendering_intent=(RenderingIntent) rendering_intent;
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if (LocaleCompare(property,"tile-offset") == 0)
+ {
+ char
+ *geometry;
+
+ geometry=GetPageGeometry(value);
+ flags=ParseAbsoluteGeometry(geometry,&image->tile_offset);
+ geometry=DestroyString(geometry);
+ break;
+ }
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ default:
+ {
+ status=AddValueToSplayTree((SplayTreeInfo *) image->properties,
+ ConstantString(property),ConstantString(value));
+ break;
+ }
+ }
+ return(status);
+}
diff --git a/magick/property.h b/magick/property.h
new file mode 100644
index 0000000..95715b0
--- /dev/null
+++ b/magick/property.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore property methods.
+*/
+#ifndef _MAGICKCORE_PROPERTY_H
+#define _MAGICKCORE_PROPERTY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport char
+ *GetNextImageProperty(const Image *),
+ *InterpretImageProperties(const ImageInfo *,Image *,const char *),
+ *RemoveImageProperty(Image *,const char *);
+
+extern MagickExport const char
+ *GetImageProperty(const Image *,const char *),
+ *GetMagickProperty(const ImageInfo *,Image *,const char *);
+
+extern MagickExport MagickBooleanType
+ CloneImageProperties(Image *,const Image *),
+ DefineImageProperty(Image *,const char *),
+ DeleteImageProperty(Image *,const char *),
+ FormatImageProperty(Image *,const char *,const char *,...)
+ magick_attribute((format (printf,3,4))),
+ FormatImagePropertyList(Image *,const char *,const char *,va_list)
+ magick_attribute((format (printf,3,0))),
+ SetImageProperty(Image *,const char *,const char *);
+
+extern MagickExport void
+ DestroyImageProperties(Image *),
+ ResetImagePropertyIterator(const Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/quantize.c b/magick/quantize.c
new file mode 100644
index 0000000..584e815
--- /dev/null
+++ b/magick/quantize.c
@@ -0,0 +1,3134 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% QQQ U U AAA N N TTTTT IIIII ZZZZZ EEEEE %
+% Q Q U U A A NN N T I ZZ E %
+% Q Q U U AAAAA N N N T I ZZZ EEEEE %
+% Q QQ U U A A N NN T I ZZ E %
+% QQQQ UUU A A N N T IIIII ZZZZZ EEEEE %
+% %
+% %
+% MagickCore Methods to Reduce the Number of Unique Colors in an Image %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Realism in computer graphics typically requires using 24 bits/pixel to
+% generate an image. Yet many graphic display devices do not contain the
+% amount of memory necessary to match the spatial and color resolution of
+% the human eye. The Quantize methods takes a 24 bit image and reduces
+% the number of colors so it can be displayed on raster device with less
+% bits per pixel. In most instances, the quantized image closely
+% resembles the original reference image.
+%
+% A reduction of colors in an image is also desirable for image
+% transmission and real-time animation.
+%
+% QuantizeImage() takes a standard RGB or monochrome images and quantizes
+% them down to some fixed number of colors.
+%
+% For purposes of color allocation, an image is a set of n pixels, where
+% each pixel is a point in RGB space. RGB space is a 3-dimensional
+% vector space, and each pixel, Pi, is defined by an ordered triple of
+% red, green, and blue coordinates, (Ri, Gi, Bi).
+%
+% Each primary color component (red, green, or blue) represents an
+% intensity which varies linearly from 0 to a maximum value, Cmax, which
+% corresponds to full saturation of that color. Color allocation is
+% defined over a domain consisting of the cube in RGB space with opposite
+% vertices at (0,0,0) and (Cmax, Cmax, Cmax). QUANTIZE requires Cmax =
+% 255.
+%
+% The algorithm maps this domain onto a tree in which each node
+% represents a cube within that domain. In the following discussion
+% these cubes are defined by the coordinate of two opposite vertices:
+% The vertex nearest the origin in RGB space and the vertex farthest from
+% the origin.
+%
+% The tree's root node represents the entire domain, (0,0,0) through
+% (Cmax,Cmax,Cmax). Each lower level in the tree is generated by
+% subdividing one node's cube into eight smaller cubes of equal size.
+% This corresponds to bisecting the parent cube with planes passing
+% through the midpoints of each edge.
+%
+% The basic algorithm operates in three phases: Classification,
+% Reduction, and Assignment. Classification builds a color description
+% tree for the image. Reduction collapses the tree until the number it
+% represents, at most, the number of colors desired in the output image.
+% Assignment defines the output image's color map and sets each pixel's
+% color by restorage_class in the reduced tree. Our goal is to minimize
+% the numerical discrepancies between the original colors and quantized
+% colors (quantization error).
+%
+% Classification begins by initializing a color description tree of
+% sufficient depth to represent each possible input color in a leaf.
+% However, it is impractical to generate a fully-formed color description
+% tree in the storage_class phase for realistic values of Cmax. If
+% colors components in the input image are quantized to k-bit precision,
+% so that Cmax= 2k-1, the tree would need k levels below the root node to
+% allow representing each possible input color in a leaf. This becomes
+% prohibitive because the tree's total number of nodes is 1 +
+% sum(i=1, k, 8k).
+%
+% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
+% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
+% Initializes data structures for nodes only as they are needed; (2)
+% Chooses a maximum depth for the tree as a function of the desired
+% number of colors in the output image (currently log2(colormap size)).
+%
+% For each pixel in the input image, storage_class scans downward from
+% the root of the color description tree. At each level of the tree it
+% identifies the single node which represents a cube in RGB space
+% containing the pixel's color. It updates the following data for each
+% such node:
+%
+% n1: Number of pixels whose color is contained in the RGB cube which
+% this node represents;
+%
+% n2: Number of pixels whose color is not represented in a node at
+% lower depth in the tree; initially, n2 = 0 for all nodes except
+% leaves of the tree.
+%
+% Sr, Sg, Sb: Sums of the red, green, and blue component values for all
+% pixels not classified at a lower depth. The combination of these sums
+% and n2 will ultimately characterize the mean color of a set of
+% pixels represented by this node.
+%
+% E: the distance squared in RGB space between each pixel contained
+% within a node and the nodes' center. This represents the
+% quantization error for a node.
+%
+% Reduction repeatedly prunes the tree until the number of nodes with n2
+% > 0 is less than or equal to the maximum number of colors allowed in
+% the output image. On any given iteration over the tree, it selects
+% those nodes whose E count is minimal for pruning and merges their color
+% statistics upward. It uses a pruning threshold, Ep, to govern node
+% selection as follows:
+%
+% Ep = 0
+% while number of nodes with (n2 > 0) > required maximum number of colors
+% prune all nodes such that E <= Ep
+% Set Ep to minimum E in remaining nodes
+%
+% This has the effect of minimizing any quantization error when merging
+% two nodes together.
+%
+% When a node to be pruned has offspring, the pruning procedure invokes
+% itself recursively in order to prune the tree from the leaves upward.
+% n2, Sr, Sg, and Sb in a node being pruned are always added to the
+% corresponding data in that node's parent. This retains the pruned
+% node's color characteristics for later averaging.
+%
+% For each node, n2 pixels exist for which that node represents the
+% smallest volume in RGB space containing those pixel's colors. When n2
+% > 0 the node will uniquely define a color in the output image. At the
+% beginning of reduction, n2 = 0 for all nodes except a the leaves of
+% the tree which represent colors present in the input image.
+%
+% The other pixel count, n1, indicates the total number of colors within
+% the cubic volume which the node represents. This includes n1 - n2
+% pixels whose colors should be defined by nodes at a lower level in the
+% tree.
+%
+% Assignment generates the output image from the pruned tree. The output
+% image consists of two parts: (1) A color map, which is an array of
+% color descriptions (RGB triples) for each color present in the output
+% image; (2) A pixel array, which represents each pixel as an index
+% into the color map array.
+%
+% First, the assignment phase makes one pass over the pruned color
+% description tree to establish the image's color map. For each node
+% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
+% color of all pixels that classify no lower than this node. Each of
+% these colors becomes an entry in the color map.
+%
+% Finally, the assignment phase reclassifies each pixel in the pruned
+% tree to identify the deepest node containing the pixel's color. The
+% pixel's value in the pixel array becomes the index of this node's mean
+% color in the color map.
+%
+% This method is based on a similar algorithm written by Paul Raveling.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/option.h"
+#include "magick/pixel-private.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/string_.h"
+
+/*
+ Define declarations.
+*/
+#define CacheShift 2
+#define ErrorQueueLength 16
+#define MaxNodes 266817
+#define MaxTreeDepth 8
+#define NodesInAList 1920
+
+/*
+ Typdef declarations.
+*/
+typedef struct _RealPixelPacket
+{
+ MagickRealType
+ red,
+ green,
+ blue,
+ opacity;
+} RealPixelPacket;
+
+typedef struct _NodeInfo
+{
+ struct _NodeInfo
+ *parent,
+ *child[16];
+
+ MagickSizeType
+ number_unique;
+
+ RealPixelPacket
+ total_color;
+
+ MagickRealType
+ quantize_error;
+
+ unsigned long
+ color_number,
+ id,
+ level;
+} NodeInfo;
+
+typedef struct _Nodes
+{
+ NodeInfo
+ *nodes;
+
+ struct _Nodes
+ *next;
+} Nodes;
+
+typedef struct _CubeInfo
+{
+ NodeInfo
+ *root;
+
+ unsigned long
+ colors,
+ maximum_colors;
+
+ long
+ transparent_index;
+
+ MagickSizeType
+ transparent_pixels;
+
+ RealPixelPacket
+ target;
+
+ MagickRealType
+ distance,
+ pruning_threshold,
+ next_threshold;
+
+ unsigned long
+ nodes,
+ free_nodes,
+ color_number;
+
+ NodeInfo
+ *next_node;
+
+ Nodes
+ *node_queue;
+
+ long
+ *cache;
+
+ RealPixelPacket
+ error[ErrorQueueLength];
+
+ MagickRealType
+ weights[ErrorQueueLength];
+
+ QuantizeInfo
+ *quantize_info;
+
+ MagickBooleanType
+ associate_alpha;
+
+ long
+ x,
+ y;
+
+ unsigned long
+ depth;
+
+ MagickOffsetType
+ offset;
+
+ MagickSizeType
+ span;
+} CubeInfo;
+
+/*
+ Method prototypes.
+*/
+static CubeInfo
+ *GetCubeInfo(const QuantizeInfo *,const unsigned long,const unsigned long);
+
+static NodeInfo
+ *GetNodeInfo(CubeInfo *,const unsigned long,const unsigned long,NodeInfo *);
+
+static MagickBooleanType
+ AssignImageColors(Image *,CubeInfo *),
+ ClassifyImageColors(CubeInfo *,const Image *,ExceptionInfo *),
+ DitherImage(Image *,CubeInfo *),
+ SetGrayscaleImage(Image *);
+
+static unsigned long
+ DefineImageColormap(Image *,CubeInfo *,NodeInfo *);
+
+static void
+ ClosestColor(const Image *,CubeInfo *,const NodeInfo *),
+ DestroyCubeInfo(CubeInfo *),
+ PruneLevel(const Image *,CubeInfo *,const NodeInfo *),
+ PruneToCubeDepth(const Image *,CubeInfo *,const NodeInfo *),
+ ReduceImageColors(const Image *,CubeInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e Q u a n t i z e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireQuantizeInfo() allocates the QuantizeInfo structure.
+%
+% The format of the AcquireQuantizeInfo method is:
+%
+% QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport QuantizeInfo *AcquireQuantizeInfo(const ImageInfo *image_info)
+{
+ QuantizeInfo
+ *quantize_info;
+
+ quantize_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*quantize_info));
+ if (quantize_info == (QuantizeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetQuantizeInfo(quantize_info);
+ if (image_info != (ImageInfo *) NULL)
+ {
+ const char
+ *option;
+
+ quantize_info->dither=image_info->dither;
+ option=GetImageOption(image_info,"dither");
+ if (option != (const char *) NULL)
+ quantize_info->dither_method=(DitherMethod) ParseMagickOption(
+ MagickDitherOptions,MagickFalse,option);
+ quantize_info->measure_error=image_info->verbose;
+ }
+ return(quantize_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A s s i g n I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AssignImageColors() generates the output image from the pruned tree. The
+% output image consists of two parts: (1) A color map, which is an array
+% of color descriptions (RGB triples) for each color present in the
+% output image; (2) A pixel array, which represents each pixel as an
+% index into the color map array.
+%
+% First, the assignment phase makes one pass over the pruned color
+% description tree to establish the image's color map. For each node
+% with n2 > 0, it divides Sr, Sg, and Sb by n2 . This produces the mean
+% color of all pixels that classify no lower than this node. Each of
+% these colors becomes an entry in the color map.
+%
+% Finally, the assignment phase reclassifies each pixel in the pruned
+% tree to identify the deepest node containing the pixel's color. The
+% pixel's value in the pixel array becomes the index of this node's mean
+% color in the color map.
+%
+% The format of the AssignImageColors() method is:
+%
+% MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+*/
+
+static inline void AssociateAlphaPixel(const CubeInfo *cube_info,
+ const PixelPacket *pixel,RealPixelPacket *alpha_pixel)
+{
+ MagickRealType
+ alpha;
+
+ if ((cube_info->associate_alpha == MagickFalse) ||
+ (pixel->opacity == OpaqueOpacity))
+ {
+ alpha_pixel->red=(MagickRealType) pixel->red;
+ alpha_pixel->green=(MagickRealType) pixel->green;
+ alpha_pixel->blue=(MagickRealType) pixel->blue;
+ alpha_pixel->opacity=(MagickRealType) pixel->opacity;
+ return;
+ }
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-pixel->opacity));
+ alpha_pixel->red=alpha*pixel->red;
+ alpha_pixel->green=alpha*pixel->green;
+ alpha_pixel->blue=alpha*pixel->blue;
+ alpha_pixel->opacity=(MagickRealType) pixel->opacity;
+}
+
+static inline Quantum ClipToQuantum(const MagickRealType value)
+{
+ if (value <= 0.0)
+ return((Quantum) 0);
+ if (value >= QuantumRange)
+ return((Quantum) QuantumRange);
+ return((Quantum) (value+0.5));
+}
+
+static inline unsigned long ColorToNodeId(const CubeInfo *cube_info,
+ const RealPixelPacket *pixel,unsigned long index)
+{
+ unsigned long
+ id;
+
+ id=(unsigned long) (
+ ((ScaleQuantumToChar(ClipToQuantum(pixel->red)) >> index) & 0x1) |
+ ((ScaleQuantumToChar(ClipToQuantum(pixel->green)) >> index) & 0x1) << 1 |
+ ((ScaleQuantumToChar(ClipToQuantum(pixel->blue)) >> index) & 0x1) << 2);
+ if (cube_info->associate_alpha != MagickFalse)
+ id|=((ScaleQuantumToChar(ClipToQuantum(pixel->opacity)) >> index) & 0x1)
+ << 3;
+ return(id);
+}
+
+static inline MagickBooleanType IsSameColor(const Image *image,
+ const PixelPacket *p,const PixelPacket *q)
+{
+ if ((p->red != q->red) || (p->green != q->green) || (p->blue != q->blue))
+ return(MagickFalse);
+ if ((image->matte != MagickFalse) && (p->opacity != q->opacity))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static MagickBooleanType AssignImageColors(Image *image,CubeInfo *cube_info)
+{
+#define AssignImageTag "Assign/Image"
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ RealPixelPacket
+ pixel;
+
+ register long
+ i,
+ x;
+
+ register const NodeInfo
+ *node_info;
+
+ ssize_t
+ count;
+
+ unsigned long
+ id,
+ index;
+
+ /*
+ Allocate image colormap.
+ */
+ if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
+ (cube_info->quantize_info->colorspace != CMYKColorspace))
+ (void) TransformImageColorspace((Image *) image,
+ cube_info->quantize_info->colorspace);
+ else
+ if ((image->colorspace != GRAYColorspace) &&
+ (image->colorspace != RGBColorspace) &&
+ (image->colorspace != CMYColorspace))
+ (void) TransformImageColorspace((Image *) image,RGBColorspace);
+ if (AcquireImageColormap(image,cube_info->colors) == MagickFalse)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ image->colors=0;
+ cube_info->transparent_pixels=0;
+ cube_info->transparent_index=(-1);
+ (void) DefineImageColormap(image,cube_info,cube_info->root);
+ /*
+ Create a reduced color image.
+ */
+ if ((cube_info->quantize_info->dither != MagickFalse) &&
+ (cube_info->quantize_info->dither_method != NoDitherMethod))
+ (void) DitherImage(image,cube_info);
+ else
+ {
+ ExceptionInfo
+ *exception;
+
+ CacheView
+ *image_view;
+
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x+=count)
+ {
+ /*
+ Identify the deepest node containing the pixel's color.
+ */
+ for (count=1; (x+count) < (long) image->columns; count++)
+ if (IsSameColor(image,q,q+count) == MagickFalse)
+ break;
+ AssociateAlphaPixel(cube_info,q,&pixel);
+ node_info=cube_info->root;
+ for (index=MaxTreeDepth-1; (long) index > 0; index--)
+ {
+ id=ColorToNodeId(cube_info,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ break;
+ node_info=node_info->child[id];
+ }
+ /*
+ Find closest color among siblings and their children.
+ */
+ cube_info->target=pixel;
+ cube_info->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*
+ (QuantumRange+1.0)+1.0);
+ ClosestColor(image,cube_info,node_info->parent);
+ index=cube_info->color_number;
+ for (i=0; i < (long) count; i++)
+ {
+ if (image->storage_class == PseudoClass)
+ indexes[x+i]=(IndexPacket) index;
+ if (cube_info->quantize_info->measure_error == MagickFalse)
+ {
+ q->red=image->colormap[index].red;
+ q->green=image->colormap[index].green;
+ q->blue=image->colormap[index].blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ q->opacity=image->colormap[index].opacity;
+ }
+ q++;
+ }
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,AssignImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ }
+ if (cube_info->quantize_info->measure_error != MagickFalse)
+ (void) GetImageQuantizeError(image);
+ if ((cube_info->quantize_info->number_colors == 2) &&
+ (cube_info->quantize_info->colorspace == GRAYColorspace))
+ {
+ Quantum
+ intensity;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Monochrome image.
+ */
+ q=image->colormap;
+ for (i=0; i < (long) image->colors; i++)
+ {
+ intensity=(Quantum) (PixelIntensity(q) < ((MagickRealType)
+ QuantumRange/2.0) ? 0 : QuantumRange);
+ q->red=intensity;
+ q->green=intensity;
+ q->blue=intensity;
+ q++;
+ }
+ }
+ (void) SyncImage(image);
+ if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
+ (cube_info->quantize_info->colorspace != CMYKColorspace))
+ (void) TransformImageColorspace((Image *) image,RGBColorspace);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l a s s i f y I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClassifyImageColors() begins by initializing a color description tree
+% of sufficient depth to represent each possible input color in a leaf.
+% However, it is impractical to generate a fully-formed color
+% description tree in the storage_class phase for realistic values of
+% Cmax. If colors components in the input image are quantized to k-bit
+% precision, so that Cmax= 2k-1, the tree would need k levels below the
+% root node to allow representing each possible input color in a leaf.
+% This becomes prohibitive because the tree's total number of nodes is
+% 1 + sum(i=1,k,8k).
+%
+% A complete tree would require 19,173,961 nodes for k = 8, Cmax = 255.
+% Therefore, to avoid building a fully populated tree, QUANTIZE: (1)
+% Initializes data structures for nodes only as they are needed; (2)
+% Chooses a maximum depth for the tree as a function of the desired
+% number of colors in the output image (currently log2(colormap size)).
+%
+% For each pixel in the input image, storage_class scans downward from
+% the root of the color description tree. At each level of the tree it
+% identifies the single node which represents a cube in RGB space
+% containing It updates the following data for each such node:
+%
+% n1 : Number of pixels whose color is contained in the RGB cube
+% which this node represents;
+%
+% n2 : Number of pixels whose color is not represented in a node at
+% lower depth in the tree; initially, n2 = 0 for all nodes except
+% leaves of the tree.
+%
+% Sr, Sg, Sb : Sums of the red, green, and blue component values for
+% all pixels not classified at a lower depth. The combination of
+% these sums and n2 will ultimately characterize the mean color of a
+% set of pixels represented by this node.
+%
+% E: the distance squared in RGB space between each pixel contained
+% within a node and the nodes' center. This represents the quantization
+% error for a node.
+%
+% The format of the ClassifyImageColors() method is:
+%
+% MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
+% const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o image: the image.
+%
+*/
+
+static inline void SetAssociatedAlpha(const Image *image,CubeInfo *cube_info)
+{
+ MagickBooleanType
+ associate_alpha;
+
+ associate_alpha=image->matte;
+ if (cube_info->quantize_info->colorspace == TransparentColorspace)
+ associate_alpha=MagickFalse;
+ if ((cube_info->quantize_info->number_colors == 2) &&
+ (cube_info->quantize_info->colorspace == GRAYColorspace))
+ associate_alpha=MagickFalse;
+ cube_info->associate_alpha=associate_alpha;
+}
+
+static MagickBooleanType ClassifyImageColors(CubeInfo *cube_info,
+ const Image *image,ExceptionInfo *exception)
+{
+#define ClassifyImageTag "Classify/Image"
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ MagickRealType
+ bisect;
+
+ NodeInfo
+ *node_info;
+
+ RealPixelPacket
+ error,
+ mid,
+ midpoint,
+ pixel;
+
+ size_t
+ count;
+
+ unsigned long
+ id,
+ index,
+ level;
+
+ CacheView
+ *image_view;
+
+ /*
+ Classify the first cube_info->maximum_colors colors to a tree depth of 8.
+ */
+ SetAssociatedAlpha(image,cube_info);
+ if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
+ (cube_info->quantize_info->colorspace != CMYKColorspace))
+ (void) TransformImageColorspace((Image *) image,
+ cube_info->quantize_info->colorspace);
+ else
+ if ((image->colorspace != GRAYColorspace) &&
+ (image->colorspace != CMYColorspace) &&
+ (image->colorspace != RGBColorspace))
+ (void) TransformImageColorspace((Image *) image,RGBColorspace);
+ midpoint.red=(MagickRealType) QuantumRange/2.0;
+ midpoint.green=(MagickRealType) QuantumRange/2.0;
+ midpoint.blue=(MagickRealType) QuantumRange/2.0;
+ midpoint.opacity=(MagickRealType) QuantumRange/2.0;
+ error.opacity=0.0;
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if (cube_info->nodes > MaxNodes)
+ {
+ /*
+ Prune one level if the color tree is too large.
+ */
+ PruneLevel(image,cube_info,cube_info->root);
+ cube_info->depth--;
+ }
+ for (x=0; x < (long) image->columns; x+=(long) count)
+ {
+ /*
+ Start at the root and descend the color cube tree.
+ */
+ for (count=1; (x+count) < image->columns; count++)
+ if (IsSameColor(image,p,p+count) == MagickFalse)
+ break;
+ AssociateAlphaPixel(cube_info,p,&pixel);
+ index=MaxTreeDepth-1;
+ bisect=((MagickRealType) QuantumRange+1.0)/2.0;
+ mid=midpoint;
+ node_info=cube_info->root;
+ for (level=1; level <= MaxTreeDepth; level++)
+ {
+ bisect*=0.5;
+ id=ColorToNodeId(cube_info,&pixel,index);
+ mid.red+=(id & 1) != 0 ? bisect : -bisect;
+ mid.green+=(id & 2) != 0 ? bisect : -bisect;
+ mid.blue+=(id & 4) != 0 ? bisect : -bisect;
+ mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ /*
+ Set colors of new node to contain pixel.
+ */
+ node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ if (level == MaxTreeDepth)
+ cube_info->colors++;
+ }
+ /*
+ Approximate the quantization error represented by this node.
+ */
+ node_info=node_info->child[id];
+ error.red=QuantumScale*(pixel.red-mid.red);
+ error.green=QuantumScale*(pixel.green-mid.green);
+ error.blue=QuantumScale*(pixel.blue-mid.blue);
+ if (cube_info->associate_alpha != MagickFalse)
+ error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
+ node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
+ count*error.green*error.green+count*error.blue*error.blue+
+ count*error.opacity*error.opacity));
+ cube_info->root->quantize_error+=node_info->quantize_error;
+ index--;
+ }
+ /*
+ Sum RGB for this leaf for later derivation of the mean cube color.
+ */
+ node_info->number_unique+=count;
+ node_info->total_color.red+=count*QuantumScale*pixel.red;
+ node_info->total_color.green+=count*QuantumScale*pixel.green;
+ node_info->total_color.blue+=count*QuantumScale*pixel.blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
+ p+=count;
+ }
+ if (cube_info->colors > cube_info->maximum_colors)
+ {
+ PruneToCubeDepth(image,cube_info,cube_info->root);
+ break;
+ }
+ proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ for (y++; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if (cube_info->nodes > MaxNodes)
+ {
+ /*
+ Prune one level if the color tree is too large.
+ */
+ PruneLevel(image,cube_info,cube_info->root);
+ cube_info->depth--;
+ }
+ for (x=0; x < (long) image->columns; x+=(long) count)
+ {
+ /*
+ Start at the root and descend the color cube tree.
+ */
+ for (count=1; (x+count) < image->columns; count++)
+ if (IsSameColor(image,p,p+count) == MagickFalse)
+ break;
+ AssociateAlphaPixel(cube_info,p,&pixel);
+ index=MaxTreeDepth-1;
+ bisect=((MagickRealType) QuantumRange+1.0)/2.0;
+ mid=midpoint;
+ node_info=cube_info->root;
+ for (level=1; level <= cube_info->depth; level++)
+ {
+ bisect*=0.5;
+ id=ColorToNodeId(cube_info,&pixel,index);
+ mid.red+=(id & 1) != 0 ? bisect : -bisect;
+ mid.green+=(id & 2) != 0 ? bisect : -bisect;
+ mid.blue+=(id & 4) != 0 ? bisect : -bisect;
+ mid.opacity+=(id & 8) != 0 ? bisect : -bisect;
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ {
+ /*
+ Set colors of new node to contain pixel.
+ */
+ node_info->child[id]=GetNodeInfo(cube_info,id,level,node_info);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","%s",
+ image->filename);
+ if (level == cube_info->depth)
+ cube_info->colors++;
+ }
+ /*
+ Approximate the quantization error represented by this node.
+ */
+ node_info=node_info->child[id];
+ error.red=QuantumScale*(pixel.red-mid.red);
+ error.green=QuantumScale*(pixel.green-mid.green);
+ error.blue=QuantumScale*(pixel.blue-mid.blue);
+ if (cube_info->associate_alpha != MagickFalse)
+ error.opacity=QuantumScale*(pixel.opacity-mid.opacity);
+ node_info->quantize_error+=sqrt((double) (count*error.red*error.red+
+ count*error.green*error.green+error.blue*error.blue+
+ count*error.opacity*error.opacity));
+ cube_info->root->quantize_error+=node_info->quantize_error;
+ index--;
+ }
+ /*
+ Sum RGB for this leaf for later derivation of the mean cube color.
+ */
+ node_info->number_unique+=count;
+ node_info->total_color.red+=count*QuantumScale*pixel.red;
+ node_info->total_color.green+=count*QuantumScale*pixel.green;
+ node_info->total_color.blue+=count*QuantumScale*pixel.blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ node_info->total_color.opacity+=count*QuantumScale*pixel.opacity;
+ p+=count;
+ }
+ proceed=SetImageProgress(image,ClassifyImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ image_view=DestroyCacheView(image_view);
+ if ((cube_info->quantize_info->colorspace != UndefinedColorspace) &&
+ (cube_info->quantize_info->colorspace != CMYKColorspace))
+ (void) TransformImageColorspace((Image *) image,RGBColorspace);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e Q u a n t i z e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneQuantizeInfo() makes a duplicate of the given quantize info structure,
+% or if quantize info is NULL, a new one.
+%
+% The format of the CloneQuantizeInfo method is:
+%
+% QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
+%
+% A description of each parameter follows:
+%
+% o clone_info: Method CloneQuantizeInfo returns a duplicate of the given
+% quantize info, or if image info is NULL a new one.
+%
+% o quantize_info: a structure of type info.
+%
+*/
+MagickExport QuantizeInfo *CloneQuantizeInfo(const QuantizeInfo *quantize_info)
+{
+ QuantizeInfo
+ *clone_info;
+
+ clone_info=(QuantizeInfo *) AcquireMagickMemory(sizeof(*clone_info));
+ if (clone_info == (QuantizeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ GetQuantizeInfo(clone_info);
+ if (quantize_info == (QuantizeInfo *) NULL)
+ return(clone_info);
+ clone_info->number_colors=quantize_info->number_colors;
+ clone_info->tree_depth=quantize_info->tree_depth;
+ clone_info->dither=quantize_info->dither;
+ clone_info->dither_method=quantize_info->dither_method;
+ clone_info->colorspace=quantize_info->colorspace;
+ clone_info->measure_error=quantize_info->measure_error;
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l o s e s t C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ClosestColor() traverses the color cube tree at a particular node and
+% determines which colormap entry best represents the input color.
+%
+% The format of the ClosestColor method is:
+%
+% void ClosestColor(const Image *image,CubeInfo *cube_info,
+% const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: the address of a structure of type NodeInfo which points to a
+% node in the color cube tree that is to be pruned.
+%
+*/
+static void ClosestColor(const Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ ClosestColor(image,cube_info,node_info->child[i]);
+ if (node_info->number_unique != 0)
+ {
+ MagickRealType
+ pixel;
+
+ register MagickRealType
+ alpha,
+ beta,
+ distance;
+
+ register PixelPacket
+ *__restrict p;
+
+ register RealPixelPacket
+ *__restrict q;
+
+ /*
+ Determine if this color is "closest".
+ */
+ p=image->colormap+node_info->color_number;
+ q=(&cube_info->target);
+ alpha=1.0;
+ beta=1.0;
+ if (cube_info->associate_alpha == MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity));
+ }
+ pixel=alpha*p->red-beta*q->red;
+ distance=pixel*pixel;
+ if (distance < cube_info->distance)
+ {
+ pixel=alpha*p->green-beta*q->green;
+ distance+=pixel*pixel;
+ if (distance < cube_info->distance)
+ {
+ pixel=alpha*p->blue-beta*q->blue;
+ distance+=pixel*pixel;
+ if (distance < cube_info->distance)
+ {
+ pixel=alpha-beta;
+ distance+=pixel*pixel;
+ if (distance < cube_info->distance)
+ {
+ cube_info->distance=distance;
+ cube_info->color_number=node_info->color_number;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p r e s s I m a g e C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompressImageColormap() compresses an image colormap by removing any
+% duplicate or unused color entries.
+%
+% The format of the CompressImageColormap method is:
+%
+% MagickBooleanType CompressImageColormap(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType CompressImageColormap(Image *image)
+{
+ QuantizeInfo
+ quantize_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (IsPaletteImage(image,&image->exception) == MagickFalse)
+ return(MagickFalse);
+ GetQuantizeInfo(&quantize_info);
+ quantize_info.number_colors=image->colors;
+ quantize_info.tree_depth=MaxTreeDepth;
+ return(QuantizeImage(&quantize_info,image));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f i n e I m a g e C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageColormap() traverses the color cube tree and notes each colormap
+% entry. A colormap entry is any node in the color cube tree where the
+% of unique colors is not zero. DefineImageColormap() returns the number of
+% colors in the image colormap.
+%
+% The format of the DefineImageColormap method is:
+%
+% unsigned long DefineImageColormap(Image *image,CubeInfo *cube_info,
+% NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: the address of a structure of type NodeInfo which points to a
+% node in the color cube tree that is to be pruned.
+%
+*/
+static unsigned long DefineImageColormap(Image *image,CubeInfo *cube_info,
+ NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ DefineImageColormap(image,cube_info,node_info->child[i]);
+ if (node_info->number_unique != 0)
+ {
+ register MagickRealType
+ alpha;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Colormap entry is defined by the mean color in this cube.
+ */
+ q=image->colormap+image->colors;
+ alpha=(MagickRealType) ((MagickOffsetType) node_info->number_unique);
+ alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
+ if (cube_info->associate_alpha == MagickFalse)
+ {
+ q->red=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.red));
+ q->green=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.green));
+ q->blue=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.blue));
+ q->opacity=OpaqueOpacity;
+ }
+ else
+ {
+ MagickRealType
+ opacity;
+
+ opacity=(MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.opacity);
+ q->opacity=RoundToQuantum(opacity);
+ if (q->opacity == OpaqueOpacity)
+ {
+ q->red=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.red));
+ q->green=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.green));
+ q->blue=RoundToQuantum((MagickRealType) (alpha*QuantumRange*
+ node_info->total_color.blue));
+ }
+ else
+ {
+ MagickRealType
+ gamma;
+
+ gamma=(MagickRealType) (QuantumScale*(QuantumRange-
+ (MagickRealType) q->opacity));
+ gamma=1.0/(fabs(gamma) <= MagickEpsilon ? 1.0 : gamma);
+ q->red=RoundToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
+ node_info->total_color.red));
+ q->green=RoundToQuantum((MagickRealType) (alpha*gamma*
+ QuantumRange*node_info->total_color.green));
+ q->blue=RoundToQuantum((MagickRealType) (alpha*gamma*QuantumRange*
+ node_info->total_color.blue));
+ if (node_info->number_unique > cube_info->transparent_pixels)
+ {
+ cube_info->transparent_pixels=node_info->number_unique;
+ cube_info->transparent_index=(long) image->colors;
+ }
+ }
+ }
+ node_info->color_number=image->colors++;
+ }
+ return(image->colors);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y C u b e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyCubeInfo() deallocates memory associated with an image.
+%
+% The format of the DestroyCubeInfo method is:
+%
+% DestroyCubeInfo(CubeInfo *cube_info)
+%
+% A description of each parameter follows:
+%
+% o cube_info: the address of a structure of type CubeInfo.
+%
+*/
+static void DestroyCubeInfo(CubeInfo *cube_info)
+{
+ register Nodes
+ *nodes;
+
+ /*
+ Release color cube tree storage.
+ */
+ do
+ {
+ nodes=cube_info->node_queue->next;
+ cube_info->node_queue->nodes=(NodeInfo *) RelinquishMagickMemory(
+ cube_info->node_queue->nodes);
+ cube_info->node_queue=(Nodes *) RelinquishMagickMemory(
+ cube_info->node_queue);
+ cube_info->node_queue=nodes;
+ } while (cube_info->node_queue != (Nodes *) NULL);
+ if (cube_info->cache != (long *) NULL)
+ cube_info->cache=(long *) RelinquishMagickMemory(cube_info->cache);
+ cube_info->quantize_info=DestroyQuantizeInfo(cube_info->quantize_info);
+ cube_info=(CubeInfo *) RelinquishMagickMemory(cube_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y Q u a n t i z e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyQuantizeInfo() deallocates memory associated with an QuantizeInfo
+% structure.
+%
+% The format of the DestroyQuantizeInfo method is:
+%
+% QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+*/
+MagickExport QuantizeInfo *DestroyQuantizeInfo(QuantizeInfo *quantize_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(quantize_info != (QuantizeInfo *) NULL);
+ assert(quantize_info->signature == MagickSignature);
+ quantize_info->signature=(~MagickSignature);
+ quantize_info=(QuantizeInfo *) RelinquishMagickMemory(quantize_info);
+ return(quantize_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D i t h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DitherImage() distributes the difference between an original image and
+% the corresponding color reduced algorithm to neighboring pixels using
+% serpentine-scan Floyd-Steinberg error diffusion. DitherImage returns
+% MagickTrue if the image is dithered otherwise MagickFalse.
+%
+% The format of the DitherImage method is:
+%
+% MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+*/
+
+static MagickBooleanType FloydSteinbergDither(Image *image,CubeInfo *cube_info)
+{
+#define DitherImageTag "Dither/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ u,
+ v,
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ RealPixelPacket
+ color,
+ *current,
+ pixel,
+ *previous,
+ *scanlines;
+
+ register CubeInfo
+ *p;
+
+ unsigned long
+ index;
+
+ CacheView
+ *image_view;
+
+ /*
+ Distribute quantization error using Floyd-Steinberg.
+ */
+ scanlines=(RealPixelPacket *) AcquireQuantumMemory(image->columns,
+ 2*sizeof(*scanlines));
+ if (scanlines == (RealPixelPacket *) NULL)
+ return(MagickFalse);
+ p=cube_info;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ i,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickFalse);
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ current=scanlines+(y & 0x01)*image->columns;
+ previous=scanlines+((y+1) & 0x01)*image->columns;
+ v=(y & 0x01) ? -1 : 1;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ u=(y & 0x01) ? (long) image->columns-1-x : x;
+ AssociateAlphaPixel(cube_info,q+u,&pixel);
+ if (x > 0)
+ {
+ pixel.red+=7*current[u-v].red/16;
+ pixel.green+=7*current[u-v].green/16;
+ pixel.blue+=7*current[u-v].blue/16;
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity+=7*current[u-v].opacity/16;
+ }
+ if (y > 0)
+ {
+ if (x < (long) (image->columns-1))
+ {
+ pixel.red+=previous[u+v].red/16;
+ pixel.green+=previous[u+v].green/16;
+ pixel.blue+=previous[u+v].blue/16;
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity+=previous[u+v].opacity/16;
+ }
+ pixel.red+=5*previous[u].red/16;
+ pixel.green+=5*previous[u].green/16;
+ pixel.blue+=5*previous[u].blue/16;
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity+=5*previous[u].opacity/16;
+ if (x > 0)
+ {
+ pixel.red+=3*previous[u-v].red/16;
+ pixel.green+=3*previous[u-v].green/16;
+ pixel.blue+=3*previous[u-v].blue/16;
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity+=3*previous[u-v].opacity/16;
+ }
+ }
+ pixel.red=(MagickRealType) ClipToQuantum(pixel.red);
+ pixel.green=(MagickRealType) ClipToQuantum(pixel.green);
+ pixel.blue=(MagickRealType) ClipToQuantum(pixel.blue);
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity=(MagickRealType) ClipToQuantum(pixel.opacity);
+ i=(long) ((ScaleQuantumToChar(ClipToQuantum(pixel.red)) >> CacheShift) |
+ (ScaleQuantumToChar(ClipToQuantum(pixel.green)) >> CacheShift) << 6 |
+ (ScaleQuantumToChar(ClipToQuantum(pixel.blue)) >> CacheShift) << 12);
+ if (cube_info->associate_alpha != MagickFalse)
+ i|=((ScaleQuantumToChar(ClipToQuantum(pixel.opacity)) >> CacheShift)
+ << 18);
+ if (p->cache[i] < 0)
+ {
+ register NodeInfo
+ *node_info;
+
+ register unsigned long
+ id;
+
+ /*
+ Identify the deepest node containing the pixel's color.
+ */
+ node_info=p->root;
+ for (index=MaxTreeDepth-1; (long) index > 0; index--)
+ {
+ id=ColorToNodeId(cube_info,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ break;
+ node_info=node_info->child[id];
+ }
+ /*
+ Find closest color among siblings and their children.
+ */
+ p->target=pixel;
+ p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*(QuantumRange+
+ 1.0)+1.0);
+ ClosestColor(image,p,node_info->parent);
+ p->cache[i]=(long) p->color_number;
+ }
+ /*
+ Assign pixel to closest colormap entry.
+ */
+ index=(unsigned long) p->cache[i];
+ if (image->storage_class == PseudoClass)
+ indexes[u]=(IndexPacket) index;
+ if (cube_info->quantize_info->measure_error == MagickFalse)
+ {
+ (q+u)->red=image->colormap[index].red;
+ (q+u)->green=image->colormap[index].green;
+ (q+u)->blue=image->colormap[index].blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ (q+u)->opacity=image->colormap[index].opacity;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Store the error.
+ */
+ AssociateAlphaPixel(cube_info,image->colormap+index,&color);
+ current[u].red=pixel.red-color.red;
+ current[u].green=pixel.green-color.green;
+ current[u].blue=pixel.blue-color.blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ current[u].opacity=pixel.opacity-color.opacity;
+ proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
+ if (proceed == MagickFalse)
+ return(MagickFalse);
+ p->offset++;
+ }
+ }
+ scanlines=(RealPixelPacket *) RelinquishMagickMemory(scanlines);
+ image_view=DestroyCacheView(image_view);
+ return(MagickTrue);
+}
+
+static MagickBooleanType
+ RiemersmaDither(Image *,CacheView *,CubeInfo *,const unsigned int);
+
+static void Riemersma(Image *image,CacheView *image_view,CubeInfo *cube_info,
+ const unsigned long level,const unsigned int direction)
+{
+ if (level == 1)
+ switch (direction)
+ {
+ case WestGravity:
+ {
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ break;
+ }
+ case EastGravity:
+ {
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ break;
+ }
+ case NorthGravity:
+ {
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ break;
+ }
+ case SouthGravity:
+ {
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ break;
+ }
+ default:
+ break;
+ }
+ else
+ switch (direction)
+ {
+ case WestGravity:
+ {
+ Riemersma(image,image_view,cube_info,level-1,NorthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ Riemersma(image,image_view,cube_info,level-1,WestGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ Riemersma(image,image_view,cube_info,level-1,WestGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ Riemersma(image,image_view,cube_info,level-1,SouthGravity);
+ break;
+ }
+ case EastGravity:
+ {
+ Riemersma(image,image_view,cube_info,level-1,SouthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ Riemersma(image,image_view,cube_info,level-1,EastGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ Riemersma(image,image_view,cube_info,level-1,EastGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ Riemersma(image,image_view,cube_info,level-1,NorthGravity);
+ break;
+ }
+ case NorthGravity:
+ {
+ Riemersma(image,image_view,cube_info,level-1,WestGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ Riemersma(image,image_view,cube_info,level-1,NorthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,EastGravity);
+ Riemersma(image,image_view,cube_info,level-1,NorthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ Riemersma(image,image_view,cube_info,level-1,EastGravity);
+ break;
+ }
+ case SouthGravity:
+ {
+ Riemersma(image,image_view,cube_info,level-1,EastGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,NorthGravity);
+ Riemersma(image,image_view,cube_info,level-1,SouthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,WestGravity);
+ Riemersma(image,image_view,cube_info,level-1,SouthGravity);
+ (void) RiemersmaDither(image,image_view,cube_info,SouthGravity);
+ Riemersma(image,image_view,cube_info,level-1,WestGravity);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static MagickBooleanType RiemersmaDither(Image *image,CacheView *image_view,
+ CubeInfo *cube_info,const unsigned int direction)
+{
+#define DitherImageTag "Dither/Image"
+
+ MagickBooleanType
+ proceed;
+
+ RealPixelPacket
+ color,
+ pixel;
+
+ register CubeInfo
+ *p;
+
+ unsigned long
+ index;
+
+ p=cube_info;
+ if ((p->x >= 0) && (p->x < (long) image->columns) &&
+ (p->y >= 0) && (p->y < (long) image->rows))
+ {
+ ExceptionInfo
+ *exception;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Distribute error.
+ */
+ exception=(&image->exception);
+ q=GetCacheViewAuthenticPixels(image_view,p->x,p->y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ return(MagickFalse);
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ AssociateAlphaPixel(cube_info,q,&pixel);
+ for (i=0; i < ErrorQueueLength; i++)
+ {
+ pixel.red+=p->weights[i]*p->error[i].red;
+ pixel.green+=p->weights[i]*p->error[i].green;
+ pixel.blue+=p->weights[i]*p->error[i].blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity+=p->weights[i]*p->error[i].opacity;
+ }
+ pixel.red=(MagickRealType) ClipToQuantum(pixel.red);
+ pixel.green=(MagickRealType) ClipToQuantum(pixel.green);
+ pixel.blue=(MagickRealType) ClipToQuantum(pixel.blue);
+ if (cube_info->associate_alpha != MagickFalse)
+ pixel.opacity=(MagickRealType) ClipToQuantum(pixel.opacity);
+ i=(long) ((ScaleQuantumToChar(ClipToQuantum(pixel.red)) >> CacheShift) |
+ (ScaleQuantumToChar(ClipToQuantum(pixel.green)) >> CacheShift) << 6 |
+ (ScaleQuantumToChar(ClipToQuantum(pixel.blue)) >> CacheShift) << 12);
+ if (cube_info->associate_alpha != MagickFalse)
+ i|=((ScaleQuantumToChar(ClipToQuantum(pixel.opacity)) >> CacheShift)
+ << 18);
+ if (p->cache[i] < 0)
+ {
+ register NodeInfo
+ *node_info;
+
+ register unsigned long
+ id;
+
+ /*
+ Identify the deepest node containing the pixel's color.
+ */
+ node_info=p->root;
+ for (index=MaxTreeDepth-1; (long) index > 0; index--)
+ {
+ id=ColorToNodeId(cube_info,&pixel,index);
+ if (node_info->child[id] == (NodeInfo *) NULL)
+ break;
+ node_info=node_info->child[id];
+ }
+ /*
+ Find closest color among siblings and their children.
+ */
+ p->target=pixel;
+ p->distance=(MagickRealType) (4.0*(QuantumRange+1.0)*((MagickRealType)
+ QuantumRange+1.0)+1.0);
+ ClosestColor(image,p,node_info->parent);
+ p->cache[i]=(long) p->color_number;
+ }
+ /*
+ Assign pixel to closest colormap entry.
+ */
+ index=(unsigned long) (1*p->cache[i]);
+ if (image->storage_class == PseudoClass)
+ *indexes=(IndexPacket) index;
+ if (cube_info->quantize_info->measure_error == MagickFalse)
+ {
+ q->red=image->colormap[index].red;
+ q->green=image->colormap[index].green;
+ q->blue=image->colormap[index].blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ q->opacity=image->colormap[index].opacity;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Propagate the error as the last entry of the error queue.
+ */
+ (void) CopyMagickMemory(p->error,p->error+1,(ErrorQueueLength-1)*
+ sizeof(p->error[0]));
+ AssociateAlphaPixel(cube_info,image->colormap+index,&color);
+ p->error[ErrorQueueLength-1].red=pixel.red-color.red;
+ p->error[ErrorQueueLength-1].green=pixel.green-color.green;
+ p->error[ErrorQueueLength-1].blue=pixel.blue-color.blue;
+ if (cube_info->associate_alpha != MagickFalse)
+ p->error[ErrorQueueLength-1].opacity=pixel.opacity-color.opacity;
+ proceed=SetImageProgress(image,DitherImageTag,p->offset,p->span);
+ if (proceed == MagickFalse)
+ return(MagickFalse);
+ p->offset++;
+ }
+ switch (direction)
+ {
+ case WestGravity: p->x--; break;
+ case EastGravity: p->x++; break;
+ case NorthGravity: p->y--; break;
+ case SouthGravity: p->y++; break;
+ }
+ return(MagickTrue);
+}
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline long MagickMin(const long x,const long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static MagickBooleanType DitherImage(Image *image,CubeInfo *cube_info)
+{
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ unsigned long
+ depth;
+
+ CacheView
+ *image_view;
+
+ if (cube_info->quantize_info->dither_method == FloydSteinbergDitherMethod)
+ return(FloydSteinbergDither(image,cube_info));
+ /*
+ Distribute quantization error along a Hilbert curve.
+ */
+ (void) ResetMagickMemory(cube_info->error,0,ErrorQueueLength*
+ sizeof(*cube_info->error));
+ cube_info->x=0;
+ cube_info->y=0;
+ i=MagickMax((long) image->columns,(long) image->rows);
+ for (depth=1; i != 0; depth++)
+ i>>=1;
+ if ((long) (1L << depth) < MagickMax((long) image->columns,(long) image->rows))
+ depth++;
+ cube_info->offset=0;
+ cube_info->span=(MagickSizeType) image->columns*image->rows;
+ image_view=AcquireCacheView(image);
+ if (depth > 1)
+ Riemersma(image,image_view,cube_info,depth-1,NorthGravity);
+ status=RiemersmaDither(image,image_view,cube_info,ForgetGravity);
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t C u b e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetCubeInfo() initialize the Cube data structure.
+%
+% The format of the GetCubeInfo method is:
+%
+% CubeInfo GetCubeInfo(const QuantizeInfo *quantize_info,
+% const unsigned long depth,const unsigned long maximum_colors)
+%
+% A description of each parameter follows.
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o depth: Normally, this integer value is zero or one. A zero or
+% one tells Quantize to choose a optimal tree depth of Log4(number_colors).
+% A tree of this depth generally allows the best representation of the
+% reference image with the least amount of memory and the fastest
+% computational speed. In some cases, such as an image with low color
+% dispersion (a few number of colors), a value other than
+% Log4(number_colors) is required. To expand the color tree completely,
+% use a value of 8.
+%
+% o maximum_colors: maximum colors.
+%
+*/
+static CubeInfo *GetCubeInfo(const QuantizeInfo *quantize_info,
+ const unsigned long depth,const unsigned long maximum_colors)
+{
+ CubeInfo
+ *cube_info;
+
+ MagickRealType
+ sum,
+ weight;
+
+ size_t
+ length;
+
+ register long
+ i;
+
+ /*
+ Initialize tree to describe color cube_info.
+ */
+ cube_info=(CubeInfo *) AcquireMagickMemory(sizeof(*cube_info));
+ if (cube_info == (CubeInfo *) NULL)
+ return((CubeInfo *) NULL);
+ (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
+ cube_info->depth=depth;
+ if (cube_info->depth > MaxTreeDepth)
+ cube_info->depth=MaxTreeDepth;
+ if (cube_info->depth < 2)
+ cube_info->depth=2;
+ cube_info->maximum_colors=maximum_colors;
+ /*
+ Initialize root node.
+ */
+ cube_info->root=GetNodeInfo(cube_info,0,0,(NodeInfo *) NULL);
+ if (cube_info->root == (NodeInfo *) NULL)
+ return((CubeInfo *) NULL);
+ cube_info->root->parent=cube_info->root;
+ cube_info->quantize_info=CloneQuantizeInfo(quantize_info);
+ if (cube_info->quantize_info->dither == MagickFalse)
+ return(cube_info);
+ /*
+ Initialize dither resources.
+ */
+ length=(size_t) (1UL << (4*(8-CacheShift)));
+ cube_info->cache=(long *) AcquireQuantumMemory(length,
+ sizeof(*cube_info->cache));
+ if (cube_info->cache == (long *) NULL)
+ return((CubeInfo *) NULL);
+ /*
+ Initialize color cache.
+ */
+ for (i=0; i < (long) length; i++)
+ cube_info->cache[i]=(-1);
+ /*
+ Distribute weights along a curve of exponential decay.
+ */
+ weight=1.0;
+ for (i=0; i < ErrorQueueLength; i++)
+ {
+ cube_info->weights[ErrorQueueLength-i-1]=1.0/weight;
+ weight*=exp(log(((double) QuantumRange+1.0))/(ErrorQueueLength-1.0));
+ }
+ /*
+ Normalize the weighting factors.
+ */
+ weight=0.0;
+ for (i=0; i < ErrorQueueLength; i++)
+ weight+=cube_info->weights[i];
+ sum=0.0;
+ for (i=0; i < ErrorQueueLength; i++)
+ {
+ cube_info->weights[i]/=weight;
+ sum+=cube_info->weights[i];
+ }
+ cube_info->weights[0]+=1.0-sum;
+ return(cube_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t N o d e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNodeInfo() allocates memory for a new node in the color cube tree and
+% presets all fields to zero.
+%
+% The format of the GetNodeInfo method is:
+%
+% NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long id,
+% const unsigned long level,NodeInfo *parent)
+%
+% A description of each parameter follows.
+%
+% o node: The GetNodeInfo method returns a pointer to a queue of nodes.
+%
+% o id: Specifies the child number of the node.
+%
+% o level: Specifies the level in the storage_class the node resides.
+%
+*/
+static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long id,
+ const unsigned long level,NodeInfo *parent)
+{
+ NodeInfo
+ *node_info;
+
+ if (cube_info->free_nodes == 0)
+ {
+ Nodes
+ *nodes;
+
+ /*
+ Allocate a new queue of nodes.
+ */
+ nodes=(Nodes *) AcquireMagickMemory(sizeof(*nodes));
+ if (nodes == (Nodes *) NULL)
+ return((NodeInfo *) NULL);
+ nodes->nodes=(NodeInfo *) AcquireQuantumMemory(NodesInAList,
+ sizeof(*nodes->nodes));
+ if (nodes->nodes == (NodeInfo *) NULL)
+ return((NodeInfo *) NULL);
+ nodes->next=cube_info->node_queue;
+ cube_info->node_queue=nodes;
+ cube_info->next_node=nodes->nodes;
+ cube_info->free_nodes=NodesInAList;
+ }
+ cube_info->nodes++;
+ cube_info->free_nodes--;
+ node_info=cube_info->next_node++;
+ (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
+ node_info->parent=parent;
+ node_info->id=id;
+ node_info->level=level;
+ return(node_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e Q u a n t i z e E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageQuantizeError() measures the difference between the original
+% and quantized images. This difference is the total quantization error.
+% The error is computed by summing over all pixels in an image the distance
+% squared in RGB space between each reference pixel value and its quantized
+% value. These values are computed:
+%
+% o mean_error_per_pixel: This value is the mean error for any single
+% pixel in the image.
+%
+% o normalized_mean_square_error: This value is the normalized mean
+% quantization error for any single pixel in the image. This distance
+% measure is normalized to a range between 0 and 1. It is independent
+% of the range of red, green, and blue values in the image.
+%
+% o normalized_maximum_square_error: Thsi value is the normalized
+% maximum quantization error for any single pixel in the image. This
+% distance measure is normalized to a range between 0 and 1. It is
+% independent of the range of red, green, and blue values in your image.
+%
+% The format of the GetImageQuantizeError method is:
+%
+% MagickBooleanType GetImageQuantizeError(Image *image)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType GetImageQuantizeError(Image *image)
+{
+ ExceptionInfo
+ *exception;
+
+ IndexPacket
+ *indexes;
+
+ long
+ y;
+
+ MagickRealType
+ alpha,
+ area,
+ beta,
+ distance,
+ maximum_error,
+ mean_error,
+ mean_error_per_pixel;
+
+ unsigned long
+ index;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ image->total_colors=GetNumberColors(image,(FILE *) NULL,&image->exception);
+ (void) ResetMagickMemory(&image->error,0,sizeof(image->error));
+ if (image->storage_class == DirectClass)
+ return(MagickTrue);
+ alpha=1.0;
+ beta=1.0;
+ area=3.0*image->columns*image->rows;
+ maximum_error=0.0;
+ mean_error_per_pixel=0.0;
+ mean_error=0.0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ index=1UL*indexes[x];
+ if (image->matte != MagickFalse)
+ {
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity));
+ beta=(MagickRealType) (QuantumScale*(QuantumRange-
+ image->colormap[index].opacity));
+ }
+ distance=fabs(alpha*p->red-beta*image->colormap[index].red);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ distance=fabs(alpha*p->green-beta*image->colormap[index].green);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ distance=fabs(alpha*p->blue-beta*image->colormap[index].blue);
+ mean_error_per_pixel+=distance;
+ mean_error+=distance*distance;
+ if (distance > maximum_error)
+ maximum_error=distance;
+ p++;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ image->error.mean_error_per_pixel=(double) mean_error_per_pixel/area;
+ image->error.normalized_mean_error=(double) QuantumScale*QuantumScale*
+ mean_error/area;
+ image->error.normalized_maximum_error=(double) QuantumScale*maximum_error;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t Q u a n t i z e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetQuantizeInfo() initializes the QuantizeInfo structure.
+%
+% The format of the GetQuantizeInfo method is:
+%
+% GetQuantizeInfo(QuantizeInfo *quantize_info)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to a QuantizeInfo structure.
+%
+*/
+MagickExport void GetQuantizeInfo(QuantizeInfo *quantize_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(quantize_info != (QuantizeInfo *) NULL);
+ (void) ResetMagickMemory(quantize_info,0,sizeof(*quantize_info));
+ quantize_info->number_colors=256;
+ quantize_info->dither=MagickTrue;
+ quantize_info->dither_method=RiemersmaDitherMethod;
+ quantize_info->colorspace=UndefinedColorspace;
+ quantize_info->measure_error=MagickFalse;
+ quantize_info->signature=MagickSignature;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P o s t e r i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PosterizeImage() reduces the image to a limited number of colors for a
+% "poster" effect.
+%
+% The format of the PosterizeImage method is:
+%
+% MagickBooleanType PosterizeImage(Image *image,const unsigned long levels,
+% const MagickBooleanType dither)
+%
+% A description of each parameter follows:
+%
+% o image: Specifies a pointer to an Image structure.
+%
+% o levels: Number of color levels allowed in each channel. Very low values
+% (2, 3, or 4) have the most visible effect.
+%
+% o dither: Set this integer value to something other than zero to
+% dither the mapped image.
+%
+*/
+MagickExport MagickBooleanType PosterizeImage(Image *image,
+ const unsigned long levels,const MagickBooleanType dither)
+{
+ ExceptionInfo
+ *exception;
+
+ Image
+ *posterize_image;
+
+ IndexPacket
+ *indexes;
+
+ long
+ j,
+ k,
+ l,
+ n;
+
+ MagickBooleanType
+ status;
+
+ QuantizeInfo
+ *quantize_info;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict q;
+
+ CacheView
+ *posterize_view;
+
+ /*
+ Posterize image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ posterize_image=AcquireImage((ImageInfo *) NULL);
+ if (posterize_image == (Image *) NULL)
+ return(MagickFalse);
+ l=1;
+ while ((l*l*l) < (long) MagickMin((long) levels*levels*levels,MaxColormapSize+1))
+ l++;
+ status=SetImageExtent(posterize_image,(unsigned long) (l*l*l),1);
+ if (status == MagickFalse)
+ {
+ posterize_image=DestroyImage(posterize_image);
+ return(MagickFalse);
+ }
+ status=AcquireImageColormap(posterize_image,levels*levels*levels);
+ if (status == MagickFalse)
+ {
+ posterize_image=DestroyImage(posterize_image);
+ return(MagickFalse);
+ }
+ posterize_view=AcquireCacheView(posterize_image);
+ exception=(&image->exception);
+ q=QueueCacheViewAuthenticPixels(posterize_view,0,0,posterize_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ posterize_view=DestroyCacheView(posterize_view);
+ posterize_image=DestroyImage(posterize_image);
+ return(MagickFalse);
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(posterize_view);
+ n=0;
+ for (i=0; i < l; i++)
+ for (j=0; j < l; j++)
+ for (k=0; k < l; k++)
+ {
+ posterize_image->colormap[n].red=(Quantum) (QuantumRange*i/
+ MagickMax(l-1L,1L));
+ posterize_image->colormap[n].green=(Quantum)
+ (QuantumRange*j/MagickMax(l-1L,1L));
+ posterize_image->colormap[n].blue=(Quantum) (QuantumRange*k/
+ MagickMax(l-1L,1L));
+ posterize_image->colormap[n].opacity=OpaqueOpacity;
+ *q++=posterize_image->colormap[n];
+ indexes[n]=(IndexPacket) n;
+ n++;
+ }
+ if (SyncCacheViewAuthenticPixels(posterize_view,exception) == MagickFalse)
+ {
+ posterize_view=DestroyCacheView(posterize_view);
+ posterize_image=DestroyImage(posterize_image);
+ return(MagickFalse);
+ }
+ posterize_view=DestroyCacheView(posterize_view);
+ quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
+ quantize_info->dither=dither;
+ status=RemapImage(quantize_info,image,posterize_image);
+ quantize_info=DestroyQuantizeInfo(quantize_info);
+ posterize_image=DestroyImage(posterize_image);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P r u n e C h i l d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PruneChild() deletes the given node and merges its statistics into its
+% parent.
+%
+% The format of the PruneSubtree method is:
+%
+% PruneChild(const Image *image,CubeInfo *cube_info,
+% const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: pointer to node in color cube tree that is to be pruned.
+%
+*/
+static void PruneChild(const Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info)
+{
+ NodeInfo
+ *parent;
+
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ PruneChild(image,cube_info,node_info->child[i]);
+ /*
+ Merge color statistics into parent.
+ */
+ parent=node_info->parent;
+ parent->number_unique+=node_info->number_unique;
+ parent->total_color.red+=node_info->total_color.red;
+ parent->total_color.green+=node_info->total_color.green;
+ parent->total_color.blue+=node_info->total_color.blue;
+ parent->total_color.opacity+=node_info->total_color.opacity;
+ parent->child[node_info->id]=(NodeInfo *) NULL;
+ cube_info->nodes--;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P r u n e L e v e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PruneLevel() deletes all nodes at the bottom level of the color tree merging
+% their color statistics into their parent node.
+%
+% The format of the PruneLevel method is:
+%
+% PruneLevel(const Image *image,CubeInfo *cube_info,
+% const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: pointer to node in color cube tree that is to be pruned.
+%
+*/
+static void PruneLevel(const Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ PruneLevel(image,cube_info,node_info->child[i]);
+ if (node_info->level == cube_info->depth)
+ PruneChild(image,cube_info,node_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ P r u n e T o C u b e D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PruneToCubeDepth() deletes any nodes at a depth greater than
+% cube_info->depth while merging their color statistics into their parent
+% node.
+%
+% The format of the PruneToCubeDepth method is:
+%
+% PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
+% const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: pointer to node in color cube tree that is to be pruned.
+%
+*/
+static void PruneToCubeDepth(const Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ PruneToCubeDepth(image,cube_info,node_info->child[i]);
+ if (node_info->level > cube_info->depth)
+ PruneChild(image,cube_info,node_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u a n t i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QuantizeImage() analyzes the colors within a reference image and chooses a
+% fixed number of colors to represent the image. The goal of the algorithm
+% is to minimize the color difference between the input and output image while
+% minimizing the processing time.
+%
+% The format of the QuantizeImage method is:
+%
+% MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
+% Image *image)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType QuantizeImage(const QuantizeInfo *quantize_info,
+ Image *image)
+{
+ CubeInfo
+ *cube_info;
+
+ MagickBooleanType
+ status;
+
+ unsigned long
+ depth,
+ maximum_colors;
+
+ assert(quantize_info != (const QuantizeInfo *) NULL);
+ assert(quantize_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ maximum_colors=quantize_info->number_colors;
+ if (maximum_colors == 0)
+ maximum_colors=MaxColormapSize;
+ if (maximum_colors > MaxColormapSize)
+ maximum_colors=MaxColormapSize;
+ if ((IsGrayImage(image,&image->exception) != MagickFalse) &&
+ (image->matte == MagickFalse))
+ (void) SetGrayscaleImage(image);
+ if ((image->storage_class == PseudoClass) &&
+ (image->colors <= maximum_colors))
+ return(MagickTrue);
+ depth=quantize_info->tree_depth;
+ if (depth == 0)
+ {
+ unsigned long
+ colors;
+
+ /*
+ Depth of color tree is: Log4(colormap size)+2.
+ */
+ colors=maximum_colors;
+ for (depth=1; colors != 0; depth++)
+ colors>>=2;
+ if ((quantize_info->dither != MagickFalse) && (depth > 2))
+ depth--;
+ if ((image->matte != MagickFalse) && (depth > 5))
+ depth--;
+ }
+ /*
+ Initialize color cube.
+ */
+ cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
+ if (cube_info == (CubeInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ status=ClassifyImageColors(cube_info,image,&image->exception);
+ if (status != MagickFalse)
+ {
+ /*
+ Reduce the number of colors in the image.
+ */
+ ReduceImageColors(image,cube_info);
+ status=AssignImageColors(image,cube_info);
+ }
+ DestroyCubeInfo(cube_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Q u a n t i z e I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QuantizeImages() analyzes the colors within a set of reference images and
+% chooses a fixed number of colors to represent the set. The goal of the
+% algorithm is to minimize the color difference between the input and output
+% images while minimizing the processing time.
+%
+% The format of the QuantizeImages method is:
+%
+% MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
+% Image *images)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o images: Specifies a pointer to a list of Image structures.
+%
+*/
+MagickExport MagickBooleanType QuantizeImages(const QuantizeInfo *quantize_info,
+ Image *images)
+{
+ CubeInfo
+ *cube_info;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ proceed,
+ status;
+
+ MagickProgressMonitor
+ progress_monitor;
+
+ register long
+ i;
+
+ unsigned long
+ depth,
+ maximum_colors,
+ number_images;
+
+ assert(quantize_info != (const QuantizeInfo *) NULL);
+ assert(quantize_info->signature == MagickSignature);
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ if (GetNextImageInList(images) == (Image *) NULL)
+ {
+ /*
+ Handle a single image with QuantizeImage.
+ */
+ status=QuantizeImage(quantize_info,images);
+ return(status);
+ }
+ status=MagickFalse;
+ maximum_colors=quantize_info->number_colors;
+ if (maximum_colors == 0)
+ maximum_colors=MaxColormapSize;
+ if (maximum_colors > MaxColormapSize)
+ maximum_colors=MaxColormapSize;
+ depth=quantize_info->tree_depth;
+ if (depth == 0)
+ {
+ unsigned long
+ colors;
+
+ /*
+ Depth of color tree is: Log4(colormap size)+2.
+ */
+ colors=maximum_colors;
+ for (depth=1; colors != 0; depth++)
+ colors>>=2;
+ if (quantize_info->dither != MagickFalse)
+ depth--;
+ }
+ /*
+ Initialize color cube.
+ */
+ cube_info=GetCubeInfo(quantize_info,depth,maximum_colors);
+ if (cube_info == (CubeInfo *) NULL)
+ {
+ (void) ThrowMagickException(&images->exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",images->filename);
+ return(MagickFalse);
+ }
+ number_images=GetImageListLength(images);
+ image=images;
+ for (i=0; image != (Image *) NULL; i++)
+ {
+ progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
+ image->client_data);
+ status=ClassifyImageColors(cube_info,image,&image->exception);
+ if (status == MagickFalse)
+ break;
+ (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
+ proceed=SetImageProgress(image,AssignImageTag,i,number_images);
+ if (proceed == MagickFalse)
+ break;
+ image=GetNextImageInList(image);
+ }
+ if (status != MagickFalse)
+ {
+ /*
+ Reduce the number of colors in an image sequence.
+ */
+ ReduceImageColors(images,cube_info);
+ image=images;
+ for (i=0; image != (Image *) NULL; i++)
+ {
+ progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor)
+ NULL,image->client_data);
+ status=AssignImageColors(image,cube_info);
+ if (status == MagickFalse)
+ break;
+ (void) SetImageProgressMonitor(image,progress_monitor,
+ image->client_data);
+ proceed=SetImageProgress(image,AssignImageTag,i,number_images);
+ if (proceed == MagickFalse)
+ break;
+ image=GetNextImageInList(image);
+ }
+ }
+ DestroyCubeInfo(cube_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e d u c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Reduce() traverses the color cube tree and prunes any node whose
+% quantization error falls below a particular threshold.
+%
+% The format of the Reduce method is:
+%
+% Reduce(const Image *image,CubeInfo *cube_info,const NodeInfo *node_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+% o node_info: pointer to node in color cube tree that is to be pruned.
+%
+*/
+static void Reduce(const Image *image,CubeInfo *cube_info,
+ const NodeInfo *node_info)
+{
+ register long
+ i;
+
+ unsigned long
+ number_children;
+
+ /*
+ Traverse any children.
+ */
+ number_children=cube_info->associate_alpha == MagickFalse ? 8UL : 16UL;
+ for (i=0; i < (long) number_children; i++)
+ if (node_info->child[i] != (NodeInfo *) NULL)
+ Reduce(image,cube_info,node_info->child[i]);
+ if (node_info->quantize_error <= cube_info->pruning_threshold)
+ PruneChild(image,cube_info,node_info);
+ else
+ {
+ /*
+ Find minimum pruning threshold.
+ */
+ if (node_info->number_unique > 0)
+ cube_info->colors++;
+ if (node_info->quantize_error < cube_info->next_threshold)
+ cube_info->next_threshold=node_info->quantize_error;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ R e d u c e I m a g e C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReduceImageColors() repeatedly prunes the tree until the number of nodes
+% with n2 > 0 is less than or equal to the maximum number of colors allowed
+% in the output image. On any given iteration over the tree, it selects
+% those nodes whose E value is minimal for pruning and merges their
+% color statistics upward. It uses a pruning threshold, Ep, to govern
+% node selection as follows:
+%
+% Ep = 0
+% while number of nodes with (n2 > 0) > required maximum number of colors
+% prune all nodes such that E <= Ep
+% Set Ep to minimum E in remaining nodes
+%
+% This has the effect of minimizing any quantization error when merging
+% two nodes together.
+%
+% When a node to be pruned has offspring, the pruning procedure invokes
+% itself recursively in order to prune the tree from the leaves upward.
+% n2, Sr, Sg, and Sb in a node being pruned are always added to the
+% corresponding data in that node's parent. This retains the pruned
+% node's color characteristics for later averaging.
+%
+% For each node, n2 pixels exist for which that node represents the
+% smallest volume in RGB space containing those pixel's colors. When n2
+% > 0 the node will uniquely define a color in the output image. At the
+% beginning of reduction, n2 = 0 for all nodes except a the leaves of
+% the tree which represent colors present in the input image.
+%
+% The other pixel count, n1, indicates the total number of colors
+% within the cubic volume which the node represents. This includes n1 -
+% n2 pixels whose colors should be defined by nodes at a lower level in
+% the tree.
+%
+% The format of the ReduceImageColors method is:
+%
+% ReduceImageColors(const Image *image,CubeInfo *cube_info)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cube_info: A pointer to the Cube structure.
+%
+*/
+static void ReduceImageColors(const Image *image,CubeInfo *cube_info)
+{
+#define ReduceImageTag "Reduce/Image"
+
+ MagickBooleanType
+ proceed;
+
+ MagickOffsetType
+ offset;
+
+ unsigned long
+ span;
+
+ cube_info->next_threshold=0.0;
+ for (span=cube_info->colors; cube_info->colors > cube_info->maximum_colors; )
+ {
+ cube_info->pruning_threshold=cube_info->next_threshold;
+ cube_info->next_threshold=cube_info->root->quantize_error-1;
+ cube_info->colors=0;
+ Reduce(image,cube_info,cube_info->root);
+ offset=(MagickOffsetType) span-cube_info->colors;
+ proceed=SetImageProgress(image,ReduceImageTag,offset,span-
+ cube_info->maximum_colors+1);
+ if (proceed == MagickFalse)
+ break;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m a p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemapImage() replaces the colors of an image with the closest color from
+% a reference image.
+%
+% The format of the RemapImage method is:
+%
+% MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
+% Image *image,const Image *remap_image)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o image: the image.
+%
+% o remap_image: the reference image.
+%
+*/
+MagickExport MagickBooleanType RemapImage(const QuantizeInfo *quantize_info,
+ Image *image,const Image *remap_image)
+{
+ CubeInfo
+ *cube_info;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Initialize color cube.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(remap_image != (Image *) NULL);
+ assert(remap_image->signature == MagickSignature);
+ cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
+ quantize_info->number_colors);
+ if (cube_info == (CubeInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ status=ClassifyImageColors(cube_info,remap_image,&image->exception);
+ if (status != MagickFalse)
+ {
+ /*
+ Classify image colors from the reference image.
+ */
+ cube_info->quantize_info->number_colors=cube_info->colors;
+ status=AssignImageColors(image,cube_info);
+ }
+ DestroyCubeInfo(cube_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m a p I m a g e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemapImages() replaces the colors of a sequence of images with the
+% closest color from a reference image.
+%
+% The format of the RemapImage method is:
+%
+% MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
+% Image *images,Image *remap_image)
+%
+% A description of each parameter follows:
+%
+% o quantize_info: Specifies a pointer to an QuantizeInfo structure.
+%
+% o images: the image sequence.
+%
+% o remap_image: the reference image.
+%
+*/
+MagickExport MagickBooleanType RemapImages(const QuantizeInfo *quantize_info,
+ Image *images,const Image *remap_image)
+{
+ CubeInfo
+ *cube_info;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ status;
+
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ image=images;
+ if (remap_image == (Image *) NULL)
+ {
+ /*
+ Create a global colormap for an image sequence.
+ */
+ status=QuantizeImages(quantize_info,images);
+ return(status);
+ }
+ /*
+ Classify image colors from the reference image.
+ */
+ cube_info=GetCubeInfo(quantize_info,MaxTreeDepth,
+ quantize_info->number_colors);
+ if (cube_info == (CubeInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ status=ClassifyImageColors(cube_info,remap_image,&image->exception);
+ if (status != MagickFalse)
+ {
+ /*
+ Classify image colors from the reference image.
+ */
+ cube_info->quantize_info->number_colors=cube_info->colors;
+ image=images;
+ for ( ; image != (Image *) NULL; image=GetNextImageInList(image))
+ {
+ status=AssignImageColors(image,cube_info);
+ if (status == MagickFalse)
+ break;
+ }
+ }
+ DestroyCubeInfo(cube_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t G r a y s c a l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetGrayscaleImage() converts an image to a PseudoClass grayscale image.
+%
+% The format of the SetGrayscaleImage method is:
+%
+% MagickBooleanType SetGrayscaleImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: The image.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int IntensityCompare(const void *x,const void *y)
+{
+ long
+ intensity;
+
+ PixelPacket
+ *color_1,
+ *color_2;
+
+ color_1=(PixelPacket *) x;
+ color_2=(PixelPacket *) y;
+ intensity=PixelIntensityToQuantum(color_1)-(long)
+ PixelIntensityToQuantum(color_2);
+ return(intensity);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static MagickBooleanType SetGrayscaleImage(Image *image)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ j,
+ y;
+
+ PixelPacket
+ *colormap;
+
+ long
+ *colormap_index;
+
+ register long
+ i;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->type != GrayscaleType)
+ (void) TransformImageColorspace(image,GRAYColorspace);
+ colormap_index=(long *) AcquireQuantumMemory(MaxMap+1,
+ sizeof(*colormap_index));
+ if (colormap_index == (long *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ if (image->storage_class != PseudoClass)
+ {
+ ExceptionInfo
+ *exception;
+
+ for (i=0; i <= (long) MaxMap; i++)
+ colormap_index[i]=(-1);
+ if (AcquireImageColormap(image,MaxMap+1) == MagickFalse)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ image->colors=0;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register const PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ register unsigned long
+ intensity;
+
+ intensity=ScaleQuantumToMap(q->red);
+ if (colormap_index[intensity] < 0)
+ {
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SetGrayscaleImage)
+#endif
+ if (colormap_index[intensity] < 0)
+ {
+ colormap_index[intensity]=(long) image->colors;
+ image->colormap[image->colors]=(*q);
+ image->colors++;
+ }
+ }
+ indexes[x]=(IndexPacket) colormap_index[intensity];
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ }
+ for (i=0; i < (long) image->colors; i++)
+ image->colormap[i].opacity=(unsigned short) i;
+ qsort((void *) image->colormap,image->colors,sizeof(PixelPacket),
+ IntensityCompare);
+ colormap=(PixelPacket *) AcquireQuantumMemory(image->colors,
+ sizeof(*colormap));
+ if (colormap == (PixelPacket *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ j=0;
+ colormap[j]=image->colormap[0];
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (IsSameColor(image,&colormap[j],&image->colormap[i]) == MagickFalse)
+ {
+ j++;
+ colormap[j]=image->colormap[i];
+ }
+ colormap_index[(long) image->colormap[i].opacity]=j;
+ }
+ image->colors=(unsigned long) (j+1);
+ image->colormap=(PixelPacket *) RelinquishMagickMemory(image->colormap);
+ image->colormap=colormap;
+ status=MagickTrue;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register const PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ indexes[x]=(IndexPacket) colormap_index[ScaleQuantumToMap(indexes[x])];
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ colormap_index=(long *) RelinquishMagickMemory(colormap_index);
+ image->type=GrayscaleType;
+ if (IsMonochromeImage(image,&image->exception) != MagickFalse)
+ image->type=BilevelType;
+ return(status);
+}
diff --git a/magick/quantize.h b/magick/quantize.h
new file mode 100644
index 0000000..6f6ca54
--- /dev/null
+++ b/magick/quantize.h
@@ -0,0 +1,80 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image quantization methods.
+*/
+#ifndef _MAGICKCORE_QUANTIZE_H
+#define _MAGICKCORE_QUANTIZE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/colorspace.h"
+
+typedef enum
+{
+ UndefinedDitherMethod,
+ NoDitherMethod,
+ RiemersmaDitherMethod,
+ FloydSteinbergDitherMethod
+} DitherMethod;
+
+typedef struct _QuantizeInfo
+{
+ unsigned long
+ number_colors;
+
+ unsigned long
+ tree_depth;
+
+ MagickBooleanType
+ dither;
+
+ ColorspaceType
+ colorspace;
+
+ MagickBooleanType
+ measure_error;
+
+ unsigned long
+ signature;
+
+ DitherMethod
+ dither_method;
+} QuantizeInfo;
+
+extern MagickExport MagickBooleanType
+ CompressImageColormap(Image *),
+ GetImageQuantizeError(Image *),
+ PosterizeImage(Image *,const unsigned long,const MagickBooleanType),
+ QuantizeImage(const QuantizeInfo *,Image *),
+ QuantizeImages(const QuantizeInfo *,Image *),
+ RemapImage(const QuantizeInfo *,Image *,const Image *),
+ RemapImages(const QuantizeInfo *,Image *,const Image *);
+
+extern MagickExport QuantizeInfo
+ *AcquireQuantizeInfo(const ImageInfo *),
+ *CloneQuantizeInfo(const QuantizeInfo *),
+ *DestroyQuantizeInfo(QuantizeInfo *);
+
+extern MagickExport void
+ GetQuantizeInfo(QuantizeInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/quantum-export.c b/magick/quantum-export.c
new file mode 100644
index 0000000..c3b9d18
--- /dev/null
+++ b/magick/quantum-export.c
@@ -0,0 +1,2360 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% QQQ U U AAA N N TTTTT U U M M %
+% Q Q U U A A NN N T U U MM MM %
+% Q Q U U AAAAA N N N T U U M M M %
+% Q QQ U U A A N NN T U U M M %
+% QQQQ UUU A A N N T UUU M M %
+% %
+% EEEEE X X PPPP OOO RRRR TTTTT %
+% E X X P P O O R R T %
+% EEE X PPPP O O RRRR T %
+% E X X P O O R R T %
+% EEEEE X X P OOO R R T %
+% %
+% MagickCore Methods to Export Quantum Pixels %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2008 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/cache.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/statistic.h"
+#include "magick/stream.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ E x p o r t Q u a n t u m P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExportQuantumPixels() transfers one or more pixel components from the image
+% pixel cache to a user supplied buffer. The pixels are returned in network
+% byte order. MagickTrue is returned if the pixels are successfully
+% transferred, otherwise MagickFalse.
+%
+% The format of the ExportQuantumPixels method is:
+%
+% size_t ExportQuantumPixels(const Image *image,const CacheView *image_view,
+% const QuantumInfo *quantum_info,const QuantumType quantum_type,
+% unsigned char *pixels,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o image_view: the image cache view.
+%
+% o quantum_info: the quantum info.
+%
+% o quantum_type: Declare which pixel components to transfer (RGB, RGBA,
+% etc).
+%
+% o pixels: The components are transferred to this buffer.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline unsigned char *PopDoublePixel(const QuantumState *quantum_state,
+ const double pixel,unsigned char *pixels)
+{
+ double
+ *p;
+
+ unsigned char
+ quantum[8];
+
+ p=(double *) quantum;
+ *p=(double) (pixel*quantum_state->inverse_scale+quantum_state->minimum);
+ if (quantum_state->endian != LSBEndian)
+ {
+ *pixels++=quantum[7];
+ *pixels++=quantum[6];
+ *pixels++=quantum[5];
+ *pixels++=quantum[4];
+ *pixels++=quantum[3];
+ *pixels++=quantum[2];
+ *pixels++=quantum[1];
+ *pixels++=quantum[0];
+ return(pixels);
+ }
+ *pixels++=quantum[0];
+ *pixels++=quantum[1];
+ *pixels++=quantum[2];
+ *pixels++=quantum[3];
+ *pixels++=quantum[4];
+ *pixels++=quantum[5];
+ *pixels++=quantum[6];
+ *pixels++=quantum[7];
+ return(pixels);
+}
+
+static inline unsigned char *PopFloatPixel(const QuantumState *quantum_state,
+ const float pixel,unsigned char *pixels)
+{
+ float
+ *p;
+
+ unsigned char
+ quantum[4];
+
+ p=(float *) quantum;
+ *p=(float) ((double) pixel*quantum_state->inverse_scale+
+ quantum_state->minimum);
+ if (quantum_state->endian != LSBEndian)
+ {
+ *pixels++=quantum[3];
+ *pixels++=quantum[2];
+ *pixels++=quantum[1];
+ *pixels++=quantum[0];
+ return(pixels);
+ }
+ *pixels++=quantum[0];
+ *pixels++=quantum[1];
+ *pixels++=quantum[2];
+ *pixels++=quantum[3];
+ return(pixels);
+}
+
+static inline unsigned char *PopQuantumPixel(QuantumState *quantum_state,
+ const unsigned long depth,const QuantumAny pixel,unsigned char *pixels)
+{
+ register long
+ i;
+
+ register unsigned long
+ quantum_bits;
+
+ if (quantum_state->bits == 0UL)
+ quantum_state->bits=8UL;
+ for (i=(long) depth; i > 0L; )
+ {
+ quantum_bits=(unsigned long) i;
+ if (quantum_bits > quantum_state->bits)
+ quantum_bits=quantum_state->bits;
+ i-=quantum_bits;
+ if (quantum_state->bits == 8)
+ *pixels='\0';
+ quantum_state->bits-=quantum_bits;
+ *pixels|=(((pixel >> i) &~ ((~0UL) << quantum_bits)) <<
+ quantum_state->bits);
+ if (quantum_state->bits == 0UL)
+ {
+ pixels++;
+ quantum_state->bits=8UL;
+ }
+ }
+ return(pixels);
+}
+
+static inline unsigned char *PopQuantumLongPixel(QuantumState *quantum_state,
+ const unsigned long depth,const unsigned long pixel,unsigned char *pixels)
+{
+ register long
+ i;
+
+ unsigned long
+ quantum_bits;
+
+ if (quantum_state->bits == 0UL)
+ quantum_state->bits=32UL;
+ for (i=(long) depth; i > 0; )
+ {
+ quantum_bits=(unsigned long) i;
+ if (quantum_bits > quantum_state->bits)
+ quantum_bits=quantum_state->bits;
+ quantum_state->pixel|=(((pixel >> (depth-i)) &
+ quantum_state->mask[quantum_bits]) << (32UL-quantum_state->bits));
+ i-=quantum_bits;
+ quantum_state->bits-=quantum_bits;
+ if (quantum_state->bits == 0U)
+ {
+ pixels=PopLongPixel(quantum_state->endian,quantum_state->pixel,pixels);
+ quantum_state->pixel=0U;
+ quantum_state->bits=32UL;
+ }
+ }
+ return(pixels);
+}
+
+MagickExport size_t ExportQuantumPixels(const Image *image,
+ const CacheView *image_view,const QuantumInfo *quantum_info,
+ const QuantumType quantum_type,unsigned char *pixels,ExceptionInfo *exception)
+{
+ EndianType
+ endian;
+
+ long
+ bit;
+
+ MagickRealType
+ alpha;
+
+ MagickSizeType
+ number_pixels;
+
+ QuantumAny
+ range;
+
+ QuantumState
+ quantum_state;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ register unsigned char
+ *q;
+
+ size_t
+ extent;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ if (pixels == (unsigned char *) NULL)
+ pixels=GetQuantumPixels(quantum_info);
+ number_pixels=GetImageExtent(image);
+ p=GetVirtualPixelQueue(image);
+ indexes=GetVirtualIndexQueue(image);
+ if (image_view != (CacheView *) NULL)
+ {
+ number_pixels=GetCacheViewExtent(image_view);
+ p=GetCacheViewVirtualPixelQueue(image_view);
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ }
+ if (quantum_info->alpha_type == AssociatedQuantumAlpha)
+ {
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Associate alpha.
+ */
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=(PixelPacket *) GetCacheViewVirtualPixelQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ alpha=QuantumScale*((double) QuantumRange-q->opacity);
+ q->red=RoundToQuantum(alpha*q->red);
+ q->green=RoundToQuantum(alpha*q->green);
+ q->blue=RoundToQuantum(alpha*q->blue);
+ q++;
+ }
+ }
+ if ((quantum_type == RGBOQuantum) || (quantum_type == CMYKOQuantum))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=(PixelPacket *) GetCacheViewVirtualPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q->opacity=(Quantum) (QuantumRange-q->opacity);
+ q++;
+ }
+ }
+ if ((quantum_type == CbYCrQuantum) || (quantum_type == CbYCrAQuantum))
+ {
+ Quantum
+ quantum;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=GetAuthenticPixelQueue(image);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ quantum=q->red;
+ q->red=q->green;
+ q->green=quantum;
+ q++;
+ }
+ }
+ x=0;
+ q=pixels;
+ InitializeQuantumState(quantum_info,image->endian,&quantum_state);
+ extent=GetQuantumExtent(image,quantum_info,quantum_type);
+ endian=quantum_state.endian;
+ switch (quantum_type)
+ {
+ case IndexQuantum:
+ {
+ if (image->storage_class != PseudoClass)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColormappedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=((long) number_pixels-7); x > 0; x-=8)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q=((pixel & 0x01) << 7);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 6);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 5);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 4);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 3);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 2);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 1);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 0);
+ q++;
+ }
+ if ((number_pixels % 8) != 0)
+ {
+ *q='\0';
+ for (bit=7; bit >= (long) (8-(number_pixels % 8)); bit--)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << (unsigned char) bit);
+ }
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) (number_pixels-1) ; x+=2)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q=((pixel & 0xf) << 4);
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0xf) << 0);
+ q++;
+ }
+ if ((number_pixels % 2) != 0)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q=((pixel & 0xf) << 4);
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopCharPixel((unsigned char) indexes[x],q);
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopShortPixel(endian,(unsigned short) indexes[x],q);
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopLongPixel(endian,(unsigned long) indexes[x],q);
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case IndexAlphaQuantum:
+ {
+ if (image->storage_class != PseudoClass)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColormappedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=((long) number_pixels-3); x > 0; x-=4)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q=((pixel & 0x01) << 7);
+ pixel=(unsigned char) (p->opacity == (Quantum) TransparentOpacity ?
+ 1 : 0);
+ *q|=((pixel & 0x01) << 6);
+ p++;
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 5);
+ pixel=(unsigned char) (p->opacity == (Quantum) TransparentOpacity ?
+ 1 : 0);
+ *q|=((pixel & 0x01) << 4);
+ p++;
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 3);
+ pixel=(unsigned char) (p->opacity == (Quantum) TransparentOpacity ?
+ 1 : 0);
+ *q|=((pixel & 0x01) << 2);
+ p++;
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << 1);
+ pixel=(unsigned char) (p->opacity == (Quantum) TransparentOpacity ?
+ 1 : 0);
+ *q|=((pixel & 0x01) << 0);
+ p++;
+ q++;
+ }
+ if ((number_pixels % 4) != 0)
+ {
+ *q='\0';
+ for (bit=3; bit >= (long) (4-(number_pixels % 4)); bit-=2)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q|=((pixel & 0x01) << (unsigned char) bit);
+ pixel=(unsigned char) (p->opacity == (Quantum)
+ TransparentOpacity ? 1 : 0);
+ *q|=((pixel & 0x01) << (unsigned char) (bit-1));
+ p++;
+ }
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels ; x++)
+ {
+ pixel=(unsigned char) *indexes++;
+ *q=((pixel & 0xf) << 4);
+ pixel=(unsigned char) (16*QuantumScale*((Quantum) (QuantumRange-
+ p->opacity))+0.5);
+ *q|=((pixel & 0xf) << 0);
+ p++;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopCharPixel((unsigned char) indexes[x],q);
+ pixel=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopShortPixel(endian,(unsigned short) indexes[x],q);
+ pixel=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ q=PopFloatPixel(&quantum_state,(float) indexes[x],q);
+ pixel=(float) (QuantumRange-p->opacity);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopLongPixel(endian,(unsigned long) indexes[x],q);
+ pixel=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ double
+ pixel;
+
+ q=PopDoublePixel(&quantum_state,(double) indexes[x],q);
+ pixel=(double) (QuantumRange-p->opacity);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,indexes[x],q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ (Quantum) (QuantumRange-p->opacity),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GrayQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register Quantum
+ threshold;
+
+ register unsigned char
+ black,
+ white;
+
+ black=0x00;
+ white=0x01;
+ if (quantum_info->min_is_white != MagickFalse)
+ {
+ black=0x01;
+ white=0x00;
+ }
+ threshold=(Quantum) (QuantumRange/2);
+ for (x=((long) number_pixels-7); x > 0; x-=8)
+ {
+ *q='\0';
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 7;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 6;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 5;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 4;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 3;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 2;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 1;
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 0;
+ p++;
+ q++;
+ }
+ if ((number_pixels % 8) != 0)
+ {
+ *q='\0';
+ for (bit=7; bit >= (long) (8-(number_pixels % 8)); bit--)
+ {
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) <<
+ bit;
+ p++;
+ }
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) (number_pixels-1) ; x+=2)
+ {
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ *q=(((pixel >> 4) & 0xf) << 4);
+ p++;
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ *q|=pixel >> 4;
+ p++;
+ q++;
+ }
+ if ((number_pixels % 2) != 0)
+ {
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ *q=(((pixel >> 4) & 0xf) << 4);
+ p++;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 10:
+ {
+ register unsigned short
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ q=PopShortPixel(endian,(unsigned short) ScaleQuantumToAny(
+ (Quantum) pixel,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ PixelIntensityToQuantum(p),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 12:
+ {
+ register unsigned short
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ q=PopShortPixel(endian,(unsigned short) (pixel >> 4),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ PixelIntensityToQuantum(p),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ pixel=(float) PixelIntensityToQuantum(p);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ double
+ pixel;
+
+ pixel=(double) PixelIntensityToQuantum(p);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ PixelIntensityToQuantum(p),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GrayAlphaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register Quantum
+ threshold;
+
+ register unsigned char
+ black,
+ pixel,
+ white;
+
+ black=0x00;
+ white=0x01;
+ if (quantum_info->min_is_white == MagickFalse)
+ {
+ black=0x01;
+ white=0x00;
+ }
+ threshold=(Quantum) (QuantumRange/2);
+ for (x=((long) number_pixels-3); x > 0; x-=4)
+ {
+ *q='\0';
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 7;
+ pixel=(unsigned char) (p->opacity == OpaqueOpacity ? 0x00 : 0x01);
+ *q|=(((int) pixel != 0 ? 0x00 : 0x01) << 6);
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 5;
+ pixel=(unsigned char) (p->opacity == OpaqueOpacity ? 0x00 : 0x01);
+ *q|=(((int) pixel != 0 ? 0x00 : 0x01) << 4);
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 3;
+ pixel=(unsigned char) (p->opacity == OpaqueOpacity ? 0x00 : 0x01);
+ *q|=(((int) pixel != 0 ? 0x00 : 0x01) << 2);
+ p++;
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) << 1;
+ pixel=(unsigned char) (p->opacity == OpaqueOpacity ? 0x00 : 0x01);
+ *q|=(((int) pixel != 0 ? 0x00 : 0x01) << 0);
+ p++;
+ q++;
+ }
+ if ((number_pixels % 4) != 0)
+ {
+ *q='\0';
+ for (bit=3; bit >= (long) (4-(number_pixels % 4)); bit-=2)
+ {
+ *q|=(PixelIntensityToQuantum(p) < threshold ? black : white) <<
+ bit;
+ pixel=(unsigned char) (p->opacity == OpaqueOpacity ? 0x00 :
+ 0x01);
+ *q|=(((int) pixel != 0 ? 0x00 : 0x01) << (unsigned char)
+ (bit-1));
+ p++;
+ }
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels ; x++)
+ {
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ *q=(((pixel >> 4) & 0xf) << 4);
+ pixel=(unsigned char) (16*QuantumScale*((Quantum) (QuantumRange-
+ p->opacity))+0.5);
+ *q|=pixel & 0xf;
+ p++;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ pixel=(float) PixelIntensityToQuantum(p);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ pixel=(float) (QuantumRange-p->opacity);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ double
+ pixel;
+
+ pixel=(double) PixelIntensityToQuantum(p);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ pixel=(double) (QuantumRange-p->opacity);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ PixelIntensityToQuantum(p),range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ (Quantum) (QuantumRange-p->opacity),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->red);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->red);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->red,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->red);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->red,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->red,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->green);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->green);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->green,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->green);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->green,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->green,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->blue);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->blue);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->blue,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->blue);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->blue,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->blue,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case AlphaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ pixel=(float) (QuantumRange-p->opacity);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ double
+ pixel;
+
+ pixel=(double) (QuantumRange-p->opacity);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ (Quantum) (QuantumRange-p->opacity),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case OpacityQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->opacity);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->opacity);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->opacity,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->opacity);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->opacity,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->opacity,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(indexes[x]);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(indexes[x]);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(indexes[x]);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ (Quantum) indexes[x],range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RGBQuantum:
+ case CbYCrQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopCharPixel(ScaleQuantumToChar(p->red),q);
+ q=PopCharPixel(ScaleQuantumToChar(p->green),q);
+ q=PopCharPixel(ScaleQuantumToChar(p->blue),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 10:
+ {
+ register unsigned long
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned long) (ScaleQuantumToAny(p->red,range) << 22 |
+ ScaleQuantumToAny(p->green,range) << 12 |
+ ScaleQuantumToAny(p->blue,range) << 2);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ if (quantum_info->quantum == 32UL)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 12:
+ {
+ register unsigned long
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) (3*number_pixels-1); x+=2)
+ {
+ switch (x % 3)
+ {
+ default:
+ case 0:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ break;
+ }
+ case 1:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ break;
+ }
+ case 2:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ p++;
+ break;
+ }
+ }
+ q=PopShortPixel(endian,(unsigned short) (pixel << 4),q);
+ switch ((x+1) % 3)
+ {
+ default:
+ case 0:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ break;
+ }
+ case 1:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ break;
+ }
+ case 2:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ p++;
+ break;
+ }
+ }
+ q=PopShortPixel(endian,(unsigned short) (pixel << 4),q);
+ q+=quantum_info->pad;
+ }
+ for (bit=0; bit < (long) (3*number_pixels % 2); bit++)
+ {
+ switch ((x+bit) % 3)
+ {
+ default:
+ case 0:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ break;
+ }
+ case 1:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ break;
+ }
+ case 2:
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ p++;
+ break;
+ }
+ }
+ q=PopShortPixel(endian,(unsigned short) (pixel << 4),q);
+ q+=quantum_info->pad;
+ }
+ if (bit != 0)
+ p++;
+ break;
+ }
+ if (quantum_info->quantum == 32UL)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ q=PopQuantumLongPixel(&quantum_state,image->depth,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned long) ScaleQuantumToAny(p->red,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->green,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ pixel=(unsigned long) ScaleQuantumToAny(p->blue,range);
+ q=PopQuantumPixel(&quantum_state,image->depth,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->red);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->green);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->blue);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->red,q);
+ q=PopFloatPixel(&quantum_state,(float) p->green,q);
+ q=PopFloatPixel(&quantum_state,(float) p->blue,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->red);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->green);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->blue);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->red,q);
+ q=PopDoublePixel(&quantum_state,(double) p->green,q);
+ q=PopDoublePixel(&quantum_state,(double) p->blue,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->red,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->green,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->blue,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RGBAQuantum:
+ case RGBOQuantum:
+ case CbYCrAQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->red);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->green);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->blue);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->red);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->green);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->blue);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ q=PopFloatPixel(&quantum_state,(float) p->red,q);
+ q=PopFloatPixel(&quantum_state,(float) p->green,q);
+ q=PopFloatPixel(&quantum_state,(float) p->blue,q);
+ pixel=(float) (QuantumRange-p->opacity);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->red);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->green);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->blue);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->red,q);
+ q=PopDoublePixel(&quantum_state,(double) p->green,q);
+ q=PopDoublePixel(&quantum_state,(double) p->blue,q);
+ pixel=(double) (QuantumRange-p->opacity);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->red,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->green,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->blue,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ (Quantum) (QuantumRange-p->opacity),range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CMYKQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->red);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->green);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->blue);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(indexes[x]);
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->red);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->green);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->blue);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(indexes[x]);
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopFloatPixel(&quantum_state,(float) p->red,q);
+ q=PopFloatPixel(&quantum_state,(float) p->green,q);
+ q=PopFloatPixel(&quantum_state,(float) p->blue,q);
+ q=PopFloatPixel(&quantum_state,(float) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->red);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->green);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->blue);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(indexes[x]);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->red,q);
+ q=PopDoublePixel(&quantum_state,(double) p->green,q);
+ q=PopDoublePixel(&quantum_state,(double) p->blue,q);
+ q=PopDoublePixel(&quantum_state,(double) indexes[x],q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->red,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->green,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->blue,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ indexes[x],range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CMYKAQuantum:
+ case CMYKOQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToChar(p->red);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->green);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(p->blue);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar(indexes[x]);
+ q=PopCharPixel(pixel,q);
+ pixel=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ q=PopCharPixel(pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ register unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToShort(p->red);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->green);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(p->blue);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort(indexes[x]);
+ q=PopShortPixel(endian,pixel,q);
+ pixel=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ q=PopShortPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ register unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ float
+ pixel;
+
+ q=PopFloatPixel(&quantum_state,(float) p->red,q);
+ q=PopFloatPixel(&quantum_state,(float) p->green,q);
+ q=PopFloatPixel(&quantum_state,(float) p->blue,q);
+ q=PopFloatPixel(&quantum_state,(float) indexes[x],q);
+ pixel=(float) (QuantumRange-p->opacity);
+ q=PopFloatPixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=ScaleQuantumToLong(p->red);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->green);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(p->blue);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong(indexes[x]);
+ q=PopLongPixel(endian,pixel,q);
+ pixel=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopDoublePixel(&quantum_state,(double) p->red,q);
+ q=PopDoublePixel(&quantum_state,(double) p->green,q);
+ q=PopDoublePixel(&quantum_state,(double) p->blue,q);
+ q=PopDoublePixel(&quantum_state,(double) indexes[x],q);
+ pixel=(double) (QuantumRange-p->opacity);
+ q=PopDoublePixel(&quantum_state,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->red,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->green,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->blue,range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ indexes[x],range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ p->opacity,range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CbYCrYQuantum:
+ {
+ long
+ n;
+
+ Quantum
+ cbcr[4];
+
+ register long
+ i;
+
+ register unsigned long
+ pixel;
+
+ unsigned long
+ quantum;
+
+ n=0;
+ quantum=0;
+ range=GetQuantumRange(image->depth);
+ switch (quantum_info->depth)
+ {
+ case 10:
+ {
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x+=2)
+ {
+ for (i=0; i < 4; i++)
+ {
+ switch (n % 3)
+ {
+ case 0:
+ {
+ quantum=p->red;
+ break;
+ }
+ case 1:
+ {
+ quantum=p->green;
+ break;
+ }
+ case 2:
+ {
+ quantum=p->blue;
+ break;
+ }
+ }
+ cbcr[i]=(Quantum) quantum;
+ n++;
+ }
+ pixel=(unsigned long) ((unsigned long) (cbcr[1]) << 22 |
+ (unsigned long) (cbcr[0]) << 12 |
+ (unsigned long) (cbcr[2]) << 2);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ pixel=(unsigned long) ((unsigned long) (cbcr[3]) << 22 |
+ (unsigned long) (cbcr[0]) << 12 |
+ (unsigned long) (cbcr[2]) << 2);
+ q=PopLongPixel(endian,pixel,q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ {
+ for (x=0; x < (long) number_pixels; x+=2)
+ {
+ for (i=0; i < 4; i++)
+ {
+ switch (n % 3)
+ {
+ case 0:
+ {
+ quantum=p->red;
+ break;
+ }
+ case 1:
+ {
+ quantum=p->green;
+ break;
+ }
+ case 2:
+ {
+ quantum=p->blue;
+ break;
+ }
+ }
+ cbcr[i]=(Quantum) quantum;
+ n++;
+ }
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[1],range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[0],range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[2],range),q);
+ p++;
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[3],range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[0],range),q);
+ q=PopQuantumPixel(&quantum_state,image->depth,ScaleQuantumToAny(
+ cbcr[2],range),q);
+ p++;
+ q+=quantum_info->pad;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((quantum_type == CbYCrQuantum) || (quantum_type == CbYCrAQuantum))
+ {
+ Quantum
+ quantum;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=(PixelPacket *) GetCacheViewVirtualPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ quantum=q->red;
+ q->red=q->green;
+ q->green=quantum;
+ q++;
+ }
+ }
+ if ((quantum_type == RGBOQuantum) || (quantum_type == CMYKOQuantum))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=(PixelPacket *) GetCacheViewVirtualPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q->opacity=(Quantum) (QuantumRange-q->opacity);
+ q++;
+ }
+ }
+ return(extent);
+}
diff --git a/magick/quantum-import.c b/magick/quantum-import.c
new file mode 100644
index 0000000..5293429
--- /dev/null
+++ b/magick/quantum-import.c
@@ -0,0 +1,2529 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% QQQ U U AAA N N TTTTT U U M M %
+% Q Q U U A A NN N T U U MM MM %
+% Q Q U U AAAAA N N N T U U M M M %
+% Q QQ U U A A N NN T U U M M %
+% QQQQ UUU A A N N T UUU M M %
+% %
+% IIIII M M PPPP OOO RRRR TTTTT %
+% I MM MM P P O O R R T %
+% I M M M PPPP O O RRRR T %
+% I M M P O O R R T %
+% IIIII M M P OOO R R T %
+% %
+% MagickCore Methods to Import Quantum Pixels %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2008 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/cache.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/statistic.h"
+#include "magick/stream.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I m p o r t Q u a n t u m P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ImportQuantumPixels() transfers one or more pixel components from a user
+% supplied buffer into the image pixel cache of an image. The pixels are
+% expected in network byte order. It returns MagickTrue if the pixels are
+% successfully transferred, otherwise MagickFalse.
+%
+% The format of the ImportQuantumPixels method is:
+%
+% size_t ImportQuantumPixels(Image *image,CacheView *image_view,
+% const QuantumInfo *quantum_info,const QuantumType quantum_type,
+% const unsigned char *pixels,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o image_view: the image cache view.
+%
+% o quantum_info: the quantum info.
+%
+% o quantum_type: Declare which pixel components to transfer (red, green,
+% blue, opacity, RGB, or RGBA).
+%
+% o pixels: The pixel components are transferred from this buffer.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline IndexPacket PushColormapIndex(Image *image,
+ const unsigned long index,MagickBooleanType *range_exception)
+{
+ if (index < image->colors)
+ return((IndexPacket) index);
+ *range_exception=MagickTrue;
+ return((IndexPacket) 0);
+}
+
+static inline const unsigned char *PushDoublePixel(
+ const QuantumState *quantum_state,const unsigned char *pixels,double *pixel)
+{
+ double
+ *p;
+
+ unsigned char
+ quantum[8];
+
+ if (quantum_state->endian != LSBEndian)
+ {
+ quantum[7]=(*pixels++);
+ quantum[6]=(*pixels++);
+ quantum[5]=(*pixels++);
+ quantum[5]=(*pixels++);
+ quantum[3]=(*pixels++);
+ quantum[2]=(*pixels++);
+ quantum[1]=(*pixels++);
+ quantum[0]=(*pixels++);
+ p=(double *) quantum;
+ *pixel=(*p);
+ *pixel-=quantum_state->minimum;
+ *pixel*=quantum_state->scale;
+ return(pixels);
+ }
+ quantum[0]=(*pixels++);
+ quantum[1]=(*pixels++);
+ quantum[2]=(*pixels++);
+ quantum[3]=(*pixels++);
+ quantum[4]=(*pixels++);
+ quantum[5]=(*pixels++);
+ quantum[6]=(*pixels++);
+ quantum[7]=(*pixels++);
+ p=(double *) quantum;
+ *pixel=(*p);
+ *pixel-=quantum_state->minimum;
+ *pixel*=quantum_state->scale;
+ return(pixels);
+}
+
+static inline const unsigned char *PushFloatPixel(
+ const QuantumState *quantum_state,const unsigned char *pixels,float *pixel)
+{
+ float
+ *p;
+
+ unsigned char
+ quantum[4];
+
+ if (quantum_state->endian != LSBEndian)
+ {
+ quantum[3]=(*pixels++);
+ quantum[2]=(*pixels++);
+ quantum[1]=(*pixels++);
+ quantum[0]=(*pixels++);
+ p=(float *) quantum;
+ *pixel=(*p);
+ *pixel-=quantum_state->minimum;
+ *pixel*=quantum_state->scale;
+ return(pixels);
+ }
+ quantum[0]=(*pixels++);
+ quantum[1]=(*pixels++);
+ quantum[2]=(*pixels++);
+ quantum[3]=(*pixels++);
+ p=(float *) quantum;
+ *pixel=(*p);
+ *pixel-=quantum_state->minimum;
+ *pixel*=quantum_state->scale;
+ return(pixels);
+}
+
+static inline const unsigned char *PushQuantumPixel(
+ QuantumState *quantum_state,const unsigned long depth,
+ const unsigned char *pixels,unsigned long *quantum)
+{
+ register long
+ i;
+
+ register unsigned long
+ quantum_bits;
+
+ *quantum=(QuantumAny) 0;
+ for (i=(long) depth; i > 0L; )
+ {
+ if (quantum_state->bits == 0UL)
+ {
+ quantum_state->pixel=(*pixels++);
+ quantum_state->bits=8UL;
+ }
+ quantum_bits=(unsigned long) i;
+ if (quantum_bits > quantum_state->bits)
+ quantum_bits=quantum_state->bits;
+ i-=quantum_bits;
+ quantum_state->bits-=quantum_bits;
+ *quantum=(*quantum << quantum_bits) | ((quantum_state->pixel >>
+ quantum_state->bits) &~ ((~0UL) << quantum_bits));
+ }
+ return(pixels);
+}
+
+static inline const unsigned char *PushQuantumLongPixel(
+ QuantumState *quantum_state,const unsigned long depth,
+ const unsigned char *pixels,unsigned long *quantum)
+{
+ register long
+ i;
+
+ register unsigned long
+ quantum_bits;
+
+ *quantum=0UL;
+ for (i=(long) depth; i > 0; )
+ {
+ if (quantum_state->bits == 0)
+ {
+ pixels=PushLongPixel(quantum_state->endian,pixels,
+ &quantum_state->pixel);
+ quantum_state->bits=32UL;
+ }
+ quantum_bits=(unsigned long) i;
+ if (quantum_bits > quantum_state->bits)
+ quantum_bits=quantum_state->bits;
+ *quantum|=(((quantum_state->pixel >> (32UL-quantum_state->bits)) &
+ quantum_state->mask[quantum_bits]) << (depth-i));
+ i-=quantum_bits;
+ quantum_state->bits-=quantum_bits;
+ }
+ return(pixels);
+}
+
+MagickExport size_t ImportQuantumPixels(Image *image,CacheView *image_view,
+ const QuantumInfo *quantum_info,const QuantumType quantum_type,
+ const unsigned char *pixels,ExceptionInfo *exception)
+{
+ EndianType
+ endian;
+
+ long
+ bit;
+
+ MagickSizeType
+ number_pixels;
+
+ QuantumAny
+ range;
+
+ QuantumState
+ quantum_state;
+
+ register const unsigned char
+ *p;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ size_t
+ extent;
+
+ unsigned long
+ pixel;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ if (pixels == (const unsigned char *) NULL)
+ pixels=GetQuantumPixels(quantum_info);
+ x=0;
+ p=pixels;
+ number_pixels=GetImageExtent(image);
+ q=GetAuthenticPixelQueue(image);
+ indexes=GetAuthenticIndexQueue(image);
+ if (image_view != (CacheView *) NULL)
+ {
+ number_pixels=GetCacheViewExtent(image_view);
+ q=GetCacheViewAuthenticPixelQueue(image_view);
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ }
+ InitializeQuantumState(quantum_info,image->endian,&quantum_state);
+ extent=GetQuantumExtent(image,quantum_info,quantum_type);
+ endian=quantum_state.endian;
+ switch (quantum_type)
+ {
+ case IndexQuantum:
+ {
+ MagickBooleanType
+ range_exception;
+
+ if (image->storage_class != PseudoClass)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColormappedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ range_exception=MagickFalse;
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < ((long) number_pixels-7); x+=8)
+ {
+ for (bit=0; bit < 8; bit++)
+ {
+ if (quantum_info->min_is_white == MagickFalse)
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) == 0 ?
+ 0x00 : 0x01);
+ else
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) != 0 ?
+ 0x00 : 0x01);
+ indexes[x+bit]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x+bit]];
+ q++;
+ }
+ p++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 8); bit++)
+ {
+ if (quantum_info->min_is_white == MagickFalse)
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) == 0 ?
+ 0x00 : 0x01);
+ else
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) != 0 ?
+ 0x00 : 0x01);
+ indexes[x+bit]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x+bit]];
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < ((long) number_pixels-1); x+=2)
+ {
+ pixel=(unsigned char) ((*p >> 4) & 0xf);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ q++;
+ pixel=(unsigned char) ((*p) & 0xf);
+ indexes[x+1]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x+1]];
+ p++;
+ q++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 2); bit++)
+ {
+ pixel=(unsigned char) ((*p++ >> 4) & 0xf);
+ indexes[x+bit]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x+bit]];
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ indexes[x]=PushColormapIndex(image,RoundToQuantum(pixel),
+ &range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ indexes[x]=PushColormapIndex(image,RoundToQuantum(pixel),
+ &range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ if (range_exception != MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ CorruptImageError,"InvalidColormapIndex","`%s'",image->filename);
+ break;
+ }
+ case IndexAlphaQuantum:
+ {
+ MagickBooleanType
+ range_exception;
+
+ if (image->storage_class != PseudoClass)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ImageError,"ColormappedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ range_exception=MagickFalse;
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < ((long) number_pixels-3); x+=4)
+ {
+ for (bit=0; bit < 8; bit+=2)
+ {
+ if (quantum_info->min_is_white == MagickFalse)
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) == 0 ?
+ 0x00 : 0x01);
+ else
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) != 0 ?
+ 0x00 : 0x01);
+ indexes[x+bit/2]=(IndexPacket) (pixel == 0 ? 0 : 1);
+ q->red=(Quantum) (pixel == 0 ? 0 : QuantumRange);
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=(Quantum) (((*p) & (1UL << (unsigned char) (6-bit)))
+ == 0 ? TransparentOpacity : OpaqueOpacity);
+ q++;
+ }
+ }
+ for (bit=0; bit < (long) (number_pixels % 4); bit+=2)
+ {
+ if (quantum_info->min_is_white == MagickFalse)
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) == 0 ?
+ 0x00 : 0x01);
+ else
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) != 0 ?
+ 0x00 : 0x01);
+ indexes[x+bit/2]=(IndexPacket) (pixel == 0 ? 0 : 1);
+ q->red=(Quantum) (pixel == 0 ? 0 : QuantumRange);
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=(Quantum) (((*p) & (1UL << (unsigned char) (6-bit))) ==
+ 0 ? TransparentOpacity : OpaqueOpacity);
+ q++;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned char) ((*p >> 4) & 0xf);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ pixel=(unsigned char) ((*p) & 0xf);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ p++;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushCharPixel(p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushShortPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ indexes[x]=PushColormapIndex(image,RoundToQuantum(pixel),
+ &range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushLongPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleLongToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ indexes[x]=PushColormapIndex(image,RoundToQuantum(pixel),
+ &range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ indexes[x]=PushColormapIndex(image,pixel,&range_exception);
+ *q=image->colormap[(long) indexes[x]];
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ if (range_exception != MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ CorruptImageError,"InvalidColormapIndex","`%s'",image->filename);
+ break;
+ }
+ case GrayQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register Quantum
+ black,
+ white;
+
+ black=0;
+ white=(Quantum) QuantumRange;
+ if (quantum_info->min_is_white != MagickFalse)
+ {
+ black=(Quantum) QuantumRange;
+ white=0;
+ }
+ for (x=0; x < ((long) number_pixels-7); x+=8)
+ {
+ for (bit=0; bit < 8; bit++)
+ {
+ q->red=(((*p) & (1 << (7-bit))) == 0 ? black : white);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ p++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 8); bit++)
+ {
+ q->red=(((*p) & (1 << (7-bit))) == 0 ? black : white);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ if (bit != 0)
+ p++;
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < ((long) number_pixels-1); x+=2)
+ {
+ pixel=(unsigned char) ((*p >> 4) & 0xf);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ pixel=(unsigned char) ((*p) & 0xf);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p++;
+ q++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 2); bit++)
+ {
+ pixel=(unsigned char) (*p++ >> 4);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ if (quantum_info->min_is_white != MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=OpaqueOpacity;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=OpaqueOpacity;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 10:
+ {
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ if (image->endian != LSBEndian)
+ {
+ for (x=0; x < (long) number_pixels/3; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((pixel >> 0) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ q->red=ScaleAnyToQuantum((pixel >> 10) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ q->red=ScaleAnyToQuantum((pixel >> 20) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels/3; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((pixel >> 22) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ q->red=ScaleAnyToQuantum((pixel >> 12) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ q->red=ScaleAnyToQuantum((pixel >> 2) & 0x3ff,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 12:
+ {
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) (number_pixels-1); x+=2)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 2); bit++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ if (bit != 0)
+ p++;
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ if (quantum_info->min_is_white != MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GrayAlphaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 1:
+ {
+ register unsigned char
+ pixel;
+
+ for (x=0; x < ((long) number_pixels-3); x+=4)
+ {
+ for (bit=0; bit < 8; bit+=2)
+ {
+ pixel=(unsigned char)
+ (((*p) & (1 << (7-bit))) != 0 ? 0x00 : 0x01);
+ q->red=(Quantum) (pixel == 0 ? 0 : QuantumRange);
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=(Quantum) (((*p) & (1UL << (unsigned char) (6-bit)))
+ == 0 ? TransparentOpacity : OpaqueOpacity);
+ q++;
+ }
+ p++;
+ }
+ for (bit=0; bit < (long) (number_pixels % 4); bit+=2)
+ {
+ pixel=(unsigned char) (((*p) & (1 << (7-bit))) != 0 ? 0x00 : 0x01);
+ q->red=(Quantum) (pixel == 0 ? 0 : QuantumRange);
+ q->green=q->red;
+ q->blue=q->red;
+ q->opacity=(Quantum) (((*p) & (1UL << (unsigned char) (6-bit))) == 0
+ ? TransparentOpacity : OpaqueOpacity);
+ q++;
+ }
+ if (bit != 0)
+ p++;
+ break;
+ }
+ case 4:
+ {
+ register unsigned char
+ pixel;
+
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ pixel=(unsigned char) ((*p >> 4) & 0xf);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ pixel=(unsigned char) ((*p) & 0xf);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ p++;
+ q++;
+ }
+ break;
+ }
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushCharPixel(p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 10:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 12:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushShortPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushLongPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleLongToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ q->green=q->red;
+ q->blue=q->red;
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->green=ScaleCharToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->green=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->green=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->blue=ScaleCharToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->blue=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->blue=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case AlphaQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleLongToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ indexes[x]=ScaleCharToQuantum(pixel);
+ p+=quantum_info->pad;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ indexes[x]=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ indexes[x]=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ indexes[x]=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ indexes[x]=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ indexes[x]=ScaleAnyToQuantum(pixel,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RGBQuantum:
+ case CbYCrQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->green=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->blue=ScaleCharToQuantum(pixel);
+ q->opacity=OpaqueOpacity;
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 10:
+ {
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleAnyToQuantum((pixel >> 22) & 0x3ff,range);
+ q->green=ScaleAnyToQuantum((pixel >> 12) & 0x3ff,range);
+ q->blue=ScaleAnyToQuantum((pixel >> 2) & 0x3ff,range);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ if (quantum_info->quantum == 32UL)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ case 12:
+ {
+ range=GetQuantumRange(image->depth);
+ if (quantum_info->pack == MagickFalse)
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) (3*number_pixels-1); x+=2)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ switch (x % 3)
+ {
+ default:
+ case 0:
+ {
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 1:
+ {
+ q->green=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 2:
+ {
+ q->blue=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q++;
+ break;
+ }
+ }
+ p=PushShortPixel(endian,p,&pixel);
+ switch ((x+1) % 3)
+ {
+ default:
+ case 0:
+ {
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 1:
+ {
+ q->green=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 2:
+ {
+ q->blue=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q++;
+ break;
+ }
+ }
+ p+=quantum_info->pad;
+ }
+ for (bit=0; bit < (long) (3*number_pixels % 2); bit++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ switch ((x+bit) % 3)
+ {
+ default:
+ case 0:
+ {
+ q->red=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 1:
+ {
+ q->green=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ break;
+ }
+ case 2:
+ {
+ q->blue=ScaleAnyToQuantum((QuantumAny) (pixel >> 4),range);
+ q++;
+ break;
+ }
+ }
+ p+=quantum_info->pad;
+ }
+ if (bit != 0)
+ p++;
+ break;
+ }
+ if (quantum_info->quantum == 32UL)
+ {
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumLongPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->green=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->blue=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->green=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->blue=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case RGBAQuantum:
+ case RGBOQuantum:
+ case CbYCrAQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->green=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->blue=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 10:
+ {
+ pixel=0;
+ if (quantum_info->pack == MagickFalse)
+ {
+ long
+ n;
+
+ register long
+ i;
+
+ unsigned long
+ quantum;
+
+ n=0;
+ quantum=0;
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ for (i=0; i < 4; i++)
+ {
+ switch (n % 3)
+ {
+ case 0:
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 22) & 0x3ff) << 6)));
+ break;
+ }
+ case 1:
+ {
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 12) & 0x3ff) << 6)));
+ break;
+ }
+ case 2:
+ {
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 2) & 0x3ff) << 6)));
+ break;
+ }
+ }
+ switch (i)
+ {
+ case 0: q->red=(Quantum) (quantum); break;
+ case 1: q->green=(Quantum) (quantum); break;
+ case 2: q->blue=(Quantum) (quantum); break;
+ case 3: q->opacity=(Quantum) (QuantumRange-quantum); break;
+ }
+ n++;
+ }
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleShortToQuantum((unsigned short) (pixel << 6));
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleShortToQuantum((unsigned short) (pixel << 6));
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleShortToQuantum((unsigned short) (pixel << 6));
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(
+ (unsigned short) (pixel << 6)));
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->green=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->blue=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->green=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->blue=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleLongToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CMYKQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->green=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->blue=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ indexes[x]=ScaleCharToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->green=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->blue=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ indexes[x]=ScaleShortToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ indexes[x]=(IndexPacket) RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->green=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->blue=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ indexes[x]=ScaleLongToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ indexes[x]=(IndexPacket) RoundToQuantum(pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ indexes[x]=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CMYKAQuantum:
+ case CMYKOQuantum:
+ {
+ if (image->colorspace != CMYKColorspace)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",image->filename);
+ return(extent);
+ }
+ switch (quantum_info->depth)
+ {
+ case 8:
+ {
+ unsigned char
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushCharPixel(p,&pixel);
+ q->red=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->green=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->blue=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ indexes[x]=ScaleCharToQuantum(pixel);
+ p=PushCharPixel(p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 16:
+ {
+ unsigned short
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushShortPixel(endian,p,&pixel);
+ q->red=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->green=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->blue=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ indexes[x]=ScaleShortToQuantum(pixel);
+ p=PushShortPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleShortToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 32:
+ {
+ unsigned long
+ pixel;
+
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ float
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ indexes[x]=(IndexPacket) RoundToQuantum(pixel);
+ p=PushFloatPixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ q->red=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->green=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->blue=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ indexes[x]=ScaleLongToQuantum(pixel);
+ p=PushLongPixel(endian,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleLongToQuantum(pixel));
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ case 64:
+ {
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ double
+ pixel;
+
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->red=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->green=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->blue=RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ indexes[x]=(IndexPacket) RoundToQuantum(pixel);
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-RoundToQuantum(pixel));
+ p=PushDoublePixel(&quantum_state,p,&pixel);
+ p+=quantum_info->pad;
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->blue=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ indexes[x]=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->opacity=(Quantum) (QuantumRange-ScaleAnyToQuantum(pixel,range));
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case CbYCrYQuantum:
+ {
+ switch (quantum_info->depth)
+ {
+ case 10:
+ {
+ Quantum
+ cbcr[4];
+
+ pixel=0;
+ if (quantum_info->pack == MagickFalse)
+ {
+ long
+ n;
+
+ register long
+ i;
+
+ unsigned long
+ quantum;
+
+ n=0;
+ quantum=0;
+ for (x=0; x < (long) number_pixels; x+=2)
+ {
+ for (i=0; i < 4; i++)
+ {
+ switch (n % 3)
+ {
+ case 0:
+ {
+ p=PushLongPixel(endian,p,&pixel);
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 22) & 0x3ff) << 6)));
+ break;
+ }
+ case 1:
+ {
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 12) & 0x3ff) << 6)));
+ break;
+ }
+ case 2:
+ {
+ quantum=(unsigned long) (ScaleShortToQuantum(
+ (unsigned short) (((pixel >> 2) & 0x3ff) << 6)));
+ break;
+ }
+ }
+ cbcr[i]=(Quantum) (quantum);
+ n++;
+ }
+ p+=quantum_info->pad;
+ q->red=cbcr[1];
+ q->green=cbcr[0];
+ q->blue=cbcr[2];
+ q++;
+ q->red=cbcr[3];
+ q->green=cbcr[0];
+ q->blue=cbcr[2];
+ q++;
+ }
+ break;
+ }
+ }
+ default:
+ {
+ range=GetQuantumRange(image->depth);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->red=ScaleAnyToQuantum(pixel,range);
+ p=PushQuantumPixel(&quantum_state,image->depth,p,&pixel);
+ q->green=ScaleAnyToQuantum(pixel,range);
+ q++;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if ((quantum_type == CbYCrQuantum) || (quantum_type == CbYCrAQuantum))
+ {
+ Quantum
+ quantum;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=GetCacheViewAuthenticPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ quantum=q->red;
+ q->red=q->green;
+ q->green=quantum;
+ q++;
+ }
+ }
+ if ((quantum_type == RGBOQuantum) || (quantum_type == CMYKOQuantum))
+ {
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=GetCacheViewAuthenticPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ q->opacity=(Quantum) (QuantumRange-q->opacity);
+ q++;
+ }
+ }
+ if (quantum_info->alpha_type == DisassociatedQuantumAlpha)
+ {
+ MagickRealType
+ alpha;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Disassociate alpha.
+ */
+ q=GetAuthenticPixelQueue(image);
+ if (image_view != (CacheView *) NULL)
+ q=GetCacheViewAuthenticPixelQueue(image_view);
+ for (x=0; x < (long) number_pixels; x++)
+ {
+ alpha=QuantumScale*((MagickRealType) QuantumRange-q->opacity);
+ alpha=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
+ q->red=RoundToQuantum(alpha*q->red);
+ q->green=RoundToQuantum(alpha*q->green);
+ q->blue=RoundToQuantum(alpha*q->blue);
+ q++;
+ }
+ }
+ return(extent);
+}
diff --git a/magick/quantum-private.h b/magick/quantum-private.h
new file mode 100644
index 0000000..856d277
--- /dev/null
+++ b/magick/quantum-private.h
@@ -0,0 +1,531 @@
+/*
+ Copyright 1999-2008 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore quantum inline methods.
+*/
+#ifndef _MAGICKCORE_QUANTUM_PRIVATE_H
+#define _MAGICKCORE_QUANTUM_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/cache.h"
+
+typedef struct _QuantumState
+{
+ EndianType
+ endian;
+
+ double
+ minimum,
+ scale,
+ inverse_scale;
+
+ unsigned long
+ pixel,
+ bits;
+
+ const unsigned long
+ *mask;
+} QuantumState;
+
+struct _QuantumInfo
+{
+ unsigned long
+ depth,
+ quantum;
+
+ QuantumFormatType
+ format;
+
+ double
+ minimum,
+ maximum,
+ scale;
+
+ size_t
+ pad;
+
+ MagickBooleanType
+ min_is_white,
+ pack;
+
+ QuantumAlphaType
+ alpha_type;
+
+ unsigned long
+ number_threads;
+
+ unsigned char
+ **pixels;
+
+ size_t
+ extent;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+static inline MagickSizeType GetQuantumRange(const unsigned long depth)
+{
+ return((MagickSizeType) ((MagickULLConstant(1) << (depth-1))+
+ ((MagickULLConstant(1) << (depth-1))-1)));
+}
+
+static inline void InitializeQuantumState(const QuantumInfo *quantum_info,
+ const EndianType endian,QuantumState *quantum_state)
+{
+ static const unsigned long mask[32] =
+ {
+ 0x00000000UL, 0x00000001UL, 0x00000003UL, 0x00000007UL, 0x0000000fUL,
+ 0x0000001fUL, 0x0000003fUL, 0x0000007fUL, 0x000000ffUL, 0x000001ffUL,
+ 0x000003ffUL, 0x000007ffUL, 0x00000fffUL, 0x00001fffUL, 0x00003fffUL,
+ 0x00007fffUL, 0x0000ffffUL, 0x0001ffffUL, 0x0003ffffUL, 0x0007ffffUL,
+ 0x000fffffUL, 0x001fffffUL, 0x003fffffUL, 0x007fffffUL, 0x00ffffffUL,
+ 0x01ffffffUL, 0x03ffffffUL, 0x07ffffffUL, 0x0fffffffUL, 0x1fffffffUL,
+ 0x3fffffffUL, 0x7fffffffUL
+ };
+
+ (void) ResetMagickMemory(quantum_state,0,sizeof(&quantum_state));
+ quantum_state->endian=endian;
+ quantum_state->minimum=quantum_info->minimum;
+ quantum_state->scale=quantum_info->scale;
+ quantum_state->inverse_scale=0.0;
+ if (quantum_state->scale != 0.0)
+ quantum_state->inverse_scale=1.0/quantum_state->scale;
+ quantum_state->bits=0;
+ quantum_state->mask=mask;
+}
+
+static inline unsigned char *PopCharPixel(const unsigned char pixel,
+ unsigned char *pixels)
+{
+ *pixels++=pixel;
+ return(pixels);
+}
+
+static inline unsigned char *PopLongPixel(const EndianType endian,
+ const unsigned long pixel,unsigned char *pixels)
+{
+ register unsigned int
+ quantum;
+
+ quantum=(unsigned int) pixel;
+ if (endian != LSBEndian)
+ {
+ *pixels++=(unsigned char) (quantum >> 24);
+ *pixels++=(unsigned char) (quantum >> 16);
+ *pixels++=(unsigned char) (quantum >> 8);
+ *pixels++=(unsigned char) (quantum);
+ return(pixels);
+ }
+ *pixels++=(unsigned char) (quantum);
+ *pixels++=(unsigned char) (quantum >> 8);
+ *pixels++=(unsigned char) (quantum >> 16);
+ *pixels++=(unsigned char) (quantum >> 24);
+ return(pixels);
+}
+
+static inline unsigned char *PopShortPixel(const EndianType endian,
+ const unsigned short pixel,unsigned char *pixels)
+{
+ register unsigned int
+ quantum;
+
+ quantum=pixel;
+ if (endian != LSBEndian)
+ {
+ *pixels++=(unsigned char) (quantum >> 8);
+ *pixels++=(unsigned char) (quantum);
+ return(pixels);
+ }
+ *pixels++=(unsigned char) (quantum);
+ *pixels++=(unsigned char) (quantum >> 8);
+ return(pixels);
+}
+
+static inline const unsigned char *PushCharPixel(const unsigned char *pixels,
+ unsigned char *pixel)
+{
+ *pixel=(*pixels++);
+ return(pixels);
+}
+
+static inline const unsigned char *PushLongPixel(const EndianType endian,
+ const unsigned char *pixels,unsigned long *pixel)
+{
+ register unsigned int
+ quantum;
+
+ if (endian != LSBEndian)
+ {
+ quantum=(unsigned int) (*pixels++ << 24);
+ quantum|=(unsigned int) (*pixels++ << 16);
+ quantum|=(unsigned int) (*pixels++ << 8);
+ quantum|=(unsigned int) (*pixels++);
+ }
+ else
+ {
+ quantum=(unsigned int) (*pixels++);
+ quantum|=(unsigned int) (*pixels++ << 8);
+ quantum|=(unsigned int) (*pixels++ << 16);
+ quantum|=(unsigned int) (*pixels++ << 24);
+ }
+ *pixel=(unsigned long) (quantum & 0xffffffff);
+ return(pixels);
+}
+
+static inline const unsigned char *PushShortPixel(const EndianType endian,
+ const unsigned char *pixels,unsigned short *pixel)
+{
+ register unsigned int
+ quantum;
+
+ if (endian != LSBEndian)
+ {
+ quantum=(unsigned int) (*pixels++ << 8);
+ quantum|=(unsigned int) *pixels++;
+ }
+ else
+ {
+ quantum=(unsigned int) *pixels++;
+ quantum|=(unsigned int) (*pixels++ << 8);
+ }
+ *pixel=(unsigned short) (quantum & 0xffff);
+ return(pixels);
+}
+
+static inline Quantum ScaleAnyToQuantum(const QuantumAny quantum,
+ const QuantumAny range)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (((MagickRealType) QuantumRange*quantum)/range+0.5));
+#else
+ return((Quantum) (((MagickRealType) QuantumRange*quantum)/range));
+#endif
+}
+
+static inline QuantumAny ScaleQuantumToAny(const Quantum quantum,
+ const QuantumAny range)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((QuantumAny) (((MagickRealType) range*quantum)/QuantumRange+0.5));
+#else
+ return((QuantumAny) (((MagickRealType) range*quantum)/QuantumRange));
+#endif
+}
+
+#if (MAGICKCORE_QUANTUM_DEPTH == 8)
+static inline Quantum ScaleCharToQuantum(const unsigned char value)
+{
+ return((Quantum) value);
+}
+
+static inline Quantum ScaleLongToQuantum(const unsigned long value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) ((value+8421504UL)/16843009UL));
+#else
+ return((Quantum) (value/16843009.0));
+#endif
+}
+
+static inline Quantum ScaleMapToQuantum(const MagickRealType value)
+{
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) value);
+#else
+ if (value <= 0.0)
+ return(0);
+ if ((value+0.5) >= MaxMap)
+ return((Quantum) QuantumRange);
+ return((Quantum) (value+0.5));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToLong(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) (16843009UL*quantum));
+#else
+ if (quantum <= 0.0)
+ return(0UL);
+ if ((16843009.0*quantum) >= 4294967295.0)
+ return(4294967295UL);
+ return((unsigned long) (16843009.0*quantum+0.5));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToMap(const Quantum quantum)
+{
+ if (quantum >= (Quantum) MaxMap)
+ return((unsigned long) MaxMap);
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) quantum);
+#else
+ if (quantum < 0.0)
+ return(0UL);
+ return((unsigned long) (quantum+0.5));
+#endif
+}
+
+static inline unsigned short ScaleQuantumToShort(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned short) (257UL*quantum));
+#else
+ if (quantum <= 0.0)
+ return(0);
+ if ((257.0*quantum) >= 65535.0)
+ return(65535);
+ return((unsigned short) (257.0*quantum+0.5));
+#endif
+}
+
+static inline Quantum ScaleShortToQuantum(const unsigned short value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) ((value+128UL)/257UL));
+#else
+ return((Quantum) (value/257.0));
+#endif
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
+static inline Quantum ScaleCharToQuantum(const unsigned char value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (257UL*value));
+#else
+ return((Quantum) (257.0*value));
+#endif
+}
+
+static inline Quantum ScaleLongToQuantum(const unsigned long value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) ((value+MagickULLConstant(32768))/
+ MagickULLConstant(65537)));
+#else
+ return((Quantum) (value/65537.0));
+#endif
+}
+
+static inline Quantum ScaleMapToQuantum(const MagickRealType value)
+{
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) value);
+#else
+ if (value <= 0.0)
+ return(0);
+ if ((value+0.5) >= MaxMap)
+ return((Quantum) QuantumRange);
+ return((Quantum) (value+0.5));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToLong(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) (65537UL*quantum));
+#else
+ if (quantum <= 0.0)
+ return(0UL);
+ if ((65537.0*quantum) >= 4294967295.0)
+ return(4294967295UL);
+ return((unsigned long) (65537.0*quantum+0.5));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToMap(const Quantum quantum)
+{
+ if (quantum >= (Quantum) MaxMap)
+ return((unsigned long) MaxMap);
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) quantum);
+#else
+ if (quantum < 0.0)
+ return(0UL);
+ return((unsigned long) (quantum+0.5));
+#endif
+}
+
+static inline unsigned short ScaleQuantumToShort(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned short) quantum);
+#else
+ if (quantum <= 0.0)
+ return(0);
+ if (quantum >= 65535.0)
+ return(65535);
+ return((unsigned short) (quantum+0.5));
+#endif
+}
+
+static inline Quantum ScaleShortToQuantum(const unsigned short value)
+{
+ return((Quantum) value);
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
+static inline Quantum ScaleCharToQuantum(const unsigned char value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (16843009UL*value));
+#else
+ return((Quantum) (16843009.0*value));
+#endif
+}
+
+static inline Quantum ScaleLongToQuantum(const unsigned long value)
+{
+ return((Quantum) value);
+}
+
+static inline Quantum ScaleMapToQuantum(const MagickRealType value)
+{
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (65537.0*value));
+#else
+ if (value <= 0.0)
+ return(0);
+ if ((value+0.5) >= MaxMap)
+ return(QuantumRange);
+ return((Quantum) (65537UL*value));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToLong(const Quantum quantum)
+{
+ return((unsigned long) quantum);
+}
+
+static inline unsigned long ScaleQuantumToMap(const Quantum quantum)
+{
+ if ((quantum/65537) >= MaxMap)
+ return((unsigned long) MaxMap);
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) ((quantum+MagickULLConstant(32768))/
+ MagickULLConstant(65537)));
+#else
+ if (quantum < 0.0)
+ return(0UL);
+ return((unsigned long) (quantum/65537.0)+0.5);
+#endif
+}
+
+static inline unsigned short ScaleQuantumToShort(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned short) ((quantum+MagickULLConstant(32768))/
+ MagickULLConstant(65537)));
+#else
+ if (quantum <= 0.0)
+ return(0);
+ if ((quantum/65537.0) >= 65535.0)
+ return(65535);
+ return((unsigned short) (quantum/65537.0+0.5));
+#endif
+}
+
+static inline Quantum ScaleShortToQuantum(const unsigned short value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (65537UL*value));
+#else
+ return((Quantum) (65537.0*value));
+#endif
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 64)
+static inline Quantum ScaleCharToQuantum(const unsigned char value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (MagickULLConstant(71777214294589695)*value));
+#else
+ return((Quantum) (71777214294589695.0*value));
+#endif
+}
+
+static inline Quantum ScaleLongToQuantum(const unsigned long value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (4294967295UL*value));
+#else
+ return((Quantum) (4294967295.0*value));
+#endif
+}
+
+static inline Quantum ScaleMapToQuantum(const MagickRealType value)
+{
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (281479271612415.0*value));
+#else
+ if (value <= 0.0)
+ return(0);
+ if ((value+0.5) >= MaxMap)
+ return(QuantumRange);
+ return((Quantum) (MagickULLConstant(281479271612415)*value));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToLong(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) ((quantum+2147483648.0)/4294967297.0));
+#else
+ return((unsigned long) (quantum/4294967297.0+0.5));
+#endif
+}
+
+static inline unsigned long ScaleQuantumToMap(const Quantum quantum)
+{
+ if ((quantum/281479271612415.0) >= MaxMap)
+ return((unsigned long) MaxMap);
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned long) ((quantum+2147450879.0)/281479271612415.0));
+#else
+ if (quantum < 0.0)
+ return(0UL);
+ return((unsigned long) (quantum/281479271612415.0)+0.5);
+#endif
+}
+
+static inline unsigned short ScaleQuantumToShort(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned short) ((quantum+2147450879.0)/281479271612415.0));
+#else
+ return((unsigned short) (quantum/281479271612415.0+0.5));
+#endif
+}
+
+static inline Quantum ScaleShortToQuantum(const unsigned short value)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) (MagickULLConstant(281479271612415)*value));
+#else
+ return((Quantum) (281479271612415.0*value));
+#endif
+}
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/quantum.c b/magick/quantum.c
new file mode 100644
index 0000000..4e57dee
--- /dev/null
+++ b/magick/quantum.c
@@ -0,0 +1,844 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% QQQ U U AAA N N TTTTT U U M M %
+% Q Q U U A A NN N T U U MM MM %
+% Q Q U U AAAAA N N N T U U M M M %
+% Q QQ U U A A N NN T U U M M %
+% QQQQ UUU A A N N T UUU M M %
+% %
+% MagicCore Methods to Acquire / Destroy Quantum Pixels %
+% %
+% Software Design %
+% John Cristy %
+% October 1998 %
+% %
+% %
+% Copyright 1999-2008 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/cache.h"
+#include "magick/constitute.h"
+#include "magick/delegate.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/pixel.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/statistic.h"
+#include "magick/stream.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/utility.h"
+
+/*
+ Define declarations.
+*/
+#define QuantumSignature 0xab
+
+/*
+ Forward declarations.
+*/
+static void
+ DestroyQuantumPixels(QuantumInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e Q u a n t u m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireQuantumInfo() allocates the QuantumInfo structure.
+%
+% The format of the AcquireQuantumInfo method is:
+%
+% QuantumInfo *AcquireQuantumInfo(const ImageInfo *image_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o image: the image.
+%
+*/
+
+static inline unsigned long MagickMax(const unsigned long x,
+ const unsigned long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+MagickExport QuantumInfo *AcquireQuantumInfo(const ImageInfo *image_info,
+ Image *image)
+{
+ MagickBooleanType
+ status;
+
+ QuantumInfo
+ *quantum_info;
+
+ quantum_info=(QuantumInfo *) AcquireMagickMemory(sizeof(*quantum_info));
+ if (quantum_info == (QuantumInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ quantum_info->signature=MagickSignature;
+ GetQuantumInfo(image_info,quantum_info);
+ if (image == (const Image *) NULL)
+ return(quantum_info);
+ status=SetQuantumDepth(image,quantum_info,image->depth);
+ if (status == MagickFalse)
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ return(quantum_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e Q u a n t u m P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireQuantumPixels() allocates the unsigned char structure.
+%
+% The format of the AcquireQuantumPixels method is:
+%
+% MagickBooleanType AcquireQuantumPixels(QuantumInfo *quantum_info,
+% const size_t extent)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o extent: the quantum info.
+%
+*/
+static MagickBooleanType AcquireQuantumPixels(QuantumInfo *quantum_info,
+ const size_t extent)
+{
+ register long
+ i;
+
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->number_threads=GetOpenMPMaximumThreads();
+ quantum_info->pixels=(unsigned char **) AcquireQuantumMemory(
+ quantum_info->number_threads,sizeof(*quantum_info->pixels));
+ if (quantum_info->pixels == (unsigned char **) NULL)
+ return(MagickFalse);
+ quantum_info->extent=extent;
+ (void) ResetMagickMemory(quantum_info->pixels,0,
+ sizeof(*quantum_info->pixels));
+ for (i=0; i < (long) quantum_info->number_threads; i++)
+ {
+ quantum_info->pixels[i]=(unsigned char *) AcquireQuantumMemory(extent+1,
+ sizeof(**quantum_info->pixels));
+ if (quantum_info->pixels[i] == (unsigned char *) NULL)
+ return(MagickFalse);
+ (void) ResetMagickMemory(quantum_info->pixels[i],0,(extent+1)*
+ sizeof(**quantum_info->pixels));
+ quantum_info->pixels[i][extent]=QuantumSignature;
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y Q u a n t u m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyQuantumInfo() deallocates memory associated with the QuantumInfo
+% structure.
+%
+% The format of the DestroyQuantumInfo method is:
+%
+% QuantumInfo *DestroyQuantumInfo(QuantumInfo *quantum_info)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+*/
+MagickExport QuantumInfo *DestroyQuantumInfo(QuantumInfo *quantum_info)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ if (quantum_info->pixels != (unsigned char **) NULL)
+ DestroyQuantumPixels(quantum_info);
+ if (quantum_info->semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&quantum_info->semaphore);
+ quantum_info->signature=(~MagickSignature);
+ quantum_info=(QuantumInfo *) RelinquishMagickMemory(quantum_info);
+ return(quantum_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y Q u a n t u m P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyQuantumPixels() destroys the quantum pixels.
+%
+% The format of the DestroyQuantumPixels() method is:
+%
+% void DestroyQuantumPixels(QuantumInfo *quantum_info)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+*/
+static void DestroyQuantumPixels(QuantumInfo *quantum_info)
+{
+ register long
+ i;
+
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ assert(quantum_info->pixels != (unsigned char **) NULL);
+ for (i=0; i < (long) quantum_info->number_threads; i++)
+ {
+ assert(quantum_info->pixels[i][quantum_info->extent] == QuantumSignature);
+ quantum_info->pixels[i]=(unsigned char *) RelinquishMagickMemory(
+ quantum_info->pixels[i]);
+ }
+ quantum_info->pixels=(unsigned char **) RelinquishMagickMemory(
+ quantum_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t Q u a n t u m E x t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetQuantumExtent() returns the quantum pixel buffer extent.
+%
+% The format of the GetQuantumExtent method is:
+%
+% size_t GetQuantumExtent(Image *image,const QuantumInfo *quantum_info,
+% const QuantumType quantum_type)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum_info: the quantum info.
+%
+% o quantum_type: Declare which pixel components to transfer (red, green,
+% blue, opacity, RGB, or RGBA).
+%
+*/
+MagickExport size_t GetQuantumExtent(const Image *image,
+ const QuantumInfo *quantum_info,const QuantumType quantum_type)
+{
+ size_t
+ packet_size;
+
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ packet_size=1;
+ switch (quantum_type)
+ {
+ case GrayAlphaQuantum: packet_size=2; break;
+ case IndexAlphaQuantum: packet_size=2; break;
+ case RGBQuantum: packet_size=3; break;
+ case RGBAQuantum: packet_size=4; break;
+ case RGBOQuantum: packet_size=4; break;
+ case CMYKQuantum: packet_size=4; break;
+ case CMYKAQuantum: packet_size=5; break;
+ default: break;
+ }
+ if (quantum_info->pack == MagickFalse)
+ return((size_t) (packet_size*image->columns*((image->depth+7)/8)));
+ return((size_t) ((packet_size*image->columns*image->depth+7)/8));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t Q u a n t u m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetQuantumInfo() initializes the QuantumInfo structure to default values.
+%
+% The format of the GetQuantumInfo method is:
+%
+% GetQuantumInfo(const ImageInfo *image_info,QuantumInfo *quantum_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o quantum_info: the quantum info.
+%
+*/
+MagickExport void GetQuantumInfo(const ImageInfo *image_info,
+ QuantumInfo *quantum_info)
+{
+ const char
+ *option;
+
+ assert(quantum_info != (QuantumInfo *) NULL);
+ (void) ResetMagickMemory(quantum_info,0,sizeof(*quantum_info));
+ quantum_info->quantum=8;
+ quantum_info->maximum=1.0;
+ quantum_info->scale=QuantumRange;
+ quantum_info->pack=MagickTrue;
+ quantum_info->semaphore=AllocateSemaphoreInfo();
+ quantum_info->signature=MagickSignature;
+ if (image_info == (const ImageInfo *) NULL)
+ return;
+ option=GetImageOption(image_info,"quantum:format");
+ if (option != (char *) NULL)
+ quantum_info->format=(QuantumFormatType) ParseMagickOption(
+ MagickQuantumFormatOptions,MagickFalse,option);
+ option=GetImageOption(image_info,"quantum:minimum");
+ if (option != (char *) NULL)
+ quantum_info->minimum=atof(option);
+ option=GetImageOption(image_info,"quantum:maximum");
+ if (option != (char *) NULL)
+ quantum_info->maximum=atof(option);
+ if ((quantum_info->minimum == 0.0) && (quantum_info->maximum == 0.0))
+ quantum_info->scale=0.0;
+ else
+ if (quantum_info->minimum == quantum_info->maximum)
+ {
+ quantum_info->scale=(MagickRealType) QuantumRange/quantum_info->minimum;
+ quantum_info->minimum=0.0;
+ }
+ else
+ quantum_info->scale=(MagickRealType) QuantumRange/(quantum_info->maximum-
+ quantum_info->minimum);
+ option=GetImageOption(image_info,"quantum:scale");
+ if (option != (char *) NULL)
+ quantum_info->scale=atof(option);
+ option=GetImageOption(image_info,"quantum:polarity");
+ if (option != (char *) NULL)
+ quantum_info->min_is_white=LocaleCompare(option,"min-is-white") == 0 ?
+ MagickTrue : MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t Q u a n t u m P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetQuantumPixels() returns the quantum pixels.
+%
+% The format of the GetQuantumPixels method is:
+%
+% unsigned char *QuantumPixels GetQuantumPixels(
+% const QuantumInfo *quantum_info)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport unsigned char *GetQuantumPixels(const QuantumInfo *quantum_info)
+{
+ long
+ id;
+
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ id=GetOpenMPThreadId();
+ return(quantum_info->pixels[id]);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t Q u a n t u m T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetQuantumType() returns the quantum type of the image.
+%
+% The format of the GetQuantumType method is:
+%
+% QuantumType GetQuantumType(Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport QuantumType GetQuantumType(Image *image,ExceptionInfo *exception)
+{
+ QuantumType
+ quantum_type;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ quantum_type=RGBQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=RGBAQuantum;
+ if (image->colorspace == CMYKColorspace)
+ {
+ quantum_type=CMYKQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=CMYKAQuantum;
+ }
+ if (IsGrayImage(image,exception) != MagickFalse)
+ {
+ quantum_type=GrayQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=GrayAlphaQuantum;
+ }
+ else
+ if (image->storage_class == PseudoClass)
+ {
+ quantum_type=IndexQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=IndexAlphaQuantum;
+ }
+ return(quantum_type);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m F o r m a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumAlphaType() sets the quantum format.
+%
+% The format of the SetQuantumAlphaType method is:
+%
+% void SetQuantumAlphaType(QuantumInfo *quantum_info,
+% const QuantumAlphaType type)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o type: the alpha type (e.g. associate).
+%
+*/
+MagickExport void SetQuantumAlphaType(QuantumInfo *quantum_info,
+ const QuantumAlphaType type)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->alpha_type=type;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumDepth() sets the quantum depth.
+%
+% The format of the SetQuantumDepth method is:
+%
+% MagickBooleanType SetQuantumDepth(const Image *image,
+% QuantumInfo *quantum_info,const unsigned long depth)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum_info: the quantum info.
+%
+% o depth: the quantum depth.
+%
+*/
+MagickExport MagickBooleanType SetQuantumDepth(const Image *image,
+ QuantumInfo *quantum_info,const unsigned long depth)
+{
+ MagickBooleanType
+ status;
+
+ /*
+ Allocate the quantum pixel buffer.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->depth=depth;
+ if (quantum_info->format == FloatingPointQuantumFormat)
+ {
+ if (quantum_info->depth > 32)
+ quantum_info->depth=64;
+ else
+ quantum_info->depth=32;
+ }
+ if (quantum_info->pixels != (unsigned char **) NULL)
+ DestroyQuantumPixels(quantum_info);
+ status=AcquireQuantumPixels(quantum_info,(quantum_info->pad+5)*image->columns*
+ ((quantum_info->depth+7)/8));
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m F o r m a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumFormat() sets the quantum format.
+%
+% The format of the SetQuantumFormat method is:
+%
+% MagickBooleanType SetQuantumFormat(const Image *image,
+% QuantumInfo *quantum_info,const QuantumFormatType format)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum_info: the quantum info.
+%
+% o format: the quantum format.
+%
+*/
+MagickExport MagickBooleanType SetQuantumFormat(const Image *image,
+ QuantumInfo *quantum_info,const QuantumFormatType format)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->format=format;
+ return(SetQuantumDepth(image,quantum_info,quantum_info->depth));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m I m a g e T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumImageType() sets the image type based on the quantum type.
+%
+% The format of the SetQuantumImageType method is:
+%
+% void ImageType SetQuantumImageType(Image *image,
+% const QuantumType quantum_type)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum_type: Declare which pixel components to transfer (red, green,
+% blue, opacity, RGB, or RGBA).
+%
+*/
+MagickExport void SetQuantumImageType(Image *image,
+ const QuantumType quantum_type)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ switch (quantum_type)
+ {
+ case IndexQuantum:
+ case IndexAlphaQuantum:
+ {
+ image->type=PaletteType;
+ break;
+ }
+ case GrayQuantum:
+ case GrayAlphaQuantum:
+ {
+ image->type=GrayscaleType;
+ if (image->depth == 1)
+ image->type=BilevelType;
+ break;
+ }
+ case CyanQuantum:
+ case MagentaQuantum:
+ case YellowQuantum:
+ case BlackQuantum:
+ case CMYKQuantum:
+ case CMYKAQuantum:
+ {
+ image->type=ColorSeparationType;
+ break;
+ }
+ default:
+ {
+ image->type=TrueColorType;
+ break;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m P a c k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumPack() sets the quantum pack flag.
+%
+% The format of the SetQuantumPack method is:
+%
+% void SetQuantumPack(QuantumInfo *quantum_info,
+% const MagickBooleanType pack)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o pack: the pack flag.
+%
+*/
+MagickExport void SetQuantumPack(QuantumInfo *quantum_info,
+ const MagickBooleanType pack)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->pack=pack;
+}
+
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m P a d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumPad() sets the quantum pad.
+%
+% The format of the SetQuantumPad method is:
+%
+% MagickBooleanType SetQuantumPad(const Image *image,
+% QuantumInfo *quantum_info,const unsigned long pad)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o quantum_info: the quantum info.
+%
+% o pad: the quantum pad.
+%
+*/
+MagickExport MagickBooleanType SetQuantumPad(const Image *image,
+ QuantumInfo *quantum_info,const unsigned long pad)
+{
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->pad=pad;
+ return(SetQuantumDepth(image,quantum_info,quantum_info->depth));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m M i n I s W h i t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumMinIsWhite() sets the quantum min-is-white flag.
+%
+% The format of the SetQuantumMinIsWhite method is:
+%
+% void SetQuantumMinIsWhite(QuantumInfo *quantum_info,
+% const MagickBooleanType min_is_white)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o min_is_white: the min-is-white flag.
+%
+*/
+MagickExport void SetQuantumMinIsWhite(QuantumInfo *quantum_info,
+ const MagickBooleanType min_is_white)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->min_is_white=min_is_white;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m Q u a n t u m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumQuantum() sets the quantum quantum.
+%
+% The format of the SetQuantumQuantum method is:
+%
+% void SetQuantumQuantum(QuantumInfo *quantum_info,
+% const unsigned long quantum)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o quantum: the quantum quantum.
+%
+*/
+MagickExport void SetQuantumQuantum(QuantumInfo *quantum_info,
+ const unsigned long quantum)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->quantum=quantum;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t Q u a n t u m S c a l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetQuantumScale() sets the quantum scale.
+%
+% The format of the SetQuantumScale method is:
+%
+% void SetQuantumScale(QuantumInfo *quantum_info,const double scale)
+%
+% A description of each parameter follows:
+%
+% o quantum_info: the quantum info.
+%
+% o scale: the quantum scale.
+%
+*/
+MagickExport void SetQuantumScale(QuantumInfo *quantum_info,const double scale)
+{
+ assert(quantum_info != (QuantumInfo *) NULL);
+ assert(quantum_info->signature == MagickSignature);
+ quantum_info->scale=scale;
+}
diff --git a/magick/quantum.h b/magick/quantum.h
new file mode 100644
index 0000000..685ed11
--- /dev/null
+++ b/magick/quantum.h
@@ -0,0 +1,180 @@
+/*
+ Copyright 1999-2008 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore quantum inline methods.
+*/
+#ifndef _MAGICKCORE_QUANTUM_H
+#define _MAGICKCORE_QUANTUM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/semaphore.h"
+
+typedef enum
+{
+ UndefinedEndian,
+ LSBEndian,
+ MSBEndian
+} EndianType;
+
+typedef enum
+{
+ UndefinedQuantumAlpha,
+ AssociatedQuantumAlpha,
+ DisassociatedQuantumAlpha
+} QuantumAlphaType;
+
+typedef enum
+{
+ UndefinedQuantumFormat,
+ FloatingPointQuantumFormat,
+ SignedQuantumFormat,
+ UnsignedQuantumFormat
+} QuantumFormatType;
+
+typedef enum
+{
+ UndefinedQuantum,
+ AlphaQuantum,
+ BlackQuantum,
+ BlueQuantum,
+ CMYKAQuantum,
+ CMYKQuantum,
+ CyanQuantum,
+ GrayAlphaQuantum,
+ GrayQuantum,
+ GreenQuantum,
+ IndexAlphaQuantum,
+ IndexQuantum,
+ MagentaQuantum,
+ OpacityQuantum,
+ RedQuantum,
+ RGBAQuantum,
+ RGBOQuantum,
+ RGBQuantum,
+ YellowQuantum,
+ GrayPadQuantum, /* deprecated */
+ RGBPadQuantum,
+ CbYCrYQuantum,
+ CbYCrQuantum,
+ CbYCrAQuantum,
+ CMYKOQuantum
+} QuantumType;
+
+typedef struct _QuantumInfo
+ QuantumInfo;
+
+static inline Quantum RoundToQuantum(const MagickRealType value)
+{
+#if defined(MAGICKCORE_HDRI_SUPPORT)
+ return((Quantum) value);
+#else
+ if (value <= 0.0)
+ return((Quantum) 0);
+ if (value >= QuantumRange)
+ return((Quantum) QuantumRange);
+ return((Quantum) (value+0.5));
+#endif
+}
+
+#if (MAGICKCORE_QUANTUM_DEPTH == 8)
+static inline unsigned char ScaleQuantumToChar(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned char) quantum);
+#else
+ if (quantum <= 0.0)
+ return(0UL);
+ if (quantum >= 255.0)
+ return(255);
+ return((unsigned char) (quantum+0.5));
+#endif
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
+static inline unsigned char ScaleQuantumToChar(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned char) (((quantum+128UL)-((quantum+128UL) >> 8)) >> 8));
+#else
+ if (quantum <= 0.0)
+ return(0);
+ if ((quantum/257.0) >= 255.0)
+ return(255);
+ return((unsigned char) (quantum/257.0+0.5));
+#endif
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
+static inline unsigned char ScaleQuantumToChar(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned char) ((quantum+MagickULLConstant(8421504))/
+ MagickULLConstant(16843009)));
+#else
+ if (quantum <= 0.0)
+ return(0);
+ if ((quantum/16843009.0) >= 255.0)
+ return(255);
+ return((unsigned char) (quantum/16843009.0+0.5));
+#endif
+}
+#elif (MAGICKCORE_QUANTUM_DEPTH == 64)
+static inline unsigned char ScaleQuantumToChar(const Quantum quantum)
+{
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+ return((unsigned char) ((quantum+2155839615.0)/71777214294589695.0));
+#else
+ return((unsigned char) (quantum/71777214294589695.0+0.5));
+#endif
+}
+#endif
+
+extern MagickExport MagickBooleanType
+ SetQuantumDepth(const Image *,QuantumInfo *,const unsigned long),
+ SetQuantumFormat(const Image *,QuantumInfo *,const QuantumFormatType),
+ SetQuantumPad(const Image *,QuantumInfo *,const unsigned long);
+
+extern MagickExport QuantumInfo
+ *AcquireQuantumInfo(const ImageInfo *,Image *),
+ *DestroyQuantumInfo(QuantumInfo *);
+
+extern MagickExport QuantumType
+ GetQuantumType(Image *,ExceptionInfo *);
+
+extern MagickExport size_t
+ ExportQuantumPixels(const Image *,const CacheView *,const QuantumInfo *,
+ const QuantumType,unsigned char *,ExceptionInfo *),
+ GetQuantumExtent(const Image *,const QuantumInfo *,const QuantumType),
+ ImportQuantumPixels(Image *,CacheView *,const QuantumInfo *,const QuantumType,
+ const unsigned char *,ExceptionInfo *);
+
+extern MagickExport unsigned char
+ *GetQuantumPixels(const QuantumInfo *);
+
+extern MagickExport void
+ GetQuantumInfo(const ImageInfo *,QuantumInfo *),
+ SetQuantumAlphaType(QuantumInfo *,const QuantumAlphaType),
+ SetQuantumImageType(Image *,const QuantumType),
+ SetQuantumMinIsWhite(QuantumInfo *,const MagickBooleanType),
+ SetQuantumPack(QuantumInfo *,const MagickBooleanType),
+ SetQuantumQuantum(QuantumInfo *,const unsigned long),
+ SetQuantumScale(QuantumInfo *,const double);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/random-private.h b/magick/random-private.h
new file mode 100644
index 0000000..ee13553
--- /dev/null
+++ b/magick/random-private.h
@@ -0,0 +1,70 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore random generation private methods.
+*/
+#ifndef _MAGICKCORE_RANDOM_PRIVATE_H
+#define _MAGICKCORE_RANDOM_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/thread-private.h"
+
+static inline RandomInfo **DestroyRandomInfoThreadSet(
+ RandomInfo **random_info)
+{
+ register long
+ i;
+
+ assert(random_info != (RandomInfo **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (random_info[i] != (RandomInfo *) NULL)
+ random_info[i]=DestroyRandomInfo(random_info[i]);
+ return((RandomInfo **) RelinquishAlignedMemory(random_info));
+}
+
+static inline RandomInfo **AcquireRandomInfoThreadSet(void)
+{
+ register long
+ i;
+
+ RandomInfo
+ **random_info;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ random_info=(RandomInfo **) AcquireAlignedMemory(number_threads,
+ sizeof(*random_info));
+ if (random_info == (RandomInfo **) NULL)
+ return((RandomInfo **) NULL);
+ (void) ResetMagickMemory(random_info,0,number_threads*sizeof(*random_info));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ random_info[i]=AcquireRandomInfo();
+ if (random_info[i] == (RandomInfo *) NULL)
+ return(DestroyRandomInfoThreadSet(random_info));
+ }
+ return(random_info);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/random.c b/magick/random.c
new file mode 100644
index 0000000..b459fc0
--- /dev/null
+++ b/magick/random.c
@@ -0,0 +1,867 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% RRRR AAA N N DDDD OOO M M %
+% R R A A NN N D D O O MM MM %
+% RRRR AAAAA N N N D D O O M M M %
+% R R A A N NN D D O O M M %
+% R R A A N N DDDD OOO M M %
+% %
+% %
+% MagickCore Methods to Generate Random Numbers %
+% %
+% Software Design %
+% John Cristy %
+% December 2001 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The generation of random numbers is too important to be left to chance.
+% -- Tom Christiansen <[email protected]>
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#if defined(__VMS)
+#include <time.h>
+#endif
+#if defined(__MINGW32__)
+#include <sys/time.h>
+#endif
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/random_.h"
+#include "magick/resource_.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+#include "magick/thread_.h"
+#include "magick/thread-private.h"
+#include "magick/utility.h"
+/*
+ Define declarations.
+*/
+#define PseudoRandomHash SHA256Hash
+#define RandomEntropyLevel 9
+#define RandomFilename "reservoir.xdm"
+#define RandomFiletype "random"
+#define RandomProtocolMajorVersion 1
+#define RandomProtocolMinorVersion 0
+
+/*
+ Typedef declarations.
+*/
+struct _RandomInfo
+{
+ SignatureInfo
+ *signature_info;
+
+ StringInfo
+ *nonce,
+ *reservoir;
+
+ size_t
+ i;
+
+ unsigned long
+ seed[4];
+
+ double
+ normalize;
+
+ unsigned short
+ protocol_major,
+ protocol_minor;
+
+ SemaphoreInfo
+ *semaphore;
+
+ long
+ timestamp;
+
+ unsigned long
+ signature;
+};
+
+/*
+ External declarations.
+*/
+#if defined(__APPLE__)
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
+#endif
+
+extern char
+ **environ;
+
+/*
+ Global declarations.
+*/
+static SemaphoreInfo
+ *random_semaphore = (SemaphoreInfo *) NULL;
+
+static unsigned long
+ random_seed = ~0UL;
+
+static MagickBooleanType
+ gather_true_random = MagickFalse;
+
+/*
+ Forward declarations.
+*/
+static StringInfo
+ *GenerateEntropicChaos(RandomInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e R a n d o m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireRandomInfo() allocates the RandomInfo structure.
+%
+% The format of the AcquireRandomInfo method is:
+%
+% RandomInfo *AcquireRandomInfo(void)
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport RandomInfo *AcquireRandomInfo(void)
+{
+ const StringInfo
+ *digest;
+
+ RandomInfo
+ *random_info;
+
+ StringInfo
+ *entropy,
+ *key,
+ *nonce;
+
+ random_info=(RandomInfo *) AcquireAlignedMemory(1,sizeof(*random_info));
+ if (random_info == (RandomInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(random_info,0,sizeof(*random_info));
+ random_info->signature_info=AcquireSignatureInfo();
+ random_info->nonce=AcquireStringInfo(2*GetSignatureDigestsize(
+ random_info->signature_info));
+ ResetStringInfo(random_info->nonce);
+ random_info->reservoir=AcquireStringInfo(GetSignatureDigestsize(
+ random_info->signature_info));
+ ResetStringInfo(random_info->reservoir);
+ random_info->normalize=1.0/(~0UL);
+ random_info->semaphore=AllocateSemaphoreInfo();
+ random_info->protocol_major=RandomProtocolMajorVersion;
+ random_info->protocol_minor=RandomProtocolMinorVersion;
+ random_info->timestamp=(long) time(0);
+ random_info->signature=MagickSignature;
+ /*
+ Seed random nonce.
+ */
+ nonce=GenerateEntropicChaos(random_info);
+ if (nonce == (StringInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ InitializeSignature(random_info->signature_info);
+ UpdateSignature(random_info->signature_info,nonce);
+ FinalizeSignature(random_info->signature_info);
+ SetStringInfoLength(nonce,(GetSignatureDigestsize(
+ random_info->signature_info)+1)/2);
+ SetStringInfo(nonce,GetSignatureDigest(random_info->signature_info));
+ SetStringInfo(random_info->nonce,nonce);
+ nonce=DestroyStringInfo(nonce);
+ /*
+ Seed random reservoir with entropic data.
+ */
+ entropy=GenerateEntropicChaos(random_info);
+ if (entropy == (StringInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ UpdateSignature(random_info->signature_info,entropy);
+ FinalizeSignature(random_info->signature_info);
+ SetStringInfo(random_info->reservoir,GetSignatureDigest(
+ random_info->signature_info));
+ entropy=DestroyStringInfo(entropy);
+ /*
+ Seed pseudo random number generator.
+ */
+ if (random_seed == ~0UL)
+ {
+ key=GetRandomKey(random_info,sizeof(random_seed));
+ (void) CopyMagickMemory(random_info->seed,GetStringInfoDatum(key),
+ GetStringInfoLength(key));
+ key=DestroyStringInfo(key);
+ }
+ else
+ {
+ SignatureInfo
+ *signature_info;
+
+ signature_info=AcquireSignatureInfo();
+ key=AcquireStringInfo(sizeof(random_seed));
+ SetStringInfoDatum(key,(unsigned char *) &random_seed);
+ UpdateSignature(signature_info,key);
+ key=DestroyStringInfo(key);
+ FinalizeSignature(signature_info);
+ digest=GetSignatureDigest(signature_info);
+ (void) CopyMagickMemory(random_info->seed,GetStringInfoDatum(digest),
+ MagickMin(GetSignatureDigestsize(signature_info),
+ sizeof(*random_info->seed)));
+ signature_info=DestroySignatureInfo(signature_info);
+ }
+ random_info->seed[1]=0x50a7f451UL;
+ random_info->seed[2]=0x5365417eUL;
+ random_info->seed[3]=0xc3a4171aUL;
+ return(random_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y R a n d o m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyRandomInfo() deallocates memory associated with the random
+% reservoir.
+%
+% The format of the DestroyRandomInfo method is:
+%
+% RandomInfo *DestroyRandomInfo(RandomInfo *random_info)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+*/
+MagickExport RandomInfo *DestroyRandomInfo(RandomInfo *random_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(random_info != (RandomInfo *) NULL);
+ assert(random_info->signature == MagickSignature);
+ (void) LockSemaphoreInfo(random_info->semaphore);
+ if (random_info->reservoir != (StringInfo *) NULL)
+ random_info->reservoir=DestroyStringInfo(random_info->reservoir);
+ if (random_info->nonce != (StringInfo *) NULL)
+ random_info->nonce=DestroyStringInfo(random_info->nonce);
+ if (random_info->signature_info != (SignatureInfo *) NULL)
+ random_info->signature_info=DestroySignatureInfo(
+ random_info->signature_info);
+ (void) ResetMagickMemory(random_info->seed,0,sizeof(*random_info->seed));
+ random_info->signature=(~MagickSignature);
+ (void) UnlockSemaphoreInfo(random_info->semaphore);
+ DestroySemaphoreInfo(&random_info->semaphore);
+ random_info=(RandomInfo *) RelinquishAlignedMemory(random_info);
+ return(random_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y R a n d o m R e s e r v i o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyRandomReservoir() deallocates memory associated with the random
+% reservoir.
+%
+% The format of the DestroyRandomReservoir method is:
+%
+% DestroyRandomReservoir(void)
+%
+*/
+MagickExport void DestroyRandomReservoir(void)
+{
+ AcquireSemaphoreInfo(&random_semaphore);
+ (void) UnlockSemaphoreInfo(random_semaphore);
+ DestroySemaphoreInfo(&random_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e n e r a t e E n t r o p i c C h a o s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GenerateEntropicChaos() generate entropic chaos used to initialize the
+% random reservoir.
+%
+% The format of the GenerateEntropicChaos method is:
+%
+% StringInfo *GenerateEntropicChaos(RandomInfo *random_info)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+*/
+
+static ssize_t ReadRandom(int file,unsigned char *source,size_t length)
+{
+ register unsigned char
+ *q;
+
+ ssize_t
+ offset,
+ count;
+
+ offset=0;
+ for (q=source; length != 0; length-=count)
+ {
+ count=(ssize_t) read(file,q,length);
+ if (count <= 0)
+ {
+ count=0;
+ if (errno == EINTR)
+ continue;
+ return(-1);
+ }
+ q+=count;
+ offset+=count;
+ }
+ return(offset);
+}
+
+static StringInfo *GenerateEntropicChaos(RandomInfo *random_info)
+{
+#define MaxEntropyExtent 64
+
+ long
+ pid;
+
+ MagickThreadType
+ tid;
+
+ StringInfo
+ *chaos,
+ *entropy;
+
+ unsigned long
+ nanoseconds,
+ seconds;
+
+ /*
+ Initialize random reservoir.
+ */
+ entropy=AcquireStringInfo(0);
+ (void) LockSemaphoreInfo(random_info->semaphore);
+ chaos=AcquireStringInfo(sizeof(unsigned char *));
+ SetStringInfoDatum(chaos,(unsigned char *) &entropy);
+ ConcatenateStringInfo(entropy,chaos);
+ SetStringInfoDatum(chaos,(unsigned char *) entropy);
+ ConcatenateStringInfo(entropy,chaos);
+ pid=(long) getpid();
+ SetStringInfoLength(chaos,sizeof(pid));
+ SetStringInfoDatum(chaos,(unsigned char *) &pid);
+ ConcatenateStringInfo(entropy,chaos);
+ tid=GetMagickThreadId();
+ SetStringInfoLength(chaos,sizeof(tid));
+ SetStringInfoDatum(chaos,(unsigned char *) &tid);
+ ConcatenateStringInfo(entropy,chaos);
+#if defined(MAGICKCORE_HAVE_GETRUSAGE) && defined(RUSAGE_SELF)
+ {
+ struct rusage
+ usage;
+
+ if (getrusage(RUSAGE_SELF,&usage) == 0)
+ {
+ SetStringInfoLength(chaos,sizeof(usage));
+ SetStringInfoDatum(chaos,(unsigned char *) &usage);
+ }
+ }
+#endif
+ seconds=time((time_t *) 0);
+ nanoseconds=0;
+#if defined(MAGICKCORE_HAVE_GETTIMEOFDAY)
+ {
+ struct timeval
+ timer;
+
+ if (gettimeofday(&timer,0) == 0)
+ {
+ seconds=timer.tv_sec;
+ nanoseconds=1000UL*timer.tv_usec;
+ }
+ }
+#endif
+#if defined(MAGICKCORE_HAVE_CLOCK_GETTIME) && defined(CLOCK_HIGHRES)
+ {
+ struct timespec
+ timer;
+
+ if (clock_gettime(CLOCK_HIGHRES,&timer) == 0)
+ {
+ seconds=timer.tv_sec;
+ nanoseconds=timer.tv_nsec;
+ }
+ }
+#endif
+ SetStringInfoLength(chaos,sizeof(seconds));
+ SetStringInfoDatum(chaos,(unsigned char *) &seconds);
+ ConcatenateStringInfo(entropy,chaos);
+ SetStringInfoLength(chaos,sizeof(nanoseconds));
+ SetStringInfoDatum(chaos,(unsigned char *) &nanoseconds);
+ ConcatenateStringInfo(entropy,chaos);
+ nanoseconds=0;
+#if defined(MAGICKCORE_HAVE_CLOCK)
+ nanoseconds=clock();
+#endif
+#if defined(MAGICKCORE_HAVE_TIMES)
+ {
+ struct tms
+ timer;
+
+ (void) times(&timer);
+ nanoseconds=timer.tms_utime+timer.tms_stime;
+ }
+#endif
+ SetStringInfoLength(chaos,sizeof(nanoseconds));
+ SetStringInfoDatum(chaos,(unsigned char *) &nanoseconds);
+ ConcatenateStringInfo(entropy,chaos);
+#if defined(MAGICKCORE_HAVE_MKSTEMP)
+ {
+ char
+ *filename;
+
+ int
+ file;
+
+ filename=ConstantString("magickXXXXXX");
+ file=mkstemp(filename);
+#if defined(__OS2__)
+ setmode(file,O_BINARY);
+#endif
+ if (file != -1)
+ (void) close(file);
+ (void) remove(filename);
+ SetStringInfoLength(chaos,strlen(filename));
+ SetStringInfoDatum(chaos,(unsigned char *) filename);
+ ConcatenateStringInfo(entropy,chaos);
+ filename=DestroyString(filename);
+ }
+#endif
+#if defined(__WINDOWS__)
+ {
+ double
+ seconds;
+
+ LARGE_INTEGER
+ nanoseconds;
+
+ MagickBooleanType
+ status;
+
+ /*
+ Not crytographically strong but better than nothing.
+ */
+ seconds=NTElapsedTime()+NTUserTime();
+ SetStringInfoLength(chaos,sizeof(seconds));
+ SetStringInfoDatum(chaos,(unsigned char *) &seconds);
+ ConcatenateStringInfo(entropy,chaos);
+ if (QueryPerformanceCounter(&nanoseconds) != 0)
+ {
+ SetStringInfoLength(chaos,sizeof(nanoseconds));
+ SetStringInfoDatum(chaos,(unsigned char *) &nanoseconds);
+ ConcatenateStringInfo(entropy,chaos);
+ }
+ /*
+ Our best hope for true entropy.
+ */
+ SetStringInfoLength(chaos,MaxEntropyExtent);
+ status=NTGatherRandomData(MaxEntropyExtent,GetStringInfoDatum(chaos));
+ ConcatenateStringInfo(entropy,chaos);
+ }
+#else
+ {
+ char
+ *filename;
+
+ int
+ file;
+
+ ssize_t
+ count;
+
+ StringInfo
+ *device;
+
+ /*
+ Not crytographically strong but better than nothing.
+ */
+ if (environ != (char **) NULL)
+ {
+ register long
+ i;
+
+ /*
+ Squeeze some entropy from the sometimes unpredicatble environment.
+ */
+ for (i=0; environ[i] != (char *) NULL; i++)
+ {
+ SetStringInfoLength(chaos,strlen(environ[i]));
+ SetStringInfoDatum(chaos,(unsigned char *) environ[i]);
+ ConcatenateStringInfo(entropy,chaos);
+ }
+ }
+ filename=AcquireString("/dev/urandom");
+ device=StringToStringInfo(filename);
+ device=DestroyStringInfo(device);
+ file=open(filename,O_RDONLY | O_BINARY);
+ filename=DestroyString(filename);
+ if (file != -1)
+ {
+ SetStringInfoLength(chaos,MaxEntropyExtent);
+ count=ReadRandom(file,GetStringInfoDatum(chaos),MaxEntropyExtent);
+ (void) close(file);
+ SetStringInfoLength(chaos,(size_t) count);
+ ConcatenateStringInfo(entropy,chaos);
+ }
+ if (gather_true_random != MagickFalse)
+ {
+ /*
+ Our best hope for true entropy.
+ */
+ filename=AcquireString("/dev/random");
+ device=StringToStringInfo(filename);
+ device=DestroyStringInfo(device);
+ file=open(filename,O_RDONLY | O_BINARY);
+ filename=DestroyString(filename);
+ if (file == -1)
+ {
+ filename=AcquireString("/dev/srandom");
+ device=StringToStringInfo(filename);
+ device=DestroyStringInfo(device);
+ file=open(filename,O_RDONLY | O_BINARY);
+ }
+ if (file != -1)
+ {
+ SetStringInfoLength(chaos,MaxEntropyExtent);
+ count=ReadRandom(file,GetStringInfoDatum(chaos),MaxEntropyExtent);
+ (void) close(file);
+ SetStringInfoLength(chaos,(size_t) count);
+ ConcatenateStringInfo(entropy,chaos);
+ }
+ }
+ }
+#endif
+ chaos=DestroyStringInfo(chaos);
+ (void) UnlockSemaphoreInfo(random_info->semaphore);
+ return(entropy);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P s e u d o R a n d o m V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPseudoRandomValue() return a non-negative double-precision floating-point
+% value uniformly distributed over the interval [0.0, 1.0) with a 2 to the
+% 128th-1 period.
+%
+% The format of the GetPseudoRandomValue method is:
+%
+% double GetPseudoRandomValue(RandomInfo *randon_info)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+*/
+MagickExport double GetPseudoRandomValue(RandomInfo *random_info)
+{
+ register unsigned long
+ *seed;
+
+ unsigned long
+ alpha;
+
+ seed=random_info->seed;
+ do
+ {
+ alpha=(unsigned long) (seed[1] ^ (seed[1] << 11));
+ seed[1]=seed[2];
+ seed[2]=seed[3];
+ seed[3]=seed[0];
+ seed[0]=(seed[0] ^ (seed[0] >> 19)) ^ (alpha ^ (alpha >> 8));
+ } while (seed[0] == ~0UL);
+ return(random_info->normalize*seed[0]);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t R a n d o m K e y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetRandomKey() gets a random key from the reservoir.
+%
+% The format of the GetRandomKey method is:
+%
+% StringInfo *GetRandomKey(RandomInfo *random_info,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+% o length: the key length.
+%
+*/
+MagickExport StringInfo *GetRandomKey(RandomInfo *random_info,
+ const size_t length)
+{
+ StringInfo
+ *key;
+
+ assert(random_info != (RandomInfo *) NULL);
+ key=AcquireStringInfo(length);
+ SetRandomKey(random_info,length,GetStringInfoDatum(key));
+ return(key);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t R a n d o m V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetRandomValue() return a non-negative double-precision floating-point
+% value uniformly distributed over the interval [0.0, 1.0) with a 2 to the
+% 128th-1 period (not cryptographically strong).
+%
+% The format of the GetRandomValue method is:
+%
+% double GetRandomValue(void)
+%
+*/
+MagickExport double GetRandomValue(RandomInfo *random_info)
+{
+ unsigned long
+ key,
+ range;
+
+ range=(~0UL);
+ do
+ {
+ SetRandomKey(random_info,sizeof(key),(unsigned char *) &key);
+ } while (key == range);
+ return((double) key/range);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e e d P s e u d o R a n d o m G e n e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SeedPseudoRandomGenerator() initializes the pseudo-random number generator
+% with a random seed.
+%
+% The format of the SeedPseudoRandomGenerator method is:
+%
+% void SeedPseudoRandomGenerator(const unsigned long seed)
+%
+% A description of each parameter follows:
+%
+% o seed: the seed.
+%
+*/
+MagickExport void SeedPseudoRandomGenerator(const unsigned long seed)
+{
+ random_seed=seed;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t R a n d o m K e y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetRandomKey() sets a random key from the reservoir.
+%
+% The format of the SetRandomKey method is:
+%
+% void SetRandomKey(RandomInfo *random_info,const size_t length,
+% unsigned char *key)
+%
+% A description of each parameter follows:
+%
+% o random_info: the random info.
+%
+% o length: the key length.
+%
+% o key: the key.
+%
+*/
+
+static inline void IncrementRandomNonce(StringInfo *nonce)
+{
+ register long
+ i;
+
+ unsigned char
+ *datum;
+
+ datum=GetStringInfoDatum(nonce);
+ for (i=(long) (GetStringInfoLength(nonce)-1); i != 0; i--)
+ {
+ datum[i]++;
+ if (datum[i] != 0)
+ return;
+ }
+ ThrowFatalException(RandomFatalError,"SequenceWrapError");
+}
+
+MagickExport void SetRandomKey(RandomInfo *random_info,const size_t length,
+ unsigned char *key)
+{
+ register size_t
+ i;
+
+ register unsigned char
+ *p;
+
+ SignatureInfo
+ *signature_info;
+
+ unsigned char
+ *datum;
+
+ assert(random_info != (RandomInfo *) NULL);
+ if (length == 0)
+ return;
+ (void) LockSemaphoreInfo(random_info->semaphore);
+ signature_info=random_info->signature_info;
+ datum=GetStringInfoDatum(random_info->reservoir);
+ i=length;
+ for (p=key; (i != 0) && (random_info->i != 0); i--)
+ {
+ *p++=datum[random_info->i];
+ random_info->i++;
+ if (random_info->i == GetSignatureDigestsize(signature_info))
+ random_info->i=0;
+ }
+ while (i >= GetSignatureDigestsize(signature_info))
+ {
+ InitializeSignature(signature_info);
+ UpdateSignature(signature_info,random_info->nonce);
+ FinalizeSignature(signature_info);
+ IncrementRandomNonce(random_info->nonce);
+ (void) CopyMagickMemory(p,GetStringInfoDatum(GetSignatureDigest(
+ signature_info)),GetSignatureDigestsize(signature_info));
+ p+=GetSignatureDigestsize(signature_info);
+ i-=GetSignatureDigestsize(signature_info);
+ }
+ if (i != 0)
+ {
+ InitializeSignature(signature_info);
+ UpdateSignature(signature_info,random_info->nonce);
+ FinalizeSignature(signature_info);
+ IncrementRandomNonce(random_info->nonce);
+ SetStringInfo(random_info->reservoir,GetSignatureDigest(signature_info));
+ random_info->i=i;
+ datum=GetStringInfoDatum(random_info->reservoir);
+ while (i-- != 0)
+ p[i]=datum[i];
+ }
+ (void) UnlockSemaphoreInfo(random_info->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t R a n d o m T r u e R a n d o m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetRandomTrueRandom() declares your intentions to use true random numbers.
+% True random numbers are encouraged but may not always be practical because
+% your application may block while entropy is gathered from your environment.
+%
+% The format of the SetRandomTrueRandom method is:
+%
+% void SetRandomTrueRandom(const MagickBooleanType true_random)
+%
+% A description of each parameter follows:
+%
+% o true_random: declare your intentions to use true-random number.
+%
+*/
+MagickExport void SetRandomTrueRandom(const MagickBooleanType true_random)
+{
+ gather_true_random=true_random;
+}
diff --git a/magick/random_.h b/magick/random_.h
new file mode 100644
index 0000000..867d5e9
--- /dev/null
+++ b/magick/random_.h
@@ -0,0 +1,57 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore random methods.
+*/
+#ifndef _MAGICKCORE_RANDOM__H
+#define _MAGICKCORE_RANDOM__H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/string_.h"
+
+/*
+ Typedef declarations.
+*/
+typedef struct _RandomInfo
+ RandomInfo;
+
+/*
+ Method declarations.
+*/
+extern MagickExport double
+ GetRandomValue(RandomInfo *),
+ GetPseudoRandomValue(RandomInfo *);
+
+extern MagickExport RandomInfo
+ *AcquireRandomInfo(void),
+ *DestroyRandomInfo(RandomInfo *);
+
+extern MagickExport StringInfo
+ *GetRandomKey(RandomInfo *,const size_t);
+
+extern MagickExport void
+ DestroyRandomReservoir(void),
+ SeedPseudoRandomGenerator(const unsigned long),
+ SetRandomKey(RandomInfo *,const size_t,unsigned char *),
+ SetRandomTrueRandom(const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/registry.c b/magick/registry.c
new file mode 100644
index 0000000..9008a97
--- /dev/null
+++ b/magick/registry.c
@@ -0,0 +1,493 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% RRRR EEEEE GGG IIIII SSSSS TTTTT RRRR Y Y %
+% R R E G I SS T R R Y Y %
+% RRRR EEE G GGG I SSS T RRRR Y %
+% R R E G G I SS T R R Y %
+% R R EEEEE GGG IIIII SSSSS T R R Y %
+% %
+% %
+% MagickCore Registry Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/registry.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+
+/*
+ Typedef declarations.
+*/
+typedef struct _RegistryInfo
+{
+ RegistryType
+ type;
+
+ void
+ *value;
+
+ unsigned long
+ signature;
+} RegistryInfo;
+
+/*
+ Static declarations.
+*/
+static SplayTreeInfo
+ *registry = (SplayTreeInfo *) NULL;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e f i n e I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineImageRegistry() associates a key/value pair with the image registry.
+%
+% The format of the DefineImageRegistry method is:
+%
+% MagickBooleanType DefineImageRegistry(const RegistryType type,
+% const char *option,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o type: the type.
+%
+% o option: the option.
+%
+% o exception: the exception.
+%
+*/
+MagickExport MagickBooleanType DefineImageRegistry(const RegistryType type,
+ const char *option,ExceptionInfo *exception)
+{
+ char
+ key[MaxTextExtent],
+ value[MaxTextExtent];
+
+ register char
+ *p;
+
+ assert(option != (const char *) NULL);
+ (void) CopyMagickString(key,option,MaxTextExtent);
+ for (p=key; *p != '\0'; p++)
+ if (*p == '=')
+ break;
+ *value='\0';
+ if (*p == '=')
+ (void) CopyMagickString(value,p+1,MaxTextExtent);
+ *p='\0';
+ return(SetImageRegistry(type,key,value,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteImageRegistry() deletes a key from the image registry.
+%
+% The format of the DeleteImageRegistry method is:
+%
+% MagickBooleanType DeleteImageRegistry(const char *key)
+%
+% A description of each parameter follows:
+%
+% o key: the registry.
+%
+*/
+MagickExport MagickBooleanType DeleteImageRegistry(const char *key)
+{
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
+ if (registry == (void *) NULL)
+ return(MagickFalse);
+ return(DeleteNodeFromSplayTree(registry,key));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyImageRegistry() releases memory associated with the image registry.
+%
+% The format of the DestroyDefines method is:
+%
+% void DestroyImageRegistry(void)
+%
+*/
+MagickExport void DestroyImageRegistry(void)
+{
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (registry != (void *) NULL)
+ registry=DestroySplayTree(registry);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageRegistry() returns a value associated with an image registry key.
+%
+% The format of the GetImageRegistry method is:
+%
+% void *GetImageRegistry(const RegistryType type,const char *key,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o type: the type.
+%
+% o key: the key.
+%
+% o exception: the exception.
+%
+*/
+MagickExport void *GetImageRegistry(const RegistryType type,const char *key,
+ ExceptionInfo *exception)
+{
+ void
+ *value;
+
+ RegistryInfo
+ *registry_info;
+
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
+ if (registry == (void *) NULL)
+ return((void *) NULL);
+ registry_info=(RegistryInfo *) GetValueFromSplayTree(registry,key);
+ if (registry_info == (void *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),RegistryError,
+ "UnableToGetRegistryID","`%s'",key);
+ return((void *) NULL);
+ }
+ value=(void *) NULL;
+ switch (type)
+ {
+ case ImageRegistryType:
+ {
+ if (type == registry_info->type)
+ value=(void *) CloneImageList((Image *) registry_info->value,exception);
+ break;
+ }
+ case ImageInfoRegistryType:
+ {
+ if (type == registry_info->type)
+ value=(void *) CloneImageInfo((ImageInfo *) registry_info->value);
+ break;
+ }
+ case StringRegistryType:
+ {
+ switch (registry_info->type)
+ {
+ case ImageRegistryType:
+ {
+ value=(Image *) ConstantString(((Image *)
+ registry_info->value)->filename);
+ break;
+ }
+ case ImageInfoRegistryType:
+ {
+ value=(Image *) ConstantString(((ImageInfo *)
+ registry_info->value)->filename);
+ break;
+ }
+ case StringRegistryType:
+ {
+ value=(void *) ConstantString((char *) registry_info->value);
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextImageRegistry() gets the next image registry value.
+%
+% The format of the GetNextImageRegistry method is:
+%
+% char *GetNextImageRegistry(void)
+%
+*/
+MagickExport char *GetNextImageRegistry(void)
+{
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (registry == (void *) NULL)
+ return((char *) NULL);
+ return((char *) GetNextKeyInSplayTree(registry));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveImageRegistry() removes a key from the image registry and returns its
+% value.
+%
+% The format of the RemoveImageRegistry method is:
+%
+% void *RemoveImageRegistry(const char *key)
+%
+% A description of each parameter follows:
+%
+% o key: the registry.
+%
+*/
+MagickExport void *RemoveImageRegistry(const char *key)
+{
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
+ if (registry == (void *) NULL)
+ return((void *) NULL);
+ return(RemoveNodeFromSplayTree(registry,key));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t I m a g e R e g i s t r y I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetImageRegistryIterator() resets the registry iterator. Use it in
+% conjunction with GetNextImageRegistry() to iterate over all the values
+% in the image registry.
+%
+% The format of the ResetImageRegistryIterator method is:
+%
+% ResetImageRegistryIterator(void)
+%
+*/
+MagickExport void ResetImageRegistryIterator(void)
+{
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (registry == (void *) NULL)
+ return;
+ ResetSplayTreeIterator(registry);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e R e g i s t r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageRegistry() associates a value with an image registry key.
+%
+% The format of the SetImageRegistry method is:
+%
+% MagickBooleanType SetImageRegistry(const RegistryType type,
+% const char *key,const void *value,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o type: the type.
+%
+% o key: the key.
+%
+% o value: the value.
+%
+% o exception: the exception.
+%
+*/
+
+static void *DestroyRegistryNode(void *registry_info)
+{
+ register RegistryInfo
+ *p;
+
+ p=(RegistryInfo *) registry_info;
+ switch (p->type)
+ {
+ case StringRegistryType:
+ default:
+ {
+ p->value=RelinquishMagickMemory(p->value);
+ break;
+ }
+ case ImageRegistryType:
+ {
+ p->value=(void *) DestroyImageList((Image *) p->value);
+ break;
+ }
+ case ImageInfoRegistryType:
+ {
+ p->value=(void *) DestroyImageInfo((ImageInfo *) p->value);
+ break;
+ }
+ }
+ return(RelinquishMagickMemory(p));
+}
+
+MagickExport MagickBooleanType SetImageRegistry(const RegistryType type,
+ const char *key,const void *value,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ RegistryInfo
+ *registry_info;
+
+ void
+ *clone_value;
+
+ if (IsEventLogging() != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",key);
+ clone_value=(void *) NULL;
+ switch (type)
+ {
+ case StringRegistryType:
+ default:
+ {
+ const char
+ *string;
+
+ string=(const char *) value;
+ clone_value=(void *) ConstantString(string);
+ break;
+ }
+ case ImageRegistryType:
+ {
+ const Image
+ *image;
+
+ image=(const Image *) value;
+ if (image->signature != MagickSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),RegistryError,
+ "UnableToSetRegistry","%s",key);
+ return(MagickFalse);
+ }
+ clone_value=(void *) CloneImageList(image,exception);
+ break;
+ }
+ case ImageInfoRegistryType:
+ {
+ const ImageInfo
+ *image_info;
+
+ image_info=(const ImageInfo *) value;
+ if (image_info->signature != MagickSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),RegistryError,
+ "UnableToSetRegistry","%s",key);
+ return(MagickFalse);
+ }
+ clone_value=(void *) CloneImageInfo(image_info);
+ break;
+ }
+ }
+ if (clone_value == (void *) NULL)
+ return(MagickFalse);
+ registry_info=(RegistryInfo *) AcquireMagickMemory(sizeof(*registry_info));
+ if (registry_info == (RegistryInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(registry_info,0,sizeof(*registry_info));
+ registry_info->type=type;
+ registry_info->value=clone_value;
+ registry_info->signature=MagickSignature;
+ if (registry == (void *) NULL)
+ registry=NewSplayTree(CompareSplayTreeString,RelinquishMagickMemory,
+ DestroyRegistryNode);
+ status=AddValueToSplayTree(registry,ConstantString(key),registry_info);
+ return(status);
+}
diff --git a/magick/registry.h b/magick/registry.h
new file mode 100644
index 0000000..cf2876c
--- /dev/null
+++ b/magick/registry.h
@@ -0,0 +1,52 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore registry methods.
+*/
+#ifndef _MAGICKCORE_REGISTRY_H
+#define _MAGICKCORE_REGISTRY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedRegistryType,
+ ImageRegistryType,
+ ImageInfoRegistryType,
+ StringRegistryType
+} RegistryType;
+
+extern MagickExport char
+ *GetNextImageRegistry(void);
+
+extern MagickExport MagickBooleanType
+ DefineImageRegistry(const RegistryType,const char *,ExceptionInfo *),
+ DeleteImageRegistry(const char *),
+ SetImageRegistry(const RegistryType,const char *,const void *,
+ ExceptionInfo *);
+
+extern MagickExport void
+ DestroyImageRegistry(void),
+ *GetImageRegistry(const RegistryType,const char *,ExceptionInfo *),
+ *RemoveImageRegistry(const char *),
+ ResetImageRegistryIterator(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/resample-private.h b/magick/resample-private.h
new file mode 100644
index 0000000..460a7fd
--- /dev/null
+++ b/magick/resample-private.h
@@ -0,0 +1,75 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image resampling private methods.
+*/
+#ifndef _MAGICKCORE_RESAMPLE_PRIVATE_H
+#define _MAGICKCORE_RESAMPLE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "magick/thread-private.h"
+
+static inline ResampleFilter **DestroyResampleFilterThreadSet(
+ ResampleFilter **filter)
+{
+ register long
+ i;
+
+ assert(filter != (ResampleFilter **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (filter[i] != (ResampleFilter *) NULL)
+ filter[i]=DestroyResampleFilter(filter[i]);
+ filter=(ResampleFilter **) RelinquishAlignedMemory(filter);
+ return(filter);
+}
+
+static inline ResampleFilter **AcquireResampleFilterThreadSet(
+ const Image *image,const MagickBooleanType interpolate,
+ ExceptionInfo *exception)
+{
+ register long
+ i;
+
+ ResampleFilter
+ **filter;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ filter=(ResampleFilter **) AcquireAlignedMemory(number_threads,
+ sizeof(*filter));
+ if (filter == (ResampleFilter **) NULL)
+ return((ResampleFilter **) NULL);
+ (void) ResetMagickMemory(filter,0,number_threads*sizeof(*filter));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ filter[i]=AcquireResampleFilter(image,exception);
+ if (filter[i] == (ResampleFilter *) NULL)
+ return(DestroyResampleFilterThreadSet(filter));
+ if (interpolate != MagickFalse)
+ SetResampleFilter(filter[i],PointFilter,1.0);
+ }
+ return(filter);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/resample.c b/magick/resample.c
new file mode 100644
index 0000000..b7242a9
--- /dev/null
+++ b/magick/resample.c
@@ -0,0 +1,1549 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% RRRR EEEEE SSSSS AAA M M PPPP L EEEEE %
+% R R E SS A A MM MM P P L E %
+% RRRR EEE SSS AAAAA M M M PPPP L EEE %
+% R R E SS A A M M P L E %
+% R R EEEEE SSSSS A A M M P LLLLL EEEEE %
+% %
+% %
+% MagickCore Pixel Resampling Methods %
+% %
+% Software Design %
+% John Cristy %
+% Anthony Thyssen %
+% August 2007 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/color-private.h"
+#include "magick/cache.h"
+#include "magick/draw.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/resample.h"
+#include "magick/resize.h"
+#include "magick/resize-private.h"
+#include "magick/transform.h"
+#include "magick/signature-private.h"
+/*
+ Typedef declarations.
+*/
+#define WLUT_WIDTH 1024
+struct _ResampleFilter
+{
+ Image
+ *image;
+
+ CacheView
+ *view;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ debug;
+
+ /* Information about image being resampled */
+ long
+ image_area;
+
+ InterpolatePixelMethod
+ interpolate;
+
+ VirtualPixelMethod
+ virtual_pixel;
+
+ FilterTypes
+ filter;
+
+ /* processing settings needed */
+ MagickBooleanType
+ limit_reached,
+ do_interpolate,
+ average_defined;
+
+ MagickPixelPacket
+ average_pixel;
+
+ /* current ellipitical area being resampled around center point */
+ double
+ A, B, C,
+ sqrtA, sqrtC, sqrtU, slope;
+
+ /* LUT of weights for filtered average in elliptical area */
+ double
+ filter_lut[WLUT_WIDTH],
+ support;
+
+ unsigned long
+ signature;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e R e s a m p l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireResampleFilter() initializes the information resample needs do to a
+% scaled lookup of a color from an image, using area sampling.
+%
+% The algorithm is based on a Elliptical Weighted Average, where the pixels
+% found in a large elliptical area is averaged together according to a
+% weighting (filter) function. For more details see "Fundamentals of Texture
+% Mapping and Image Warping" a master's thesis by Paul.S.Heckbert, June 17,
+% 1989. Available for free from, http://www.cs.cmu.edu/~ph/
+%
+% As EWA resampling (or any sort of resampling) can require a lot of
+% calculations to produce a distorted scaling of the source image for each
+% output pixel, the ResampleFilter structure generated holds that information
+% between individual image resampling.
+%
+% This function will make the appropriate AcquireCacheView() calls
+% to view the image, calling functions do not need to open a cache view.
+%
+% Usage Example...
+% resample_filter=AcquireResampleFilter(image,exception);
+% for (y=0; y < (long) image->rows; y++) {
+% for (x=0; x < (long) image->columns; x++) {
+% X= ....; Y= ....;
+% ScaleResampleFilter(resample_filter, ... scaling vectors ...);
+% (void) ResamplePixelColor(resample_filter,X,Y,&pixel);
+% ... assign resampled pixel value ...
+% }
+% }
+% DestroyResampleFilter(resample_filter);
+%
+% The format of the AcquireResampleFilter method is:
+%
+% ResampleFilter *AcquireResampleFilter(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ResampleFilter *AcquireResampleFilter(const Image *image,
+ ExceptionInfo *exception)
+{
+ register ResampleFilter
+ *resample_filter;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ resample_filter=(ResampleFilter *) AcquireMagickMemory(
+ sizeof(*resample_filter));
+ if (resample_filter == (ResampleFilter *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(resample_filter,0,sizeof(*resample_filter));
+
+ resample_filter->image=ReferenceImage((Image *) image);
+ resample_filter->view=AcquireCacheView(resample_filter->image);
+ resample_filter->exception=exception;
+
+ resample_filter->debug=IsEventLogging();
+ resample_filter->signature=MagickSignature;
+
+ resample_filter->image_area = (long) resample_filter->image->columns *
+ resample_filter->image->rows;
+ resample_filter->average_defined = MagickFalse;
+
+ /* initialise the resampling filter settings */
+ SetResampleFilter(resample_filter, resample_filter->image->filter,
+ resample_filter->image->blur);
+ resample_filter->interpolate = resample_filter->image->interpolate;
+ resample_filter->virtual_pixel=GetImageVirtualPixelMethod(image);
+
+ /* init scale to a default of a unit circle */
+ ScaleResampleFilter(resample_filter, 1.0, 0.0, 0.0, 1.0);
+
+ return(resample_filter);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y R e s a m p l e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyResampleFilter() finalizes and cleans up the resampling
+% resample_filter as returned by AcquireResampleFilter(), freeing any memory
+% or other information as needed.
+%
+% The format of the DestroyResampleFilter method is:
+%
+% ResampleFilter *DestroyResampleFilter(ResampleFilter *resample_filter)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: resampling information structure
+%
+*/
+MagickExport ResampleFilter *DestroyResampleFilter(
+ ResampleFilter *resample_filter)
+{
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+ assert(resample_filter->image != (Image *) NULL);
+ if (resample_filter->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ resample_filter->image->filename);
+ resample_filter->view=DestroyCacheView(resample_filter->view);
+ resample_filter->image=DestroyImage(resample_filter->image);
+ resample_filter->signature=(~MagickSignature);
+ resample_filter=(ResampleFilter *) RelinquishMagickMemory(resample_filter);
+ return(resample_filter);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n t e r p o l a t e R e s a m p l e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InterpolateResampleFilter() applies bi-linear or tri-linear interpolation
+% between a floating point coordinate and the pixels surrounding that
+% coordinate. No pixel area resampling, or scaling of the result is
+% performed.
+%
+% The format of the InterpolateResampleFilter method is:
+%
+% MagickBooleanType InterpolateResampleFilter(
+% ResampleInfo *resample_filter,const InterpolatePixelMethod method,
+% const double x,const double y,MagickPixelPacket *pixel)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: the resample filter.
+%
+% o method: the pixel clor interpolation method.
+%
+% o x,y: A double representing the current (x,y) position of the pixel.
+%
+% o pixel: return the interpolated pixel here.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static void BicubicInterpolate(const MagickPixelPacket *pixels,const double dx,
+ MagickPixelPacket *pixel)
+{
+ MagickRealType
+ dx2,
+ p,
+ q,
+ r,
+ s;
+
+ dx2=dx*dx;
+ p=(pixels[3].red-pixels[2].red)-(pixels[0].red-pixels[1].red);
+ q=(pixels[0].red-pixels[1].red)-p;
+ r=pixels[2].red-pixels[0].red;
+ s=pixels[1].red;
+ pixel->red=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].green-pixels[2].green)-(pixels[0].green-pixels[1].green);
+ q=(pixels[0].green-pixels[1].green)-p;
+ r=pixels[2].green-pixels[0].green;
+ s=pixels[1].green;
+ pixel->green=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].blue-pixels[2].blue)-(pixels[0].blue-pixels[1].blue);
+ q=(pixels[0].blue-pixels[1].blue)-p;
+ r=pixels[2].blue-pixels[0].blue;
+ s=pixels[1].blue;
+ pixel->blue=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ p=(pixels[3].opacity-pixels[2].opacity)-(pixels[0].opacity-pixels[1].opacity);
+ q=(pixels[0].opacity-pixels[1].opacity)-p;
+ r=pixels[2].opacity-pixels[0].opacity;
+ s=pixels[1].opacity;
+ pixel->opacity=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ if (pixel->colorspace == CMYKColorspace)
+ {
+ p=(pixels[3].index-pixels[2].index)-(pixels[0].index-pixels[1].index);
+ q=(pixels[0].index-pixels[1].index)-p;
+ r=pixels[2].index-pixels[0].index;
+ s=pixels[1].index;
+ pixel->index=(dx*dx2*p)+(dx2*q)+(dx*r)+s;
+ }
+}
+
+static inline MagickRealType CubicWeightingFunction(const MagickRealType x)
+{
+ MagickRealType
+ alpha,
+ gamma;
+
+ alpha=MagickMax(x+2.0,0.0);
+ gamma=1.0*alpha*alpha*alpha;
+ alpha=MagickMax(x+1.0,0.0);
+ gamma-=4.0*alpha*alpha*alpha;
+ alpha=MagickMax(x+0.0,0.0);
+ gamma+=6.0*alpha*alpha*alpha;
+ alpha=MagickMax(x-1.0,0.0);
+ gamma-=4.0*alpha*alpha*alpha;
+ return(gamma/6.0);
+}
+
+static inline double MeshInterpolate(const PointInfo *delta,const double p,
+ const double x,const double y)
+{
+ return(delta->x*x+delta->y*y+(1.0-delta->x-delta->y)*p);
+}
+
+static inline long NearestNeighbor(MagickRealType x)
+{
+ if (x >= 0.0)
+ return((long) (x+0.5));
+ return((long) (x-0.5));
+}
+
+static MagickBooleanType InterpolateResampleFilter(
+ ResampleFilter *resample_filter,const InterpolatePixelMethod method,
+ const double x,const double y,MagickPixelPacket *pixel)
+{
+ MagickBooleanType
+ status;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i;
+
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+ status=MagickTrue;
+ switch (method)
+ {
+ case AverageInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[16];
+
+ MagickRealType
+ alpha[16],
+ gamma;
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
+ floor(y)-1,4,4,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ for (i=0; i < 16L; i++)
+ {
+ GetMagickPixelPacket(resample_filter->image,pixels+i);
+ SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (resample_filter->image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ gamma=alpha[i];
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red+=gamma*0.0625*pixels[i].red;
+ pixel->green+=gamma*0.0625*pixels[i].green;
+ pixel->blue+=gamma*0.0625*pixels[i].blue;
+ pixel->opacity+=0.0625*pixels[i].opacity;
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index+=gamma*0.0625*pixels[i].index;
+ p++;
+ }
+ break;
+ }
+ case BicubicInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[16],
+ u[4];
+
+ MagickRealType
+ alpha[16];
+
+ PointInfo
+ delta;
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
+ floor(y)-1,4,4,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ for (i=0; i < 16L; i++)
+ {
+ GetMagickPixelPacket(resample_filter->image,pixels+i);
+ SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (resample_filter->image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ p++;
+ }
+ delta.x=x-floor(x);
+ for (i=0; i < 4L; i++)
+ BicubicInterpolate(pixels+4*i,delta.x,u+i);
+ delta.y=y-floor(y);
+ BicubicInterpolate(u,delta.y,pixel);
+ break;
+ }
+ case BilinearInterpolatePixel:
+ default:
+ {
+ MagickPixelPacket
+ pixels[4];
+
+ MagickRealType
+ alpha[4],
+ gamma;
+
+ PointInfo
+ delta,
+ epsilon;
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
+ floor(y),2,2,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ for (i=0; i < 4L; i++)
+ {
+ pixels[i].red=(MagickRealType) p[i].red;
+ pixels[i].green=(MagickRealType) p[i].green;
+ pixels[i].blue=(MagickRealType) p[i].blue;
+ pixels[i].opacity=(MagickRealType) p[i].opacity;
+ alpha[i]=1.0;
+ }
+ if (resample_filter->image->matte != MagickFalse)
+ for (i=0; i < 4L; i++)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p[i].opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ }
+ if (indexes != (IndexPacket *) NULL)
+ for (i=0; i < 4L; i++)
+ {
+ pixels[i].index=(MagickRealType) indexes[i];
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ epsilon.x=1.0-delta.x;
+ epsilon.y=1.0-delta.y;
+ gamma=((epsilon.y*(epsilon.x*alpha[0]+delta.x*alpha[1])+delta.y*
+ (epsilon.x*alpha[2]+delta.x*alpha[3])));
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red=gamma*(epsilon.y*(epsilon.x*pixels[0].red+delta.x*
+ pixels[1].red)+delta.y*(epsilon.x*pixels[2].red+delta.x*pixels[3].red));
+ pixel->green=gamma*(epsilon.y*(epsilon.x*pixels[0].green+delta.x*
+ pixels[1].green)+delta.y*(epsilon.x*pixels[2].green+delta.x*
+ pixels[3].green));
+ pixel->blue=gamma*(epsilon.y*(epsilon.x*pixels[0].blue+delta.x*
+ pixels[1].blue)+delta.y*(epsilon.x*pixels[2].blue+delta.x*
+ pixels[3].blue));
+ pixel->opacity=(epsilon.y*(epsilon.x*pixels[0].opacity+delta.x*
+ pixels[1].opacity)+delta.y*(epsilon.x*pixels[2].opacity+delta.x*
+ pixels[3].opacity));
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index=gamma*(epsilon.y*(epsilon.x*pixels[0].index+delta.x*
+ pixels[1].index)+delta.y*(epsilon.x*pixels[2].index+delta.x*
+ pixels[3].index));
+ break;
+ }
+ case FilterInterpolatePixel:
+ {
+ Image
+ *excerpt_image,
+ *filter_image;
+
+ MagickPixelPacket
+ pixels[1];
+
+ RectangleInfo
+ geometry;
+
+ CacheView
+ *filter_view;
+
+ geometry.width=4L;
+ geometry.height=4L;
+ geometry.x=(long) floor(x)-1L;
+ geometry.y=(long) floor(y)-1L;
+ excerpt_image=ExcerptImage(resample_filter->image,&geometry,
+ resample_filter->exception);
+ if (excerpt_image == (Image *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ filter_image=ResizeImage(excerpt_image,1,1,resample_filter->image->filter,
+ resample_filter->image->blur,resample_filter->exception);
+ excerpt_image=DestroyImage(excerpt_image);
+ if (filter_image == (Image *) NULL)
+ break;
+ filter_view=AcquireCacheView(filter_image);
+ p=GetCacheViewVirtualPixels(filter_view,0,0,1,1,
+ resample_filter->exception);
+ if (p != (const PixelPacket *) NULL)
+ {
+ indexes=GetVirtualIndexQueue(filter_image);
+ GetMagickPixelPacket(resample_filter->image,pixels);
+ SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
+ }
+ filter_view=DestroyCacheView(filter_view);
+ filter_image=DestroyImage(filter_image);
+ break;
+ }
+ case IntegerInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[1];
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
+ floor(y),1,1,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ GetMagickPixelPacket(resample_filter->image,pixels);
+ SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
+ break;
+ }
+ case MeshInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[4];
+
+ MagickRealType
+ alpha[4],
+ gamma;
+
+ PointInfo
+ delta,
+ luminance;
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x),(long)
+ floor(y),2,2,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ for (i=0; i < 4L; i++)
+ {
+ GetMagickPixelPacket(resample_filter->image,pixels+i);
+ SetMagickPixelPacket(resample_filter->image,p,indexes+i,pixels+i);
+ alpha[i]=1.0;
+ if (resample_filter->image->matte != MagickFalse)
+ {
+ alpha[i]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[i].red*=alpha[i];
+ pixels[i].green*=alpha[i];
+ pixels[i].blue*=alpha[i];
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixels[i].index*=alpha[i];
+ }
+ p++;
+ }
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ luminance.x=MagickPixelLuminance(pixels+0)-MagickPixelLuminance(pixels+3);
+ luminance.y=MagickPixelLuminance(pixels+1)-MagickPixelLuminance(pixels+2);
+ if (fabs(luminance.x) < fabs(luminance.y))
+ {
+ /*
+ Diagonal 0-3 NW-SE.
+ */
+ if (delta.x <= delta.y)
+ {
+ /*
+ Bottom-left triangle (pixel:2, diagonal: 0-3).
+ */
+ delta.y=1.0-delta.y;
+ gamma=MeshInterpolate(&delta,alpha[2],alpha[3],alpha[0]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red=gamma*MeshInterpolate(&delta,pixels[2].red,
+ pixels[3].red,pixels[0].red);
+ pixel->green=gamma*MeshInterpolate(&delta,pixels[2].green,
+ pixels[3].green,pixels[0].green);
+ pixel->blue=gamma*MeshInterpolate(&delta,pixels[2].blue,
+ pixels[3].blue,pixels[0].blue);
+ pixel->opacity=gamma*MeshInterpolate(&delta,pixels[2].opacity,
+ pixels[3].opacity,pixels[0].opacity);
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index=gamma*MeshInterpolate(&delta,pixels[2].index,
+ pixels[3].index,pixels[0].index);
+ }
+ else
+ {
+ /*
+ Top-right triangle (pixel:1, diagonal: 0-3).
+ */
+ delta.x=1.0-delta.x;
+ gamma=MeshInterpolate(&delta,alpha[1],alpha[0],alpha[3]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red=gamma*MeshInterpolate(&delta,pixels[1].red,
+ pixels[0].red,pixels[3].red);
+ pixel->green=gamma*MeshInterpolate(&delta,pixels[1].green,
+ pixels[0].green,pixels[3].green);
+ pixel->blue=gamma*MeshInterpolate(&delta,pixels[1].blue,
+ pixels[0].blue,pixels[3].blue);
+ pixel->opacity=gamma*MeshInterpolate(&delta,pixels[1].opacity,
+ pixels[0].opacity,pixels[3].opacity);
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index=gamma*MeshInterpolate(&delta,pixels[1].index,
+ pixels[0].index,pixels[3].index);
+ }
+ }
+ else
+ {
+ /*
+ Diagonal 1-2 NE-SW.
+ */
+ if (delta.x <= (1.0-delta.y))
+ {
+ /*
+ Top-left triangle (pixel 0, diagonal: 1-2).
+ */
+ gamma=MeshInterpolate(&delta,alpha[0],alpha[1],alpha[2]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red=gamma*MeshInterpolate(&delta,pixels[0].red,
+ pixels[1].red,pixels[2].red);
+ pixel->green=gamma*MeshInterpolate(&delta,pixels[0].green,
+ pixels[1].green,pixels[2].green);
+ pixel->blue=gamma*MeshInterpolate(&delta,pixels[0].blue,
+ pixels[1].blue,pixels[2].blue);
+ pixel->opacity=gamma*MeshInterpolate(&delta,pixels[0].opacity,
+ pixels[1].opacity,pixels[2].opacity);
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index=gamma*MeshInterpolate(&delta,pixels[0].index,
+ pixels[1].index,pixels[2].index);
+ }
+ else
+ {
+ /*
+ Bottom-right triangle (pixel: 3, diagonal: 1-2).
+ */
+ delta.x=1.0-delta.x;
+ delta.y=1.0-delta.y;
+ gamma=MeshInterpolate(&delta,alpha[3],alpha[2],alpha[1]);
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red=gamma*MeshInterpolate(&delta,pixels[3].red,
+ pixels[2].red,pixels[1].red);
+ pixel->green=gamma*MeshInterpolate(&delta,pixels[3].green,
+ pixels[2].green,pixels[1].green);
+ pixel->blue=gamma*MeshInterpolate(&delta,pixels[3].blue,
+ pixels[2].blue,pixels[1].blue);
+ pixel->opacity=gamma*MeshInterpolate(&delta,pixels[3].opacity,
+ pixels[2].opacity,pixels[1].opacity);
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index=gamma*MeshInterpolate(&delta,pixels[3].index,
+ pixels[2].index,pixels[1].index);
+ }
+ }
+ break;
+ }
+ case NearestNeighborInterpolatePixel:
+ {
+ MagickPixelPacket
+ pixels[1];
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,NearestNeighbor(x),
+ NearestNeighbor(y),1,1,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ GetMagickPixelPacket(resample_filter->image,pixels);
+ SetMagickPixelPacket(resample_filter->image,p,indexes,pixel);
+ break;
+ }
+ case SplineInterpolatePixel:
+ {
+ long
+ j,
+ n;
+
+ MagickPixelPacket
+ pixels[16];
+
+ MagickRealType
+ alpha[16],
+ dx,
+ dy,
+ gamma;
+
+ PointInfo
+ delta;
+
+ p=GetCacheViewVirtualPixels(resample_filter->view,(long) floor(x)-1,(long)
+ floor(y)-1,4,4,resample_filter->exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+ n=0;
+ delta.x=x-floor(x);
+ delta.y=y-floor(y);
+ for (i=(-1); i < 3L; i++)
+ {
+ dy=CubicWeightingFunction((MagickRealType) i-delta.y);
+ for (j=(-1); j < 3L; j++)
+ {
+ GetMagickPixelPacket(resample_filter->image,pixels+n);
+ SetMagickPixelPacket(resample_filter->image,p,indexes+n,pixels+n);
+ alpha[n]=1.0;
+ if (resample_filter->image->matte != MagickFalse)
+ {
+ alpha[n]=QuantumScale*((MagickRealType) QuantumRange-p->opacity);
+ pixels[n].red*=alpha[n];
+ pixels[n].green*=alpha[n];
+ pixels[n].blue*=alpha[n];
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixels[n].index*=alpha[n];
+ }
+ dx=CubicWeightingFunction(delta.x-(MagickRealType) j);
+ gamma=alpha[n];
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ pixel->red+=gamma*dx*dy*pixels[n].red;
+ pixel->green+=gamma*dx*dy*pixels[n].green;
+ pixel->blue+=gamma*dx*dy*pixels[n].blue;
+ if (resample_filter->image->matte != MagickFalse)
+ pixel->opacity+=dx*dy*pixels[n].opacity;
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index+=gamma*dx*dy*pixels[n].index;
+ n++;
+ p++;
+ }
+ }
+ break;
+ }
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s a m p l e P i x e l C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResamplePixelColor() samples the pixel values surrounding the location
+% given using an elliptical weighted average, at the scale previously
+% calculated, and in the most efficent manner possible for the
+% VirtualPixelMethod setting.
+%
+% The format of the ResamplePixelColor method is:
+%
+% MagickBooleanType ResamplePixelColor(ResampleFilter *resample_filter,
+% const double u0,const double v0,MagickPixelPacket *pixel)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: the resample filter.
+%
+% o u0,v0: A double representing the center of the area to resample,
+% The distortion transformed transformed x,y coordinate.
+%
+% o pixel: the resampled pixel is returned here.
+%
+*/
+MagickExport MagickBooleanType ResamplePixelColor(
+ ResampleFilter *resample_filter,const double u0,const double v0,
+ MagickPixelPacket *pixel)
+{
+ MagickBooleanType
+ status;
+
+ long u,v, uw,v1,v2, hit;
+ double u1;
+ double U,V,Q,DQ,DDQ;
+ double divisor_c,divisor_m;
+ register double weight;
+ register const PixelPacket *pixels;
+ register const IndexPacket *indexes;
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+
+ status=MagickTrue;
+ GetMagickPixelPacket(resample_filter->image,pixel);
+ if ( resample_filter->do_interpolate ) {
+ status=InterpolateResampleFilter(resample_filter,
+ resample_filter->interpolate,u0,v0,pixel);
+ return(status);
+ }
+
+ /*
+ Does resample area Miss the image?
+ And is that area a simple solid color - then return that color
+ */
+ hit = 0;
+ switch ( resample_filter->virtual_pixel ) {
+ case BackgroundVirtualPixelMethod:
+ case ConstantVirtualPixelMethod:
+ case TransparentVirtualPixelMethod:
+ case BlackVirtualPixelMethod:
+ case GrayVirtualPixelMethod:
+ case WhiteVirtualPixelMethod:
+ case MaskVirtualPixelMethod:
+ if ( resample_filter->limit_reached
+ || u0 + resample_filter->sqrtC < 0.0
+ || u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
+ || v0 + resample_filter->sqrtA < 0.0
+ || v0 - resample_filter->sqrtA > (double) resample_filter->image->rows
+ )
+ hit++;
+ break;
+
+ case UndefinedVirtualPixelMethod:
+ case EdgeVirtualPixelMethod:
+ if ( ( u0 + resample_filter->sqrtC < 0.0 && v0 + resample_filter->sqrtA < 0.0 )
+ || ( u0 + resample_filter->sqrtC < 0.0
+ && v0 - resample_filter->sqrtA > (double) resample_filter->image->rows )
+ || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
+ && v0 + resample_filter->sqrtA < 0.0 )
+ || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
+ && v0 - resample_filter->sqrtA > (double) resample_filter->image->rows )
+ )
+ hit++;
+ break;
+ case HorizontalTileVirtualPixelMethod:
+ if ( v0 + resample_filter->sqrtA < 0.0
+ || v0 - resample_filter->sqrtA > (double) resample_filter->image->rows
+ )
+ hit++; /* outside the horizontally tiled images. */
+ break;
+ case VerticalTileVirtualPixelMethod:
+ if ( u0 + resample_filter->sqrtC < 0.0
+ || u0 - resample_filter->sqrtC > (double) resample_filter->image->columns
+ )
+ hit++; /* outside the vertically tiled images. */
+ break;
+ case DitherVirtualPixelMethod:
+ if ( ( u0 + resample_filter->sqrtC < -32.0 && v0 + resample_filter->sqrtA < -32.0 )
+ || ( u0 + resample_filter->sqrtC < -32.0
+ && v0 - resample_filter->sqrtA > (double) resample_filter->image->rows+32.0 )
+ || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns+32.0
+ && v0 + resample_filter->sqrtA < -32.0 )
+ || ( u0 - resample_filter->sqrtC > (double) resample_filter->image->columns+32.0
+ && v0 - resample_filter->sqrtA > (double) resample_filter->image->rows+32.0 )
+ )
+ hit++;
+ break;
+ case TileVirtualPixelMethod:
+ case MirrorVirtualPixelMethod:
+ case RandomVirtualPixelMethod:
+ case HorizontalTileEdgeVirtualPixelMethod:
+ case VerticalTileEdgeVirtualPixelMethod:
+ case CheckerTileVirtualPixelMethod:
+ /* resampling of area is always needed - no VP limits */
+ break;
+ }
+ if ( hit ) {
+ /* whole area is a solid color -- just return that color */
+ status=InterpolateResampleFilter(resample_filter,IntegerInterpolatePixel,
+ u0,v0,pixel);
+ return(status);
+ }
+
+ /*
+ Scaling limits reached, return an 'averaged' result.
+ */
+ if ( resample_filter->limit_reached ) {
+ switch ( resample_filter->virtual_pixel ) {
+ /* This is always handled by the above, so no need.
+ case BackgroundVirtualPixelMethod:
+ case ConstantVirtualPixelMethod:
+ case TransparentVirtualPixelMethod:
+ case GrayVirtualPixelMethod,
+ case WhiteVirtualPixelMethod
+ case MaskVirtualPixelMethod:
+ */
+ case UndefinedVirtualPixelMethod:
+ case EdgeVirtualPixelMethod:
+ case DitherVirtualPixelMethod:
+ case HorizontalTileEdgeVirtualPixelMethod:
+ case VerticalTileEdgeVirtualPixelMethod:
+ /* We need an average edge pixel, for the right edge!
+ How should I calculate an average edge color?
+ Just returning an averaged neighbourhood,
+ works well in general, but falls down for TileEdge methods.
+ This needs to be done properly!!!!!!
+ */
+ status=InterpolateResampleFilter(resample_filter,
+ AverageInterpolatePixel,u0,v0,pixel);
+ break;
+ case HorizontalTileVirtualPixelMethod:
+ case VerticalTileVirtualPixelMethod:
+ /* just return the background pixel - Is there more direct way? */
+ status=InterpolateResampleFilter(resample_filter,
+ IntegerInterpolatePixel,(double)-1,(double)-1,pixel);
+ break;
+ case TileVirtualPixelMethod:
+ case MirrorVirtualPixelMethod:
+ case RandomVirtualPixelMethod:
+ case CheckerTileVirtualPixelMethod:
+ default:
+ /* generate a average color of the WHOLE image */
+ if ( resample_filter->average_defined == MagickFalse ) {
+ Image
+ *average_image;
+
+ CacheView
+ *average_view;
+
+ GetMagickPixelPacket(resample_filter->image,
+ (MagickPixelPacket *)&(resample_filter->average_pixel));
+ resample_filter->average_defined = MagickTrue;
+
+ /* Try to get an averaged pixel color of whole image */
+ average_image=ResizeImage(resample_filter->image,1,1,BoxFilter,1.0,
+ resample_filter->exception);
+ if (average_image == (Image *) NULL)
+ {
+ *pixel=resample_filter->average_pixel; /* FAILED */
+ break;
+ }
+ average_view=AcquireCacheView(average_image);
+ pixels=(PixelPacket *)GetCacheViewVirtualPixels(average_view,0,0,1,1,
+ resample_filter->exception);
+ if (pixels == (const PixelPacket *) NULL) {
+ average_view=DestroyCacheView(average_view);
+ average_image=DestroyImage(average_image);
+ *pixel=resample_filter->average_pixel; /* FAILED */
+ break;
+ }
+ indexes=(IndexPacket *) GetCacheViewAuthenticIndexQueue(average_view);
+ SetMagickPixelPacket(resample_filter->image,pixels,indexes,
+ &(resample_filter->average_pixel));
+ average_view=DestroyCacheView(average_view);
+ average_image=DestroyImage(average_image);
+#if 0
+ /* CheckerTile should average the image with background color */
+ //if ( resample_filter->virtual_pixel == CheckerTileVirtualPixelMethod ) {
+#if 0
+ resample_filter->average_pixel.red =
+ ( resample_filter->average_pixel.red +
+ resample_filter->image->background_color.red ) /2;
+ resample_filter->average_pixel.green =
+ ( resample_filter->average_pixel.green +
+ resample_filter->image->background_color.green ) /2;
+ resample_filter->average_pixel.blue =
+ ( resample_filter->average_pixel.blue +
+ resample_filter->image->background_color.blue ) /2;
+ resample_filter->average_pixel.matte =
+ ( resample_filter->average_pixel.matte +
+ resample_filter->image->background_color.matte ) /2;
+ resample_filter->average_pixel.black =
+ ( resample_filter->average_pixel.black +
+ resample_filter->image->background_color.black ) /2;
+#else
+ resample_filter->average_pixel =
+ resample_filter->image->background_color;
+#endif
+ }
+#endif
+ }
+ *pixel=resample_filter->average_pixel;
+ break;
+ }
+ return(status);
+ }
+
+ /*
+ Initialize weighted average data collection
+ */
+ hit = 0;
+ divisor_c = 0.0;
+ divisor_m = 0.0;
+ pixel->red = pixel->green = pixel->blue = 0.0;
+ if (resample_filter->image->matte != MagickFalse) pixel->opacity = 0.0;
+ if (resample_filter->image->colorspace == CMYKColorspace) pixel->index = 0.0;
+
+ /*
+ Determine the parellelogram bounding box fitted to the ellipse
+ centered at u0,v0. This area is bounding by the lines...
+ v = +/- sqrt(A)
+ u = -By/2A +/- sqrt(F/A)
+ Which has been pre-calculated above.
+ */
+ v1 = (long)(v0 - resample_filter->sqrtA); /* range of scan lines */
+ v2 = (long)(v0 + resample_filter->sqrtA + 1);
+
+ u1 = u0 + (v1-v0)*resample_filter->slope - resample_filter->sqrtU; /* start of scanline for v=v1 */
+ uw = (long)(2*resample_filter->sqrtU)+1; /* width of parallelogram */
+
+ /*
+ Do weighted resampling of all pixels, within the scaled ellipse,
+ bound by a Parellelogram fitted to the ellipse.
+ */
+ DDQ = 2*resample_filter->A;
+ for( v=v1; v<=v2; v++, u1+=resample_filter->slope ) {
+ u = (long)u1; /* first pixel in scanline ( floor(u1) ) */
+ U = (double)u-u0; /* location of that pixel, relative to u0,v0 */
+ V = (double)v-v0;
+
+ /* Q = ellipse quotent ( if Q<F then pixel is inside ellipse) */
+ Q = U*(resample_filter->A*U + resample_filter->B*V) + resample_filter->C*V*V;
+ DQ = resample_filter->A*(2.0*U+1) + resample_filter->B*V;
+
+ /* get the scanline of pixels for this v */
+ pixels=GetCacheViewVirtualPixels(resample_filter->view,u,v,(unsigned long) uw,
+ 1,resample_filter->exception);
+ if (pixels == (const PixelPacket *) NULL)
+ return(MagickFalse);
+ indexes=GetCacheViewVirtualIndexQueue(resample_filter->view);
+
+ /* count up the weighted pixel colors */
+ for( u=0; u<uw; u++ ) {
+ /* Note that the ellipse has been pre-scaled so F = WLUT_WIDTH */
+ if ( Q < (double)WLUT_WIDTH ) {
+ weight = resample_filter->filter_lut[(int)Q];
+
+ pixel->opacity += weight*pixels->opacity;
+ divisor_m += weight;
+
+ if (resample_filter->image->matte != MagickFalse)
+ weight *= QuantumScale*((MagickRealType)(QuantumRange-pixels->opacity));
+ pixel->red += weight*pixels->red;
+ pixel->green += weight*pixels->green;
+ pixel->blue += weight*pixels->blue;
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index += weight*(*indexes);
+ divisor_c += weight;
+
+ hit++;
+ }
+ pixels++;
+ indexes++;
+ Q += DQ;
+ DQ += DDQ;
+ }
+ }
+
+ /*
+ Result sanity check -- this should NOT happen
+ */
+ if ( hit < 4 || divisor_c < 1.0 ) {
+ /* not enough pixels in resampling, resort to direct interpolation */
+ status=InterpolateResampleFilter(resample_filter,
+ resample_filter->interpolate,u0,v0,pixel);
+ return status;
+ }
+
+ /*
+ Finialize results of resampling
+ */
+ divisor_m = 1.0/divisor_m;
+ pixel->opacity = (MagickRealType) RoundToQuantum(divisor_m*pixel->opacity);
+ divisor_c = 1.0/divisor_c;
+ pixel->red = (MagickRealType) RoundToQuantum(divisor_c*pixel->red);
+ pixel->green = (MagickRealType) RoundToQuantum(divisor_c*pixel->green);
+ pixel->blue = (MagickRealType) RoundToQuantum(divisor_c*pixel->blue);
+ if (resample_filter->image->colorspace == CMYKColorspace)
+ pixel->index = (MagickRealType) RoundToQuantum(divisor_c*pixel->index);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S c a l e R e s a m p l e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ScaleResampleFilter() does all the calculations needed to resample an image
+% at a specific scale, defined by two scaling vectors. This not using
+% a orthogonal scaling, but two distorted scaling vectors, to allow the
+% generation of a angled ellipse.
+%
+% As only two deritive scaling vectors are used the center of the ellipse
+% must be the center of the lookup. That is any curvature that the
+% distortion may produce is discounted.
+%
+% The input vectors are produced by either finding the derivitives of the
+% distortion function, or the partial derivitives from a distortion mapping.
+% They do not need to be the orthogonal dx,dy scaling vectors, but can be
+% calculated from other derivatives. For example you could use dr,da/r
+% polar coordinate vector scaling vectors
+%
+% If u,v = DistortEquation(x,y)
+% Then the scaling vectors dx,dy (in u,v space) are the derivitives...
+% du/dx, dv/dx and du/dy, dv/dy
+% If the scaling is only othogonally aligned then...
+% dv/dx = 0 and du/dy = 0
+% Producing an othogonally alligned ellipse for the area to be resampled.
+%
+% Note that scaling vectors are different to argument order. Argument order
+% is the general order the deritives are extracted from the distortion
+% equations, EG: U(x,y), V(x,y). Caution is advised if you are trying to
+% define the ellipse directly from scaling vectors.
+%
+% The format of the ScaleResampleFilter method is:
+%
+% void ScaleResampleFilter(const ResampleFilter *resample_filter,
+% const double dux,const double duy,const double dvx,const double dvy)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: the resampling resample_filterrmation defining the
+% image being resampled
+%
+% o dux,duy,dvx,dvy:
+% The partial derivitives or scaling vectors for resampling.
+% dx = du/dx, dv/dx and dy = du/dy, dv/dy
+%
+% The values are used to define the size and angle of the
+% elliptical resampling area, centered on the lookup point.
+%
+*/
+MagickExport void ScaleResampleFilter(ResampleFilter *resample_filter,
+ const double dux,const double duy,const double dvx,const double dvy)
+{
+ double A,B,C,F, area;
+
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+
+ resample_filter->limit_reached = MagickFalse;
+ resample_filter->do_interpolate = MagickFalse;
+
+ /* A 'point' filter forces use of interpolation instead of area sampling */
+ if ( resample_filter->filter == PointFilter ) {
+ resample_filter->do_interpolate = MagickTrue;
+ return;
+ }
+
+ /* Find Ellipse Coefficents such that
+ A*u^2 + B*u*v + C*v^2 = F
+ With u,v relative to point around which we are resampling.
+ And the given scaling dx,dy vectors in u,v space
+ du/dx,dv/dx and du/dy,dv/dy
+ */
+#if 0
+ /* Direct conversions of derivatives to elliptical coefficients
+ No scaling will result in F == 1.0 and a unit circle.
+ */
+ A = dvx*dvx+dvy*dvy;
+ B = (dux*dvx+duy*dvy)*-2.0;
+ C = dux*dux+duy*duy;
+ F = dux*dvy+duy*dvx;
+ F *= F;
+#define F_UNITY 1.0
+#else
+ /* This Paul Heckbert's recomended "Higher Quality EWA" formula, from page
+ 60 in his thesis, which adds a unit circle to the elliptical area so are
+ to do both Reconstruction and Prefiltering of the pixels in the
+ resampling. It also means it is likely to have at least 4 pixels within
+ the area of the ellipse, for weighted averaging.
+ No scaling will result if F == 4.0 and a circle of radius 2.0
+ */
+ A = dvx*dvx+dvy*dvy+1;
+ B = (dux*dvx+duy*dvy)*-2.0;
+ C = dux*dux+duy*duy+1;
+ F = A*C - B*B/4;
+#define F_UNITY 4.0
+#endif
+
+/* DEBUGGING OUTPUT */
+#if 0
+ fprintf(stderr, "dux=%lf; dvx=%lf; duy=%lf; dvy%lf;\n",
+ dux, dvx, duy, dvy);
+ fprintf(stderr, "A=%lf; B=%lf; C=%lf; F=%lf\n", A,B,C,F);
+#endif
+
+#if 0
+ /* Figure out the Ellipses Major and Minor Axis, and other info.
+ This information currently not needed at this time, but may be
+ needed later for better limit determination.
+ */
+ { double alpha, beta, gamma, Major, Minor;
+ double Eccentricity, Ellipse_Area, Ellipse_angle;
+ double max_horizontal_cross_section, max_vertical_cross_section;
+ alpha = A+C;
+ beta = A-C;
+ gamma = sqrt(beta*beta + B*B );
+
+ if ( alpha - gamma <= MagickEpsilon )
+ Major = MagickHuge;
+ else
+ Major = sqrt(2*F/(alpha - gamma));
+ Minor = sqrt(2*F/(alpha + gamma));
+
+ fprintf(stderr, "\tMajor=%lf; Minor=%lf\n",
+ Major, Minor );
+
+ /* other information about ellipse include... */
+ Eccentricity = Major/Minor;
+ Ellipse_Area = MagickPI*Major*Minor;
+ Ellipse_angle = atan2(B, A-C);
+
+ fprintf(stderr, "\tAngle=%lf Area=%lf\n",
+ RadiansToDegrees(Ellipse_angle), Ellipse_Area );
+
+ /* Ellipse limits */
+
+ /* orthogonal rectangle - improved ellipse */
+ max_horizontal_orthogonal = sqrt(A); /* = sqrt(4*A*F/(4*A*C-B*B)) */
+ max_vertical_orthogonal = sqrt(C); /* = sqrt(4*C*F/(4*A*C-B*B)) */
+
+ /* parallelogram bounds -- what we are using */
+ max_horizontal_cross_section = sqrt(F/A);
+ max_vertical_cross_section = sqrt(F/C);
+ }
+#endif
+
+ /* Is default elliptical area, too small? Image being magnified?
+ Switch to doing pure 'point' interpolation of the pixel.
+ That is turn off EWA Resampling.
+ */
+ if ( F <= F_UNITY ) {
+ resample_filter->do_interpolate = MagickTrue;
+ return;
+ }
+
+
+ /* If F is impossibly large, we may as well not bother doing any
+ * form of resampling, as you risk an infinite resampled area.
+ */
+ if ( F > MagickHuge ) {
+ resample_filter->limit_reached = MagickTrue;
+ return;
+ }
+
+ /* Othogonal bounds of the ellipse */
+ resample_filter->sqrtA = sqrt(A)+1.0; /* Vertical Orthogonal Limit */
+ resample_filter->sqrtC = sqrt(C)+1.0; /* Horizontal Orthogonal Limit */
+
+ /* Horizontally aligned Parallelogram fitted to ellipse */
+ resample_filter->sqrtU = sqrt(F/A)+1.0; /* Parallelogram Width */
+ resample_filter->slope = -B/(2*A); /* Slope of the parallelogram */
+
+ /* The size of the area of the parallelogram we will be sampling */
+ area = 4 * resample_filter->sqrtA * resample_filter->sqrtU;
+
+ /* Absolute limit on the area to be resampled
+ * This limit needs more work, as it gets too slow for
+ * larger images involved with tiled views of the horizon. */
+ if ( area > 20.0*resample_filter->image_area ) {
+ resample_filter->limit_reached = MagickTrue;
+ return;
+ }
+
+ /* Scale ellipse formula to directly fit the Filter Lookup Table */
+ { register double scale;
+ scale = (double)WLUT_WIDTH/F;
+ resample_filter->A = A*scale;
+ resample_filter->B = B*scale;
+ resample_filter->C = C*scale;
+ /* ..ple_filter->F = WLUT_WIDTH; -- hardcoded */
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t R e s a m p l e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetResampleFilter() set the resampling filter lookup table based on a
+% specific filter. Note that the filter is used as a radial filter not as a
+% two pass othogonally aligned resampling filter.
+%
+% The default Filter, is Gaussian, which is the standard filter used by the
+% original paper on the Elliptical Weighted Everage Algorithm. However other
+% filters can also be used.
+%
+% The format of the SetResampleFilter method is:
+%
+% void SetResampleFilter(ResampleFilter *resample_filter,
+% const FilterTypes filter,const double blur)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: resampling resample_filterrmation structure
+%
+% o filter: the resize filter for elliptical weighting LUT
+%
+% o blur: filter blur factor (radial scaling) for elliptical weighting LUT
+%
+*/
+MagickExport void SetResampleFilter(ResampleFilter *resample_filter,
+ const FilterTypes filter,const double blur)
+{
+ register int
+ Q;
+
+ double
+ r_scale;
+
+ ResizeFilter
+ *resize_filter;
+
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+
+ resample_filter->filter = filter;
+
+ /* Scale radius so it equals 1.0, at edge of ellipse when a
+ default blurring factor of 1.0 is used.
+
+ Note that these filters are being used as a radial filter, not as
+ an othoginally alligned filter. How this effects results is still
+ being worked out.
+
+ Future: Direct use of teh resize filters in "resize.c" to set the lookup
+ table, based on the filters working support window.
+ */
+ r_scale = sqrt(1.0/(double)WLUT_WIDTH)/blur;
+ r_scale *= 2; /* for 2 pixel radius of Improved Elliptical Formula */
+
+ switch ( filter ) {
+ case PointFilter:
+ /* This equivelent to turning off the EWA algroithm.
+ Only Interpolated lookup will be used. */
+ break;
+ default:
+ /*
+ Fill the LUT with a 1D resize filter function
+ But make the Sinc/Bessel tapered window 2.0
+ I also normalize the result so the filter is 1.0
+ */
+ resize_filter = AcquireResizeFilter(resample_filter->image,filter,
+ (MagickRealType)1.0,MagickTrue,resample_filter->exception);
+ if (resize_filter != (ResizeFilter *) NULL) {
+ resample_filter->support = GetResizeFilterSupport(resize_filter);
+ resample_filter->support /= blur; /* taken into account above */
+ resample_filter->support *= resample_filter->support;
+ resample_filter->support *= (double)WLUT_WIDTH/4;
+ if ( resample_filter->support >= (double)WLUT_WIDTH )
+ resample_filter->support = (double)WLUT_WIDTH; /* hack */
+ for(Q=0; Q<WLUT_WIDTH; Q++)
+ if ( (double) Q < resample_filter->support )
+ resample_filter->filter_lut[Q] = (double)
+ GetResizeFilterWeight(resize_filter,sqrt((double)Q)*r_scale);
+ else
+ resample_filter->filter_lut[Q] = 0.0;
+ resize_filter = DestroyResizeFilter(resize_filter);
+ break;
+ }
+ else {
+ (void) ThrowMagickException(resample_filter->exception,GetMagickModule(),
+ ModuleError, "UnableToSetFilteringValue",
+ "Fall back to default EWA gaussian filter");
+ }
+ /* FALLTHRU - on exception */
+ /*case GaussianFilter:*/
+ case UndefinedFilter:
+ /*
+ Create Normal Gaussian 2D Filter Weighted Lookup Table.
+ A normal EWA guassual lookup would use exp(Q*ALPHA)
+ where Q = distantce squared from 0.0 (center) to 1.0 (edge)
+ and ALPHA = -4.0*ln(2.0) ==> -2.77258872223978123767
+ However the table is of length 1024, and equates to a radius of 2px
+ thus needs to be scaled by ALPHA*4/1024 and any blur factor squared
+ */
+ /*r_scale = -2.77258872223978123767*4/WLUT_WIDTH/blur/blur;*/
+ r_scale = -2.77258872223978123767/WLUT_WIDTH/blur/blur;
+ for(Q=0; Q<WLUT_WIDTH; Q++)
+ resample_filter->filter_lut[Q] = exp((double)Q*r_scale);
+ resample_filter->support = WLUT_WIDTH;
+ break;
+ }
+ if (GetImageArtifact(resample_filter->image,"resample:verbose")
+ != (const char *) NULL)
+ /* Debug output of the filter weighting LUT
+ Gnuplot the LUT with hoizontal adjusted to 'r' using...
+ plot [0:2][-.2:1] "lut.dat" using (sqrt($0/1024)*2):1 with lines
+ THe filter values is normalized for comparision
+ */
+ for(Q=0; Q<WLUT_WIDTH; Q++)
+ printf("%lf\n", resample_filter->filter_lut[Q]
+ /resample_filter->filter_lut[0] );
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t R e s a m p l e F i l t e r I n t e r p o l a t e M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetResampleFilterInterpolateMethod() changes the interpolation method
+% associated with the specified resample filter.
+%
+% The format of the SetResampleFilterInterpolateMethod method is:
+%
+% MagickBooleanType SetResampleFilterInterpolateMethod(
+% ResampleFilter *resample_filter,const InterpolateMethod method)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: the resample filter.
+%
+% o method: the interpolation method.
+%
+*/
+MagickExport MagickBooleanType SetResampleFilterInterpolateMethod(
+ ResampleFilter *resample_filter,const InterpolatePixelMethod method)
+{
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+ assert(resample_filter->image != (Image *) NULL);
+ if (resample_filter->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ resample_filter->image->filename);
+ resample_filter->interpolate=method;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t R e s a m p l e F i l t e r V i r t u a l P i x e l M e t h o d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetResampleFilterVirtualPixelMethod() changes the virtual pixel method
+% associated with the specified resample filter.
+%
+% The format of the SetResampleFilterVirtualPixelMethod method is:
+%
+% MagickBooleanType SetResampleFilterVirtualPixelMethod(
+% ResampleFilter *resample_filter,const VirtualPixelMethod method)
+%
+% A description of each parameter follows:
+%
+% o resample_filter: the resample filter.
+%
+% o method: the virtual pixel method.
+%
+*/
+MagickExport MagickBooleanType SetResampleFilterVirtualPixelMethod(
+ ResampleFilter *resample_filter,const VirtualPixelMethod method)
+{
+ assert(resample_filter != (ResampleFilter *) NULL);
+ assert(resample_filter->signature == MagickSignature);
+ assert(resample_filter->image != (Image *) NULL);
+ if (resample_filter->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ resample_filter->image->filename);
+ resample_filter->virtual_pixel=method;
+ (void) SetCacheViewVirtualPixelMethod(resample_filter->view,method);
+ return(MagickTrue);
+}
diff --git a/magick/resample.h b/magick/resample.h
new file mode 100644
index 0000000..7be9c3b
--- /dev/null
+++ b/magick/resample.h
@@ -0,0 +1,91 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore graphic resample methods.
+*/
+#ifndef _MAGICKCORE_RESAMPLE_H
+#define _MAGICKCORE_RESAMPLE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/cache-view.h>
+
+typedef enum
+{
+ UndefinedFilter,
+ PointFilter,
+ BoxFilter,
+ TriangleFilter,
+ HermiteFilter,
+ HanningFilter,
+ HammingFilter,
+ BlackmanFilter,
+ GaussianFilter,
+ QuadraticFilter,
+ CubicFilter,
+ CatromFilter,
+ MitchellFilter,
+ LanczosFilter,
+ BesselFilter,
+ SincFilter,
+ KaiserFilter,
+ WelshFilter,
+ ParzenFilter,
+ LagrangeFilter,
+ BohmanFilter,
+ BartlettFilter,
+ SentinelFilter /* a count of all the filters, not a real filter */
+} FilterTypes;
+
+typedef enum
+{
+ UndefinedInterpolatePixel,
+ AverageInterpolatePixel,
+ BicubicInterpolatePixel,
+ BilinearInterpolatePixel,
+ FilterInterpolatePixel,
+ IntegerInterpolatePixel,
+ MeshInterpolatePixel,
+ NearestNeighborInterpolatePixel,
+ SplineInterpolatePixel
+} InterpolatePixelMethod;
+
+typedef struct _ResampleFilter
+ ResampleFilter;
+
+extern MagickExport MagickBooleanType
+ ResamplePixelColor(ResampleFilter *,const double,const double,
+ MagickPixelPacket *),
+ SetResampleFilterInterpolateMethod(ResampleFilter *,
+ const InterpolatePixelMethod),
+ SetResampleFilterVirtualPixelMethod(ResampleFilter *,
+ const VirtualPixelMethod);
+
+extern MagickExport ResampleFilter
+ *AcquireResampleFilter(const Image *,ExceptionInfo *),
+ *DestroyResampleFilter(ResampleFilter *);
+
+extern MagickExport void
+ ScaleResampleFilter(ResampleFilter *,const double,const double,const double,
+ const double),
+ SetResampleFilter(ResampleFilter *,const FilterTypes,const double);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/resize-private.h b/magick/resize-private.h
new file mode 100644
index 0000000..f70a941
--- /dev/null
+++ b/magick/resize-private.h
@@ -0,0 +1,44 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image resize private methods.
+*/
+#ifndef _MAGICKCORE_RESIZE_PRIVATE_H
+#define _MAGICKCORE_RESIZE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _ResizeFilter
+ ResizeFilter;
+
+extern MagickExport MagickRealType
+ GetResizeFilterSupport(const ResizeFilter *),
+ GetResizeFilterWeight(const ResizeFilter *,const MagickRealType);
+
+extern MagickExport ResizeFilter
+ *AcquireResizeFilter(const Image *,const FilterTypes,const MagickRealType,
+ const MagickBooleanType,ExceptionInfo *),
+ *DestroyResizeFilter(ResizeFilter *);
+
+extern MagickExport void
+ SetResizeFilterSupport(ResizeFilter *,const MagickRealType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/resize.c b/magick/resize.c
new file mode 100644
index 0000000..f763c99
--- /dev/null
+++ b/magick/resize.c
@@ -0,0 +1,3021 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% RRRR EEEEE SSSSS IIIII ZZZZZ EEEEE %
+% R R E SS I ZZ E %
+% RRRR EEE SSS I ZZZ EEE %
+% R R E SS I ZZ E %
+% R R EEEEE SSSSS IIIII ZZZZZ EEEEE %
+% %
+% %
+% MagickCore Image Resize Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/memory_.h"
+#include "magick/pixel-private.h"
+#include "magick/property.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel.h"
+#include "magick/option.h"
+#include "magick/resample.h"
+#include "magick/resize.h"
+#include "magick/resize-private.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+#if defined(MAGICKCORE_LQR_DELEGATE)
+#include <lqr.h>
+#endif
+
+/*
+ Typedef declarations.
+*/
+struct _ResizeFilter
+{
+ MagickRealType
+ (*filter)(const MagickRealType,const ResizeFilter *),
+ (*window)(const MagickRealType,const ResizeFilter *),
+ support, /* filter region of support - the filter support limit */
+ window_support, /* window support, usally equal to support (expert only) */
+ scale, /* dimension to scale to fit window support (usally 1.0) */
+ blur, /* x-scale (blur-sharpen) */
+ cubic[8]; /* cubic coefficents for smooth Cubic filters */
+
+ unsigned long
+ signature;
+};
+
+/*
+ Forward declaractions.
+*/
+static MagickRealType
+ I0(MagickRealType x),
+ BesselOrderOne(MagickRealType);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F i l t e r F u n c t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% These are the various filter and windowing functions that are provided,
+%
+% They are all internal to this module only. See AcquireResizeFilterInfo()
+% for details of the access to these functions, via the
+% GetResizeFilterSupport() and GetResizeFilterWeight() API interface.
+%
+% The individual filter functions have this format...
+%
+% static MagickRealtype *FilterName(const MagickRealType x,
+% const MagickRealType support)
+%
+% o x: the distance from the sampling point
+% generally in the range of 0 to support
+% The GetResizeFilterWeight() ensures this a positive value.
+%
+% o resize_filter: Current Filter Information
+% This allows function to access support, and posibly other
+% pre-calculated information defineding the functions.
+%
+*/
+
+static MagickRealType Bessel(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ See Pratt "Digital Image Processing" p.97 for Bessel functions
+
+ This function actually a X-scaled Jinc(x) function.
+ http://mathworld.wolfram.com/JincFunction.html
+ And on page 11 of...
+ http://www.ph.ed.ac.uk/%7ewjh/teaching/mo/slides/lens/lens.pdf
+ */
+ if (x == 0.0)
+ return((MagickRealType) (MagickPI/4.0));
+ return(BesselOrderOne(MagickPI*x)/(2.0*x));
+}
+
+static MagickRealType Blackman(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ Blackman: 2rd Order cosine windowing function.
+ */
+ return(0.42+0.5*cos(MagickPI*(double) x)+0.08*cos(2.0*MagickPI*(double) x));
+}
+
+static MagickRealType Bohman(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ Bohman: 2rd Order cosine windowing function.
+ */
+ return((1-x)*cos(MagickPI*(double) x)+sin(MagickPI*(double) x)/MagickPI);
+}
+
+static MagickRealType Box(const MagickRealType magick_unused(x),
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ Just return 1.0, filter will still be clipped by its support window.
+ */
+ return(1.0);
+}
+
+static MagickRealType CubicBC(const MagickRealType x,
+ const ResizeFilter *resize_filter)
+{
+ /*
+ Cubic Filters using B,C determined values:
+
+ Mitchell-Netravali B=1/3 C=1/3 Qualitively ideal Cubic Filter
+ Catmull-Rom B= 0 C=1/2 Cublic Interpolation Function
+ Cubic B-Spline B= 1 C= 0 Spline Approximation of Gaussian
+ Hermite B= 0 C= 0 Quadratic Spline (support = 1)
+
+ See paper by Mitchell and Netravali,
+ Reconstruction Filters in Computer Graphics
+ Computer Graphics, Volume 22, Number 4, August 1988
+ http://www.cs.utexas.edu/users/fussell/courses/cs384g/
+ lectures/mitchell/Mitchell.pdf
+
+ Coefficents are determined from B,C values
+ P0 = ( 6 - 2*B )/6
+ P1 = 0
+ P2 = (-18 +12*B + 6*C )/6
+ P3 = ( 12 - 9*B - 6*C )/6
+ Q0 = ( 8*B +24*C )/6
+ Q1 = ( -12*B -48*C )/6
+ Q2 = ( 6*B +30*C )/6
+ Q3 = ( - 1*B - 6*C )/6
+
+ Which is used to define the filter...
+ P0 + P1*x + P2*x^2 + P3*x^3 0 <= x < 1
+ Q0 + Q1*x + Q2*x^2 + Q3*x^3 1 <= x <= 2
+
+ Which ensures function is continuous in value and derivative (slope).
+ */
+ if (x < 1.0)
+ return(resize_filter->cubic[0]+x*(resize_filter->cubic[1]+x*
+ (resize_filter->cubic[2]+x*resize_filter->cubic[3])));
+ if (x < 2.0)
+ return(resize_filter->cubic[4] +x*(resize_filter->cubic[5]+x*
+ (resize_filter->cubic[6] +x*resize_filter->cubic[7])));
+ return(0.0);
+}
+
+static MagickRealType Gaussian(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ return(exp((double) (-2.0*x*x))*sqrt(2.0/MagickPI));
+}
+
+static MagickRealType Hanning(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ A Cosine windowing function.
+ */
+ return(0.5+0.5*cos(MagickPI*(double) x));
+}
+
+static MagickRealType Hamming(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ A offset Cosine windowing function.
+ */
+ return(0.54+0.46*cos(MagickPI*(double) x));
+}
+
+static MagickRealType Kaiser(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+#define Alpha 6.5
+#define I0A (1.0/I0(Alpha))
+
+ /*
+ Kaiser Windowing Function (bessel windowing):
+ Alpha is a free value from 5 to 8 (currently hardcoded to 6.5)
+ Future: make alphand the IOA pre-calculation, a 'expert' setting.
+ */
+ return(I0A*I0(Alpha*sqrt((double) (1.0-x*x))));
+}
+
+static MagickRealType Lagrange(const MagickRealType x,
+ const ResizeFilter *resize_filter)
+{
+ long
+ n,
+ order;
+
+ MagickRealType
+ value;
+
+ register long
+ i;
+
+ /*
+ Lagrange Piece-Wise polynomial fit of Sinc:
+ N is the 'order' of the lagrange function and depends on
+ the overall support window size of the filter. That is for
+ a support of 2, gives a lagrange-4 or piece-wise cubic functions
+
+ Note that n is the specific piece of the piece-wise function to calculate.
+
+ See Survey: Interpolation Methods, IEEE Transactions on Medical Imaging,
+ Vol 18, No 11, November 1999, p1049-1075, -- Equation 27 on p1064
+ */
+ if (x > resize_filter->support)
+ return(0.0);
+ order=(long) (2.0*resize_filter->window_support); /* number of pieces */
+ n=(long) ((1.0*order)/2.0+x); /* which piece does x belong to */
+ value=1.0f;
+ for (i=0; i < order; i++)
+ if (i != n)
+ value*=(n-i-x)/(n-i);
+ return(value);
+}
+
+static MagickRealType Quadratic(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ 2rd order (quadratic) B-Spline approximation of Gaussian.
+ */
+ if (x < 0.5)
+ return(0.75-x*x);
+ if (x < 1.5)
+ return(0.5*(x-1.5)*(x-1.5));
+ return(0.0);
+}
+
+static MagickRealType Sinc(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ This function actually a X-scaled Sinc(x) function.
+ */
+ if (x == 0.0)
+ return(1.0);
+ return(sin(MagickPI*(double) x)/(MagickPI*(double) x));
+}
+
+static MagickRealType Triangle(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ 1rd order (linear) B-Spline, bilinear interpolation,
+ Tent 1D filter, or a Bartlett 2D Cone filter
+ */
+ if (x < 1.0)
+ return(1.0-x);
+ return(0.0);
+}
+
+static MagickRealType Welsh(const MagickRealType x,
+ const ResizeFilter *magick_unused(resize_filter))
+{
+ /*
+ Welsh parabolic windowing filter.
+ */
+ if (x < 1.0)
+ return(1.0-x*x);
+ return(0.0);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e R e s i z e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireResizeFilter() allocates the ResizeFilter structure. Choose from
+% these filters:
+%
+% FIR (Finite impulse Response) Filters
+% Box Triangle Quadratic
+% Cubic Hermite Catrom
+% Mitchell
+%
+% IIR (Infinite impulse Response) Filters
+% Gaussian Sinc Bessel
+%
+% Windowed Sinc/Bessel Method
+% Blackman Hanning Hamming
+% Kaiser Lancos (Sinc)
+%
+% FIR filters are used as is, and are limited by that filters support window
+% (unless over-ridden). 'Gaussian' while classed as an IIR filter, is also
+% simply clipped by its support size (1.5).
+%
+% Requesting a windowed filter will return either a windowed Sinc, for a one
+% dimentional orthogonal filtering method, such as ResizeImage(), or a
+% windowed Bessel for image operations requiring a two dimentional
+% cylindrical filtering method, such a DistortImage(). Which function is
+% is used set by the "cylindrical" boolean argument.
+%
+% Directly requesting 'Sinc' or 'Bessel' will force the use of that filter
+% function, with a default 'Blackman' windowing method. This not however
+% recommended as it removes the correct filter selection for different
+% filtering image operations. Selecting a window filtering method is better.
+%
+% Lanczos is purely special case of a Sinc windowed Sinc, but defulting to
+% a 3 lobe support, rather that the default 4 lobe support.
+%
+% Special options can be used to override specific, or all the filter
+% settings. However doing so is not advisible unless you have expert
+% knowledge of the use of resampling filtered techniques. Extreme caution is
+% advised.
+%
+% "filter:filter" Select this function as the filter.
+% If a "filter:window" operation is not provided, then no windowing
+% will be performed on the selected filter, (support clipped)
+%
+% This can be used to force the use of a windowing method as filter,
+% request a 'Sinc' filter in a radially filtered operation, or the
+% 'Bessel' filter for a othogonal filtered operation.
+%
+% "filter:window" Select this windowing function for the filter.
+% While any filter could be used as a windowing function,
+% using that filters first lobe over the whole support window,
+% using a non-windowing method is not advisible.
+%
+% "filter:lobes" Number of lobes to use for the Sinc/Bessel filter.
+% This a simper method of setting filter support size that will
+% correctly handle the Sinc/Bessel switch for an operators filtering
+% requirements.
+%
+% "filter:support" Set the support size for filtering to the size given
+% This not recomented for Sinc/Bessel windowed filters, but is
+% used for simple filters like FIR filters, and the Gaussian Filter.
+% This will override any 'filter:lobes' option.
+%
+% "filter:blur" Scale the filter and support window by this amount.
+% A value >1 will generally result in a more burred image with
+% more ringing effects, while a value <1 will sharpen the
+% resulting image with more aliasing and Morie effects.
+%
+% "filter:win-support" Scale windowing function to this size instead.
+% This causes the windowing (or self-windowing Lagrange filter)
+% to act is if the support winodw it much much larger than what
+% is actually supplied to the calling operator. The filter however
+% is still clipped to the real support size given. If unset this
+% will equal the normal filter support size.
+%
+% "filter:b"
+% "filter:c" Override the preset B,C values for a Cubic type of filter
+% If only one of these are given it is assumes to be a 'Keys'
+% type of filter such that B+2C=1, where Keys 'alpha' value = C
+%
+% "filter:verbose" Output verbose plotting data for graphing the
+% resulting filter over the whole support range (with blur effect).
+%
+% Set a true un-windowed Sinc filter with 10 lobes (very slow)
+% -set option:filter:filter Sinc
+% -set option:filter:lobes 8
+%
+% For example force an 8 lobe Lanczos (Sinc or Bessel) filter...
+% -filter Lanczos
+% -set option:filter:lobes 8
+%
+% The format of the AcquireResizeFilter method is:
+%
+% ResizeFilter *AcquireResizeFilter(const Image *image,
+% const FilterTypes filter_type, const MagickBooleanType radial,
+% ExceptionInfo *exception)
+%
+% o image: the image.
+%
+% o filter: the filter type, defining a preset filter, window and support.
+%
+% o blur: blur the filter by this amount, use 1.0 if unknown.
+% Image artifact "filter:blur" will override this old usage
+%
+% o radial: 1D orthogonal filter (Sinc) or 2D radial filter (Bessel)
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ResizeFilter *AcquireResizeFilter(const Image *image,
+ const FilterTypes filter, const MagickRealType blur,
+ const MagickBooleanType cylindrical,ExceptionInfo *exception)
+{
+ const char
+ *artifact;
+
+ FilterTypes
+ filter_type,
+ window_type;
+
+ long
+ filter_artifact;
+
+ MagickRealType
+ B,
+ C;
+
+ register ResizeFilter
+ *resize_filter;
+
+ /*
+ Table Mapping given Filter, into Weighting and Windowing functions.
+ A 'Box' windowing function means its a simble non-windowed filter.
+ A 'Sinc' filter function (must be windowed) could be upgraded to a
+ 'Bessel' filter if a "cylindrical" filter is requested, unless a "Sinc"
+ filter specifically request.
+ */
+ static struct
+ {
+ FilterTypes
+ filter,
+ window;
+ } const mapping[SentinelFilter] =
+ {
+ { UndefinedFilter, BoxFilter }, /* undefined */
+ { PointFilter, BoxFilter }, /* special, nearest-neighbour filter */
+ { BoxFilter, BoxFilter }, /* Box averaging Filter */
+ { TriangleFilter, BoxFilter }, /* Linear Interpolation Filter */
+ { HermiteFilter, BoxFilter }, /* Hermite interpolation filter */
+ { SincFilter, HanningFilter }, /* Hanning -- Cosine-Sinc */
+ { SincFilter, HammingFilter }, /* Hamming -- '' variation */
+ { SincFilter, BlackmanFilter }, /* Blackman -- 2*Cosine-Sinc */
+ { GaussianFilter, BoxFilter }, /* Gaussain Blurring filter */
+ { QuadraticFilter, BoxFilter }, /* Quadratic Gaussian approximation */
+ { CubicFilter, BoxFilter }, /* Cubic Gaussian approximation */
+ { CatromFilter, BoxFilter }, /* Cubic Interpolator */
+ { MitchellFilter, BoxFilter }, /* 'ideal' Cubic Filter */
+ { LanczosFilter, SincFilter }, /* Special, 3 lobed Sinc-Sinc */
+ { BesselFilter, BlackmanFilter }, /* 3 lobed bessel -specific request */
+ { SincFilter, BlackmanFilter }, /* 4 lobed sinc - specific request */
+ { SincFilter, KaiserFilter }, /* Kaiser -- SqRoot-Sinc */
+ { SincFilter, WelshFilter }, /* Welsh -- Parabolic-Sinc */
+ { SincFilter, CubicFilter }, /* Parzen -- Cubic-Sinc */
+ { LagrangeFilter, BoxFilter }, /* Lagrange self-windowing filter */
+ { SincFilter, BohmanFilter }, /* Bohman -- 2*Cosine-Sinc */
+ { SincFilter, TriangleFilter } /* Bartlett -- Triangle-Sinc */
+ };
+ /*
+ Table maping the filter/window function from the above table to the actual
+ filter/window function call to use. The default support size for that
+ filter as a weighting function, and the point to scale when that function
+ is used as a windowing function (typ 1.0).
+ */
+ static struct
+ {
+ MagickRealType
+ (*function)(const MagickRealType, const ResizeFilter*),
+ support, /* default support size for function as a filter */
+ scale, /* size windowing function, for scaling windowing function */
+ B,
+ C; /* Cubic Filter factors for a CubicBC function, else ignored */
+ } const filters[SentinelFilter] =
+ {
+ { Box, 0.0f, 0.5f, 0.0f, 0.0f }, /* Undefined */
+ { Box, 0.0f, 0.5f, 0.0f, 0.0f }, /* Point */
+ { Box, 0.5f, 0.5f, 0.0f, 0.0f }, /* Box */
+ { Triangle, 1.0f, 1.0f, 0.0f, 0.0f }, /* Triangle */
+ { CubicBC, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hermite, Cubic B=C=0 */
+ { Hanning, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hanning, Cosine window */
+ { Hamming, 1.0f, 1.0f, 0.0f, 0.0f }, /* Hamming, '' variation */
+ { Blackman, 1.0f, 1.0f, 0.0f, 0.0f }, /* Blackman, 2*cos window */
+ { Gaussian, 1.5f, 1.5f, 0.0f, 0.0f }, /* Gaussian */
+ { Quadratic, 1.5f, 1.5f, 0.0f, 0.0f }, /* Quadratic Gaussian */
+ { CubicBC, 2.0f, 2.0f, 1.0f, 0.0f }, /* B-Spline of Gaussian B=1 C=0 */
+ { CubicBC, 2.0f, 1.0f, 0.0f, 0.5f }, /* Catmull-Rom B=0 C=1/2 */
+ { CubicBC, 2.0f, 1.0f, 1.0f/3.0f, 1.0f/3.0f }, /* Mitchel B=C=1/3 */
+ { Sinc, 3.0f, 1.0f, 0.0f, 0.0f }, /* Lanczos, 3 lobed Sinc-Sinc */
+ { Bessel, 3.2383f,1.2197f,.0f,.0f }, /* 3 lobed Blackman-Bessel */
+ { Sinc, 4.0f, 1.0f, 0.0f, 0.0f }, /* 4 lobed Blackman-Sinc */
+ { Kaiser, 1.0f, 1.0f, 0.0f, 0.0f }, /* Kaiser, sq-root windowing */
+ { Welsh, 1.0f, 1.0f, 0.0f, 0.0f }, /* Welsh, Parabolic windowing */
+ { CubicBC, 2.0f, 2.0f, 1.0f, 0.0f }, /* Parzen, B-Spline windowing */
+ { Lagrange, 2.0f, 1.0f, 0.0f, 0.0f }, /* Lagrangian Filter */
+ { Bohman, 1.0f, 1.0f, 0.0f, 0.0f }, /* Bohman, 2*Cosine windowing */
+ { Triangle, 1.0f, 1.0f, 0.0f, 0.0f } /* Bartlett, Triangle windowing */
+ };
+ /*
+ The known zero crossings of the Bessel() or the Jinc(x*PI) function
+ Found by using
+ http://cose.math.bas.bg/webMathematica/webComputing/BesselZeros.jsp
+ for Jv-function with v=1, then dividing X-roots by PI (tabled below)
+ */
+ static MagickRealType
+ bessel_zeros[16] =
+ {
+ 1.21966989126651f,
+ 2.23313059438153f,
+ 3.23831548416624f,
+ 4.24106286379607f,
+ 5.24276437687019f,
+ 6.24392168986449f,
+ 7.24475986871996f,
+ 8.24539491395205f,
+ 9.24589268494948f,
+ 10.2462933487549f,
+ 11.2466227948779f,
+ 12.2468984611381f,
+ 13.2471325221811f,
+ 14.2473337358069f,
+ 15.2475085630373f,
+ 16.247661874701f
+ };
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(UndefinedFilter < filter && filter < SentinelFilter);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+
+ resize_filter=(ResizeFilter *) AcquireMagickMemory(sizeof(*resize_filter));
+ if (resize_filter == (ResizeFilter *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+
+ /* defaults for the requested filter */
+ filter_type = mapping[filter].filter;
+ window_type = mapping[filter].window;
+
+
+ /* Filter blur -- scaling both filter and support window */
+ resize_filter->blur = blur;
+ artifact=GetImageArtifact(image,"filter:blur");
+ if (artifact != (const char *) NULL)
+ resize_filter->blur = atof(artifact);
+ if ( resize_filter->blur < MagickEpsilon )
+ resize_filter->blur = (MagickRealType) MagickEpsilon;
+
+ /* Modifications for Cylindrical filter use */
+ if ( cylindrical != MagickFalse && filter != SincFilter ) {
+ /* promote 1D Sinc Filter to a 2D Bessel filter */
+ if ( filter_type == SincFilter )
+ filter_type = BesselFilter;
+ /* Prompote Lanczos (Sinc-Sinc) to Lanczos (Bessel-Bessel) */
+ else if ( filter_type == LanczosFilter ) {
+ filter_type = BesselFilter;
+ window_type = BesselFilter;
+ }
+ /* Blur other filters appropriatally correct cylindrical usage */
+ else if ( filter_type == GaussianFilter )
+ /* Gaussian is scaled by 4*ln(2) and not 4*sqrt(2/MagickPI)
+ - according to Paul Heckbert's paper on EWA resampling */
+ resize_filter->blur *= 2.0*log(2.0)/sqrt(2.0/MagickPI);
+ else if ( filter_type != BesselFilter )
+ /* filters with a 1.0 zero root crossing by the first bessel_zero */
+ resize_filter->blur *= bessel_zeros[0];
+ }
+
+ /* Override Filter Selection */
+ artifact=GetImageArtifact(image,"filter:filter");
+ if (artifact != (const char *) NULL) {
+ /* raw filter request - no window function */
+ filter_artifact=ParseMagickOption(MagickFilterOptions,
+ MagickFalse,artifact);
+ if ( UndefinedFilter < filter_artifact &&
+ filter_artifact < SentinelFilter ) {
+ filter_type = (FilterTypes) filter_artifact;
+ window_type = BoxFilter;
+ }
+ /* Lanczos is nor a real filter but a self windowing Sinc/Bessel */
+ if ( filter_artifact == LanczosFilter ) {
+ filter_type = (cylindrical!=MagickFalse) ? BesselFilter : LanczosFilter;
+ window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
+ }
+ /* Filter overwide with a specific window function? */
+ artifact=GetImageArtifact(image,"filter:window");
+ if (artifact != (const char *) NULL) {
+ filter_artifact=ParseMagickOption(MagickFilterOptions,
+ MagickFalse,artifact);
+ if ( UndefinedFilter < filter_artifact &&
+ filter_artifact < SentinelFilter ) {
+ if ( filter_artifact != LanczosFilter )
+ window_type = (FilterTypes) filter_artifact;
+ else
+ window_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
+ }
+ }
+ }
+ else {
+ /* window specified, but no filter function? Assume Sinc/Bessel */
+ artifact=GetImageArtifact(image,"filter:window");
+ if (artifact != (const char *) NULL) {
+ filter_artifact=ParseMagickOption(MagickFilterOptions,MagickFalse,
+ artifact);
+ if ( UndefinedFilter < filter_artifact &&
+ filter_artifact < SentinelFilter ) {
+ filter_type = (cylindrical!=MagickFalse) ? BesselFilter : SincFilter;
+ if ( filter_artifact != LanczosFilter )
+ window_type = (FilterTypes) filter_artifact;
+ else
+ window_type = filter_type;
+ }
+ }
+ }
+
+ resize_filter->filter = filters[filter_type].function;
+ resize_filter->support = filters[filter_type].support;
+ resize_filter->window = filters[window_type].function;
+ resize_filter->scale = filters[window_type].scale;
+ resize_filter->signature=MagickSignature;
+
+ /* Filter support overrides */
+ artifact=GetImageArtifact(image,"filter:lobes");
+ if (artifact != (const char *) NULL) {
+ long lobes = atol(artifact);
+ if ( lobes < 1 ) lobes = 1;
+ resize_filter->support = (MagickRealType) lobes;
+ if ( filter_type == BesselFilter ) {
+ if ( lobes > 16 ) lobes = 16;
+ resize_filter->support = bessel_zeros[lobes-1];
+ }
+ }
+ artifact=GetImageArtifact(image,"filter:support");
+ if (artifact != (const char *) NULL)
+ resize_filter->support = fabs(atof(artifact));
+
+ /* Scale windowing function separatally to the support 'clipping' window
+ that calling operator is planning to actually use. - Expert Use Only
+ */
+ resize_filter->window_support = resize_filter->support;
+ artifact=GetImageArtifact(image,"filter:win-support");
+ if (artifact != (const char *) NULL)
+ resize_filter->window_support = fabs(atof(artifact));
+
+ /* Set Cubic Spline B,C values, calculate Cubic coefficents */
+ B=0.0;
+ C=0.0;
+ if ( filters[filter_type].function == CubicBC
+ || filters[window_type].function == CubicBC ) {
+ if ( filters[filter_type].function == CubicBC ) {
+ B=filters[filter_type].B;
+ C=filters[filter_type].C;
+ }
+ else if ( filters[window_type].function == CubicBC ) {
+ B=filters[window_type].B;
+ C=filters[window_type].C;
+ }
+ artifact=GetImageArtifact(image,"filter:b");
+ if (artifact != (const char *) NULL) {
+ B=atof(artifact);
+ C=(1.0-B)/2.0; /* Calculate C as if it is a Keys cubic filter */
+ artifact=GetImageArtifact(image,"filter:c");
+ if (artifact != (const char *) NULL)
+ C=atof(artifact);
+ }
+ else {
+ artifact=GetImageArtifact(image,"filter:c");
+ if (artifact != (const char *) NULL) {
+ C=atof(artifact);
+ B=1.0-2.0*C; /* Calculate B as if it is a Keys cubic filter */
+ }
+ }
+ /* Convert B,C values into Cubic Coefficents - See CubicBC() */
+ resize_filter->cubic[0]=( 6.0 -2.0*B )/6.0;
+ resize_filter->cubic[1]=0.0;
+ resize_filter->cubic[2]=(-18.0+12.0*B+ 6.0*C)/6.0;
+ resize_filter->cubic[3]=( 12.0- 9.0*B- 6.0*C)/6.0;
+ resize_filter->cubic[4]=( 8.0*B+24.0*C)/6.0;
+ resize_filter->cubic[5]=( -12.0*B-48.0*C)/6.0;
+ resize_filter->cubic[6]=( 6.0*B+30.0*C)/6.0;
+ resize_filter->cubic[7]=( - 1.0*B- 6.0*C)/6.0;
+ }
+ artifact=GetImageArtifact(image,"filter:verbose");
+ if (artifact != (const char *) NULL)
+ {
+ double
+ support,
+ x;
+
+ /*
+ Output filter graph -- for graphing filter result.
+ */
+ support=GetResizeFilterSupport(resize_filter);
+ (void) printf("# support = %lg\n",support);
+ for (x=0.0; x <= support; x+=0.01f)
+ (void) printf("%5.2lf\t%lf\n",x,GetResizeFilterWeight(resize_filter,x));
+ (void) printf("%5.2lf\t%lf\n",support,0.0);
+ }
+ return(resize_filter);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d a p t i v e R e s i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AdaptiveResizeImage() adaptively resize image with pixel resampling.
+%
+% The format of the AdaptiveResizeImage method is:
+%
+% Image *AdaptiveResizeImage(const Image *image,
+% const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the resized image.
+%
+% o rows: the number of rows in the resized image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AdaptiveResizeImage(const Image *image,
+ const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
+{
+#define AdaptiveResizeImageTag "Resize/Image"
+
+ Image
+ *resize_image;
+
+ long
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ MagickPixelPacket
+ pixel;
+
+ PointInfo
+ offset;
+
+ ResampleFilter
+ *resample_filter;
+
+ CacheView
+ *resize_view;
+
+ /*
+ Adaptively resize image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((columns == 0) || (rows == 0))
+ return((Image *) NULL);
+ if ((columns == image->columns) && (rows == image->rows))
+ return(CloneImage(image,0,0,MagickTrue,exception));
+ resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
+ if (resize_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(resize_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&resize_image->exception);
+ resize_image=DestroyImage(resize_image);
+ return((Image *) NULL);
+ }
+ GetMagickPixelPacket(image,&pixel);
+ resample_filter=AcquireResampleFilter(image,exception);
+ if (image->interpolate == UndefinedInterpolatePixel)
+ (void) SetResampleFilterInterpolateMethod(resample_filter,
+ MeshInterpolatePixel);
+ resize_view=AcquireCacheView(resize_image);
+ for (y=0; y < (long) resize_image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict resize_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
+ offset.y=((MagickRealType) y*image->rows/resize_image->rows);
+ for (x=0; x < (long) resize_image->columns; x++)
+ {
+ offset.x=((MagickRealType) x*image->columns/resize_image->columns);
+ (void) ResamplePixelColor(resample_filter,offset.x-0.5,offset.y-0.5,
+ &pixel);
+ SetPixelPacket(resize_image,&pixel,q,resize_indexes+x);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,AdaptiveResizeImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ resample_filter=DestroyResampleFilter(resample_filter);
+ resize_view=DestroyCacheView(resize_view);
+ return(resize_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ B e s s e l O r d e r O n e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BesselOrderOne() computes the Bessel function of x of the first kind of
+% order 0:
+%
+% Reduce x to |x| since j1(x)= -j1(-x), and for x in (0,8]
+%
+% j1(x) = x*j1(x);
+%
+% For x in (8,inf)
+%
+% j1(x) = sqrt(2/(pi*x))*(p1(x)*cos(x1)-q1(x)*sin(x1))
+%
+% where x1 = x-3*pi/4. Compute sin(x1) and cos(x1) as follow:
+%
+% cos(x1) = cos(x)cos(3pi/4)+sin(x)sin(3pi/4)
+% = 1/sqrt(2) * (sin(x) - cos(x))
+% sin(x1) = sin(x)cos(3pi/4)-cos(x)sin(3pi/4)
+% = -1/sqrt(2) * (sin(x) + cos(x))
+%
+% The format of the BesselOrderOne method is:
+%
+% MagickRealType BesselOrderOne(MagickRealType x)
+%
+% A description of each parameter follows:
+%
+% o x: MagickRealType value.
+%
+*/
+
+#undef I0
+static MagickRealType I0(MagickRealType x)
+{
+ MagickRealType
+ sum,
+ t,
+ y;
+
+ register long
+ i;
+
+ /*
+ Zeroth order Bessel function of the first kind.
+ */
+ sum=1.0;
+ y=x*x/4.0;
+ t=y;
+ for (i=2; t > MagickEpsilon; i++)
+ {
+ sum+=t;
+ t*=y/((MagickRealType) i*i);
+ }
+ return(sum);
+}
+
+#undef J1
+static MagickRealType J1(MagickRealType x)
+{
+ MagickRealType
+ p,
+ q;
+
+ register long
+ i;
+
+ static const double
+ Pone[] =
+ {
+ 0.581199354001606143928050809e+21,
+ -0.6672106568924916298020941484e+20,
+ 0.2316433580634002297931815435e+19,
+ -0.3588817569910106050743641413e+17,
+ 0.2908795263834775409737601689e+15,
+ -0.1322983480332126453125473247e+13,
+ 0.3413234182301700539091292655e+10,
+ -0.4695753530642995859767162166e+7,
+ 0.270112271089232341485679099e+4
+ },
+ Qone[] =
+ {
+ 0.11623987080032122878585294e+22,
+ 0.1185770712190320999837113348e+20,
+ 0.6092061398917521746105196863e+17,
+ 0.2081661221307607351240184229e+15,
+ 0.5243710262167649715406728642e+12,
+ 0.1013863514358673989967045588e+10,
+ 0.1501793594998585505921097578e+7,
+ 0.1606931573481487801970916749e+4,
+ 0.1e+1
+ };
+
+ p=Pone[8];
+ q=Qone[8];
+ for (i=7; i >= 0; i--)
+ {
+ p=p*x*x+Pone[i];
+ q=q*x*x+Qone[i];
+ }
+ return(p/q);
+}
+
+#undef P1
+static MagickRealType P1(MagickRealType x)
+{
+ MagickRealType
+ p,
+ q;
+
+ register long
+ i;
+
+ static const double
+ Pone[] =
+ {
+ 0.352246649133679798341724373e+5,
+ 0.62758845247161281269005675e+5,
+ 0.313539631109159574238669888e+5,
+ 0.49854832060594338434500455e+4,
+ 0.2111529182853962382105718e+3,
+ 0.12571716929145341558495e+1
+ },
+ Qone[] =
+ {
+ 0.352246649133679798068390431e+5,
+ 0.626943469593560511888833731e+5,
+ 0.312404063819041039923015703e+5,
+ 0.4930396490181088979386097e+4,
+ 0.2030775189134759322293574e+3,
+ 0.1e+1
+ };
+
+ p=Pone[5];
+ q=Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p=p*(8.0/x)*(8.0/x)+Pone[i];
+ q=q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return(p/q);
+}
+
+#undef Q1
+static MagickRealType Q1(MagickRealType x)
+{
+ MagickRealType
+ p,
+ q;
+
+ register long
+ i;
+
+ static const double
+ Pone[] =
+ {
+ 0.3511751914303552822533318e+3,
+ 0.7210391804904475039280863e+3,
+ 0.4259873011654442389886993e+3,
+ 0.831898957673850827325226e+2,
+ 0.45681716295512267064405e+1,
+ 0.3532840052740123642735e-1
+ },
+ Qone[] =
+ {
+ 0.74917374171809127714519505e+4,
+ 0.154141773392650970499848051e+5,
+ 0.91522317015169922705904727e+4,
+ 0.18111867005523513506724158e+4,
+ 0.1038187585462133728776636e+3,
+ 0.1e+1
+ };
+
+ p=Pone[5];
+ q=Qone[5];
+ for (i=4; i >= 0; i--)
+ {
+ p=p*(8.0/x)*(8.0/x)+Pone[i];
+ q=q*(8.0/x)*(8.0/x)+Qone[i];
+ }
+ return(p/q);
+}
+
+static MagickRealType BesselOrderOne(MagickRealType x)
+{
+ MagickRealType
+ p,
+ q;
+
+ if (x == 0.0)
+ return(0.0);
+ p=x;
+ if (x < 0.0)
+ x=(-x);
+ if (x < 8.0)
+ return(p*J1(x));
+ q=sqrt((double) (2.0/(MagickPI*x)))*(P1(x)*(1.0/sqrt(2.0)*(sin((double) x)-
+ cos((double) x)))-8.0/x*Q1(x)*(-1.0/sqrt(2.0)*(sin((double) x)+
+ cos((double) x))));
+ if (p < 0.0)
+ q=(-q);
+ return(q);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y R e s i z e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyResizeFilter() destroy the resize filter.
+%
+% The format of the AcquireResizeFilter method is:
+%
+% ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
+%
+% A description of each parameter follows:
+%
+% o resize_filter: the resize filter.
+%
+*/
+MagickExport ResizeFilter *DestroyResizeFilter(ResizeFilter *resize_filter)
+{
+ assert(resize_filter != (ResizeFilter *) NULL);
+ assert(resize_filter->signature == MagickSignature);
+ resize_filter->signature=(~MagickSignature);
+ resize_filter=(ResizeFilter *) RelinquishMagickMemory(resize_filter);
+ return(resize_filter);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t R e s i z e F i l t e r S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetResizeFilterSupport() return the current support window size for this
+% filter. Note that this may have been enlarged by filter:blur factor.
+%
+% The format of the GetResizeFilterSupport method is:
+%
+% MagickRealType GetResizeFilterSupport(const ResizeFilter *resize_filter)
+%
+% A description of each parameter follows:
+%
+% o filter: Image filter to use.
+%
+*/
+MagickExport MagickRealType GetResizeFilterSupport(
+ const ResizeFilter *resize_filter)
+{
+ assert(resize_filter != (ResizeFilter *) NULL);
+ assert(resize_filter->signature == MagickSignature);
+ return(resize_filter->support*resize_filter->blur);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t R e s i z e F i l t e r W e i g h t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetResizeFilterWeight evaluates the specified resize filter at the point x
+% which usally lies between zero and the filters current 'support' and
+% returns the weight of the filter function at that point.
+%
+% The format of the GetResizeFilterWeight method is:
+%
+% MagickRealType GetResizeFilterWeight(const ResizeFilter *resize_filter,
+% const MagickRealType x)
+%
+% A description of each parameter follows:
+%
+% o filter: the filter type.
+%
+% o x: the point.
+%
+*/
+MagickExport MagickRealType GetResizeFilterWeight(
+ const ResizeFilter *resize_filter,const MagickRealType x)
+{
+ MagickRealType
+ blur,
+ scale;
+
+ /*
+ Windowing function - scale the weighting filter by this amount.
+ */
+ assert(resize_filter != (ResizeFilter *) NULL);
+ assert(resize_filter->signature == MagickSignature);
+ blur=fabs(x)/resize_filter->blur; /* X offset with blur scaling */
+ if ((resize_filter->window_support < MagickEpsilon) ||
+ (resize_filter->window == Box))
+ scale=1.0; /* Point/Box Filter -- avoid division by zero */
+ else
+ {
+ scale=resize_filter->scale/resize_filter->window_support;
+ scale=resize_filter->window(blur*scale,resize_filter);
+ }
+ return(scale*resize_filter->filter(blur,resize_filter));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g n i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagnifyImage() is a convenience method that scales an image proportionally
+% to twice its size.
+%
+% The format of the MagnifyImage method is:
+%
+% Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MagnifyImage(const Image *image,ExceptionInfo *exception)
+{
+ Image
+ *magnify_image;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ magnify_image=ResizeImage(image,2*image->columns,2*image->rows,CubicFilter,
+ 1.0,exception);
+ return(magnify_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M i n i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MinifyImage() is a convenience method that scales an image proportionally
+% to half its size.
+%
+% The format of the MinifyImage method is:
+%
+% Image *MinifyImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *MinifyImage(const Image *image,ExceptionInfo *exception)
+{
+ Image
+ *minify_image;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ minify_image=ResizeImage(image,image->columns/2,image->rows/2,CubicFilter,
+ 1.0,exception);
+ return(minify_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s a m p l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResampleImage() resize image in terms of its pixel size, so that when
+% displayed at the given resolution it will be the same size in terms of
+% real world units as the original image at the original resolution.
+%
+% The format of the ResampleImage method is:
+%
+% Image *ResampleImage(Image *image,const double x_resolution,
+% const double y_resolution,const FilterTypes filter,const double blur,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image to be resized to fit the given resolution.
+%
+% o x_resolution: the new image x resolution.
+%
+% o y_resolution: the new image y resolution.
+%
+% o filter: Image filter to use.
+%
+% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
+%
+*/
+MagickExport Image *ResampleImage(const Image *image,const double x_resolution,
+ const double y_resolution,const FilterTypes filter,const double blur,
+ ExceptionInfo *exception)
+{
+#define ResampleImageTag "Resample/Image"
+
+ Image
+ *resample_image;
+
+ unsigned long
+ height,
+ width;
+
+ /*
+ Initialize sampled image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ width=(unsigned long) (x_resolution*image->columns/
+ (image->x_resolution == 0.0 ? 72.0 : image->x_resolution)+0.5);
+ height=(unsigned long) (y_resolution*image->rows/
+ (image->y_resolution == 0.0 ? 72.0 : image->y_resolution)+0.5);
+ resample_image=ResizeImage(image,width,height,filter,blur,exception);
+ if (resample_image != (Image *) NULL)
+ {
+ resample_image->x_resolution=x_resolution;
+ resample_image->y_resolution=y_resolution;
+ }
+ return(resample_image);
+}
+#if defined(MAGICKCORE_LQR_DELEGATE)
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i q u i d R e s c a l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LiquidRescaleImage() rescales image with seam carving.
+%
+% The format of the LiquidRescaleImage method is:
+%
+% Image *LiquidRescaleImage(const Image *image,
+% const unsigned long columns,const unsigned long rows,
+% const double delta_x,const double rigidity,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the rescaled image.
+%
+% o rows: the number of rows in the rescaled image.
+%
+% o delta_x: maximum seam transversal step (0 means straight seams).
+%
+% o rigidity: introduce a bias for non-straight seams (typically 0).
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *LiquidRescaleImage(const Image *image,
+ const unsigned long columns,const unsigned long rows,
+ const double delta_x,const double rigidity,ExceptionInfo *exception)
+{
+#define LiquidRescaleImageTag "Rescale/Image"
+
+ const char
+ *map;
+
+ guchar
+ *packet;
+
+ Image
+ *rescale_image;
+
+ int
+ x,
+ y;
+
+ LqrCarver
+ *carver;
+
+ LqrRetVal
+ lqr_status;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ pixel;
+
+ unsigned char
+ *pixels;
+
+ /*
+ Liquid rescale image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((columns == 0) || (rows == 0))
+ return((Image *) NULL);
+ if ((columns == image->columns) && (rows == image->rows))
+ return(CloneImage(image,0,0,MagickTrue,exception));
+ if ((columns <= 2) || (rows <= 2))
+ return(ZoomImage(image,columns,rows,exception));
+ if ((columns >= (2*image->columns)) || (rows >= (2*image->rows)))
+ {
+ Image
+ *resize_image;
+
+ unsigned long
+ height,
+ width;
+
+ /*
+ Honor liquid resize size limitations.
+ */
+ for (width=image->columns; columns >= (2*width-1); width*=2);
+ for (height=image->rows; rows >= (2*height-1); height*=2);
+ resize_image=ResizeImage(image,width,height,image->filter,image->blur,
+ exception);
+ if (resize_image == (Image *) NULL)
+ return((Image *) NULL);
+ rescale_image=LiquidRescaleImage(resize_image,columns,rows,delta_x,
+ rigidity,exception);
+ resize_image=DestroyImage(resize_image);
+ return(rescale_image);
+ }
+ map="RGB";
+ if (image->matte == MagickFalse)
+ map="RGBA";
+ if (image->colorspace == CMYKColorspace)
+ {
+ map="CMYK";
+ if (image->matte == MagickFalse)
+ map="CMYKA";
+ }
+ pixels=(unsigned char *) AcquireQuantumMemory(image->columns,image->rows*
+ strlen(map)*sizeof(*pixels));
+ if (pixels == (unsigned char *) NULL)
+ return((Image *) NULL);
+ status=ExportImagePixels(image,0,0,image->columns,image->rows,map,CharPixel,
+ pixels,exception);
+ if (status == MagickFalse)
+ {
+ pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ carver=lqr_carver_new(pixels,image->columns,image->rows,strlen(map));
+ if (carver == (LqrCarver *) NULL)
+ {
+ pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ lqr_status=lqr_carver_init(carver,(int) delta_x,rigidity);
+ lqr_status=lqr_carver_resize(carver,columns,rows);
+ rescale_image=CloneImage(image,lqr_carver_get_width(carver),
+ lqr_carver_get_height(carver),MagickTrue,exception);
+ if (rescale_image == (Image *) NULL)
+ {
+ pixels=(unsigned char *) RelinquishMagickMemory(pixels);
+ return((Image *) NULL);
+ }
+ if (SetImageStorageClass(rescale_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&rescale_image->exception);
+ rescale_image=DestroyImage(rescale_image);
+ return((Image *) NULL);
+ }
+ GetMagickPixelPacket(rescale_image,&pixel);
+ (void) lqr_carver_scan_reset(carver);
+ while (lqr_carver_scan(carver,&x,&y,&packet) != 0)
+ {
+ register IndexPacket
+ *__restrict rescale_indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(rescale_image,x,y,1,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ rescale_indexes=GetAuthenticIndexQueue(rescale_image);
+ pixel.red=QuantumRange*(packet[0]/255.0);
+ pixel.green=QuantumRange*(packet[1]/255.0);
+ pixel.blue=QuantumRange*(packet[2]/255.0);
+ if (image->colorspace != CMYKColorspace)
+ {
+ if (image->matte == MagickFalse)
+ pixel.opacity=QuantumRange*(packet[3]/255.0);
+ }
+ else
+ {
+ pixel.index=QuantumRange*(packet[3]/255.0);
+ if (image->matte == MagickFalse)
+ pixel.opacity=QuantumRange*(packet[4]/255.0);
+ }
+ SetPixelPacket(rescale_image,&pixel,q,rescale_indexes);
+ if (SyncAuthenticPixels(rescale_image,exception) == MagickFalse)
+ break;
+ }
+ /*
+ Relinquish resources.
+ */
+ lqr_carver_destroy(carver);
+ return(rescale_image);
+}
+#else
+MagickExport Image *LiquidRescaleImage(const Image *image,
+ const unsigned long magick_unused(columns),
+ const unsigned long magick_unused(rows),const double magick_unused(delta_x),
+ const double magick_unused(rigidity),ExceptionInfo *exception)
+{
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
+ "DelegateLibrarySupportNotBuiltIn","`%s' (LQR)",image->filename);
+ return((Image *) NULL);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResizeImage() scales an image to the desired dimensions, using the given
+% filter (see AcquireFilterInfo() ).
+%
+% If an undefined filter is given the filter defaults to Mitchell for a
+% colormapped image, a image with a matte channel, or if the image is
+% enlarged. Otherwise the filter defaults to a Lanczos.
+%
+% ResizeImage() was inspired by Paul Heckbert's "zoom" program.
+%
+% The format of the ResizeImage method is:
+%
+% Image *ResizeImage(Image *image,const unsigned long columns,
+% const unsigned long rows,const FilterTypes filter,const double blur,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the scaled image.
+%
+% o rows: the number of rows in the scaled image.
+%
+% o filter: Image filter to use.
+%
+% o blur: the blur factor where > 1 is blurry, < 1 is sharp.
+% Typically set this to 1.0.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+typedef struct _ContributionInfo
+{
+ MagickRealType
+ weight;
+
+ long
+ pixel;
+} ContributionInfo;
+
+static ContributionInfo **DestroyContributionThreadSet(
+ ContributionInfo **contribution)
+{
+ register long
+ i;
+
+ assert(contribution != (ContributionInfo **) NULL);
+ for (i=0; i < (long) GetOpenMPMaximumThreads(); i++)
+ if (contribution[i] != (ContributionInfo *) NULL)
+ contribution[i]=(ContributionInfo *) RelinquishMagickMemory(
+ contribution[i]);
+ contribution=(ContributionInfo **) RelinquishAlignedMemory(contribution);
+ return(contribution);
+}
+
+static ContributionInfo **AcquireContributionThreadSet(const size_t count)
+{
+ register long
+ i;
+
+ ContributionInfo
+ **contribution;
+
+ unsigned long
+ number_threads;
+
+ number_threads=GetOpenMPMaximumThreads();
+ contribution=(ContributionInfo **) AcquireAlignedMemory(number_threads,
+ sizeof(*contribution));
+ if (contribution == (ContributionInfo **) NULL)
+ return((ContributionInfo **) NULL);
+ (void) ResetMagickMemory(contribution,0,number_threads*sizeof(*contribution));
+ for (i=0; i < (long) number_threads; i++)
+ {
+ contribution[i]=(ContributionInfo *) AcquireQuantumMemory(count,
+ sizeof(**contribution));
+ if (contribution[i] == (ContributionInfo *) NULL)
+ return(DestroyContributionThreadSet(contribution));
+ }
+ return(contribution);
+}
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static MagickBooleanType HorizontalFilter(const ResizeFilter *resize_filter,
+ const Image *image,Image *resize_image,const MagickRealType x_factor,
+ const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
+{
+#define ResizeImageTag "Resize/Image"
+
+ ClassType
+ storage_class;
+
+ ContributionInfo
+ **contributions;
+
+ long
+ x;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ scale,
+ support;
+
+ CacheView
+ *image_view,
+ *resize_view;
+
+ /*
+ Apply filter to resize horizontally from image to resize image.
+ */
+ scale=MagickMax(1.0/x_factor,1.0);
+ support=scale*GetResizeFilterSupport(resize_filter);
+ storage_class=support > 0.5 ? DirectClass : image->storage_class;
+ if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
+ {
+ InheritException(exception,&resize_image->exception);
+ return(MagickFalse);
+ }
+ if (support < 0.5)
+ {
+ /*
+ Support too small even for nearest neighbour: reduce to point sampling.
+ */
+ support=(MagickRealType) 0.5;
+ scale=1.0;
+ }
+ contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
+ if (contributions == (ContributionInfo **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ status=MagickTrue;
+ scale=1.0/scale;
+ (void) ResetMagickMemory(&zero,0,sizeof(zero));
+ image_view=AcquireCacheView(image);
+ resize_view=AcquireCacheView(resize_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for shared(status)
+#endif
+ for (x=0; x < (long) resize_image->columns; x++)
+ {
+ long
+ n,
+ start,
+ stop;
+
+ MagickRealType
+ center,
+ density;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register ContributionInfo
+ *__restrict contribution;
+
+ register IndexPacket
+ *__restrict resize_indexes;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ center=(MagickRealType) (x+0.5)/x_factor;
+ start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
+ stop=(long) (MagickMin(center+support,(double) image->columns)+0.5);
+ density=0.0;
+ contribution=contributions[GetOpenMPThreadId()];
+ for (n=0; n < (stop-start); n++)
+ {
+ contribution[n].pixel=start+n;
+ contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
+ ((MagickRealType) (start+n)-center+0.5));
+ density+=contribution[n].weight;
+ }
+ if ((density != 0.0) && (density != 1.0))
+ {
+ register long
+ i;
+
+ /*
+ Normalize.
+ */
+ density=1.0/density;
+ for (i=0; i < n; i++)
+ contribution[i].weight*=density;
+ }
+ p=GetCacheViewVirtualPixels(image_view,contribution[0].pixel,0,
+ (unsigned long) (contribution[n-1].pixel-contribution[0].pixel+1),
+ image->rows,exception);
+ q=QueueCacheViewAuthenticPixels(resize_view,x,0,1,resize_image->rows,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
+ for (y=0; y < (long) resize_image->rows; y++)
+ {
+ long
+ j;
+
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ alpha;
+
+ register long
+ i;
+
+ pixel=zero;
+ if (image->matte == MagickFalse)
+ {
+ for (i=0; i < n; i++)
+ {
+ j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
+ (contribution[i].pixel-contribution[0].pixel);
+ alpha=contribution[i].weight;
+ pixel.red+=alpha*(p+j)->red;
+ pixel.green+=alpha*(p+j)->green;
+ pixel.blue+=alpha*(p+j)->blue;
+ pixel.opacity+=alpha*(p+j)->opacity;
+ }
+ q->red=RoundToQuantum(pixel.red);
+ q->green=RoundToQuantum(pixel.green);
+ q->blue=RoundToQuantum(pixel.blue);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ if ((image->colorspace == CMYKColorspace) &&
+ (resize_image->colorspace == CMYKColorspace))
+ {
+ for (i=0; i < n; i++)
+ {
+ j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
+ (contribution[i].pixel-contribution[0].pixel);
+ alpha=contribution[i].weight;
+ pixel.index+=alpha*indexes[j];
+ }
+ resize_indexes[y]=(IndexPacket) RoundToQuantum(pixel.index);
+ }
+ }
+ else
+ {
+ MagickRealType
+ gamma;
+
+ gamma=0.0;
+ for (i=0; i < n; i++)
+ {
+ j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
+ (contribution[i].pixel-contribution[0].pixel);
+ alpha=contribution[i].weight*QuantumScale*((MagickRealType)
+ QuantumRange-(p+j)->opacity);
+ pixel.red+=alpha*(p+j)->red;
+ pixel.green+=alpha*(p+j)->green;
+ pixel.blue+=alpha*(p+j)->blue;
+ pixel.opacity+=contribution[i].weight*(p+j)->opacity;
+ gamma+=alpha;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ q->red=RoundToQuantum(gamma*pixel.red);
+ q->green=RoundToQuantum(gamma*pixel.green);
+ q->blue=RoundToQuantum(gamma*pixel.blue);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ if ((image->colorspace == CMYKColorspace) &&
+ (resize_image->colorspace == CMYKColorspace))
+ {
+ for (i=0; i < n; i++)
+ {
+ j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
+ (contribution[i].pixel-contribution[0].pixel);
+ alpha=contribution[i].weight*QuantumScale*((MagickRealType)
+ QuantumRange-(p+j)->opacity);
+ gamma+=alpha;
+ }
+ resize_indexes[y]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
+ }
+ }
+ if ((resize_image->storage_class == PseudoClass) &&
+ (image->storage_class == PseudoClass))
+ {
+ i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
+ 1.0)+0.5);
+ j=y*(contribution[n-1].pixel-contribution[0].pixel+1)+
+ (contribution[i-start].pixel-contribution[0].pixel);
+ resize_indexes[y]=indexes[j];
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_HorizontalFilter)
+#endif
+ proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ resize_view=DestroyCacheView(resize_view);
+ image_view=DestroyCacheView(image_view);
+ contributions=DestroyContributionThreadSet(contributions);
+ return(status);
+}
+
+static MagickBooleanType VerticalFilter(const ResizeFilter *resize_filter,
+ const Image *image,Image *resize_image,const MagickRealType y_factor,
+ const MagickSizeType span,MagickOffsetType *quantum,ExceptionInfo *exception)
+{
+ ClassType
+ storage_class;
+
+ ContributionInfo
+ **contributions;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ scale,
+ support;
+
+ CacheView
+ *image_view,
+ *resize_view;
+
+ /*
+ Apply filter to resize vertically from image to resize_image.
+ */
+ scale=MagickMax(1.0/y_factor,1.0);
+ support=scale*GetResizeFilterSupport(resize_filter);
+ storage_class=support > 0.5 ? DirectClass : image->storage_class;
+ if (SetImageStorageClass(resize_image,storage_class) == MagickFalse)
+ {
+ InheritException(exception,&resize_image->exception);
+ return(MagickFalse);
+ }
+ if (support < 0.5)
+ {
+ /*
+ Support too small even for nearest neighbour: reduce to point sampling.
+ */
+ support=(MagickRealType) 0.5;
+ scale=1.0;
+ }
+ contributions=AcquireContributionThreadSet((size_t) (2.0*support+3.0));
+ if (contributions == (ContributionInfo **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ status=MagickTrue;
+ scale=1.0/scale;
+ (void) ResetMagickMemory(&zero,0,sizeof(zero));
+ image_view=AcquireCacheView(image);
+ resize_view=AcquireCacheView(resize_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for shared(status)
+#endif
+ for (y=0; y < (long) resize_image->rows; y++)
+ {
+ long
+ n,
+ start,
+ stop;
+
+ MagickRealType
+ center,
+ density;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register ContributionInfo
+ *__restrict contribution;
+
+ register IndexPacket
+ *__restrict resize_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ center=(MagickRealType) (y+0.5)/y_factor;
+ start=(long) (MagickMax(center-support-MagickEpsilon,0.0)+0.5);
+ stop=(long) (MagickMin(center+support,(double) image->rows)+0.5);
+ density=0.0;
+ contribution=contributions[GetOpenMPThreadId()];
+ for (n=0; n < (stop-start); n++)
+ {
+ contribution[n].pixel=start+n;
+ contribution[n].weight=GetResizeFilterWeight(resize_filter,scale*
+ ((MagickRealType) (start+n)-center+0.5));
+ density+=contribution[n].weight;
+ }
+ if ((density != 0.0) && (density != 1.0))
+ {
+ register long
+ i;
+
+ /*
+ Normalize.
+ */
+ density=1.0/density;
+ for (i=0; i < n; i++)
+ contribution[i].weight*=density;
+ }
+ p=GetCacheViewVirtualPixels(image_view,0,contribution[0].pixel,
+ image->columns,(unsigned long) (contribution[n-1].pixel-
+ contribution[0].pixel+1),exception);
+ q=QueueCacheViewAuthenticPixels(resize_view,0,y,resize_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ resize_indexes=GetCacheViewAuthenticIndexQueue(resize_view);
+ for (x=0; x < (long) resize_image->columns; x++)
+ {
+ long
+ j;
+
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ alpha;
+
+ register long
+ i;
+
+ pixel=zero;
+ if (image->matte == MagickFalse)
+ {
+ for (i=0; i < n; i++)
+ {
+ j=(long) ((contribution[i].pixel-contribution[0].pixel)*
+ image->columns+x);
+ alpha=contribution[i].weight;
+ pixel.red+=alpha*(p+j)->red;
+ pixel.green+=alpha*(p+j)->green;
+ pixel.blue+=alpha*(p+j)->blue;
+ pixel.opacity+=alpha*(p+j)->opacity;
+ }
+ q->red=RoundToQuantum(pixel.red);
+ q->green=RoundToQuantum(pixel.green);
+ q->blue=RoundToQuantum(pixel.blue);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ if ((image->colorspace == CMYKColorspace) &&
+ (resize_image->colorspace == CMYKColorspace))
+ {
+ for (i=0; i < n; i++)
+ {
+ j=(long) ((contribution[i].pixel-contribution[0].pixel)*
+ image->columns+x);
+ alpha=contribution[i].weight;
+ pixel.index+=alpha*indexes[j];
+ }
+ resize_indexes[x]=(IndexPacket) RoundToQuantum(pixel.index);
+ }
+ }
+ else
+ {
+ MagickRealType
+ gamma;
+
+ gamma=0.0;
+ for (i=0; i < n; i++)
+ {
+ j=(long) ((contribution[i].pixel-contribution[0].pixel)*
+ image->columns+x);
+ alpha=contribution[i].weight*QuantumScale*((MagickRealType)
+ QuantumRange-(p+j)->opacity);
+ pixel.red+=alpha*(p+j)->red;
+ pixel.green+=alpha*(p+j)->green;
+ pixel.blue+=alpha*(p+j)->blue;
+ pixel.opacity+=contribution[i].weight*(p+j)->opacity;
+ gamma+=alpha;
+ }
+ gamma=1.0/(fabs((double) gamma) <= MagickEpsilon ? 1.0 : gamma);
+ q->red=RoundToQuantum(gamma*pixel.red);
+ q->green=RoundToQuantum(gamma*pixel.green);
+ q->blue=RoundToQuantum(gamma*pixel.blue);
+ q->opacity=RoundToQuantum(pixel.opacity);
+ if ((image->colorspace == CMYKColorspace) &&
+ (resize_image->colorspace == CMYKColorspace))
+ {
+ for (i=0; i < n; i++)
+ {
+ j=(long) ((contribution[i].pixel-contribution[0].pixel)*
+ image->columns+x);
+ alpha=contribution[i].weight*QuantumScale*((MagickRealType)
+ QuantumRange-(p+j)->opacity);
+ pixel.index+=alpha*indexes[j];
+ }
+ resize_indexes[x]=(IndexPacket) RoundToQuantum(gamma*pixel.index);
+ }
+ }
+ if ((resize_image->storage_class == PseudoClass) &&
+ (image->storage_class == PseudoClass))
+ {
+ i=(long) (MagickMin(MagickMax(center,(double) start),(double) stop-
+ 1.0)+0.5);
+ j=(long) ((contribution[i-start].pixel-contribution[0].pixel)*
+ image->columns+x);
+ resize_indexes[x]=indexes[j];
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(resize_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_VerticalFilter)
+#endif
+ proceed=SetImageProgress(image,ResizeImageTag,(*quantum)++,span);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ resize_view=DestroyCacheView(resize_view);
+ image_view=DestroyCacheView(image_view);
+ contributions=DestroyContributionThreadSet(contributions);
+ return(status);
+}
+
+MagickExport Image *ResizeImage(const Image *image,const unsigned long columns,
+ const unsigned long rows,const FilterTypes filter,const double blur,
+ ExceptionInfo *exception)
+{
+#define WorkLoadFactor 0.265
+
+ FilterTypes
+ filter_type;
+
+ Image
+ *filter_image,
+ *resize_image;
+
+ MagickRealType
+ x_factor,
+ y_factor;
+
+ MagickSizeType
+ span;
+
+ MagickStatusType
+ status;
+
+ ResizeFilter
+ *resize_filter;
+
+ MagickOffsetType
+ quantum;
+
+ /*
+ Acquire resize image.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((columns == 0) || (rows == 0))
+ ThrowImageException(ImageError,"NegativeOrZeroImageSize");
+ if ((columns == image->columns) && (rows == image->rows) &&
+ (filter == UndefinedFilter) && (blur == 1.0))
+ return(CloneImage(image,0,0,MagickTrue,exception));
+ resize_image=CloneImage(image,columns,rows,MagickTrue,exception);
+ if (resize_image == (Image *) NULL)
+ return(resize_image);
+ /*
+ Acquire resize filter.
+ */
+ x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
+ y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
+ if ((x_factor*y_factor) > WorkLoadFactor)
+ filter_image=CloneImage(image,columns,image->rows,MagickTrue,exception);
+ else
+ filter_image=CloneImage(image,image->columns,rows,MagickTrue,exception);
+ if (filter_image == (Image *) NULL)
+ return(DestroyImage(resize_image));
+ filter_type=LanczosFilter;
+ if (filter != UndefinedFilter)
+ filter_type=filter;
+ else
+ if ((x_factor == 1.0) && (y_factor == 1.0))
+ filter_type=PointFilter;
+ else
+ if ((image->storage_class == PseudoClass) ||
+ (image->matte != MagickFalse) || ((x_factor*y_factor) > 1.0))
+ filter_type=MitchellFilter;
+ resize_filter=AcquireResizeFilter(image,filter_type,blur,MagickFalse,
+ exception);
+ /*
+ Resize image.
+ */
+ quantum=0;
+ if ((x_factor*y_factor) > WorkLoadFactor)
+ {
+ span=(MagickSizeType) (filter_image->columns+rows);
+ status=HorizontalFilter(resize_filter,image,filter_image,x_factor,span,
+ &quantum,exception);
+ status&=VerticalFilter(resize_filter,filter_image,resize_image,y_factor,
+ span,&quantum,exception);
+ }
+ else
+ {
+ span=(MagickSizeType) (filter_image->rows+columns);
+ status=VerticalFilter(resize_filter,image,filter_image,y_factor,span,
+ &quantum,exception);
+ status&=HorizontalFilter(resize_filter,filter_image,resize_image,x_factor,
+ span,&quantum,exception);
+ }
+ /*
+ Free resources.
+ */
+ filter_image=DestroyImage(filter_image);
+ resize_filter=DestroyResizeFilter(resize_filter);
+ if ((status == MagickFalse) || (resize_image == (Image *) NULL))
+ return((Image *) NULL);
+ resize_image->type=image->type;
+ return(resize_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S a m p l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SampleImage() scales an image to the desired dimensions with pixel
+% sampling. Unlike other scaling methods, this method does not introduce
+% any additional color into the scaled image.
+%
+% The format of the SampleImage method is:
+%
+% Image *SampleImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the sampled image.
+%
+% o rows: the number of rows in the sampled image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SampleImage(const Image *image,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+#define SampleImageTag "Sample/Image"
+
+ Image
+ *sample_image;
+
+ long
+ progress,
+ *x_offset,
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ x;
+
+ CacheView
+ *image_view,
+ *sample_view;
+
+ /*
+ Initialize sampled image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((columns == 0) || (rows == 0))
+ ThrowImageException(ImageError,"NegativeOrZeroImageSize");
+ if ((columns == image->columns) && (rows == image->rows))
+ return(CloneImage(image,0,0,MagickTrue,exception));
+ sample_image=CloneImage(image,columns,rows,MagickTrue,exception);
+ if (sample_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Allocate scan line buffer and column offset buffers.
+ */
+ x_offset=(long *) AcquireQuantumMemory((size_t) sample_image->columns,
+ sizeof(*x_offset));
+ if (x_offset == (long *) NULL)
+ {
+ sample_image=DestroyImage(sample_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ for (x=0; x < (long) sample_image->columns; x++)
+ x_offset[x]=(long) (((MagickRealType) x+0.5)*image->columns/
+ sample_image->columns);
+ /*
+ Sample each row.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ sample_view=AcquireCacheView(sample_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic) shared(progress,status)
+#endif
+ for (y=0; y < (long) sample_image->rows; y++)
+ {
+ long
+ y_offset;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict sample_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ y_offset=(long) (((MagickRealType) y+0.5)*image->rows/sample_image->rows);
+ p=GetCacheViewVirtualPixels(image_view,0,y_offset,image->columns,1,
+ exception);
+ q=QueueCacheViewAuthenticPixels(sample_view,0,y,sample_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ sample_indexes=GetCacheViewAuthenticIndexQueue(sample_view);
+ /*
+ Sample each column.
+ */
+ for (x=0; x < (long) sample_image->columns; x++)
+ *q++=p[x_offset[x]];
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ for (x=0; x < (long) sample_image->columns; x++)
+ sample_indexes[x]=indexes[x_offset[x]];
+ if (SyncCacheViewAuthenticPixels(sample_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_SampleImage)
+#endif
+ proceed=SetImageProgress(image,SampleImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ sample_view=DestroyCacheView(sample_view);
+ x_offset=(long *) RelinquishMagickMemory(x_offset);
+ sample_image->type=image->type;
+ return(sample_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S c a l e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ScaleImage() changes the size of an image to the given dimensions.
+%
+% The format of the ScaleImage method is:
+%
+% Image *ScaleImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the scaled image.
+%
+% o rows: the number of rows in the scaled image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ScaleImage(const Image *image,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+#define ScaleImageTag "Scale/Image"
+
+ Image
+ *scale_image;
+
+ long
+ number_rows,
+ y;
+
+ MagickBooleanType
+ next_column,
+ next_row,
+ proceed;
+
+ MagickPixelPacket
+ pixel,
+ *scale_scanline,
+ *scanline,
+ *x_vector,
+ *y_vector,
+ zero;
+
+ MagickRealType
+ alpha,
+ gamma;
+
+ PointInfo
+ scale,
+ span;
+
+ register long
+ i;
+
+ /*
+ Initialize scaled image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((columns == 0) || (rows == 0))
+ return((Image *) NULL);
+ if ((columns == image->columns) && (rows == image->rows))
+ return(CloneImage(image,0,0,MagickTrue,exception));
+ scale_image=CloneImage(image,columns,rows,MagickTrue,exception);
+ if (scale_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(scale_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&scale_image->exception);
+ scale_image=DestroyImage(scale_image);
+ return((Image *) NULL);
+ }
+ /*
+ Allocate memory.
+ */
+ x_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
+ sizeof(*x_vector));
+ scanline=x_vector;
+ if (image->rows != scale_image->rows)
+ scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
+ sizeof(*scanline));
+ scale_scanline=(MagickPixelPacket *) AcquireQuantumMemory((size_t)
+ scale_image->columns,sizeof(*scale_scanline));
+ y_vector=(MagickPixelPacket *) AcquireQuantumMemory((size_t) image->columns,
+ sizeof(*y_vector));
+ if ((scanline == (MagickPixelPacket *) NULL) ||
+ (scale_scanline == (MagickPixelPacket *) NULL) ||
+ (x_vector == (MagickPixelPacket *) NULL) ||
+ (y_vector == (MagickPixelPacket *) NULL))
+ {
+ scale_image=DestroyImage(scale_image);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ /*
+ Scale image.
+ */
+ number_rows=0;
+ next_row=MagickTrue;
+ span.y=1.0;
+ scale.y=(double) scale_image->rows/(double) image->rows;
+ (void) ResetMagickMemory(y_vector,0,(size_t) image->columns*
+ sizeof(*y_vector));
+ GetMagickPixelPacket(image,&pixel);
+ (void) ResetMagickMemory(&zero,0,sizeof(zero));
+ i=0;
+ for (y=0; y < (long) scale_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict scale_indexes;
+
+ register long
+ x;
+
+ register MagickPixelPacket
+ *__restrict s,
+ *__restrict t;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(scale_image,0,y,scale_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ scale_indexes=GetAuthenticIndexQueue(scale_image);
+ if (scale_image->rows == image->rows)
+ {
+ /*
+ Read a new scanline.
+ */
+ p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ x_vector[x].red=(MagickRealType) p->red;
+ x_vector[x].green=(MagickRealType) p->green;
+ x_vector[x].blue=(MagickRealType) p->blue;
+ if (image->matte != MagickFalse)
+ x_vector[x].opacity=(MagickRealType) p->opacity;
+ if (indexes != (IndexPacket *) NULL)
+ x_vector[x].index=(MagickRealType) indexes[x];
+ p++;
+ }
+ }
+ else
+ {
+ /*
+ Scale Y direction.
+ */
+ while (scale.y < span.y)
+ {
+ if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
+ {
+ /*
+ Read a new scanline.
+ */
+ p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ x_vector[x].red=(MagickRealType) p->red;
+ x_vector[x].green=(MagickRealType) p->green;
+ x_vector[x].blue=(MagickRealType) p->blue;
+ if (image->matte != MagickFalse)
+ x_vector[x].opacity=(MagickRealType) p->opacity;
+ if (indexes != (IndexPacket *) NULL)
+ x_vector[x].index=(MagickRealType) indexes[x];
+ p++;
+ }
+ number_rows++;
+ }
+ for (x=0; x < (long) image->columns; x++)
+ {
+ y_vector[x].red+=scale.y*x_vector[x].red;
+ y_vector[x].green+=scale.y*x_vector[x].green;
+ y_vector[x].blue+=scale.y*x_vector[x].blue;
+ if (scale_image->matte != MagickFalse)
+ y_vector[x].opacity+=scale.y*x_vector[x].opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ y_vector[x].index+=scale.y*x_vector[x].index;
+ }
+ span.y-=scale.y;
+ scale.y=(double) scale_image->rows/(double) image->rows;
+ next_row=MagickTrue;
+ }
+ if ((next_row != MagickFalse) && (number_rows < (long) image->rows))
+ {
+ /*
+ Read a new scanline.
+ */
+ p=GetVirtualPixels(image,0,i++,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ x_vector[x].red=(MagickRealType) p->red;
+ x_vector[x].green=(MagickRealType) p->green;
+ x_vector[x].blue=(MagickRealType) p->blue;
+ if (image->matte != MagickFalse)
+ x_vector[x].opacity=(MagickRealType) p->opacity;
+ if (indexes != (IndexPacket *) NULL)
+ x_vector[x].index=(MagickRealType) indexes[x];
+ p++;
+ }
+ number_rows++;
+ next_row=MagickFalse;
+ }
+ s=scanline;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ pixel.red=y_vector[x].red+span.y*x_vector[x].red;
+ pixel.green=y_vector[x].green+span.y*x_vector[x].green;
+ pixel.blue=y_vector[x].blue+span.y*x_vector[x].blue;
+ if (image->matte != MagickFalse)
+ pixel.opacity=y_vector[x].opacity+span.y*x_vector[x].opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ pixel.index=y_vector[x].index+span.y*x_vector[x].index;
+ s->red=pixel.red;
+ s->green=pixel.green;
+ s->blue=pixel.blue;
+ if (scale_image->matte != MagickFalse)
+ s->opacity=pixel.opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ s->index=pixel.index;
+ s++;
+ y_vector[x]=zero;
+ }
+ scale.y-=span.y;
+ if (scale.y <= 0)
+ {
+ scale.y=(double) scale_image->rows/(double) image->rows;
+ next_row=MagickTrue;
+ }
+ span.y=1.0;
+ }
+ if (scale_image->columns == image->columns)
+ {
+ /*
+ Transfer scanline to scaled image.
+ */
+ s=scanline;
+ for (x=0; x < (long) scale_image->columns; x++)
+ {
+ q->red=RoundToQuantum(s->red);
+ q->green=RoundToQuantum(s->green);
+ q->blue=RoundToQuantum(s->blue);
+ if (scale_image->matte != MagickFalse)
+ q->opacity=RoundToQuantum(s->opacity);
+ if (scale_indexes != (IndexPacket *) NULL)
+ scale_indexes[x]=(IndexPacket) RoundToQuantum(s->index);
+ q++;
+ s++;
+ }
+ }
+ else
+ {
+ /*
+ Scale X direction.
+ */
+ pixel=zero;
+ next_column=MagickFalse;
+ span.x=1.0;
+ s=scanline;
+ t=scale_scanline;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ scale.x=(double) scale_image->columns/(double) image->columns;
+ while (scale.x >= span.x)
+ {
+ if (next_column != MagickFalse)
+ {
+ pixel=zero;
+ t++;
+ }
+ pixel.red+=span.x*s->red;
+ pixel.green+=span.x*s->green;
+ pixel.blue+=span.x*s->blue;
+ if (image->matte != MagickFalse)
+ pixel.opacity+=span.x*s->opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ pixel.index+=span.x*s->index;
+ t->red=pixel.red;
+ t->green=pixel.green;
+ t->blue=pixel.blue;
+ if (scale_image->matte != MagickFalse)
+ t->opacity=pixel.opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ t->index=pixel.index;
+ scale.x-=span.x;
+ span.x=1.0;
+ next_column=MagickTrue;
+ }
+ if (scale.x > 0)
+ {
+ if (next_column != MagickFalse)
+ {
+ pixel=zero;
+ next_column=MagickFalse;
+ t++;
+ }
+ pixel.red+=scale.x*s->red;
+ pixel.green+=scale.x*s->green;
+ pixel.blue+=scale.x*s->blue;
+ if (scale_image->matte != MagickFalse)
+ pixel.opacity+=scale.x*s->opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ pixel.index+=scale.x*s->index;
+ span.x-=scale.x;
+ }
+ s++;
+ }
+ if (span.x > 0)
+ {
+ s--;
+ pixel.red+=span.x*s->red;
+ pixel.green+=span.x*s->green;
+ pixel.blue+=span.x*s->blue;
+ if (scale_image->matte != MagickFalse)
+ pixel.opacity+=span.x*s->opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ pixel.index+=span.x*s->index;
+ }
+ if ((next_column == MagickFalse) &&
+ ((long) (t-scale_scanline) < (long) scale_image->columns))
+ {
+ t->red=pixel.red;
+ t->green=pixel.green;
+ t->blue=pixel.blue;
+ if (scale_image->matte != MagickFalse)
+ t->opacity=pixel.opacity;
+ if (scale_indexes != (IndexPacket *) NULL)
+ t->index=pixel.index;
+ }
+ /*
+ Transfer scanline to scaled image.
+ */
+ t=scale_scanline;
+ for (x=0; x < (long) scale_image->columns; x++)
+ {
+ alpha=1.0;
+ if (image->matte != MagickFalse)
+ alpha=(MagickRealType) (QuantumScale*(QuantumRange-t->opacity));
+ gamma=1.0/(fabs((double) alpha) <= MagickEpsilon ? 1.0 : alpha);
+ q->red=RoundToQuantum(gamma*t->red);
+ q->green=RoundToQuantum(gamma*t->green);
+ q->blue=RoundToQuantum(gamma*t->blue);
+ if (scale_image->matte != MagickFalse)
+ q->opacity=RoundToQuantum(t->opacity);
+ if (scale_indexes != (IndexPacket *) NULL)
+ scale_indexes[x]=(IndexPacket) RoundToQuantum(gamma*t->index);
+ t++;
+ q++;
+ }
+ }
+ if (SyncAuthenticPixels(scale_image,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,ScaleImageTag,y,image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ /*
+ Free allocated memory.
+ */
+ y_vector=(MagickPixelPacket *) RelinquishMagickMemory(y_vector);
+ scale_scanline=(MagickPixelPacket *) RelinquishMagickMemory(scale_scanline);
+ if (scale_image->rows != image->rows)
+ scanline=(MagickPixelPacket *) RelinquishMagickMemory(scanline);
+ x_vector=(MagickPixelPacket *) RelinquishMagickMemory(x_vector);
+ scale_image->type=image->type;
+ return(scale_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t R e s i z e F i l t e r S u p p o r t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetResizeFilterSupport() specifies which IR filter to use to window
+%
+% The format of the SetResizeFilterSupport method is:
+%
+% void SetResizeFilterSupport(ResizeFilter *resize_filter,
+% const MagickRealType support)
+%
+% A description of each parameter follows:
+%
+% o resize_filter: the resize filter.
+%
+% o support: the filter spport radius.
+%
+*/
+MagickExport void SetResizeFilterSupport(ResizeFilter *resize_filter,
+ const MagickRealType support)
+{
+ assert(resize_filter != (ResizeFilter *) NULL);
+ assert(resize_filter->signature == MagickSignature);
+ resize_filter->support=support;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T h u m b n a i l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ThumbnailImage() changes the size of an image to the given dimensions and
+% removes any associated profiles. The goal is to produce small low cost
+% thumbnail images suited for display on the Web.
+%
+% The format of the ThumbnailImage method is:
+%
+% Image *ThumbnailImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: the number of columns in the scaled image.
+%
+% o rows: the number of rows in the scaled image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ThumbnailImage(const Image *image,
+ const unsigned long columns,const unsigned long rows,ExceptionInfo *exception)
+{
+#define SampleFactor 5
+
+ char
+ value[MaxTextExtent];
+
+ const char
+ *name;
+
+ Image
+ *thumbnail_image;
+
+ MagickRealType
+ x_factor,
+ y_factor;
+
+ struct stat
+ attributes;
+
+ unsigned long
+ version;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ x_factor=(MagickRealType) columns/(MagickRealType) image->columns;
+ y_factor=(MagickRealType) rows/(MagickRealType) image->rows;
+ if ((x_factor*y_factor) > 0.1)
+ thumbnail_image=ZoomImage(image,columns,rows,exception);
+ else
+ if (((SampleFactor*columns) < 128) || ((SampleFactor*rows) < 128))
+ thumbnail_image=ZoomImage(image,columns,rows,exception);
+ else
+ {
+ Image
+ *sample_image;
+
+ sample_image=SampleImage(image,SampleFactor*columns,SampleFactor*rows,
+ exception);
+ if (sample_image == (Image *) NULL)
+ return((Image *) NULL);
+ thumbnail_image=ZoomImage(sample_image,columns,rows,exception);
+ sample_image=DestroyImage(sample_image);
+ }
+ if (thumbnail_image == (Image *) NULL)
+ return(thumbnail_image);
+ (void) ParseAbsoluteGeometry("0x0+0+0",&thumbnail_image->page);
+ if (thumbnail_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(thumbnail_image,OpaqueAlphaChannel);
+ thumbnail_image->depth=8;
+ thumbnail_image->interlace=NoInterlace;
+ /*
+ Strip all profiles except color profiles.
+ */
+ ResetImageProfileIterator(thumbnail_image);
+ for (name=GetNextImageProfile(thumbnail_image); name != (const char *) NULL; )
+ {
+ if ((LocaleCompare(name,"icc") != 0) && (LocaleCompare(name,"icm") != 0))
+ {
+ DeleteImageProfile(thumbnail_image,name);
+ ResetImageProfileIterator(thumbnail_image);
+ }
+ name=GetNextImageProfile(thumbnail_image);
+ }
+ (void) DeleteImageProperty(thumbnail_image,"comment");
+ (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
+ if (strstr(image->magick_filename,"///") == (char *) NULL)
+ (void) FormatMagickString(value,MaxTextExtent,"file:///%s",
+ image->magick_filename);
+ (void) SetImageProperty(thumbnail_image,"Thumb::URI",value);
+ (void) CopyMagickString(value,image->magick_filename,MaxTextExtent);
+ if (GetPathAttributes(image->filename,&attributes) != MagickFalse)
+ {
+ (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
+ attributes.st_mtime);
+ (void) SetImageProperty(thumbnail_image,"Thumb::MTime",value);
+ }
+ (void) FormatMagickString(value,MaxTextExtent,"%ld",(long)
+ attributes.st_mtime);
+ (void) FormatMagickSize(GetBlobSize(image),value);
+ (void) SetImageProperty(thumbnail_image,"Thumb::Size",value);
+ (void) FormatMagickString(value,MaxTextExtent,"image/%s",image->magick);
+ LocaleLower(value);
+ (void) SetImageProperty(thumbnail_image,"Thumb::Mimetype",value);
+ (void) SetImageProperty(thumbnail_image,"software",
+ GetMagickVersion(&version));
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_columns);
+ (void) SetImageProperty(thumbnail_image,"Thumb::Image::Width",value);
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",image->magick_rows);
+ (void) SetImageProperty(thumbnail_image,"Thumb::Image::height",value);
+ (void) FormatMagickString(value,MaxTextExtent,"%lu",
+ GetImageListLength(image));
+ (void) SetImageProperty(thumbnail_image,"Thumb::Document::Pages",value);
+ return(thumbnail_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% Z o o m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ZoomImage() creates a new image that is a scaled size of an existing one.
+% It allocates the memory necessary for the new Image structure and returns a
+% pointer to the new image. The Point filter gives fast pixel replication,
+% Triangle is equivalent to bi-linear interpolation, and Mitchel giver slower,
+% very high-quality results. See Graphic Gems III for details on this
+% algorithm.
+%
+% The filter member of the Image structure specifies which image filter to
+% use. Blur specifies the blur factor where > 1 is blurry, < 1 is sharp.
+%
+% The format of the ZoomImage method is:
+%
+% Image *ZoomImage(const Image *image,const unsigned long columns,
+% const unsigned long rows,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o columns: An integer that specifies the number of columns in the zoom
+% image.
+%
+% o rows: An integer that specifies the number of rows in the scaled
+% image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ZoomImage(const Image *image,const unsigned long columns,
+ const unsigned long rows,ExceptionInfo *exception)
+{
+ Image
+ *zoom_image;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ zoom_image=ResizeImage(image,columns,rows,image->filter,image->blur,
+ exception);
+ return(zoom_image);
+}
diff --git a/magick/resize.h b/magick/resize.h
new file mode 100644
index 0000000..b0eb78e
--- /dev/null
+++ b/magick/resize.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image resize methods.
+*/
+#ifndef _MAGICKCORE_RESIZE_H
+#define _MAGICKCORE_RESIZE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport Image
+ *AdaptiveResizeImage(const Image *,const unsigned long,const unsigned long,
+ ExceptionInfo *),
+ *LiquidRescaleImage(const Image *,const unsigned long,const unsigned long,
+ const double,const double,ExceptionInfo *),
+ *MagnifyImage(const Image *,ExceptionInfo *),
+ *MinifyImage(const Image *,ExceptionInfo *),
+ *ResampleImage(const Image *,const double,const double,const FilterTypes,
+ const double,ExceptionInfo *),
+ *ResizeImage(const Image *,const unsigned long,const unsigned long,
+ const FilterTypes,const double,ExceptionInfo *),
+ *SampleImage(const Image *,const unsigned long,const unsigned long,
+ ExceptionInfo *),
+ *ScaleImage(const Image *,const unsigned long,const unsigned long,
+ ExceptionInfo *),
+ *ThumbnailImage(const Image *,const unsigned long,const unsigned long,
+ ExceptionInfo *),
+ *ZoomImage(const Image *,const unsigned long,const unsigned long,
+ ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/resource.c b/magick/resource.c
new file mode 100644
index 0000000..5b45c62
--- /dev/null
+++ b/magick/resource.c
@@ -0,0 +1,1085 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% RRRR EEEEE SSSSS OOO U U RRRR CCCC EEEEE %
+% R R E SS O O U U R R C E %
+% RRRR EEE SSS O O U U RRRR C EEE %
+% R R E SS O O U U R R C E %
+% R R EEEEE SSSSS OOO UUU R R CCCC EEEEE %
+% %
+% %
+% Get/Set MagickCore Resources %
+% %
+% Software Design %
+% John Cristy %
+% September 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/log.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/policy.h"
+#include "magick/random_.h"
+#include "magick/registry.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+#include "magick/splay-tree.h"
+#include "magick/thread-private.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+
+/*
+ Typedef declarations.
+*/
+typedef struct _ResourceInfo
+{
+ MagickOffsetType
+ area,
+ memory,
+ map,
+ disk,
+ file,
+ thread,
+ time;
+
+ MagickSizeType
+ area_limit,
+ memory_limit,
+ map_limit,
+ disk_limit,
+ file_limit,
+ thread_limit,
+ time_limit;
+} ResourceInfo;
+
+/*
+ Global declarations.
+*/
+static RandomInfo
+ *random_info = (RandomInfo *) NULL;
+
+static ResourceInfo
+ resource_info =
+ {
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(0),
+ MagickULLConstant(2048)*1024*1024,
+ MagickULLConstant(1536)*1024*1024,
+ MagickULLConstant(8192)*1024*1024,
+ MagickResourceInfinity,
+ MagickULLConstant(768),
+ MagickULLConstant(8),
+ MagickResourceInfinity
+ };
+
+static SemaphoreInfo
+ *resource_semaphore = (SemaphoreInfo *) NULL;
+
+static SplayTreeInfo
+ *temporary_resources = (SplayTreeInfo *) NULL;
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e M a g i c k R e s o u r c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireMagickResource() acquires resources of the specified type.
+% MagickFalse is returned if the specified resource is exhausted otherwise
+% MagickTrue.
+%
+% The format of the AcquireMagickResource() method is:
+%
+% MagickBooleanType AcquireMagickResource(const ResourceType type,
+% const MagickSizeType size)
+%
+% A description of each parameter follows:
+%
+% o type: the type of resource.
+%
+% o size: the number of bytes needed from for this resource.
+%
+*/
+MagickExport MagickBooleanType AcquireMagickResource(const ResourceType type,
+ const MagickSizeType size)
+{
+ char
+ resource_current[MaxTextExtent],
+ resource_limit[MaxTextExtent],
+ resource_request[MaxTextExtent];
+
+ MagickBooleanType
+ status;
+
+ MagickSizeType
+ limit;
+
+ status=MagickFalse;
+ (void) FormatMagickSize(size,resource_request);
+ AcquireSemaphoreInfo(&resource_semaphore);
+ switch (type)
+ {
+ case AreaResource:
+ {
+ resource_info.area=(MagickOffsetType) size;
+ limit=resource_info.area_limit;
+ status=(resource_info.area_limit == MagickResourceInfinity) ||
+ (size < limit) ? MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.area,
+ resource_current);
+ (void) FormatMagickSize(resource_info.area_limit,resource_limit);
+ break;
+ }
+ case MemoryResource:
+ {
+ resource_info.memory+=size;
+ limit=resource_info.memory_limit;
+ status=(resource_info.memory_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.memory < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.memory,
+ resource_current);
+ (void) FormatMagickSize(resource_info.memory_limit,
+ resource_limit);
+ break;
+ }
+ case MapResource:
+ {
+ resource_info.map+=size;
+ limit=resource_info.map_limit;
+ status=(resource_info.map_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.map < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.map,
+ resource_current);
+ (void) FormatMagickSize(resource_info.map_limit,
+ resource_limit);
+ break;
+ }
+ case DiskResource:
+ {
+ resource_info.disk+=size;
+ limit=resource_info.disk_limit;
+ status=(resource_info.disk_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.disk < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.disk,
+ resource_current);
+ (void) FormatMagickSize(resource_info.disk_limit,resource_limit);
+ break;
+ }
+ case FileResource:
+ {
+ resource_info.file+=size;
+ limit=resource_info.file_limit;
+ status=(resource_info.file_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.file < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.file,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.file_limit,
+ resource_limit);
+ break;
+ }
+ case ThreadResource:
+ {
+ resource_info.thread+=size;
+ limit=resource_info.thread_limit;
+ status=(resource_info.thread_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.thread < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.thread,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.thread_limit,
+ resource_limit);
+ break;
+ }
+ case TimeResource:
+ {
+ resource_info.time+=size;
+ limit=resource_info.time_limit;
+ status=(resource_info.time_limit == MagickResourceInfinity) ||
+ ((MagickSizeType) resource_info.time < limit) ?
+ MagickTrue : MagickFalse;
+ (void) FormatMagickSize((MagickSizeType) resource_info.time,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.time_limit,
+ resource_limit);
+ break;
+ }
+ default:
+ break;
+ }
+ RelinquishSemaphoreInfo(resource_semaphore);
+ (void) LogMagickEvent(ResourceEvent,GetMagickModule(),"%s: %s/%s/%s",
+ MagickOptionToMnemonic(MagickResourceOptions,(long) type),resource_request,
+ resource_current,resource_limit);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A s y n c h r o n o u s D e s t r o y M a g i c k R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AsynchronousDestroyMagickResources() destroys the resource environment.
+% It differs from DestroyMagickResources() in that it can be called from a
+% asynchronous signal handler.
+%
+% The format of the DestroyMagickResources() method is:
+%
+% DestroyMagickResources(void)
+%
+*/
+MagickExport void AsynchronousDestroyMagickResources(void)
+{
+ const char
+ *path;
+
+ if (temporary_resources == (SplayTreeInfo *) NULL)
+ return;
+ /*
+ Remove any lingering temporary files.
+ */
+ ResetSplayTreeIterator(temporary_resources);
+ path=(const char *) GetNextKeyInSplayTree(temporary_resources);
+ while (path != (const char *) NULL)
+ {
+ (void) remove(path);
+ path=(const char *) GetNextKeyInSplayTree(temporary_resources);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e U n i q u e F i l e R e s o u r c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireUniqueFileResource() returns a unique file name, and returns a file
+% descriptor for the file open for reading and writing.
+%
+% The format of the AcquireUniqueFileResource() method is:
+%
+% int AcquireUniqueFileResource(char *path)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies a pointer to an array of characters. The unique path
+% name is returned in this array.
+%
+*/
+
+static void *DestroyTemporaryResources(void *temporary_resource)
+{
+ (void) remove((char *) temporary_resource);
+ return((void *) NULL);
+}
+
+static MagickBooleanType GetPathTemplate(char *path)
+{
+ char
+ *directory;
+
+ ExceptionInfo
+ *exception;
+
+ MagickBooleanType
+ status;
+
+ register char
+ *p;
+
+ struct stat
+ attributes;
+
+ (void) CopyMagickString(path,"magick-XXXXXXXX",MaxTextExtent);
+ exception=AcquireExceptionInfo();
+ directory=(char *) GetImageRegistry(StringRegistryType,"temporary-path",
+ exception);
+ exception=DestroyExceptionInfo(exception);
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("MAGICK_TEMPORARY_PATH");
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("MAGICK_TMPDIR");
+ if (directory == (char *) NULL)
+ directory=GetPolicyValue("temporary-path");
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("TMPDIR");
+#if defined(__WINDOWS__) || defined(__OS2__)
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("TMP");
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("TEMP");
+#endif
+#if defined(__VMS)
+ if (directory == (char *) NULL)
+ directory=GetEnvironmentValue("MTMPDIR");
+#endif
+#if defined(P_tmpdir)
+ if (directory == (char *) NULL)
+ directory=ConstantString(P_tmpdir);
+#endif
+ if (directory == (char *) NULL)
+ return(MagickTrue);
+ if (strlen(directory) > (MaxTextExtent-15))
+ {
+ directory=DestroyString(directory);
+ return(MagickTrue);
+ }
+ status=GetPathAttributes(directory,&attributes);
+ if ((status == MagickFalse) || !S_ISDIR(attributes.st_mode))
+ {
+ directory=DestroyString(directory);
+ return(MagickTrue);
+ }
+ if (directory[strlen(directory)-1] == *DirectorySeparator)
+ (void) FormatMagickString(path,MaxTextExtent,"%smagick-XXXXXXXX",directory);
+ else
+ (void) FormatMagickString(path,MaxTextExtent,"%s%smagick-XXXXXXXX",
+ directory,DirectorySeparator);
+ directory=DestroyString(directory);
+ if (*DirectorySeparator != '/')
+ for (p=path; *p != '\0'; p++)
+ if (*p == *DirectorySeparator)
+ *p='/';
+ return(MagickTrue);
+}
+
+MagickExport int AcquireUniqueFileResource(char *path)
+{
+#if !defined(O_NOFOLLOW)
+#define O_NOFOLLOW 0
+#endif
+#if !defined(TMP_MAX)
+# define TMP_MAX 238328
+#endif
+
+ char
+ *resource;
+
+ int
+ c,
+ file;
+
+ register char
+ *p;
+
+ register long
+ i;
+
+ static const char
+ portable_filename[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";
+
+ StringInfo
+ *key;
+
+ unsigned char
+ *datum;
+
+ assert(path != (char *) NULL);
+ (void) LogMagickEvent(ResourceEvent,GetMagickModule(),"%s",path);
+ if (random_info == (RandomInfo *) NULL)
+ random_info=AcquireRandomInfo();
+ file=(-1);
+ for (i=0; i < TMP_MAX; i++)
+ {
+ /*
+ Get temporary pathname.
+ */
+ (void) GetPathTemplate(path);
+#if defined(MAGICKCORE_HAVE_MKSTEMP)
+ file=mkstemp(path);
+#if defined(__OS2__)
+ setmode(file,O_BINARY);
+#endif
+ if (file != -1)
+ break;
+#endif
+ key=GetRandomKey(random_info,8);
+ p=path+strlen(path)-8;
+ datum=GetStringInfoDatum(key);
+ for (i=0; i < 8; i++)
+ {
+ c=(int) (datum[i] & 0x3f);
+ *p++=portable_filename[c];
+ }
+ key=DestroyStringInfo(key);
+ file=open(path,O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_NOFOLLOW,S_MODE);
+ if ((file > 0) || (errno != EEXIST))
+ break;
+ }
+ (void) LogMagickEvent(ResourceEvent,GetMagickModule(),"%s",path);
+ if (file == -1)
+ return(file);
+ AcquireSemaphoreInfo(&resource_semaphore);
+ if (temporary_resources == (SplayTreeInfo *) NULL)
+ temporary_resources=NewSplayTree(CompareSplayTreeString,
+ RelinquishMagickMemory,DestroyTemporaryResources);
+ RelinquishSemaphoreInfo(resource_semaphore);
+ resource=ConstantString(path);
+ (void) AddValueToSplayTree(temporary_resources,resource,resource);
+ return(file);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y M a g i c k R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyMagickResources() destroys the resource environment.
+%
+% The format of the DestroyMagickResources() method is:
+%
+% DestroyMagickResources(void)
+%
+*/
+MagickExport void DestroyMagickResources(void)
+{
+ AcquireSemaphoreInfo(&resource_semaphore);
+ if (temporary_resources != (SplayTreeInfo *) NULL)
+ temporary_resources=DestroySplayTree(temporary_resources);
+ if (random_info != (RandomInfo *) NULL)
+ random_info=DestroyRandomInfo(random_info);
+ RelinquishSemaphoreInfo(resource_semaphore);
+ DestroySemaphoreInfo(&resource_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k R e s o u r c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickResource() returns the specified resource.
+%
+% The format of the GetMagickResource() method is:
+%
+% MagickSizeType GetMagickResource(const ResourceType type)
+%
+% A description of each parameter follows:
+%
+% o type: the type of resource.
+%
+*/
+MagickExport MagickSizeType GetMagickResource(const ResourceType type)
+{
+ MagickSizeType
+ resource;
+
+ resource=0;
+ AcquireSemaphoreInfo(&resource_semaphore);
+ switch (type)
+ {
+ case AreaResource:
+ {
+ resource=(MagickSizeType) resource_info.area;
+ break;
+ }
+ case MemoryResource:
+ {
+ resource=(MagickSizeType) resource_info.memory;
+ break;
+ }
+ case MapResource:
+ {
+ resource=(MagickSizeType) resource_info.map;
+ break;
+ }
+ case DiskResource:
+ {
+ resource=(MagickSizeType) resource_info.disk;
+ break;
+ }
+ case FileResource:
+ {
+ resource=(MagickSizeType) resource_info.file;
+ break;
+ }
+ case ThreadResource:
+ {
+ resource=(MagickSizeType) resource_info.thread;
+ break;
+ }
+ case TimeResource:
+ {
+ resource=(MagickSizeType) resource_info.time;
+ break;
+ }
+ default:
+ break;
+ }
+ RelinquishSemaphoreInfo(resource_semaphore);
+ return(resource);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k R e s o u r c e L i m i t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickResource() returns the specified resource limit.
+%
+% The format of the GetMagickResourceLimit() method is:
+%
+% unsigned long GetMagickResourceLimit(const ResourceType type)
+%
+% A description of each parameter follows:
+%
+% o type: the type of resource.
+%
+*/
+MagickExport MagickSizeType GetMagickResourceLimit(const ResourceType type)
+{
+ MagickSizeType
+ resource;
+
+ resource=0;
+ AcquireSemaphoreInfo(&resource_semaphore);
+ switch (type)
+ {
+ case AreaResource:
+ {
+ resource=resource_info.area_limit;
+ break;
+ }
+ case MemoryResource:
+ {
+ resource=resource_info.memory_limit;
+ break;
+ }
+ case MapResource:
+ {
+ resource=resource_info.map_limit;
+ break;
+ }
+ case DiskResource:
+ {
+ resource=resource_info.disk_limit;
+ break;
+ }
+ case FileResource:
+ {
+ resource=resource_info.file_limit;
+ break;
+ }
+ case ThreadResource:
+ {
+ resource=resource_info.thread_limit;
+ break;
+ }
+ case TimeResource:
+ {
+ resource=resource_info.time_limit;
+ break;
+ }
+ default:
+ break;
+ }
+ RelinquishSemaphoreInfo(resource_semaphore);
+ return(resource);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e M a g i c k R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeMagickResources() initializes the resource environment.
+%
+% The format of the InitializeMagickResources() method is:
+%
+% InitializeMagickResources(void)
+%
+*/
+
+static inline unsigned long MagickMax(const unsigned long x,
+ const unsigned long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline MagickSizeType StringToSizeType(const char *string,
+ const double interval)
+{
+ double
+ value;
+
+ value=StringToDouble(string,interval);
+ if (value >= (double) MagickULLConstant(~0))
+ return(MagickULLConstant(~0));
+ return((MagickSizeType) value);
+}
+
+MagickExport void InitializeMagickResources(void)
+{
+ char
+ *limit;
+
+ long
+ files,
+ pages,
+ pagesize;
+
+ MagickSizeType
+ memory;
+
+ /*
+ Set Magick resource limits.
+ */
+ pagesize=(-1);
+#if defined(MAGICKCORE_HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+ pagesize=sysconf(_SC_PAGESIZE);
+#elif defined(MAGICKCORE_HAVE_GETPAGESIZE) && defined(MAGICKCORE_POSIX_SUPPORT)
+ pagesize=getpagesize();
+#endif
+ pages=(-1);
+#if defined(MAGICKCORE_HAVE_SYSCONF) && defined(_SC_PHYS_PAGES)
+ pages=sysconf(_SC_PHYS_PAGES);
+#endif
+ memory=(MagickSizeType) pages*pagesize;
+ if ((pagesize <= 0) || (pages <= 0))
+ memory=2048UL*1024UL*1024UL;
+#if defined(PixelCacheThreshold)
+ memory=PixelCacheThreshold;
+#endif
+ (void) SetMagickResourceLimit(AreaResource,2UL*memory);
+ (void) SetMagickResourceLimit(MemoryResource,3UL*memory/2UL);
+ (void) SetMagickResourceLimit(MapResource,4UL*memory);
+ limit=GetEnvironmentValue("MAGICK_AREA_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("area");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(AreaResource,StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+ limit=GetEnvironmentValue("MAGICK_MEMORY_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("memory");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(MemoryResource,
+ StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+ limit=GetEnvironmentValue("MAGICK_MAP_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("map");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(MapResource,StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+ limit=GetEnvironmentValue("MAGICK_DISK_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("disk");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(DiskResource,StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+ files=(-1);
+#if defined(MAGICKCORE_HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+ files=sysconf(_SC_OPEN_MAX);
+#elif defined(MAGICKCORE_HAVE_GETDTABLESIZE) && defined(MAGICKCORE_POSIX_SUPPORT)
+ files=getdtablesize();
+#endif
+ (void) SetMagickResourceLimit(FileResource,MagickMax((unsigned long)
+ (3*files/4),64));
+ limit=GetEnvironmentValue("MAGICK_FILE_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("file");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(FileResource,StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+ (void) SetMagickResourceLimit(ThreadResource,GetOpenMPMaximumThreads());
+ limit=GetEnvironmentValue("MAGICK_THREAD_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("thread");
+ if (limit != (char *) NULL)
+ {
+ SetOpenMPMaximumThreads((unsigned long) atol(limit));
+ (void) SetMagickResourceLimit(ThreadResource,StringToSizeType(limit,
+ 100.0));
+ limit=DestroyString(limit);
+ }
+ limit=GetEnvironmentValue("MAGICK_TIME_LIMIT");
+ if (limit == (char *) NULL)
+ limit=GetPolicyValue("time");
+ if (limit != (char *) NULL)
+ {
+ (void) SetMagickResourceLimit(TimeResource,StringToSizeType(limit,100.0));
+ limit=DestroyString(limit);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t M a g i c k R e s o u r c e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListMagickResourceInfo() lists the resource info to a file.
+%
+% The format of the ListMagickResourceInfo method is:
+%
+% MagickBooleanType ListMagickResourceInfo(FILE *file,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListMagickResourceInfo(FILE *file,
+ ExceptionInfo *magick_unused(exception))
+{
+ char
+ area_limit[MaxTextExtent],
+ disk_limit[MaxTextExtent],
+ map_limit[MaxTextExtent],
+ memory_limit[MaxTextExtent],
+ time_limit[MaxTextExtent];
+
+ if (file == (const FILE *) NULL)
+ file=stdout;
+ AcquireSemaphoreInfo(&resource_semaphore);
+ (void) FormatMagickSize(resource_info.area_limit,area_limit);
+ (void) FormatMagickSize(resource_info.memory_limit,memory_limit);
+ (void) FormatMagickSize(resource_info.map_limit,map_limit);
+ (void) FormatMagickSize(resource_info.disk_limit,disk_limit);
+ (void) CopyMagickString(time_limit,"unlimited",MaxTextExtent);
+ if (resource_info.time_limit != MagickResourceInfinity)
+ (void) FormatMagickString(time_limit,MaxTextExtent,"%lu",(unsigned long)
+ resource_info.time_limit);
+ (void) fprintf(file,"File Area Memory Map"
+ " Disk Thread Time\n");
+ (void) fprintf(file,"-----------------------------------------------------"
+ "--------------\n");
+ (void) fprintf(file,"%4lu %9s %9s %9s %9s %6lu %9s\n",(unsigned long)
+ resource_info.file_limit,area_limit,memory_limit,map_limit,disk_limit,
+ (unsigned long) resource_info.thread_limit,time_limit);
+ (void) fflush(file);
+ RelinquishSemaphoreInfo(resource_semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n q u i s h M a g i c k R e s o u r c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishMagickResource() relinquishes resources of the specified type.
+%
+% The format of the RelinquishMagickResource() method is:
+%
+% void RelinquishMagickResource(const ResourceType type,
+% const MagickSizeType size)
+%
+% A description of each parameter follows:
+%
+% o type: the type of resource.
+%
+% o size: the size of the resource.
+%
+*/
+MagickExport void RelinquishMagickResource(const ResourceType type,
+ const MagickSizeType size)
+{
+ char
+ resource_current[MaxTextExtent],
+ resource_limit[MaxTextExtent],
+ resource_request[MaxTextExtent];
+
+ (void) FormatMagickSize(size,resource_request);
+ AcquireSemaphoreInfo(&resource_semaphore);
+ switch (type)
+ {
+ case AreaResource:
+ {
+ resource_info.area=(MagickOffsetType) size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.area,
+ resource_current);
+ (void) FormatMagickSize(resource_info.area_limit,resource_limit);
+ break;
+ }
+ case MemoryResource:
+ {
+ resource_info.memory-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.memory,
+ resource_current);
+ (void) FormatMagickSize(resource_info.memory_limit,resource_limit);
+ break;
+ }
+ case MapResource:
+ {
+ resource_info.map-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.map,
+ resource_current);
+ (void) FormatMagickSize(resource_info.map_limit,resource_limit);
+ break;
+ }
+ case DiskResource:
+ {
+ resource_info.disk-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.disk,
+ resource_current);
+ (void) FormatMagickSize(resource_info.disk_limit,resource_limit);
+ break;
+ }
+ case FileResource:
+ {
+ resource_info.file-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.file,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.file_limit,
+ resource_limit);
+ break;
+ }
+ case ThreadResource:
+ {
+ resource_info.thread-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.thread,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.thread_limit,
+ resource_limit);
+ break;
+ }
+ case TimeResource:
+ {
+ resource_info.time-=size;
+ (void) FormatMagickSize((MagickSizeType) resource_info.time,
+ resource_current);
+ (void) FormatMagickSize((MagickSizeType) resource_info.time_limit,
+ resource_limit);
+ break;
+ }
+ default:
+ break;
+ }
+ RelinquishSemaphoreInfo(resource_semaphore);
+ (void) LogMagickEvent(ResourceEvent,GetMagickModule(),"%s: %s/%s/%s",
+ MagickOptionToMnemonic(MagickResourceOptions,(long) type),resource_request,
+ resource_current,resource_limit);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n q u i s h U n i q u e F i l e R e s o u r c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishUniqueFileResource() relinquishes a unique file resource.
+%
+% The format of the RelinquishUniqueFileResource() method is:
+%
+% MagickBooleanType RelinquishUniqueFileResource(const char *path)
+%
+% A description of each parameter follows:
+%
+% o name: the name of the temporary resource.
+%
+*/
+MagickExport MagickBooleanType RelinquishUniqueFileResource(const char *path)
+{
+ char
+ cache_path[MaxTextExtent];
+
+ assert(path != (const char *) NULL);
+ (void) LogMagickEvent(ResourceEvent,GetMagickModule(),"%s",path);
+ if (temporary_resources != (SplayTreeInfo *) NULL)
+ {
+ register char
+ *p;
+
+ ResetSplayTreeIterator(temporary_resources);
+ p=(char *) GetNextKeyInSplayTree(temporary_resources);
+ while (p != (char *) NULL)
+ {
+ if (LocaleCompare(p,path) == 0)
+ break;
+ p=(char *) GetNextKeyInSplayTree(temporary_resources);
+ }
+ if (p != (char *) NULL)
+ (void) DeleteNodeFromSplayTree(temporary_resources,p);
+ }
+ (void) CopyMagickString(cache_path,path,MaxTextExtent);
+ AppendImageFormat("cache",cache_path);
+ (void) remove(cache_path);
+ return(remove(path) == 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t M a g i c k R e s o u r c e L i m i t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetMagickResourceLimit() sets the limit for a particular resource.
+%
+% The format of the SetMagickResourceLimit() method is:
+%
+% MagickBooleanType SetMagickResourceLimit(const ResourceType type,
+% const MagickSizeType limit)
+%
+% A description of each parameter follows:
+%
+% o type: the type of resource.
+%
+% o limit: the maximum limit for the resource.
+%
+*/
+MagickExport MagickBooleanType SetMagickResourceLimit(const ResourceType type,
+ const MagickSizeType limit)
+{
+ AcquireSemaphoreInfo(&resource_semaphore);
+ switch (type)
+ {
+ case AreaResource:
+ {
+ resource_info.area_limit=limit;
+ break;
+ }
+ case MemoryResource:
+ {
+ resource_info.memory_limit=limit;
+ break;
+ }
+ case MapResource:
+ {
+ resource_info.map_limit=limit;
+ break;
+ }
+ case DiskResource:
+ {
+ resource_info.disk_limit=limit;
+ break;
+ }
+ case FileResource:
+ {
+ resource_info.file_limit=limit;
+ break;
+ }
+ case ThreadResource:
+ {
+ SetOpenMPMaximumThreads((unsigned long) limit);
+ resource_info.thread_limit=GetOpenMPMaximumThreads();
+ break;
+ }
+ case TimeResource:
+ {
+ resource_info.time_limit=limit;
+ break;
+ }
+ default:
+ break;
+ }
+ RelinquishSemaphoreInfo(resource_semaphore);
+ return(MagickTrue);
+}
diff --git a/magick/resource_.h b/magick/resource_.h
new file mode 100644
index 0000000..7bac7e9
--- /dev/null
+++ b/magick/resource_.h
@@ -0,0 +1,62 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore resource methods.
+*/
+#ifndef _MAGICKCORE_RESOURCE_H
+#define _MAGICKCORE_RESOURCE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedResource,
+ AreaResource,
+ DiskResource,
+ FileResource,
+ MapResource,
+ MemoryResource,
+ ThreadResource,
+ TimeResource
+} ResourceType;
+
+#define MagickResourceInfinity MagickULLConstant(~0)
+
+extern MagickExport int
+ AcquireUniqueFileResource(char *);
+
+extern MagickExport MagickBooleanType
+ AcquireMagickResource(const ResourceType,const MagickSizeType),
+ RelinquishUniqueFileResource(const char *),
+ ListMagickResourceInfo(FILE *,ExceptionInfo *),
+ SetMagickResourceLimit(const ResourceType,const MagickSizeType);
+
+extern MagickExport MagickSizeType
+ GetMagickResource(const ResourceType),
+ GetMagickResourceLimit(const ResourceType);
+
+extern MagickExport void
+ AsynchronousDestroyMagickResources(void),
+ DestroyMagickResources(void),
+ InitializeMagickResources(void),
+ RelinquishMagickResource(const ResourceType,const MagickSizeType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/segment.c b/magick/segment.c
new file mode 100644
index 0000000..4ea18ba
--- /dev/null
+++ b/magick/segment.c
@@ -0,0 +1,1912 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS EEEEE GGGG M M EEEEE N N TTTTT %
+% SS E G MM MM E NN N T %
+% SSS EEE G GGG M M M EEE N N N T %
+% SS E G G M M E N NN T %
+% SSSSS EEEEE GGGG M M EEEEE N N T %
+% %
+% %
+% MagickCore Methods to Segment an Image with Thresholding Fuzzy c-Means %
+% %
+% Software Design %
+% John Cristy %
+% April 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Segment segments an image by analyzing the histograms of the color
+% components and identifying units that are homogeneous with the fuzzy
+% c-means technique. The scale-space filter analyzes the histograms of
+% the three color components of the image and identifies a set of
+% classes. The extents of each class is used to coarsely segment the
+% image with thresholding. The color associated with each class is
+% determined by the mean color of all pixels within the extents of a
+% particular class. Finally, any unclassified pixels are assigned to
+% the closest class with the fuzzy c-means technique.
+%
+% The fuzzy c-Means algorithm can be summarized as follows:
+%
+% o Build a histogram, one for each color component of the image.
+%
+% o For each histogram, successively apply the scale-space filter and
+% build an interval tree of zero crossings in the second derivative
+% at each scale. Analyze this scale-space ``fingerprint'' to
+% determine which peaks and valleys in the histogram are most
+% predominant.
+%
+% o The fingerprint defines intervals on the axis of the histogram.
+% Each interval contains either a minima or a maxima in the original
+% signal. If each color component lies within the maxima interval,
+% that pixel is considered ``classified'' and is assigned an unique
+% class number.
+%
+% o Any pixel that fails to be classified in the above thresholding
+% pass is classified using the fuzzy c-Means technique. It is
+% assigned to one of the classes discovered in the histogram analysis
+% phase.
+%
+% The fuzzy c-Means technique attempts to cluster a pixel by finding
+% the local minima of the generalized within group sum of squared error
+% objective function. A pixel is assigned to the closest class of
+% which the fuzzy membership has a maximum value.
+%
+% Segment is strongly based on software written by Andy Gallo,
+% University of Delaware.
+%
+% The following reference was used in creating this program:
+%
+% Young Won Lim, Sang Uk Lee, "On The Color Image Segmentation
+% Algorithm Based on the Thresholding and the Fuzzy c-Means
+% Techniques", Pattern Recognition, Volume 23, Number 9, pages
+% 935-952, 1990.
+%
+%
+*/
+
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/color.h"
+#include "magick/colorspace.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/segment.h"
+#include "magick/string_.h"
+
+/*
+ Define declarations.
+*/
+#define MaxDimension 3
+#define DeltaTau 0.5f
+#if defined(FastClassify)
+#define WeightingExponent 2.0
+#define SegmentPower(ratio) (ratio)
+#else
+#define WeightingExponent 2.5
+#define SegmentPower(ratio) pow(ratio,(double) (1.0/(weighting_exponent-1.0)));
+#endif
+#define Tau 5.2f
+
+/*
+ Typedef declarations.
+*/
+typedef struct _ExtentPacket
+{
+ MagickRealType
+ center;
+
+ long
+ index,
+ left,
+ right;
+} ExtentPacket;
+
+typedef struct _Cluster
+{
+ struct _Cluster
+ *next;
+
+ ExtentPacket
+ red,
+ green,
+ blue;
+
+ long
+ count,
+ id;
+} Cluster;
+
+typedef struct _IntervalTree
+{
+ MagickRealType
+ tau;
+
+ long
+ left,
+ right;
+
+ MagickRealType
+ mean_stability,
+ stability;
+
+ struct _IntervalTree
+ *sibling,
+ *child;
+} IntervalTree;
+
+typedef struct _ZeroCrossing
+{
+ MagickRealType
+ tau,
+ histogram[256];
+
+ short
+ crossings[256];
+} ZeroCrossing;
+
+/*
+ Constant declarations.
+*/
+static const int
+ Blue = 2,
+ Green = 1,
+ Red = 0,
+ SafeMargin = 3,
+ TreeLength = 600;
+
+/*
+ Method prototypes.
+*/
+static MagickRealType
+ OptimalTau(const long *,const double,const double,const double,
+ const double,short *);
+
+static long
+ DefineRegion(const short *,ExtentPacket *);
+
+static void
+ InitializeHistogram(const Image *,long **,ExceptionInfo *),
+ ScaleSpace(const long *,const MagickRealType,MagickRealType *),
+ ZeroCrossHistogram(MagickRealType *,const MagickRealType,short *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C l a s s i f y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Classify() defines one or more classes. Each pixel is thresholded to
+% determine which class it belongs to. If the class is not identified it is
+% assigned to the closest class based on the fuzzy c-Means technique.
+%
+% The format of the Classify method is:
+%
+% MagickBooleanType Classify(Image *image,short **extrema,
+% const MagickRealType cluster_threshold,
+% const MagickRealType weighting_exponent,
+% const MagickBooleanType verbose)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o extrema: Specifies a pointer to an array of integers. They
+% represent the peaks and valleys of the histogram for each color
+% component.
+%
+% o cluster_threshold: This MagickRealType represents the minimum number of
+% pixels contained in a hexahedra before it can be considered valid
+% (expressed as a percentage).
+%
+% o weighting_exponent: Specifies the membership weighting exponent.
+%
+% o verbose: A value greater than zero prints detailed information about
+% the identified classes.
+%
+*/
+static MagickBooleanType Classify(Image *image,short **extrema,
+ const MagickRealType cluster_threshold,
+ const MagickRealType weighting_exponent,const MagickBooleanType verbose)
+{
+#define SegmentImageTag "Segment/Image"
+
+ Cluster
+ *cluster,
+ *head,
+ *last_cluster,
+ *next_cluster;
+
+ ExceptionInfo
+ *exception;
+
+ ExtentPacket
+ blue,
+ green,
+ red;
+
+ long
+ count,
+ progress,
+ y;
+
+ MagickRealType
+ *free_squares;
+
+ MagickStatusType
+ status;
+
+ register long
+ i;
+
+ register MagickRealType
+ *squares;
+
+ unsigned long
+ number_clusters;
+
+ CacheView
+ *image_view;
+
+ /*
+ Form clusters.
+ */
+ cluster=(Cluster *) NULL;
+ head=(Cluster *) NULL;
+ (void) ResetMagickMemory(&red,0,sizeof(red));
+ (void) ResetMagickMemory(&green,0,sizeof(green));
+ (void) ResetMagickMemory(&blue,0,sizeof(blue));
+ while (DefineRegion(extrema[Red],&red) != 0)
+ {
+ green.index=0;
+ while (DefineRegion(extrema[Green],&green) != 0)
+ {
+ blue.index=0;
+ while (DefineRegion(extrema[Blue],&blue) != 0)
+ {
+ /*
+ Allocate a new class.
+ */
+ if (head != (Cluster *) NULL)
+ {
+ cluster->next=(Cluster *) AcquireMagickMemory(
+ sizeof(*cluster->next));
+ cluster=cluster->next;
+ }
+ else
+ {
+ cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
+ head=cluster;
+ }
+ if (cluster == (Cluster *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ /*
+ Initialize a new class.
+ */
+ cluster->count=0;
+ cluster->red=red;
+ cluster->green=green;
+ cluster->blue=blue;
+ cluster->next=(Cluster *) NULL;
+ }
+ }
+ }
+ if (head == (Cluster *) NULL)
+ {
+ /*
+ No classes were identified-- create one.
+ */
+ cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
+ if (cluster == (Cluster *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ /*
+ Initialize a new class.
+ */
+ cluster->count=0;
+ cluster->red=red;
+ cluster->green=green;
+ cluster->blue=blue;
+ cluster->next=(Cluster *) NULL;
+ head=cluster;
+ }
+ /*
+ Count the pixels for each cluster.
+ */
+ status=MagickTrue;
+ count=0;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *p;
+
+ register long
+ x;
+
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ if (((long) ScaleQuantumToChar(p->red) >=
+ (cluster->red.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->red) <=
+ (cluster->red.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->green) >=
+ (cluster->green.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->green) <=
+ (cluster->green.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->blue) >=
+ (cluster->blue.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->blue) <=
+ (cluster->blue.right+SafeMargin)))
+ {
+ /*
+ Count this pixel.
+ */
+ count++;
+ cluster->red.center+=(MagickRealType) ScaleQuantumToChar(p->red);
+ cluster->green.center+=(MagickRealType)
+ ScaleQuantumToChar(p->green);
+ cluster->blue.center+=(MagickRealType) ScaleQuantumToChar(p->blue);
+ cluster->count++;
+ break;
+ }
+ p++;
+ }
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_Classify)
+#endif
+ proceed=SetImageProgress(image,SegmentImageTag,progress++,
+ 2*image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ /*
+ Remove clusters that do not meet minimum cluster threshold.
+ */
+ count=0;
+ last_cluster=head;
+ next_cluster=head;
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
+ {
+ next_cluster=cluster->next;
+ if ((cluster->count > 0) &&
+ (cluster->count >= (count*cluster_threshold/100.0)))
+ {
+ /*
+ Initialize cluster.
+ */
+ cluster->id=count;
+ cluster->red.center/=cluster->count;
+ cluster->green.center/=cluster->count;
+ cluster->blue.center/=cluster->count;
+ count++;
+ last_cluster=cluster;
+ continue;
+ }
+ /*
+ Delete cluster.
+ */
+ if (cluster == head)
+ head=next_cluster;
+ else
+ last_cluster->next=next_cluster;
+ cluster=(Cluster *) RelinquishMagickMemory(cluster);
+ }
+ number_clusters=(unsigned long) count;
+ if (verbose != MagickFalse)
+ {
+ /*
+ Print cluster statistics.
+ */
+ (void) fprintf(stdout,"Fuzzy C-means Statistics\n");
+ (void) fprintf(stdout,"===================\n\n");
+ (void) fprintf(stdout,"\tCluster Threshold = %g\n",cluster_threshold);
+ (void) fprintf(stdout,"\tWeighting Exponent = %g\n",weighting_exponent);
+ (void) fprintf(stdout,"\tTotal Number of Clusters = %lu\n\n",
+ number_clusters);
+ /*
+ Print the total number of points per cluster.
+ */
+ (void) fprintf(stdout,"\n\nNumber of Vectors Per Cluster\n");
+ (void) fprintf(stdout,"=============================\n\n");
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ (void) fprintf(stdout,"Cluster #%ld = %ld\n",cluster->id,
+ cluster->count);
+ /*
+ Print the cluster extents.
+ */
+ (void) fprintf(stdout,
+ "\n\n\nCluster Extents: (Vector Size: %d)\n",MaxDimension);
+ (void) fprintf(stdout,"================");
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ {
+ (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
+ (void) fprintf(stdout,"%ld-%ld %ld-%ld %ld-%ld\n",cluster->red.left,
+ cluster->red.right,cluster->green.left,cluster->green.right,
+ cluster->blue.left,cluster->blue.right);
+ }
+ /*
+ Print the cluster center values.
+ */
+ (void) fprintf(stdout,
+ "\n\n\nCluster Center Values: (Vector Size: %d)\n",MaxDimension);
+ (void) fprintf(stdout,"=====================");
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ {
+ (void) fprintf(stdout,"\n\nCluster #%ld\n\n",cluster->id);
+ (void) fprintf(stdout,"%g %g %g\n",(double) cluster->red.center,
+ (double) cluster->green.center,(double) cluster->blue.center);
+ }
+ (void) fprintf(stdout,"\n");
+ }
+ if (number_clusters > 256)
+ ThrowBinaryException(ImageError,"TooManyClusters",image->filename);
+ /*
+ Speed up distance calculations.
+ */
+ squares=(MagickRealType *) AcquireQuantumMemory(513UL,sizeof(*squares));
+ if (squares == (MagickRealType *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ squares+=255;
+ for (i=(-255); i <= 255; i++)
+ squares[i]=(MagickRealType) i*(MagickRealType) i;
+ /*
+ Allocate image colormap.
+ */
+ if (AcquireImageColormap(image,number_clusters) == MagickFalse)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ i=0;
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ {
+ image->colormap[i].red=ScaleCharToQuantum((unsigned char)
+ (cluster->red.center+0.5));
+ image->colormap[i].green=ScaleCharToQuantum((unsigned char)
+ (cluster->green.center+0.5));
+ image->colormap[i].blue=ScaleCharToQuantum((unsigned char)
+ (cluster->blue.center+0.5));
+ i++;
+ }
+ /*
+ Do course grain classes.
+ */
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ Cluster
+ *cluster;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ indexes[x]=(IndexPacket) 0;
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ {
+ if (((long) ScaleQuantumToChar(q->red) >=
+ (cluster->red.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(q->red) <=
+ (cluster->red.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(q->green) >=
+ (cluster->green.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(q->green) <=
+ (cluster->green.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(q->blue) >=
+ (cluster->blue.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(q->blue) <=
+ (cluster->blue.right+SafeMargin)))
+ {
+ /*
+ Classify this pixel.
+ */
+ indexes[x]=(IndexPacket) cluster->id;
+ break;
+ }
+ }
+ if (cluster == (Cluster *) NULL)
+ {
+ MagickRealType
+ distance_squared,
+ local_minima,
+ numerator,
+ ratio,
+ sum;
+
+ register long
+ j,
+ k;
+
+ /*
+ Compute fuzzy membership.
+ */
+ local_minima=0.0;
+ for (j=0; j < (long) image->colors; j++)
+ {
+ sum=0.0;
+ p=image->colormap+j;
+ distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
+ (long) ScaleQuantumToChar(p->red)]+
+ squares[(long) ScaleQuantumToChar(q->green)-
+ (long) ScaleQuantumToChar(p->green)]+
+ squares[(long) ScaleQuantumToChar(q->blue)-
+ (long) ScaleQuantumToChar(p->blue)];
+ numerator=distance_squared;
+ for (k=0; k < (long) image->colors; k++)
+ {
+ p=image->colormap+k;
+ distance_squared=squares[(long) ScaleQuantumToChar(q->red)-
+ (long) ScaleQuantumToChar(p->red)]+
+ squares[(long) ScaleQuantumToChar(q->green)-
+ (long) ScaleQuantumToChar(p->green)]+
+ squares[(long) ScaleQuantumToChar(q->blue)-
+ (long) ScaleQuantumToChar(p->blue)];
+ ratio=numerator/distance_squared;
+ sum+=SegmentPower(ratio);
+ }
+ if ((sum != 0.0) && ((1.0/sum) > local_minima))
+ {
+ /*
+ Classify this pixel.
+ */
+ local_minima=1.0/sum;
+ indexes[x]=(IndexPacket) j;
+ }
+ }
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_Classify)
+#endif
+ proceed=SetImageProgress(image,SegmentImageTag,progress++,
+ 2*image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ status&=SyncImage(image);
+ /*
+ Relinquish resources.
+ */
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
+ {
+ next_cluster=cluster->next;
+ cluster=(Cluster *) RelinquishMagickMemory(cluster);
+ }
+ squares-=255;
+ free_squares=squares;
+ free_squares=(MagickRealType *) RelinquishMagickMemory(free_squares);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o n s o l i d a t e C r o s s i n g s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConsolidateCrossings() guarantees that an even number of zero crossings
+% always lie between two crossings.
+%
+% The format of the ConsolidateCrossings method is:
+%
+% ConsolidateCrossings(ZeroCrossing *zero_crossing,
+% const unsigned long number_crossings)
+%
+% A description of each parameter follows.
+%
+% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
+%
+% o number_crossings: This unsigned long specifies the number of elements
+% in the zero_crossing array.
+%
+*/
+
+static inline long MagickAbsoluteValue(const long x)
+{
+ if (x < 0)
+ return(-x);
+ return(x);
+}
+
+static inline long MagickMax(const long x,const long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline long MagickMin(const long x,const long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static void ConsolidateCrossings(ZeroCrossing *zero_crossing,
+ const unsigned long number_crossings)
+{
+ long
+ center,
+ correct,
+ count,
+ left,
+ right;
+
+ register long
+ i,
+ j,
+ k,
+ l;
+
+ /*
+ Consolidate zero crossings.
+ */
+ for (i=(long) number_crossings-1; i >= 0; i--)
+ for (j=0; j <= 255; j++)
+ {
+ if (zero_crossing[i].crossings[j] == 0)
+ continue;
+ /*
+ Find the entry that is closest to j and still preserves the
+ property that there are an even number of crossings between
+ intervals.
+ */
+ for (k=j-1; k > 0; k--)
+ if (zero_crossing[i+1].crossings[k] != 0)
+ break;
+ left=MagickMax(k,0);
+ center=j;
+ for (k=j+1; k < 255; k++)
+ if (zero_crossing[i+1].crossings[k] != 0)
+ break;
+ right=MagickMin(k,255);
+ /*
+ K is the zero crossing just left of j.
+ */
+ for (k=j-1; k > 0; k--)
+ if (zero_crossing[i].crossings[k] != 0)
+ break;
+ if (k < 0)
+ k=0;
+ /*
+ Check center for an even number of crossings between k and j.
+ */
+ correct=(-1);
+ if (zero_crossing[i+1].crossings[j] != 0)
+ {
+ count=0;
+ for (l=k+1; l < center; l++)
+ if (zero_crossing[i+1].crossings[l] != 0)
+ count++;
+ if (((count % 2) == 0) && (center != k))
+ correct=center;
+ }
+ /*
+ Check left for an even number of crossings between k and j.
+ */
+ if (correct == -1)
+ {
+ count=0;
+ for (l=k+1; l < left; l++)
+ if (zero_crossing[i+1].crossings[l] != 0)
+ count++;
+ if (((count % 2) == 0) && (left != k))
+ correct=left;
+ }
+ /*
+ Check right for an even number of crossings between k and j.
+ */
+ if (correct == -1)
+ {
+ count=0;
+ for (l=k+1; l < right; l++)
+ if (zero_crossing[i+1].crossings[l] != 0)
+ count++;
+ if (((count % 2) == 0) && (right != k))
+ correct=right;
+ }
+ l=zero_crossing[i].crossings[j];
+ zero_crossing[i].crossings[j]=0;
+ if (correct != -1)
+ zero_crossing[i].crossings[correct]=(short) l;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e f i n e R e g i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DefineRegion() defines the left and right boundaries of a peak region.
+%
+% The format of the DefineRegion method is:
+%
+% long DefineRegion(const short *extrema,ExtentPacket *extents)
+%
+% A description of each parameter follows.
+%
+% o extrema: Specifies a pointer to an array of integers. They
+% represent the peaks and valleys of the histogram for each color
+% component.
+%
+% o extents: This pointer to an ExtentPacket represent the extends
+% of a particular peak or valley of a color component.
+%
+*/
+static long DefineRegion(const short *extrema,ExtentPacket *extents)
+{
+ /*
+ Initialize to default values.
+ */
+ extents->left=0;
+ extents->center=0.0;
+ extents->right=255;
+ /*
+ Find the left side (maxima).
+ */
+ for ( ; extents->index <= 255; extents->index++)
+ if (extrema[extents->index] > 0)
+ break;
+ if (extents->index > 255)
+ return(MagickFalse); /* no left side - no region exists */
+ extents->left=extents->index;
+ /*
+ Find the right side (minima).
+ */
+ for ( ; extents->index <= 255; extents->index++)
+ if (extrema[extents->index] < 0)
+ break;
+ extents->right=extents->index-1;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e r i v a t i v e H i s t o g r a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DerivativeHistogram() determines the derivative of the histogram using
+% central differencing.
+%
+% The format of the DerivativeHistogram method is:
+%
+% DerivativeHistogram(const MagickRealType *histogram,
+% MagickRealType *derivative)
+%
+% A description of each parameter follows.
+%
+% o histogram: Specifies an array of MagickRealTypes representing the number
+% of pixels for each intensity of a particular color component.
+%
+% o derivative: This array of MagickRealTypes is initialized by
+% DerivativeHistogram to the derivative of the histogram using central
+% differencing.
+%
+*/
+static void DerivativeHistogram(const MagickRealType *histogram,
+ MagickRealType *derivative)
+{
+ register long
+ i,
+ n;
+
+ /*
+ Compute endpoints using second order polynomial interpolation.
+ */
+ n=255;
+ derivative[0]=(-1.5*histogram[0]+2.0*histogram[1]-0.5*histogram[2]);
+ derivative[n]=(0.5*histogram[n-2]-2.0*histogram[n-1]+1.5*histogram[n]);
+ /*
+ Compute derivative using central differencing.
+ */
+ for (i=1; i < n; i++)
+ derivative[i]=(histogram[i+1]-histogram[i-1])/2.0;
+ return;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e D y n a m i c T h r e s h o l d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageDynamicThreshold() returns the dynamic threshold for an image.
+%
+% The format of the GetImageDynamicThreshold method is:
+%
+% MagickBooleanType GetImageDynamicThreshold(const Image *image,
+% const double cluster_threshold,const double smooth_threshold,
+% MagickPixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o cluster_threshold: This MagickRealType represents the minimum number of
+% pixels contained in a hexahedra before it can be considered valid
+% (expressed as a percentage).
+%
+% o smooth_threshold: the smoothing threshold eliminates noise in the second
+% derivative of the histogram. As the value is increased, you can expect a
+% smoother second derivative.
+%
+% o pixel: return the dynamic threshold here.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType GetImageDynamicThreshold(const Image *image,
+ const double cluster_threshold,const double smooth_threshold,
+ MagickPixelPacket *pixel,ExceptionInfo *exception)
+{
+ Cluster
+ *background,
+ *cluster,
+ *object,
+ *head,
+ *last_cluster,
+ *next_cluster;
+
+ ExtentPacket
+ blue,
+ green,
+ red;
+
+ long
+ count,
+ *histogram[MaxDimension],
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ MagickRealType
+ threshold;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ short
+ *extrema[MaxDimension];
+
+ /*
+ Allocate histogram and extrema.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ GetMagickPixelPacket(image,pixel);
+ for (i=0; i < MaxDimension; i++)
+ {
+ histogram[i]=(long *) AcquireQuantumMemory(256UL,sizeof(**histogram));
+ extrema[i]=(short *) AcquireQuantumMemory(256UL,sizeof(**histogram));
+ if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
+ {
+ for (i-- ; i >= 0; i--)
+ {
+ extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
+ histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
+ }
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ }
+ /*
+ Initialize histogram.
+ */
+ InitializeHistogram(image,histogram,exception);
+ (void) OptimalTau(histogram[Red],Tau,0.2f,DeltaTau,
+ (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Red]);
+ (void) OptimalTau(histogram[Green],Tau,0.2f,DeltaTau,
+ (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Green]);
+ (void) OptimalTau(histogram[Blue],Tau,0.2f,DeltaTau,
+ (smooth_threshold == 0.0f ? 1.0f : smooth_threshold),extrema[Blue]);
+ /*
+ Form clusters.
+ */
+ cluster=(Cluster *) NULL;
+ head=(Cluster *) NULL;
+ (void) ResetMagickMemory(&red,0,sizeof(red));
+ (void) ResetMagickMemory(&green,0,sizeof(green));
+ (void) ResetMagickMemory(&blue,0,sizeof(blue));
+ while (DefineRegion(extrema[Red],&red) != 0)
+ {
+ green.index=0;
+ while (DefineRegion(extrema[Green],&green) != 0)
+ {
+ blue.index=0;
+ while (DefineRegion(extrema[Blue],&blue) != 0)
+ {
+ /*
+ Allocate a new class.
+ */
+ if (head != (Cluster *) NULL)
+ {
+ cluster->next=(Cluster *) AcquireMagickMemory(
+ sizeof(*cluster->next));
+ cluster=cluster->next;
+ }
+ else
+ {
+ cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
+ head=cluster;
+ }
+ if (cluster == (Cluster *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ image->filename);
+ return(MagickFalse);
+ }
+ /*
+ Initialize a new class.
+ */
+ cluster->count=0;
+ cluster->red=red;
+ cluster->green=green;
+ cluster->blue=blue;
+ cluster->next=(Cluster *) NULL;
+ }
+ }
+ }
+ if (head == (Cluster *) NULL)
+ {
+ /*
+ No classes were identified-- create one.
+ */
+ cluster=(Cluster *) AcquireMagickMemory(sizeof(*cluster));
+ if (cluster == (Cluster *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ /*
+ Initialize a new class.
+ */
+ cluster->count=0;
+ cluster->red=red;
+ cluster->green=green;
+ cluster->blue=blue;
+ cluster->next=(Cluster *) NULL;
+ head=cluster;
+ }
+ /*
+ Count the pixels for each cluster.
+ */
+ count=0;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=cluster->next)
+ if (((long) ScaleQuantumToChar(p->red) >=
+ (cluster->red.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->red) <=
+ (cluster->red.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->green) >=
+ (cluster->green.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->green) <=
+ (cluster->green.right+SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->blue) >=
+ (cluster->blue.left-SafeMargin)) &&
+ ((long) ScaleQuantumToChar(p->blue) <=
+ (cluster->blue.right+SafeMargin)))
+ {
+ /*
+ Count this pixel.
+ */
+ count++;
+ cluster->red.center+=(MagickRealType)
+ ScaleQuantumToChar(p->red);
+ cluster->green.center+=(MagickRealType)
+ ScaleQuantumToChar(p->green);
+ cluster->blue.center+=(MagickRealType)
+ ScaleQuantumToChar(p->blue);
+ cluster->count++;
+ break;
+ }
+ p++;
+ }
+ proceed=SetImageProgress(image,SegmentImageTag,y,2*image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ /*
+ Remove clusters that do not meet minimum cluster threshold.
+ */
+ count=0;
+ last_cluster=head;
+ next_cluster=head;
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
+ {
+ next_cluster=cluster->next;
+ if ((cluster->count > 0) &&
+ (cluster->count >= (count*cluster_threshold/100.0)))
+ {
+ /*
+ Initialize cluster.
+ */
+ cluster->id=count;
+ cluster->red.center/=cluster->count;
+ cluster->green.center/=cluster->count;
+ cluster->blue.center/=cluster->count;
+ count++;
+ last_cluster=cluster;
+ continue;
+ }
+ /*
+ Delete cluster.
+ */
+ if (cluster == head)
+ head=next_cluster;
+ else
+ last_cluster->next=next_cluster;
+ cluster=(Cluster *) RelinquishMagickMemory(cluster);
+ }
+ object=head;
+ background=head;
+ if (count > 1)
+ {
+ object=head->next;
+ for (cluster=object; cluster->next != (Cluster *) NULL; )
+ {
+ if (cluster->count < object->count)
+ object=cluster;
+ cluster=cluster->next;
+ }
+ background=head->next;
+ for (cluster=background; cluster->next != (Cluster *) NULL; )
+ {
+ if (cluster->count > background->count)
+ background=cluster;
+ cluster=cluster->next;
+ }
+ }
+ threshold=(background->red.center+object->red.center)/2.0;
+ pixel->red=(MagickRealType) ScaleCharToQuantum((unsigned char)
+ (threshold+0.5));
+ threshold=(background->green.center+object->green.center)/2.0;
+ pixel->green=(MagickRealType) ScaleCharToQuantum((unsigned char)
+ (threshold+0.5));
+ threshold=(background->blue.center+object->blue.center)/2.0;
+ pixel->blue=(MagickRealType) ScaleCharToQuantum((unsigned char)
+ (threshold+0.5));
+ /*
+ Relinquish resources.
+ */
+ for (cluster=head; cluster != (Cluster *) NULL; cluster=next_cluster)
+ {
+ next_cluster=cluster->next;
+ cluster=(Cluster *) RelinquishMagickMemory(cluster);
+ }
+ for (i=0; i < MaxDimension; i++)
+ {
+ extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
+ histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e H i s t o g r a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeHistogram() computes the histogram for an image.
+%
+% The format of the InitializeHistogram method is:
+%
+% InitializeHistogram(const Image *image,long **histogram)
+%
+% A description of each parameter follows.
+%
+% o image: Specifies a pointer to an Image structure; returned from
+% ReadImage.
+%
+% o histogram: Specifies an array of integers representing the number
+% of pixels for each intensity of a particular color component.
+%
+*/
+static void InitializeHistogram(const Image *image,long **histogram,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ register const PixelPacket
+ *p;
+
+ register long
+ i,
+ x;
+
+ /*
+ Initialize histogram.
+ */
+ for (i=0; i <= 255; i++)
+ {
+ histogram[Red][i]=0;
+ histogram[Green][i]=0;
+ histogram[Blue][i]=0;
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ histogram[Red][(long) ScaleQuantumToChar(p->red)]++;
+ histogram[Green][(long) ScaleQuantumToChar(p->green)]++;
+ histogram[Blue][(long) ScaleQuantumToChar(p->blue)]++;
+ p++;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e I n t e r v a l T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeIntervalTree() initializes an interval tree from the lists of
+% zero crossings.
+%
+% The format of the InitializeIntervalTree method is:
+%
+% InitializeIntervalTree(IntervalTree **list,long *number_nodes,
+% IntervalTree *node)
+%
+% A description of each parameter follows.
+%
+% o zero_crossing: Specifies an array of structures of type ZeroCrossing.
+%
+% o number_crossings: This unsigned long specifies the number of elements
+% in the zero_crossing array.
+%
+*/
+
+static void InitializeList(IntervalTree **list,long *number_nodes,
+ IntervalTree *node)
+{
+ if (node == (IntervalTree *) NULL)
+ return;
+ if (node->child == (IntervalTree *) NULL)
+ list[(*number_nodes)++]=node;
+ InitializeList(list,number_nodes,node->sibling);
+ InitializeList(list,number_nodes,node->child);
+}
+
+static void MeanStability(IntervalTree *node)
+{
+ register IntervalTree
+ *child;
+
+ if (node == (IntervalTree *) NULL)
+ return;
+ node->mean_stability=0.0;
+ child=node->child;
+ if (child != (IntervalTree *) NULL)
+ {
+ register long
+ count;
+
+ register MagickRealType
+ sum;
+
+ sum=0.0;
+ count=0;
+ for ( ; child != (IntervalTree *) NULL; child=child->sibling)
+ {
+ sum+=child->stability;
+ count++;
+ }
+ node->mean_stability=sum/(MagickRealType) count;
+ }
+ MeanStability(node->sibling);
+ MeanStability(node->child);
+}
+
+static void Stability(IntervalTree *node)
+{
+ if (node == (IntervalTree *) NULL)
+ return;
+ if (node->child == (IntervalTree *) NULL)
+ node->stability=0.0;
+ else
+ node->stability=node->tau-(node->child)->tau;
+ Stability(node->sibling);
+ Stability(node->child);
+}
+
+static IntervalTree *InitializeIntervalTree(const ZeroCrossing *zero_crossing,
+ const unsigned long number_crossings)
+{
+ IntervalTree
+ *head,
+ **list,
+ *node,
+ *root;
+
+ long
+ j,
+ k,
+ left,
+ number_nodes;
+
+ register long
+ i;
+
+ /*
+ Allocate interval tree.
+ */
+ list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
+ sizeof(*list));
+ if (list == (IntervalTree **) NULL)
+ return((IntervalTree *) NULL);
+ /*
+ The root is the entire histogram.
+ */
+ root=(IntervalTree *) AcquireMagickMemory(sizeof(*root));
+ root->child=(IntervalTree *) NULL;
+ root->sibling=(IntervalTree *) NULL;
+ root->tau=0.0;
+ root->left=0;
+ root->right=255;
+ for (i=(-1); i < (long) number_crossings; i++)
+ {
+ /*
+ Initialize list with all nodes with no children.
+ */
+ number_nodes=0;
+ InitializeList(list,&number_nodes,root);
+ /*
+ Split list.
+ */
+ for (j=0; j < number_nodes; j++)
+ {
+ head=list[j];
+ left=head->left;
+ node=head;
+ for (k=head->left+1; k < head->right; k++)
+ {
+ if (zero_crossing[i+1].crossings[k] != 0)
+ {
+ if (node == head)
+ {
+ node->child=(IntervalTree *) AcquireMagickMemory(
+ sizeof(*node->child));
+ node=node->child;
+ }
+ else
+ {
+ node->sibling=(IntervalTree *) AcquireMagickMemory(
+ sizeof(*node->sibling));
+ node=node->sibling;
+ }
+ node->tau=zero_crossing[i+1].tau;
+ node->child=(IntervalTree *) NULL;
+ node->sibling=(IntervalTree *) NULL;
+ node->left=left;
+ node->right=k;
+ left=k;
+ }
+ }
+ if (left != head->left)
+ {
+ node->sibling=(IntervalTree *) AcquireMagickMemory(
+ sizeof(*node->sibling));
+ node=node->sibling;
+ node->tau=zero_crossing[i+1].tau;
+ node->child=(IntervalTree *) NULL;
+ node->sibling=(IntervalTree *) NULL;
+ node->left=left;
+ node->right=head->right;
+ }
+ }
+ }
+ /*
+ Determine the stability: difference between a nodes tau and its child.
+ */
+ Stability(root->child);
+ MeanStability(root->child);
+ list=(IntervalTree **) RelinquishMagickMemory(list);
+ return(root);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p t i m a l T a u %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OptimalTau() finds the optimal tau for each band of the histogram.
+%
+% The format of the OptimalTau method is:
+%
+% MagickRealType OptimalTau(const long *histogram,const double max_tau,
+% const double min_tau,const double delta_tau,
+% const double smooth_threshold,short *extrema)
+%
+% A description of each parameter follows.
+%
+% o histogram: Specifies an array of integers representing the number
+% of pixels for each intensity of a particular color component.
+%
+% o extrema: Specifies a pointer to an array of integers. They
+% represent the peaks and valleys of the histogram for each color
+% component.
+%
+*/
+
+static void ActiveNodes(IntervalTree **list,long *number_nodes,
+ IntervalTree *node)
+{
+ if (node == (IntervalTree *) NULL)
+ return;
+ if (node->stability >= node->mean_stability)
+ {
+ list[(*number_nodes)++]=node;
+ ActiveNodes(list,number_nodes,node->sibling);
+ }
+ else
+ {
+ ActiveNodes(list,number_nodes,node->sibling);
+ ActiveNodes(list,number_nodes,node->child);
+ }
+}
+
+static void FreeNodes(IntervalTree *node)
+{
+ if (node == (IntervalTree *) NULL)
+ return;
+ FreeNodes(node->sibling);
+ FreeNodes(node->child);
+ node=(IntervalTree *) RelinquishMagickMemory(node);
+}
+
+static MagickRealType OptimalTau(const long *histogram,const double max_tau,
+ const double min_tau,const double delta_tau,const double smooth_threshold,
+ short *extrema)
+{
+ IntervalTree
+ **list,
+ *node,
+ *root;
+
+ long
+ index,
+ j,
+ k,
+ number_nodes;
+
+ MagickRealType
+ average_tau,
+ *derivative,
+ *second_derivative,
+ tau,
+ value;
+
+ register long
+ i,
+ x;
+
+ MagickBooleanType
+ peak;
+
+ unsigned long
+ count,
+ number_crossings;
+
+ ZeroCrossing
+ *zero_crossing;
+
+ /*
+ Allocate interval tree.
+ */
+ list=(IntervalTree **) AcquireQuantumMemory((size_t) TreeLength,
+ sizeof(*list));
+ if (list == (IntervalTree **) NULL)
+ return(0.0);
+ /*
+ Allocate zero crossing list.
+ */
+ count=(unsigned long) ((max_tau-min_tau)/delta_tau)+2;
+ zero_crossing=(ZeroCrossing *) AcquireQuantumMemory((size_t) count,
+ sizeof(*zero_crossing));
+ if (zero_crossing == (ZeroCrossing *) NULL)
+ return(0.0);
+ for (i=0; i < (long) count; i++)
+ zero_crossing[i].tau=(-1.0);
+ /*
+ Initialize zero crossing list.
+ */
+ derivative=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*derivative));
+ second_derivative=(MagickRealType *) AcquireQuantumMemory(256,
+ sizeof(*second_derivative));
+ if ((derivative == (MagickRealType *) NULL) ||
+ (second_derivative == (MagickRealType *) NULL))
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAllocateDerivatives");
+ i=0;
+ for (tau=max_tau; tau >= min_tau; tau-=delta_tau)
+ {
+ zero_crossing[i].tau=tau;
+ ScaleSpace(histogram,tau,zero_crossing[i].histogram);
+ DerivativeHistogram(zero_crossing[i].histogram,derivative);
+ DerivativeHistogram(derivative,second_derivative);
+ ZeroCrossHistogram(second_derivative,smooth_threshold,
+ zero_crossing[i].crossings);
+ i++;
+ }
+ /*
+ Add an entry for the original histogram.
+ */
+ zero_crossing[i].tau=0.0;
+ for (j=0; j <= 255; j++)
+ zero_crossing[i].histogram[j]=(MagickRealType) histogram[j];
+ DerivativeHistogram(zero_crossing[i].histogram,derivative);
+ DerivativeHistogram(derivative,second_derivative);
+ ZeroCrossHistogram(second_derivative,smooth_threshold,
+ zero_crossing[i].crossings);
+ number_crossings=(unsigned long) i;
+ derivative=(MagickRealType *) RelinquishMagickMemory(derivative);
+ second_derivative=(MagickRealType *)
+ RelinquishMagickMemory(second_derivative);
+ /*
+ Ensure the scale-space fingerprints form lines in scale-space, not loops.
+ */
+ ConsolidateCrossings(zero_crossing,number_crossings);
+ /*
+ Force endpoints to be included in the interval.
+ */
+ for (i=0; i <= (long) number_crossings; i++)
+ {
+ for (j=0; j < 255; j++)
+ if (zero_crossing[i].crossings[j] != 0)
+ break;
+ zero_crossing[i].crossings[0]=(-zero_crossing[i].crossings[j]);
+ for (j=255; j > 0; j--)
+ if (zero_crossing[i].crossings[j] != 0)
+ break;
+ zero_crossing[i].crossings[255]=(-zero_crossing[i].crossings[j]);
+ }
+ /*
+ Initialize interval tree.
+ */
+ root=InitializeIntervalTree(zero_crossing,number_crossings);
+ if (root == (IntervalTree *) NULL)
+ return(0.0);
+ /*
+ Find active nodes: stability is greater (or equal) to the mean stability of
+ its children.
+ */
+ number_nodes=0;
+ ActiveNodes(list,&number_nodes,root->child);
+ /*
+ Initialize extrema.
+ */
+ for (i=0; i <= 255; i++)
+ extrema[i]=0;
+ for (i=0; i < number_nodes; i++)
+ {
+ /*
+ Find this tau in zero crossings list.
+ */
+ k=0;
+ node=list[i];
+ for (j=0; j <= (long) number_crossings; j++)
+ if (zero_crossing[j].tau == node->tau)
+ k=j;
+ /*
+ Find the value of the peak.
+ */
+ peak=zero_crossing[k].crossings[node->right] == -1 ? MagickTrue :
+ MagickFalse;
+ index=node->left;
+ value=zero_crossing[k].histogram[index];
+ for (x=node->left; x <= node->right; x++)
+ {
+ if (peak != MagickFalse)
+ {
+ if (zero_crossing[k].histogram[x] > value)
+ {
+ value=zero_crossing[k].histogram[x];
+ index=x;
+ }
+ }
+ else
+ if (zero_crossing[k].histogram[x] < value)
+ {
+ value=zero_crossing[k].histogram[x];
+ index=x;
+ }
+ }
+ for (x=node->left; x <= node->right; x++)
+ {
+ if (index == 0)
+ index=256;
+ if (peak != MagickFalse)
+ extrema[x]=(short) index;
+ else
+ extrema[x]=(short) (-index);
+ }
+ }
+ /*
+ Determine the average tau.
+ */
+ average_tau=0.0;
+ for (i=0; i < number_nodes; i++)
+ average_tau+=list[i]->tau;
+ average_tau/=(MagickRealType) number_nodes;
+ /*
+ Relinquish resources.
+ */
+ FreeNodes(root);
+ zero_crossing=(ZeroCrossing *) RelinquishMagickMemory(zero_crossing);
+ list=(IntervalTree **) RelinquishMagickMemory(list);
+ return(average_tau);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S c a l e S p a c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ScaleSpace() performs a scale-space filter on the 1D histogram.
+%
+% The format of the ScaleSpace method is:
+%
+% ScaleSpace(const long *histogram,const MagickRealType tau,
+% MagickRealType *scale_histogram)
+%
+% A description of each parameter follows.
+%
+% o histogram: Specifies an array of MagickRealTypes representing the number
+% of pixels for each intensity of a particular color component.
+%
+*/
+
+static void ScaleSpace(const long *histogram,const MagickRealType tau,
+ MagickRealType *scale_histogram)
+{
+ MagickRealType
+ alpha,
+ beta,
+ *gamma,
+ sum;
+
+ register long
+ u,
+ x;
+
+ gamma=(MagickRealType *) AcquireQuantumMemory(256,sizeof(*gamma));
+ if (gamma == (MagickRealType *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAllocateGammaMap");
+ alpha=1.0/(tau*sqrt(2.0*MagickPI));
+ beta=(-1.0/(2.0*tau*tau));
+ for (x=0; x <= 255; x++)
+ gamma[x]=0.0;
+ for (x=0; x <= 255; x++)
+ {
+ gamma[x]=exp((double) beta*x*x);
+ if (gamma[x] < MagickEpsilon)
+ break;
+ }
+ for (x=0; x <= 255; x++)
+ {
+ sum=0.0;
+ for (u=0; u <= 255; u++)
+ sum+=(MagickRealType) histogram[u]*gamma[MagickAbsoluteValue(x-u)];
+ scale_histogram[x]=alpha*sum;
+ }
+ gamma=(MagickRealType *) RelinquishMagickMemory(gamma);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e g m e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SegmentImage() segment an image by analyzing the histograms of the color
+% components and identifying units that are homogeneous with the fuzzy
+% C-means technique.
+%
+% The format of the SegmentImage method is:
+%
+% MagickBooleanType SegmentImage(Image *image,
+% const ColorspaceType colorspace,const MagickBooleanType verbose,
+% const double cluster_threshold,const double smooth_threshold)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o colorspace: Indicate the colorspace.
+%
+% o verbose: Set to MagickTrue to print detailed information about the
+% identified classes.
+%
+% o cluster_threshold: This represents the minimum number of pixels
+% contained in a hexahedra before it can be considered valid (expressed
+% as a percentage).
+%
+% o smooth_threshold: the smoothing threshold eliminates noise in the second
+% derivative of the histogram. As the value is increased, you can expect a
+% smoother second derivative.
+%
+*/
+MagickExport MagickBooleanType SegmentImage(Image *image,
+ const ColorspaceType colorspace,const MagickBooleanType verbose,
+ const double cluster_threshold,const double smooth_threshold)
+{
+ long
+ *histogram[MaxDimension];
+
+ MagickBooleanType
+ status;
+
+ register long
+ i;
+
+ short
+ *extrema[MaxDimension];
+
+ /*
+ Allocate histogram and extrema.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ for (i=0; i < MaxDimension; i++)
+ {
+ histogram[i]=(long *) AcquireQuantumMemory(256,sizeof(**histogram));
+ extrema[i]=(short *) AcquireQuantumMemory(256,sizeof(**extrema));
+ if ((histogram[i] == (long *) NULL) || (extrema[i] == (short *) NULL))
+ {
+ for (i-- ; i >= 0; i--)
+ {
+ extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
+ histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
+ }
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename)
+ }
+ }
+ if (colorspace != RGBColorspace)
+ (void) TransformImageColorspace(image,colorspace);
+ /*
+ Initialize histogram.
+ */
+ InitializeHistogram(image,histogram,&image->exception);
+ (void) OptimalTau(histogram[Red],Tau,0.2,DeltaTau,
+ smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Red]);
+ (void) OptimalTau(histogram[Green],Tau,0.2,DeltaTau,
+ smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Green]);
+ (void) OptimalTau(histogram[Blue],Tau,0.2,DeltaTau,
+ smooth_threshold == 0.0 ? 1.0 : smooth_threshold,extrema[Blue]);
+ /*
+ Classify using the fuzzy c-Means technique.
+ */
+ status=Classify(image,extrema,cluster_threshold,WeightingExponent,verbose);
+ if (colorspace != RGBColorspace)
+ (void) TransformImageColorspace(image,colorspace);
+ /*
+ Relinquish resources.
+ */
+ for (i=0; i < MaxDimension; i++)
+ {
+ extrema[i]=(short *) RelinquishMagickMemory(extrema[i]);
+ histogram[i]=(long *) RelinquishMagickMemory(histogram[i]);
+ }
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ Z e r o C r o s s H i s t o g r a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ZeroCrossHistogram() find the zero crossings in a histogram and marks
+% directions as: 1 is negative to positive; 0 is zero crossing; and -1
+% is positive to negative.
+%
+% The format of the ZeroCrossHistogram method is:
+%
+% ZeroCrossHistogram(MagickRealType *second_derivative,
+% const MagickRealType smooth_threshold,short *crossings)
+%
+% A description of each parameter follows.
+%
+% o second_derivative: Specifies an array of MagickRealTypes representing the
+% second derivative of the histogram of a particular color component.
+%
+% o crossings: This array of integers is initialized with
+% -1, 0, or 1 representing the slope of the first derivative of the
+% of a particular color component.
+%
+*/
+static void ZeroCrossHistogram(MagickRealType *second_derivative,
+ const MagickRealType smooth_threshold,short *crossings)
+{
+ long
+ parity;
+
+ register long
+ i;
+
+ /*
+ Merge low numbers to zero to help prevent noise.
+ */
+ for (i=0; i <= 255; i++)
+ if ((second_derivative[i] < smooth_threshold) &&
+ (second_derivative[i] >= -smooth_threshold))
+ second_derivative[i]=0.0;
+ /*
+ Mark zero crossings.
+ */
+ parity=0;
+ for (i=0; i <= 255; i++)
+ {
+ crossings[i]=0;
+ if (second_derivative[i] < 0.0)
+ {
+ if (parity > 0)
+ crossings[i]=(-1);
+ parity=1;
+ }
+ else
+ if (second_derivative[i] > 0.0)
+ {
+ if (parity < 0)
+ crossings[i]=1;
+ parity=(-1);
+ }
+ }
+}
diff --git a/magick/segment.h b/magick/segment.h
new file mode 100644
index 0000000..0be37b6
--- /dev/null
+++ b/magick/segment.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image segment methods.
+*/
+#ifndef _MAGICKCORE_SEGMENT_H
+#define _MAGICKCORE_SEGMENT_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ GetImageDynamicThreshold(const Image *,const double,const double,
+ MagickPixelPacket *,ExceptionInfo *),
+ SegmentImage(Image *,const ColorspaceType,const MagickBooleanType,
+ const double,const double);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/semaphore.c b/magick/semaphore.c
new file mode 100644
index 0000000..e666f20
--- /dev/null
+++ b/magick/semaphore.c
@@ -0,0 +1,442 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS EEEEE M M AAA PPPP H H OOO RRRR EEEEE %
+% SS E MM MM A A P P H H O O R R E %
+% SSS EEE M M M AAAAA PPPP HHHHH O O RRRR EEE %
+% SS E M M A A P H H O O R R E %
+% SSSSS EEEEE M M A A P H H OOO R R EEEEE %
+% %
+% %
+% MagickCore Semaphore Methods %
+% %
+% Software Design %
+% William Radcliffe %
+% John Cristy %
+% June 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/thread_.h"
+#include "magick/thread-private.h"
+
+/*
+ Struct declaractions.
+*/
+struct SemaphoreInfo
+{
+ MagickMutexType
+ mutex;
+
+ MagickThreadType
+ id;
+
+ long
+ reference_count;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Static declaractions.
+*/
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+static pthread_mutex_t
+ semaphore_mutex = PTHREAD_MUTEX_INITIALIZER;
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+static LONG
+ semaphore_mutex = 0;
+#else
+static long
+ semaphore_mutex = 0;
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireSemaphoreInfo() acquires a semaphore.
+%
+% The format of the AcquireSemaphoreInfo method is:
+%
+% void AcquireSemaphoreInfo(SemaphoreInfo **semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport void AcquireSemaphoreInfo(SemaphoreInfo **semaphore_info)
+{
+ assert(semaphore_info != (SemaphoreInfo **) NULL);
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ if (pthread_mutex_lock(&semaphore_mutex) != 0)
+ (void) fprintf(stderr,"pthread_mutex_lock failed %s\n",
+ GetExceptionMessage(errno));
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ while (InterlockedCompareExchange(&semaphore_mutex,1L,0L) != 0)
+ Sleep(10);
+#endif
+ if (*semaphore_info == (SemaphoreInfo *) NULL)
+ *semaphore_info=AllocateSemaphoreInfo();
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ if (pthread_mutex_unlock(&semaphore_mutex) != 0)
+ (void) fprintf(stderr,"pthread_mutex_unlock failed %s\n",
+ GetExceptionMessage(errno));
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ InterlockedExchange(&semaphore_mutex,0L);
+#endif
+ (void) LockSemaphoreInfo(*semaphore_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A l l o c a t e S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AllocateSemaphoreInfo() initializes the SemaphoreInfo structure.
+%
+% The format of the AllocateSemaphoreInfo method is:
+%
+% SemaphoreInfo *AllocateSemaphoreInfo(void)
+%
+*/
+MagickExport SemaphoreInfo *AllocateSemaphoreInfo(void)
+{
+ SemaphoreInfo
+ *semaphore_info;
+
+ /*
+ Allocate semaphore.
+ */
+ semaphore_info=(SemaphoreInfo *) AcquireAlignedMemory(1,
+ sizeof(SemaphoreInfo));
+ if (semaphore_info == (SemaphoreInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(semaphore_info,0,sizeof(SemaphoreInfo));
+ /*
+ Initialize the semaphore.
+ */
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ {
+ int
+ status;
+
+ pthread_mutexattr_t
+ mutex_info;
+
+ status=pthread_mutexattr_init(&mutex_info);
+ if (status != 0)
+ {
+ semaphore_info=(SemaphoreInfo *) RelinquishAlignedMemory(
+ semaphore_info);
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToInitializeSemaphore");
+ }
+ status=pthread_mutex_init(&semaphore_info->mutex,&mutex_info);
+ (void) pthread_mutexattr_destroy(&mutex_info);
+ if (status != 0)
+ {
+ semaphore_info=(SemaphoreInfo *) RelinquishAlignedMemory(
+ semaphore_info);
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToInitializeSemaphore");
+ }
+ }
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ InitializeCriticalSection(&semaphore_info->mutex);
+#endif
+ semaphore_info->id=GetMagickThreadId();
+ semaphore_info->reference_count=0;
+ semaphore_info->signature=MagickSignature;
+ return(semaphore_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S e m a p h o r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroySemaphore() destroys the semaphore environment.
+%
+% The format of the DestroySemaphore method is:
+%
+% DestroySemaphore(void)
+%
+*/
+MagickExport void DestroySemaphore(void)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ if (pthread_mutex_destroy(&semaphore_mutex) != 0)
+ (void) fprintf(stderr,"pthread_mutex_destroy failed %s\n",
+ GetExceptionMessage(errno));
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroySemaphoreInfo() destroys a semaphore.
+%
+% The format of the DestroySemaphoreInfo method is:
+%
+% void DestroySemaphoreInfo(SemaphoreInfo **semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport void DestroySemaphoreInfo(SemaphoreInfo **semaphore_info)
+{
+ assert(semaphore_info != (SemaphoreInfo **) NULL);
+ assert((*semaphore_info) != (SemaphoreInfo *) NULL);
+ assert((*semaphore_info)->signature == MagickSignature);
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ (void) pthread_mutex_lock(&semaphore_mutex);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ while (InterlockedCompareExchange(&semaphore_mutex,1L,0L) != 0)
+ Sleep(10);
+#endif
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ (void) pthread_mutex_destroy(&(*semaphore_info)->mutex);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ DeleteCriticalSection(&(*semaphore_info)->mutex);
+#endif
+ (*semaphore_info)->signature=(~MagickSignature);
+ *semaphore_info=(SemaphoreInfo *) RelinquishAlignedMemory(*semaphore_info);
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ (void) pthread_mutex_unlock(&semaphore_mutex);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ InterlockedExchange(&semaphore_mutex,0L);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n i t i a l i z e S e m a p h o r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeSemaphore() initializes the semaphore environment.
+%
+% The format of the InitializeSemaphore method is:
+%
+% InitializeSemaphore(void)
+%
+*/
+MagickExport void InitializeSemaphore(void)
+{
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o c k S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LockSemaphoreInfo() locks a semaphore.
+%
+% The format of the LockSemaphoreInfo method is:
+%
+% MagickBooleanType LockSemaphoreInfo(SemaphoreInfo *semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport MagickBooleanType LockSemaphoreInfo(SemaphoreInfo *semaphore_info)
+{
+ assert(semaphore_info != (SemaphoreInfo *) NULL);
+ assert(semaphore_info->signature == MagickSignature);
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ {
+ int
+ status;
+
+ status=pthread_mutex_lock(&semaphore_info->mutex);
+ if (status != 0)
+ return(MagickFalse);
+#if defined(MAGICKCORE_DEBUG)
+ {
+ if ((semaphore_info->reference_count > 0) &&
+ (IsMagickThreadEqual(semaphore_info->id) != MagickFalse))
+ {
+ (void) fprintf(stderr,"Warning: unexpected recursive lock!\n");
+ (void) fflush(stderr);
+ }
+ }
+#endif
+ }
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ {
+ EnterCriticalSection(&semaphore_info->mutex);
+#if defined(MAGICKCORE_DEBUG)
+ {
+ if ((semaphore_info->reference_count > 0) &&
+ (IsMagickThreadEqual(semaphore_info->id) != MagickFalse))
+ {
+ (void) fprintf(stderr,"Warning: unexpected recursive lock!\n");
+ (void) fflush(stderr);
+ }
+ }
+#endif
+ }
+#endif
+ semaphore_info->id=GetMagickThreadId();
+ semaphore_info->reference_count++;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e l i n g u i s h S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RelinquishSemaphoreInfo() relinquishes a semaphore.
+%
+% The format of the RelinquishSemaphoreInfo method is:
+%
+% RelinquishSemaphoreInfo(SemaphoreInfo *semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport void RelinquishSemaphoreInfo(SemaphoreInfo *semaphore_info)
+{
+ assert(semaphore_info != (SemaphoreInfo *) NULL);
+ assert(semaphore_info->signature == MagickSignature);
+ (void) UnlockSemaphoreInfo(semaphore_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n l o c k S e m a p h o r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnlockSemaphoreInfo() unlocks a semaphore.
+%
+% The format of the UnlockSemaphoreInfo method is:
+%
+% MagickBooleanType UnlockSemaphoreInfo(SemaphoreInfo *semaphore_info)
+%
+% A description of each parameter follows:
+%
+% o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.
+%
+*/
+MagickExport MagickBooleanType UnlockSemaphoreInfo(
+ SemaphoreInfo *semaphore_info)
+{
+ assert(semaphore_info != (SemaphoreInfo *) NULL);
+ assert(semaphore_info->signature == MagickSignature);
+#if defined(MAGICKCORE_DEBUG)
+ assert(IsMagickThreadEqual(semaphore_info->id) != MagickFalse);
+ if (semaphore_info->reference_count == 0)
+ {
+ (void) fprintf(stderr,"Warning: semaphore lock already unlocked!\n");
+ (void) fflush(stderr);
+ return(MagickFalse);
+ }
+ semaphore_info->reference_count--;
+#endif
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ {
+ int
+ status;
+
+ status=pthread_mutex_unlock(&semaphore_info->mutex);
+ if (status != 0)
+ {
+ semaphore_info->reference_count++;
+ return(MagickFalse);
+ }
+ }
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ LeaveCriticalSection(&semaphore_info->mutex);
+#endif
+ return(MagickTrue);
+}
diff --git a/magick/semaphore.h b/magick/semaphore.h
new file mode 100644
index 0000000..acd4d48
--- /dev/null
+++ b/magick/semaphore.h
@@ -0,0 +1,46 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore methods to lock and unlock semaphores.
+*/
+#ifndef _MAGICKCORE_SEMAPHORE_H
+#define _MAGICKCORE_SEMAPHORE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct SemaphoreInfo
+ SemaphoreInfo;
+
+extern MagickExport MagickBooleanType
+ LockSemaphoreInfo(SemaphoreInfo *),
+ UnlockSemaphoreInfo(SemaphoreInfo *);
+
+extern MagickExport SemaphoreInfo
+ *AllocateSemaphoreInfo(void);
+
+extern MagickExport void
+ AcquireSemaphoreInfo(SemaphoreInfo **),
+ DestroySemaphore(void),
+ DestroySemaphoreInfo(SemaphoreInfo **),
+ InitializeSemaphore(void),
+ RelinquishSemaphoreInfo(SemaphoreInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/shear.c b/magick/shear.c
new file mode 100644
index 0000000..788610c
--- /dev/null
+++ b/magick/shear.c
@@ -0,0 +1,2110 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS H H EEEEE AAA RRRR %
+% SS H H E A A R R %
+% SSS HHHHH EEE AAAAA RRRR %
+% SS H H E A A R R %
+% SSSSS H H EEEEE A A R R %
+% %
+% %
+% MagickCore Methods to Shear or Rotate an Image by an Arbitrary Angle %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The RotateImage, XShearImage, and YShearImage methods are based on the
+% paper "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
+% Graphics Interface '86 (Vancouver). RotateImage is adapted from a similar
+% method based on the Paeth paper written by Michael Halle of the Spatial
+% Imaging Group, MIT Media Lab.
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/artifact.h"
+#include "magick/blob-private.h"
+#include "magick/cache-private.h"
+#include "magick/color-private.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/decorate.h"
+#include "magick/distort.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/memory_.h"
+#include "magick/list.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel-private.h"
+#include "magick/quantum.h"
+#include "magick/resource_.h"
+#include "magick/shear.h"
+#include "magick/statistic.h"
+#include "magick/threshold.h"
+#include "magick/transform.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A f f i n e T r a n s f o r m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AffineTransformImage() transforms an image as dictated by the affine matrix.
+% It allocates the memory necessary for the new Image structure and returns
+% a pointer to the new image.
+%
+% The format of the AffineTransformImage method is:
+%
+% Image *AffineTransformImage(const Image *image,
+% AffineMatrix *affine_matrix,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o affine_matrix: the affine matrix.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AffineTransformImage(const Image *image,
+ const AffineMatrix *affine_matrix,ExceptionInfo *exception)
+{
+ double
+ distort[6];
+
+ Image
+ *deskew_image;
+
+ /*
+ Affine transform image.
+ */
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(affine_matrix != (AffineMatrix *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ distort[0]=affine_matrix->sx;
+ distort[1]=affine_matrix->rx;
+ distort[2]=affine_matrix->ry;
+ distort[3]=affine_matrix->sy;
+ distort[4]=affine_matrix->tx;
+ distort[5]=affine_matrix->ty;
+ deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
+ MagickTrue,exception);
+ return(deskew_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C r o p T o F i t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CropToFitImage() crops the sheared image as determined by the bounding box
+% as defined by width and height and shearing angles.
+%
+% The format of the CropToFitImage method is:
+%
+% Image *CropToFitImage(Image **image,const MagickRealType x_shear,
+% const MagickRealType x_shear,const MagickRealType width,
+% const MagickRealType height,const MagickBooleanType rotate,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o x_shear, y_shear, width, height: Defines a region of the image to crop.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static inline void CropToFitImage(Image **image,const MagickRealType x_shear,
+ const MagickRealType y_shear,const MagickRealType width,
+ const MagickRealType height,const MagickBooleanType rotate,
+ ExceptionInfo *exception)
+{
+ Image
+ *crop_image;
+
+ PointInfo
+ extent[4],
+ min,
+ max;
+
+ RectangleInfo
+ geometry,
+ page;
+
+ register long
+ i;
+
+ /*
+ Calculate the rotated image size.
+ */
+ extent[0].x=(double) (-width/2.0);
+ extent[0].y=(double) (-height/2.0);
+ extent[1].x=(double) width/2.0;
+ extent[1].y=(double) (-height/2.0);
+ extent[2].x=(double) (-width/2.0);
+ extent[2].y=(double) height/2.0;
+ extent[3].x=(double) width/2.0;
+ extent[3].y=(double) height/2.0;
+ for (i=0; i < 4; i++)
+ {
+ extent[i].x+=x_shear*extent[i].y;
+ extent[i].y+=y_shear*extent[i].x;
+ if (rotate != MagickFalse)
+ extent[i].x+=x_shear*extent[i].y;
+ extent[i].x+=(double) (*image)->columns/2.0;
+ extent[i].y+=(double) (*image)->rows/2.0;
+ }
+ min=extent[0];
+ max=extent[0];
+ for (i=1; i < 4; i++)
+ {
+ if (min.x > extent[i].x)
+ min.x=extent[i].x;
+ if (min.y > extent[i].y)
+ min.y=extent[i].y;
+ if (max.x < extent[i].x)
+ max.x=extent[i].x;
+ if (max.y < extent[i].y)
+ max.y=extent[i].y;
+ }
+ geometry.x=(long) (min.x+0.5);
+ geometry.y=(long) (min.y+0.5);
+ geometry.width=(unsigned long) ((long) (max.x+0.5)-(long) (min.x+0.5));
+ geometry.height=(unsigned long) ((long) (max.y+0.5)-(long) (min.y+0.5));
+ page=(*image)->page;
+ (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
+ crop_image=CropImage(*image,&geometry,exception);
+ (*image)->page=page;
+ if (crop_image != (Image *) NULL)
+ {
+ crop_image->page=page;
+ *image=DestroyImage(*image);
+ *image=crop_image;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s k e w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeskewImage() removes skew from the image. Skew is an artifact that
+% occurs in scanned images because of the camera being misaligned,
+% imperfections in the scanning or surface, or simply because the paper was
+% not placed completely flat when scanned.
+%
+% The format of the DeskewImage method is:
+%
+% Image *DeskewImage(const Image *image,const double threshold,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o threshold: separate background from foreground.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+typedef struct _RadonInfo
+{
+ CacheType
+ type;
+
+ unsigned long
+ width,
+ height;
+
+ MagickSizeType
+ length;
+
+ MagickBooleanType
+ mapped;
+
+ char
+ path[MaxTextExtent];
+
+ int
+ file;
+
+ unsigned short
+ *cells;
+} RadonInfo;
+
+static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
+{
+ assert(radon_info != (RadonInfo *) NULL);
+ switch (radon_info->type)
+ {
+ case MemoryCache:
+ {
+ if (radon_info->mapped == MagickFalse)
+ radon_info->cells=(unsigned short *) RelinquishMagickMemory(
+ radon_info->cells);
+ else
+ radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
+ (size_t) radon_info->length);
+ RelinquishMagickResource(MemoryResource,radon_info->length);
+ break;
+ }
+ case MapCache:
+ {
+ radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
+ radon_info->length);
+ RelinquishMagickResource(MapResource,radon_info->length);
+ }
+ case DiskCache:
+ {
+ if (radon_info->file != -1)
+ (void) close(radon_info->file);
+ (void) RelinquishUniqueFileResource(radon_info->path);
+ RelinquishMagickResource(DiskResource,radon_info->length);
+ break;
+ }
+ default:
+ break;
+ }
+ return((RadonInfo *) RelinquishMagickMemory(radon_info));
+}
+
+static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
+{
+ long
+ y;
+
+ register long
+ x;
+
+ ssize_t
+ count;
+
+ unsigned short
+ value;
+
+ if (radon_info->type != DiskCache)
+ {
+ (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
+ return(MagickTrue);
+ }
+ value=0;
+ (void) MagickSeek(radon_info->file,0,SEEK_SET);
+ for (y=0; y < (long) radon_info->height; y++)
+ {
+ for (x=0; x < (long) radon_info->width; x++)
+ {
+ count=write(radon_info->file,&value,sizeof(*radon_info->cells));
+ if (count != (ssize_t) sizeof(*radon_info->cells))
+ break;
+ }
+ if (x < (long) radon_info->width)
+ break;
+ }
+ return(y < (long) radon_info->height ? MagickFalse : MagickTrue);
+}
+
+static RadonInfo *AcquireRadonInfo(const Image *image,const unsigned long width,
+ const unsigned long height,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ RadonInfo
+ *radon_info;
+
+ radon_info=(RadonInfo *) AcquireMagickMemory(sizeof(*radon_info));
+ if (radon_info == (RadonInfo *) NULL)
+ return((RadonInfo *) NULL);
+ (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
+ radon_info->width=width;
+ radon_info->height=height;
+ radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
+ radon_info->type=MemoryCache;
+ status=AcquireMagickResource(AreaResource,radon_info->length);
+ if ((status != MagickFalse) &&
+ (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
+ {
+ status=AcquireMagickResource(MemoryResource,radon_info->length);
+ if (status != MagickFalse)
+ {
+ radon_info->mapped=MagickFalse;
+ radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
+ radon_info->length);
+ if (radon_info->cells == (unsigned short *) NULL)
+ {
+ radon_info->mapped=MagickTrue;
+ radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
+ radon_info->length);
+ }
+ if (radon_info->cells == (unsigned short *) NULL)
+ RelinquishMagickResource(MemoryResource,radon_info->length);
+ }
+ }
+ radon_info->file=(-1);
+ if (radon_info->cells == (unsigned short *) NULL)
+ {
+ status=AcquireMagickResource(DiskResource,radon_info->length);
+ if (status == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
+ "CacheResourcesExhausted","`%s'",image->filename);
+ return(DestroyRadonInfo(radon_info));
+ }
+ radon_info->type=DiskCache;
+ (void) AcquireMagickResource(MemoryResource,radon_info->length);
+ radon_info->file=AcquireUniqueFileResource(radon_info->path);
+ if (radon_info->file == -1)
+ return(DestroyRadonInfo(radon_info));
+ status=AcquireMagickResource(MapResource,radon_info->length);
+ if (status != MagickFalse)
+ {
+ status=ResetRadonCells(radon_info);
+ if (status != MagickFalse)
+ {
+ radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
+ IOMode,0,(size_t) radon_info->length);
+ if (radon_info->cells != (unsigned short *) NULL)
+ radon_info->type=MapCache;
+ else
+ RelinquishMagickResource(MapResource,radon_info->length);
+ }
+ }
+ }
+ return(radon_info);
+}
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
+ const off_t offset,const size_t length,unsigned char *buffer)
+{
+ register ssize_t
+ i;
+
+ ssize_t
+ count;
+
+#if !defined(MAGICKCORE_HAVE_PPREAD)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ReadRadonCell)
+#endif
+ {
+ i=(-1);
+ if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
+ {
+#endif
+ count=0;
+ for (i=0; i < (ssize_t) length; i+=count)
+ {
+#if !defined(MAGICKCORE_HAVE_PPREAD)
+ count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
+ SSIZE_MAX));
+#else
+ count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
+ SSIZE_MAX),(off_t) (offset+i));
+#endif
+ if (count > 0)
+ continue;
+ count=0;
+ if (errno != EINTR)
+ {
+ i=(-1);
+ break;
+ }
+ }
+#if !defined(MAGICKCORE_HAVE_PPREAD)
+ }
+ }
+#endif
+ return(i);
+}
+
+static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
+ const off_t offset,const size_t length,const unsigned char *buffer)
+{
+ register ssize_t
+ i;
+
+ ssize_t
+ count;
+
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_WriteRadonCell)
+#endif
+ {
+ if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
+ {
+#endif
+ count=0;
+ for (i=0; i < (ssize_t) length; i+=count)
+ {
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+ count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
+ SSIZE_MAX));
+#else
+ count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
+ SSIZE_MAX),(off_t) (offset+i));
+#endif
+ if (count > 0)
+ continue;
+ count=0;
+ if (errno != EINTR)
+ {
+ i=(-1);
+ break;
+ }
+ }
+#if !defined(MAGICKCORE_HAVE_PWRITE)
+ }
+ }
+#endif
+ return(i);
+}
+
+static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
+ const long x,const long y)
+{
+ off_t
+ i;
+
+ unsigned short
+ value;
+
+ i=(off_t) radon_info->height*x+y;
+ if ((i < 0) ||
+ ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
+ return(0);
+ if (radon_info->type != DiskCache)
+ return(radon_info->cells[i]);
+ value=0;
+ (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
+ sizeof(*radon_info->cells),(unsigned char *) &value);
+ return(value);
+}
+
+static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
+ const long x,const long y,const unsigned short value)
+{
+ off_t
+ i;
+
+ ssize_t
+ count;
+
+ i=(off_t) radon_info->height*x+y;
+ if ((i < 0) ||
+ ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
+ return(MagickFalse);
+ if (radon_info->type != DiskCache)
+ {
+ radon_info->cells[i]=value;
+ return(MagickTrue);
+ }
+ count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
+ sizeof(*radon_info->cells),(unsigned char *) &value);
+ if (count != (ssize_t) sizeof(*radon_info->cells))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static void RadonProjection(RadonInfo *source_cells,
+ RadonInfo *destination_cells,const long sign,unsigned long *projection)
+{
+ RadonInfo
+ *swap;
+
+ register long
+ x;
+
+ register RadonInfo
+ *p,
+ *q;
+
+ unsigned long
+ step;
+
+ p=source_cells;
+ q=destination_cells;
+ for (step=1; step < p->width; step*=2)
+ {
+ for (x=0; x < (long) p->width; x+=2*step)
+ {
+ long
+ y;
+
+ register long
+ i;
+
+ unsigned short
+ cell;
+
+ for (i=0; i < (long) step; i++)
+ {
+ for (y=0; y < (long) (p->height-i-1); y++)
+ {
+ cell=GetRadonCell(p,x+i,y);
+ (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
+ (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+step,y+i+1));
+ }
+ for ( ; y < (long) (p->height-i); y++)
+ {
+ cell=GetRadonCell(p,x+i,y);
+ (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
+ (void) SetRadonCell(q,x+2*i+1,y,cell);
+ }
+ for ( ; y < (long) p->height; y++)
+ {
+ cell=GetRadonCell(p,x+i,y);
+ (void) SetRadonCell(q,x+2*i,y,cell);
+ (void) SetRadonCell(q,x+2*i+1,y,cell);
+ }
+ }
+ }
+ swap=p;
+ p=q;
+ q=swap;
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for
+#endif
+ for (x=0; x < (long) p->width; x++)
+ {
+ register long
+ y;
+
+ unsigned long
+ sum;
+
+ sum=0;
+ for (y=0; y < (long) (p->height-1); y++)
+ {
+ long
+ delta;
+
+ delta=GetRadonCell(p,x,y)-(long) GetRadonCell(p,x,y+1);
+ sum+=delta*delta;
+ }
+ projection[p->width+sign*x-1]=sum;
+ }
+}
+
+static MagickBooleanType RadonTransform(const Image *image,
+ const double threshold,unsigned long *projection,ExceptionInfo *exception)
+{
+ CacheView
+ *image_view;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ RadonInfo
+ *destination_cells,
+ *source_cells;
+
+ register long
+ i;
+
+ unsigned char
+ byte;
+
+ unsigned long
+ count,
+ width;
+
+ unsigned short
+ bits[256];
+
+ for (width=1; width < ((image->columns+7)/8); width<<=1) ;
+ source_cells=AcquireRadonInfo(image,width,image->rows,exception);
+ destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
+ if ((source_cells == (RadonInfo *) NULL) ||
+ (destination_cells == (RadonInfo *) NULL))
+ {
+ if (destination_cells != (RadonInfo *) NULL)
+ destination_cells=DestroyRadonInfo(destination_cells);
+ if (source_cells != (RadonInfo *) NULL)
+ source_cells=DestroyRadonInfo(source_cells);
+ return(MagickFalse);
+ }
+ if (ResetRadonCells(source_cells) == MagickFalse)
+ {
+ destination_cells=DestroyRadonInfo(destination_cells);
+ source_cells=DestroyRadonInfo(source_cells);
+ return(MagickFalse);
+ }
+ for (i=0; i < 256; i++)
+ {
+ byte=(unsigned char) i;
+ for (count=0; byte != 0; byte>>=1)
+ count+=byte & 0x01;
+ bits[i]=(unsigned short) count;
+ }
+ status=MagickTrue;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ bit,
+ byte;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ bit=0;
+ byte=0;
+ i=(long) (image->columns+7)/8;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ byte<<=1;
+ if (((MagickRealType) p->red < threshold) ||
+ ((MagickRealType) p->green < threshold) ||
+ ((MagickRealType) p->blue < threshold))
+ byte|=0x01;
+ bit++;
+ if (bit == 8)
+ {
+ (void) SetRadonCell(source_cells,--i,y,bits[byte]);
+ bit=0;
+ byte=0;
+ }
+ p++;
+ }
+ if (bit != 0)
+ {
+ byte<<=(8-bit);
+ (void) SetRadonCell(source_cells,--i,y,bits[byte]);
+ }
+ }
+ RadonProjection(source_cells,destination_cells,-1,projection);
+ (void) ResetRadonCells(source_cells);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ i,
+ x;
+
+ unsigned long
+ bit,
+ byte;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ bit=0;
+ byte=0;
+ i=0;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ byte<<=1;
+ if (((MagickRealType) p->red < threshold) ||
+ ((MagickRealType) p->green < threshold) ||
+ ((MagickRealType) p->blue < threshold))
+ byte|=0x01;
+ bit++;
+ if (bit == 8)
+ {
+ (void) SetRadonCell(source_cells,i++,y,bits[byte]);
+ bit=0;
+ byte=0;
+ }
+ p++;
+ }
+ if (bit != 0)
+ {
+ byte<<=(8-bit);
+ (void) SetRadonCell(source_cells,i++,y,bits[byte]);
+ }
+ }
+ RadonProjection(source_cells,destination_cells,1,projection);
+ image_view=DestroyCacheView(image_view);
+ destination_cells=DestroyRadonInfo(destination_cells);
+ source_cells=DestroyRadonInfo(source_cells);
+ return(MagickTrue);
+}
+
+static void GetImageBackgroundColor(Image *image,const long offset,
+ ExceptionInfo *exception)
+{
+ CacheView
+ *image_view;
+
+ long
+ y;
+
+ MagickPixelPacket
+ background;
+
+ MagickRealType
+ count;
+
+ /*
+ Compute average background color.
+ */
+ if (offset <= 0)
+ return;
+ GetMagickPixelPacket(image,&background);
+ count=0.0;
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ if ((y >= offset) && (y < ((long) image->rows-offset)))
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((x >= offset) && (x < ((long) image->columns-offset)))
+ continue;
+ background.red+=QuantumScale*p->red;
+ background.green+=QuantumScale*p->green;
+ background.blue+=QuantumScale*p->blue;
+ background.opacity+=QuantumScale*p->opacity;
+ count++;
+ p++;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ image->background_color.red=RoundToQuantum((MagickRealType) QuantumRange*
+ background.red/count);
+ image->background_color.green=RoundToQuantum((MagickRealType) QuantumRange*
+ background.green/count);
+ image->background_color.blue=RoundToQuantum((MagickRealType) QuantumRange*
+ background.blue/count);
+ image->background_color.opacity=RoundToQuantum((MagickRealType) QuantumRange*
+ background.opacity/count);
+}
+
+MagickExport Image *DeskewImage(const Image *image,const double threshold,
+ ExceptionInfo *exception)
+{
+ AffineMatrix
+ affine_matrix;
+
+ const char
+ *artifact;
+
+ double
+ degrees;
+
+ Image
+ *clone_image,
+ *crop_image,
+ *deskew_image,
+ *median_image;
+
+ long
+ skew;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ geometry;
+
+ register long
+ i;
+
+ unsigned long
+ max_projection,
+ *projection,
+ width;
+
+ /*
+ Compute deskew angle.
+ */
+ for (width=1; width < ((image->columns+7)/8); width<<=1) ;
+ projection=(unsigned long *) AcquireQuantumMemory((size_t) (2*width-1),
+ sizeof(*projection));
+ if (projection == (unsigned long *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ status=RadonTransform(image,threshold,projection,exception);
+ if (status == MagickFalse)
+ {
+ projection=(unsigned long *) RelinquishMagickMemory(projection);
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ }
+ max_projection=0;
+ skew=0;
+ for (i=0; i < (long) (2*width-1); i++)
+ {
+ if (projection[i] > max_projection)
+ {
+ skew=i-(long) width+1;
+ max_projection=projection[i];
+ }
+ }
+ projection=(unsigned long *) RelinquishMagickMemory(projection);
+ /*
+ Deskew image.
+ */
+ clone_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (clone_image == (Image *) NULL)
+ return((Image *) NULL);
+ (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod);
+ degrees=RadiansToDegrees(-atan((double) skew/width/8));
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TransformEvent,GetMagickModule()," Deskew angle: %g",
+ degrees);
+ affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
+ affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
+ affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
+ affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
+ affine_matrix.tx=0.0;
+ affine_matrix.ty=0.0;
+ artifact=GetImageArtifact(image,"deskew:auto-crop");
+ if (artifact == (const char *) NULL)
+ {
+ deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
+ clone_image=DestroyImage(clone_image);
+ return(deskew_image);
+ }
+ /*
+ Auto-crop image.
+ */
+ GetImageBackgroundColor(clone_image,atol(artifact),exception);
+ deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
+ clone_image=DestroyImage(clone_image);
+ if (deskew_image == (Image *) NULL)
+ return((Image *) NULL);
+ median_image=MedianFilterImage(deskew_image,0.0,exception);
+ if (median_image == (Image *) NULL)
+ {
+ deskew_image=DestroyImage(deskew_image);
+ return((Image *) NULL);
+ }
+ geometry=GetImageBoundingBox(median_image,exception);
+ median_image=DestroyImage(median_image);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TransformEvent,GetMagickModule()," Deskew geometry: "
+ "%lux%lu%+ld%+ld",geometry.width,geometry.height,geometry.x,geometry.y);
+ crop_image=CropImage(deskew_image,&geometry,exception);
+ deskew_image=DestroyImage(deskew_image);
+ return(crop_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n t e g r a l R o t a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IntegralRotateImage() rotates the image an integral of 90 degrees. It
+% allocates the memory necessary for the new Image structure and returns a
+% pointer to the rotated image.
+%
+% The format of the IntegralRotateImage method is:
+%
+% Image *IntegralRotateImage(const Image *image,unsigned long rotations,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o rotations: Specifies the number of 90 degree rotations.
+%
+*/
+static Image *IntegralRotateImage(const Image *image,unsigned long rotations,
+ ExceptionInfo *exception)
+{
+#define TileHeight 128
+#define TileWidth 128
+#define RotateImageTag "Rotate/Image"
+
+ CacheView
+ *image_view,
+ *rotate_view;
+
+ Image
+ *rotate_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ page;
+
+ /*
+ Initialize rotated image attributes.
+ */
+ assert(image != (Image *) NULL);
+ page=image->page;
+ rotations%=4;
+ if ((rotations == 1) || (rotations == 3))
+ rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
+ exception);
+ else
+ rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
+ exception);
+ if (rotate_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Integral rotate the image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ rotate_view=AcquireCacheView(rotate_image);
+ switch (rotations)
+ {
+ case 0:
+ {
+ /*
+ Rotate 0 degrees.
+ */
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict rotate_indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(rotate_view,0,y,rotate_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
+ (void) CopyMagickMemory(q,p,image->columns*sizeof(*p));
+ if ((indexes != (IndexPacket *) NULL) &&
+ (rotate_indexes != (IndexPacket *) NULL))
+ (void) CopyMagickMemory(rotate_indexes,indexes,image->columns*
+ sizeof(*indexes));
+ sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RotateImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ break;
+ }
+ case 1:
+ {
+ long
+ tile_y;
+
+ /*
+ Rotate 90 degrees.
+ */
+ for (tile_y=0; tile_y < (long) image->rows; tile_y+=TileHeight)
+ {
+ register long
+ tile_x;
+
+ if (status == MagickFalse)
+ continue;
+ for (tile_x=0; tile_x < (long) image->columns; tile_x+=TileWidth)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict rotate_indexes;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict q;
+
+ unsigned long
+ tile_height,
+ tile_width;
+
+ tile_width=TileWidth;
+ if ((tile_x+TileWidth) > (long) image->columns)
+ tile_width=(unsigned long) (TileWidth-(tile_x+TileWidth-
+ image->columns));
+ tile_height=TileHeight;
+ if ((tile_y+TileHeight) > (long) image->rows)
+ tile_height=(unsigned long) (TileHeight-(tile_y+TileHeight-
+ image->rows));
+ p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,tile_width,
+ tile_height,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (y=0; y < (long) tile_width; y++)
+ {
+ register const PixelPacket
+ *__restrict tile_pixels;
+
+ register long
+ x;
+
+ q=QueueCacheViewAuthenticPixels(rotate_view,(long)
+ rotate_image->columns-(tile_y+tile_height),y+tile_x,tile_height,
+ 1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ tile_pixels=p+(tile_height-1)*tile_width+y;
+ for (x=0; x < (long) tile_height; x++)
+ {
+ *q++=(*tile_pixels);
+ tile_pixels-=tile_width;
+ }
+ rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
+ if ((indexes != (IndexPacket *) NULL) &&
+ (rotate_indexes != (IndexPacket *) NULL))
+ {
+ register const IndexPacket
+ *__restrict tile_indexes;
+
+ tile_indexes=indexes+(tile_height-1)*tile_width+y;
+ for (x=0; x < (long) tile_height; x++)
+ {
+ *rotate_indexes++=(*tile_indexes);
+ tile_indexes-=tile_width;
+ }
+ }
+ sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RotateImageTag,progress+=TileHeight,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ Swap(page.width,page.height);
+ Swap(page.x,page.y);
+ if (page.width != 0)
+ page.x=(long) (page.width-rotate_image->columns-page.x);
+ break;
+ }
+ case 2:
+ {
+ /*
+ Rotate 180 degrees.
+ */
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict rotate_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
+ exception);
+ q=QueueCacheViewAuthenticPixels(rotate_view,0,(long) (image->rows-
+ y-1),image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
+ q+=image->columns;
+ for (x=0; x < (long) image->columns; x++)
+ *--q=(*p++);
+ if ((indexes != (IndexPacket *) NULL) &&
+ (rotate_indexes != (IndexPacket *) NULL))
+ for (x=0; x < (long) image->columns; x++)
+ rotate_indexes[image->columns-x-1]=indexes[x];
+ sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RotateImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ if (page.width != 0)
+ page.x=(long) (page.width-rotate_image->columns-page.x);
+ if (page.height != 0)
+ page.y=(long) (page.height-rotate_image->rows-page.y);
+ break;
+ }
+ case 3:
+ {
+ long
+ tile_y;
+
+ /*
+ Rotate 270 degrees.
+ */
+ for (tile_y=0; tile_y < (long) image->rows; tile_y+=TileHeight)
+ {
+ register long
+ tile_x;
+
+ if (status == MagickFalse)
+ continue;
+ for (tile_x=0; tile_x < (long) image->columns; tile_x+=TileWidth)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict rotate_indexes;
+
+ register long
+ y;
+
+ register PixelPacket
+ *__restrict q;
+
+ unsigned long
+ tile_height,
+ tile_width;
+
+ tile_width=TileWidth;
+ if ((tile_x+TileWidth) > (long) image->columns)
+ tile_width=(unsigned long) (TileWidth-(tile_x+TileWidth-
+ image->columns));
+ tile_height=TileHeight;
+ if ((tile_y+TileHeight) > (long) image->rows)
+ tile_height=(unsigned long) (TileHeight-(tile_y+TileHeight-
+ image->rows));
+ p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,tile_width,
+ tile_height,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (y=0; y < (long) tile_width; y++)
+ {
+ register const PixelPacket
+ *__restrict tile_pixels;
+
+ register long
+ x;
+
+ q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,(long)
+ y+rotate_image->rows-(tile_x+tile_width),tile_height,1,exception);
+ if (q == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ break;
+ }
+ tile_pixels=p+(tile_width-1)-y;
+ for (x=0; x < (long) tile_height; x++)
+ {
+ *q++=(*tile_pixels);
+ tile_pixels+=tile_width;
+ }
+ rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
+ if ((indexes != (IndexPacket *) NULL) &&
+ (rotate_indexes != (IndexPacket *) NULL))
+ {
+ register const IndexPacket
+ *__restrict tile_indexes;
+
+ tile_indexes=indexes+(tile_width-1)-y;
+ for (x=0; x < (long) tile_height; x++)
+ {
+ *rotate_indexes++=(*tile_indexes);
+ tile_indexes+=tile_width;
+ }
+ }
+ sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+ proceed=SetImageProgress(image,RotateImageTag,progress+=TileHeight,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ Swap(page.width,page.height);
+ Swap(page.x,page.y);
+ if (page.height != 0)
+ page.y=(long) (page.height-rotate_image->rows-page.y);
+ break;
+ }
+ }
+ rotate_view=DestroyCacheView(rotate_view);
+ image_view=DestroyCacheView(image_view);
+ rotate_image->type=image->type;
+ rotate_image->page=page;
+ if (status == MagickFalse)
+ rotate_image=DestroyImage(rotate_image);
+ return(rotate_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S h e a r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XShearImage() shears the image in the X direction with a shear angle of
+% 'degrees'. Positive angles shear counter-clockwise (right-hand rule), and
+% negative angles shear clockwise. Angles are measured relative to a vertical
+% Y-axis. X shears will widen an image creating 'empty' triangles on the left
+% and right sides of the source image.
+%
+% The format of the XShearImage method is:
+%
+% MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
+% const unsigned long width,const unsigned long height,
+% const long x_offset,const long y_offset)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o degrees: A MagickRealType representing the shearing angle along the X
+% axis.
+%
+% o width, height, x_offset, y_offset: Defines a region of the image
+% to shear.
+%
+*/
+static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
+ const unsigned long width,const unsigned long height,const long x_offset,
+ const long y_offset)
+{
+#define XShearImageTag "XShear/Image"
+
+ typedef enum
+ {
+ LEFT,
+ RIGHT
+ } ShearDirection;
+
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ background;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ GetMagickPixelPacket(image,&background);
+ SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
+ &background);
+ if (image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&background);
+ /*
+ XShear image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress, status)
+#endif
+ for (y=0; y < (long) height; y++)
+ {
+ long
+ step;
+
+ MagickPixelPacket
+ pixel,
+ source,
+ destination;
+
+ MagickRealType
+ area,
+ displacement;
+
+ register long
+ i;
+
+ register IndexPacket
+ *__restrict indexes,
+ *__restrict shear_indexes;
+
+ register PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ ShearDirection
+ direction;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
+ exception);
+ if (p == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ p+=x_offset;
+ indexes+=x_offset;
+ displacement=degrees*(MagickRealType) (y-height/2.0);
+ if (displacement == 0.0)
+ continue;
+ if (displacement > 0.0)
+ direction=RIGHT;
+ else
+ {
+ displacement*=(-1.0);
+ direction=LEFT;
+ }
+ step=(long) floor((double) displacement);
+ area=(MagickRealType) (displacement-step);
+ step++;
+ pixel=background;
+ GetMagickPixelPacket(image,&source);
+ GetMagickPixelPacket(image,&destination);
+ switch (direction)
+ {
+ case LEFT:
+ {
+ /*
+ Transfer pixels left-to-right.
+ */
+ if (step > x_offset)
+ break;
+ q=p-step;
+ shear_indexes=indexes-step;
+ for (i=0; i < (long) width; i++)
+ {
+ if ((x_offset+i) < step)
+ {
+ SetMagickPixelPacket(image,++p,++indexes,&pixel);
+ q++;
+ shear_indexes++;
+ continue;
+ }
+ SetMagickPixelPacket(image,p,indexes,&source);
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &source,(MagickRealType) p->opacity,area,&destination);
+ SetPixelPacket(image,&destination,q++,shear_indexes++);
+ SetMagickPixelPacket(image,p++,indexes++,&pixel);
+ }
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &background,(MagickRealType) background.opacity,area,&destination);
+ SetPixelPacket(image,&destination,q++,shear_indexes++);
+ for (i=0; i < (step-1); i++)
+ SetPixelPacket(image,&background,q++,shear_indexes++);
+ break;
+ }
+ case RIGHT:
+ {
+ /*
+ Transfer pixels right-to-left.
+ */
+ p+=width;
+ indexes+=width;
+ q=p+step;
+ shear_indexes=indexes+step;
+ for (i=0; i < (long) width; i++)
+ {
+ p--;
+ indexes--;
+ q--;
+ shear_indexes--;
+ if ((unsigned long) (x_offset+width+step-i) >= image->columns)
+ continue;
+ SetMagickPixelPacket(image,p,indexes,&source);
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &source,(MagickRealType) p->opacity,area,&destination);
+ SetPixelPacket(image,&destination,q,shear_indexes);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ }
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &background,(MagickRealType) background.opacity,area,&destination);
+ SetPixelPacket(image,&destination,--q,--shear_indexes);
+ for (i=0; i < (step-1); i++)
+ SetPixelPacket(image,&background,--q,--shear_indexes);
+ break;
+ }
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_XShearImage)
+#endif
+ proceed=SetImageProgress(image,XShearImageTag,progress++,height);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ Y S h e a r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% YShearImage shears the image in the Y direction with a shear angle of
+% 'degrees'. Positive angles shear counter-clockwise (right-hand rule), and
+% negative angles shear clockwise. Angles are measured relative to a
+% horizontal X-axis. Y shears will increase the height of an image creating
+% 'empty' triangles on the top and bottom of the source image.
+%
+% The format of the YShearImage method is:
+%
+% MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
+% const unsigned long width,const unsigned long height,
+% const long x_offset,const long y_offset)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o degrees: A MagickRealType representing the shearing angle along the Y
+% axis.
+%
+% o width, height, x_offset, y_offset: Defines a region of the image
+% to shear.
+%
+*/
+static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
+ const unsigned long width,const unsigned long height,const long x_offset,
+ const long y_offset)
+{
+#define YShearImageTag "YShear/Image"
+
+ typedef enum
+ {
+ UP,
+ DOWN
+ } ShearDirection;
+
+ CacheView
+ *image_view;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ x;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ background;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ GetMagickPixelPacket(image,&background);
+ SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
+ &background);
+ if (image->colorspace == CMYKColorspace)
+ ConvertRGBToCMYK(&background);
+ /*
+ Y Shear image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress, status)
+#endif
+ for (x=0; x < (long) width; x++)
+ {
+ long
+ step;
+
+ MagickPixelPacket
+ pixel,
+ source,
+ destination;
+
+ MagickRealType
+ area,
+ displacement;
+
+ register IndexPacket
+ *__restrict indexes,
+ *__restrict shear_indexes;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict p,
+ *__restrict q;
+
+ ShearDirection
+ direction;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
+ exception);
+ if (p == (PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ p+=y_offset;
+ indexes+=y_offset;
+ displacement=degrees*(MagickRealType) (x-width/2.0);
+ if (displacement == 0.0)
+ continue;
+ if (displacement > 0.0)
+ direction=DOWN;
+ else
+ {
+ displacement*=(-1.0);
+ direction=UP;
+ }
+ step=(long) floor((double) displacement);
+ area=(MagickRealType) (displacement-step);
+ step++;
+ pixel=background;
+ GetMagickPixelPacket(image,&source);
+ GetMagickPixelPacket(image,&destination);
+ switch (direction)
+ {
+ case UP:
+ {
+ /*
+ Transfer pixels top-to-bottom.
+ */
+ if (step > y_offset)
+ break;
+ q=p-step;
+ shear_indexes=indexes-step;
+ for (i=0; i < (long) height; i++)
+ {
+ if ((y_offset+i) < step)
+ {
+ SetMagickPixelPacket(image,++p,++indexes,&pixel);
+ q++;
+ shear_indexes++;
+ continue;
+ }
+ SetMagickPixelPacket(image,p,indexes,&source);
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &source,(MagickRealType) p->opacity,area,&destination);
+ SetPixelPacket(image,&destination,q++,shear_indexes++);
+ SetMagickPixelPacket(image,p++,indexes++,&pixel);
+ }
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &background,(MagickRealType) background.opacity,area,&destination);
+ SetPixelPacket(image,&destination,q++,shear_indexes++);
+ for (i=0; i < (step-1); i++)
+ SetPixelPacket(image,&background,q++,shear_indexes++);
+ break;
+ }
+ case DOWN:
+ {
+ /*
+ Transfer pixels bottom-to-top.
+ */
+ p+=height;
+ indexes+=height;
+ q=p+step;
+ shear_indexes=indexes+step;
+ for (i=0; i < (long) height; i++)
+ {
+ p--;
+ indexes--;
+ q--;
+ shear_indexes--;
+ if ((unsigned long) (y_offset+height+step-i) >= image->rows)
+ continue;
+ SetMagickPixelPacket(image,p,indexes,&source);
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &source,(MagickRealType) p->opacity,area,&destination);
+ SetPixelPacket(image,&destination,q,shear_indexes);
+ SetMagickPixelPacket(image,p,indexes,&pixel);
+ }
+ MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
+ &background,(MagickRealType) background.opacity,area,&destination);
+ SetPixelPacket(image,&destination,--q,--shear_indexes);
+ for (i=0; i < (step-1); i++)
+ SetPixelPacket(image,&background,--q,--shear_indexes);
+ break;
+ }
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_YShearImage)
+#endif
+ proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R o t a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RotateImage() creates a new image that is a rotated copy of an existing
+% one. Positive angles rotate counter-clockwise (right-hand rule), while
+% negative angles rotate clockwise. Rotated images are usually larger than
+% the originals and have 'empty' triangular corners. X axis. Empty
+% triangles left over from shearing the image are filled with the background
+% color defined by member 'background_color' of the image. RotateImage
+% allocates the memory necessary for the new Image structure and returns a
+% pointer to the new image.
+%
+% RotateImage() is based on the paper "A Fast Algorithm for General
+% Raster Rotatation" by Alan W. Paeth. RotateImage is adapted from a similar
+% method based on the Paeth paper written by Michael Halle of the Spatial
+% Imaging Group, MIT Media Lab.
+%
+% The format of the RotateImage method is:
+%
+% Image *RotateImage(const Image *image,const double degrees,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o degrees: Specifies the number of degrees to rotate the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *RotateImage(const Image *image,const double degrees,
+ ExceptionInfo *exception)
+{
+ Image
+ *integral_image,
+ *rotate_image;
+
+ long
+ x_offset,
+ y_offset;
+
+ MagickRealType
+ angle;
+
+ PointInfo
+ shear;
+
+ RectangleInfo
+ border_info;
+
+ unsigned long
+ height,
+ rotations,
+ width,
+ y_width;
+
+ /*
+ Adjust rotation angle.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ angle=degrees;
+ while (angle < -45.0)
+ angle+=360.0;
+ for (rotations=0; angle > 45.0; rotations++)
+ angle-=90.0;
+ rotations%=4;
+ /*
+ Calculate shear equations.
+ */
+ integral_image=IntegralRotateImage(image,rotations,exception);
+ if (integral_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
+ shear.y=sin((double) DegreesToRadians(angle));
+ if ((shear.x == 0.0) && (shear.y == 0.0))
+ return(integral_image);
+ if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&integral_image->exception);
+ integral_image=DestroyImage(integral_image);
+ return(integral_image);
+ }
+ if (integral_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
+ /*
+ Compute image size.
+ */
+ width=image->columns;
+ height=image->rows;
+ if ((rotations == 1) || (rotations == 3))
+ {
+ width=image->rows;
+ height=image->columns;
+ }
+ y_width=width+(long) (fabs(shear.x)*height+0.5);
+ x_offset=(long) (width+((fabs(shear.y)*height)-width)/2.0+0.5);
+ y_offset=(long) (height+((fabs(shear.y)*y_width)-height)/2.0+0.5);
+ /*
+ Surround image with a border.
+ */
+ integral_image->border_color=integral_image->background_color;
+ integral_image->compose=CopyCompositeOp;
+ border_info.width=(unsigned long) x_offset;
+ border_info.height=(unsigned long) y_offset;
+ rotate_image=BorderImage(integral_image,&border_info,exception);
+ integral_image=DestroyImage(integral_image);
+ if (rotate_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ /*
+ Rotate the image.
+ */
+ (void) XShearImage(rotate_image,shear.x,width,height,x_offset,
+ ((long) rotate_image->rows-height)/2);
+ (void) YShearImage(rotate_image,shear.y,y_width,height,
+ ((long) rotate_image->columns-y_width)/2,y_offset);
+ (void) XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,
+ ((long) rotate_image->columns-y_width)/2,0);
+ CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
+ (MagickRealType) height,MagickTrue,exception);
+ rotate_image->compose=image->compose;
+ rotate_image->page.width=0;
+ rotate_image->page.height=0;
+ return(rotate_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h e a r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ShearImage() creates a new image that is a shear_image copy of an existing
+% one. Shearing slides one edge of an image along the X or Y axis, creating
+% a parallelogram. An X direction shear slides an edge along the X axis,
+% while a Y direction shear slides an edge along the Y axis. The amount of
+% the shear is controlled by a shear angle. For X direction shears, x_shear
+% is measured relative to the Y axis, and similarly, for Y direction shears
+% y_shear is measured relative to the X axis. Empty triangles left over from
+% shearing the image are filled with the background color defined by member
+% 'background_color' of the image.. ShearImage() allocates the memory
+% necessary for the new Image structure and returns a pointer to the new image.
+%
+% ShearImage() is based on the paper "A Fast Algorithm for General Raster
+% Rotatation" by Alan W. Paeth.
+%
+% The format of the ShearImage method is:
+%
+% Image *ShearImage(const Image *image,const double x_shear,
+% const double y_shear,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o image: the image.
+%
+% o x_shear, y_shear: Specifies the number of degrees to shear the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ShearImage(const Image *image,const double x_shear,
+ const double y_shear,ExceptionInfo *exception)
+{
+ Image
+ *integral_image,
+ *shear_image;
+
+ long
+ x_offset,
+ y_offset;
+
+ PointInfo
+ shear;
+
+ RectangleInfo
+ border_info;
+
+ unsigned long
+ y_width;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
+ ThrowImageException(ImageError,"AngleIsDiscontinuous");
+ if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
+ ThrowImageException(ImageError,"AngleIsDiscontinuous");
+ /*
+ Initialize shear angle.
+ */
+ integral_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (integral_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
+ shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
+ if ((shear.x == 0.0) && (shear.y == 0.0))
+ return(integral_image);
+ if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&integral_image->exception);
+ integral_image=DestroyImage(integral_image);
+ return(integral_image);
+ }
+ if (integral_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
+ /*
+ Compute image size.
+ */
+ y_width=image->columns+(long) (fabs(shear.x)*image->rows+0.5);
+ x_offset=(long) (image->columns+((fabs(shear.x)*image->rows)-image->columns)/
+ 2.0+0.5);
+ y_offset=(long) (image->rows+((fabs(shear.y)*y_width)-image->rows)/2.0+0.5);
+ /*
+ Surround image with border.
+ */
+ integral_image->border_color=integral_image->background_color;
+ integral_image->compose=CopyCompositeOp;
+ border_info.width=(unsigned long) x_offset;
+ border_info.height=(unsigned long) y_offset;
+ shear_image=BorderImage(integral_image,&border_info,exception);
+ if (shear_image == (Image *) NULL)
+ ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
+ integral_image=DestroyImage(integral_image);
+ /*
+ Shear the image.
+ */
+ if (shear_image->matte == MagickFalse)
+ (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel);
+ (void) XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
+ ((long) shear_image->rows-image->rows)/2);
+ (void) YShearImage(shear_image,shear.y,y_width,image->rows,
+ ((long) shear_image->columns-y_width)/2,y_offset);
+ CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType) image->columns,
+ (MagickRealType) image->rows,MagickFalse,exception);
+ shear_image->compose=image->compose;
+ shear_image->page.width=0;
+ shear_image->page.height=0;
+ return(shear_image);
+}
diff --git a/magick/shear.h b/magick/shear.h
new file mode 100644
index 0000000..2c33572
--- /dev/null
+++ b/magick/shear.h
@@ -0,0 +1,35 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image stream methods.
+*/
+#ifndef _MAGICKCORE_SHEAR_H
+#define _MAGICKCORE_SHEAR_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport Image
+ *AffineTransformImage(const Image *,const AffineMatrix *,ExceptionInfo *),
+ *DeskewImage(const Image *,const double,ExceptionInfo *),
+ *RotateImage(const Image *,const double,ExceptionInfo *),
+ *ShearImage(const Image *,const double,const double,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/signature-private.h b/magick/signature-private.h
new file mode 100644
index 0000000..a3d8c5b
--- /dev/null
+++ b/magick/signature-private.h
@@ -0,0 +1,56 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore digital signature methods.
+*/
+#ifndef _MAGICKCORE_SIGNATURE_PRIVATE_H
+#define _MAGICKCORE_SIGNATURE_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#define MagickSignatureSize 64
+
+#include <magick/string_.h>
+
+typedef struct _SignatureInfo
+ SignatureInfo;
+
+extern MagickExport MagickBooleanType
+ SignatureImage(Image *);
+
+extern MagickExport SignatureInfo
+ *AcquireSignatureInfo(void),
+ *DestroySignatureInfo(SignatureInfo *);
+
+extern MagickExport const StringInfo
+ *GetSignatureDigest(const SignatureInfo *);
+
+extern MagickExport unsigned int
+ GetSignatureBlocksize(const SignatureInfo *),
+ GetSignatureDigestsize(const SignatureInfo *);
+
+extern MagickExport void
+ InitializeSignature(SignatureInfo *),
+ FinalizeSignature(SignatureInfo *),
+ SetSignatureDigest(SignatureInfo *,const StringInfo *),
+ UpdateSignature(SignatureInfo *,const StringInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/signature.c b/magick/signature.c
new file mode 100644
index 0000000..2d48678
--- /dev/null
+++ b/magick/signature.c
@@ -0,0 +1,821 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% SSSSS IIIII GGGG N N AAA TTTTT U U RRRR EEEEE %
+% SS I G NN N A A T U U R R E %
+% SSS I G GG N N N AAAAA T U U RRRR EEE %
+% SS I G G N NN A A T U U R R E %
+% SSSSS IIIII GGG N N A A T UUU R R EEEEE %
+% %
+% %
+% MagickCore Methods to Compute a Message Digest for an Image %
+% %
+% Software Design %
+% John Cristy %
+% December 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/property.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/signature.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+/*
+ Define declarations.
+*/
+#define SignatureBlocksize 64
+#define SignatureDigestsize 32
+
+/*
+ Typedef declarations.
+*/
+struct _SignatureInfo
+{
+ unsigned int
+ digestsize,
+ blocksize;
+
+ StringInfo
+ *digest,
+ *message;
+
+ unsigned int
+ *accumulator,
+ low_order,
+ high_order;
+
+ size_t
+ offset;
+
+ MagickBooleanType
+ lsb_first;
+
+ long
+ timestamp;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Forward declarations.
+*/
+static void
+ TransformSignature(SignatureInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e S i g n a t u r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireSignatureInfo() allocate the SignatureInfo structure.
+%
+% The format of the AcquireSignatureInfo method is:
+%
+% SignatureInfo *AcquireSignatureInfo(void)
+%
+*/
+MagickExport SignatureInfo *AcquireSignatureInfo(void)
+{
+ SignatureInfo
+ *signature_info;
+
+ unsigned int
+ lsb_first;
+
+ signature_info=(SignatureInfo *) AcquireMagickMemory(sizeof(*signature_info));
+ if (signature_info == (SignatureInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(signature_info,0,sizeof(*signature_info));
+ signature_info->digestsize=SignatureDigestsize;
+ signature_info->blocksize=SignatureBlocksize;
+ signature_info->digest=AcquireStringInfo(SignatureDigestsize);
+ signature_info->message=AcquireStringInfo(SignatureBlocksize);
+ signature_info->accumulator=(unsigned int *) AcquireQuantumMemory(
+ SignatureBlocksize,sizeof(*signature_info->accumulator));
+ if (signature_info->accumulator == (unsigned int *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ lsb_first=1;
+ signature_info->lsb_first=(int) (*(char *) &lsb_first) == 1 ? MagickTrue :
+ MagickFalse;
+ signature_info->timestamp=(long) time(0);
+ signature_info->signature=MagickSignature;
+ InitializeSignature(signature_info);
+ return(signature_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y S i g n a t u r e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroySignatureInfo() zeros memory associated with the SignatureInfo
+% structure.
+%
+% The format of the DestroySignatureInfo method is:
+%
+% SignatureInfo *DestroySignatureInfo(SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the cipher signature_info.
+%
+*/
+MagickExport SignatureInfo *DestroySignatureInfo(SignatureInfo *signature_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ if (signature_info->accumulator != (unsigned int *) NULL)
+ signature_info->accumulator=(unsigned int *) RelinquishMagickMemory(
+ signature_info->accumulator);
+ if (signature_info->message != (StringInfo *) NULL)
+ signature_info->message=DestroyStringInfo(signature_info->message);
+ if (signature_info->digest != (StringInfo *) NULL)
+ signature_info->digest=DestroyStringInfo(signature_info->digest);
+ signature_info->signature=(~MagickSignature);
+ signature_info=(SignatureInfo *) RelinquishMagickMemory(signature_info);
+ return(signature_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ F i n a l i z e S i g n a t u r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FinalizeSignature() finalizes the Signature message accumulator computation.
+%
+% The format of the FinalizeSignature method is:
+%
+% FinalizeSignature(SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the address of a structure of type SignatureInfo.
+%
+*/
+MagickExport void FinalizeSignature(SignatureInfo *signature_info)
+{
+ register long
+ i;
+
+ register unsigned char
+ *q;
+
+ register unsigned int
+ *p;
+
+ unsigned char
+ *datum;
+
+ unsigned int
+ count,
+ high_order,
+ low_order;
+
+ /*
+ Add padding and return the message accumulator.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ low_order=signature_info->low_order;
+ high_order=signature_info->high_order;
+ count=((low_order >> 3) & 0x3f);
+ datum=GetStringInfoDatum(signature_info->message);
+ datum[count++]=(unsigned char) 0x80;
+ if (count <= (unsigned int) (GetStringInfoLength(signature_info->message)-8))
+ (void) ResetMagickMemory(datum+count,0,GetStringInfoLength(
+ signature_info->message)-8-count);
+ else
+ {
+ (void) ResetMagickMemory(datum+count,0,GetStringInfoLength(
+ signature_info->message)-count);
+ TransformSignature(signature_info);
+ (void) ResetMagickMemory(datum,0,GetStringInfoLength(
+ signature_info->message)-8);
+ }
+ datum[56]=(unsigned char) (high_order >> 24);
+ datum[57]=(unsigned char) (high_order >> 16);
+ datum[58]=(unsigned char) (high_order >> 8);
+ datum[59]=(unsigned char) high_order;
+ datum[60]=(unsigned char) (low_order >> 24);
+ datum[61]=(unsigned char) (low_order >> 16);
+ datum[62]=(unsigned char) (low_order >> 8);
+ datum[63]=(unsigned char) low_order;
+ TransformSignature(signature_info);
+ p=signature_info->accumulator;
+ q=GetStringInfoDatum(signature_info->digest);
+ for (i=0; i < (SignatureDigestsize/4); i++)
+ {
+ *q++=(unsigned char) ((*p >> 24) & 0xff);
+ *q++=(unsigned char) ((*p >> 16) & 0xff);
+ *q++=(unsigned char) ((*p >> 8) & 0xff);
+ *q++=(unsigned char) (*p & 0xff);
+ p++;
+ }
+ /*
+ Reset working registers.
+ */
+ count=0;
+ high_order=0;
+ low_order=0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t S i g n a t u r e B l o c k s i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetSignatureBlocksize() returns the Signature blocksize.
+%
+% The format of the GetSignatureBlocksize method is:
+%
+% unsigned int *GetSignatureBlocksize(const SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the signature info.
+%
+*/
+MagickExport unsigned int GetSignatureBlocksize(
+ const SignatureInfo *signature_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ return(signature_info->blocksize);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t S i g n a t u r e D i g e s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetSignatureDigest() returns the signature digest.
+%
+% The format of the GetSignatureDigest method is:
+%
+% const StringInfo *GetSignatureDigest(const SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the signature info.
+%
+*/
+MagickExport const StringInfo *GetSignatureDigest(
+ const SignatureInfo *signature_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ return(signature_info->digest);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t S i g n a t u r e D i g e s t s i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetSignatureDigestsize() returns the Signature digest size.
+%
+% The format of the GetSignatureDigestsize method is:
+%
+% unsigned int *GetSignatureDigestsize(const SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the signature info.
+%
+*/
+MagickExport unsigned int GetSignatureDigestsize(
+ const SignatureInfo *signature_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ return(signature_info->digestsize);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e S i g n a t u r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IntializeSignature() intializes the Signature accumulator.
+%
+% The format of the DestroySignatureInfo method is:
+%
+% void InitializeSignatureInfo(SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the cipher signature_info.
+%
+*/
+MagickExport void InitializeSignature(SignatureInfo *signature_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ signature_info->accumulator[0]=0x6a09e667U;
+ signature_info->accumulator[1]=0xbb67ae85U;
+ signature_info->accumulator[2]=0x3c6ef372U;
+ signature_info->accumulator[3]=0xa54ff53aU;
+ signature_info->accumulator[4]=0x510e527fU;
+ signature_info->accumulator[5]=0x9b05688cU;
+ signature_info->accumulator[6]=0x1f83d9abU;
+ signature_info->accumulator[7]=0x5be0cd19U;
+ signature_info->low_order=0;
+ signature_info->high_order=0;
+ signature_info->offset=0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t S i g n a t u r e D i g e s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetSignatureDigest() set the signature digest.
+%
+% The format of the SetSignatureDigest method is:
+%
+% SetSignatureDigest(SignatureInfo *signature_info,
+% const StringInfo *digest)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the signature info.
+%
+% o digest: the digest.
+%
+*/
+MagickExport void SetSignatureDigest(SignatureInfo *signature_info,
+ const StringInfo *digest)
+{
+ /*
+ Set the signature accumulator.
+ */
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ SetStringInfo(signature_info->digest,digest);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S i g n a t u r e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SignatureImage() computes a message digest from an image pixel stream with
+% an implementation of the NIST SHA-256 Message Digest algorithm. This
+% signature uniquely identifies the image and is convenient for determining
+% if an image has been modified or whether two images are identical.
+%
+% The format of the SignatureImage method is:
+%
+% MagickBooleanType SignatureImage(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType SignatureImage(Image *image)
+{
+ char
+ *hex_signature;
+
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ QuantumInfo
+ *quantum_info;
+
+ QuantumType
+ quantum_type;
+
+ register const PixelPacket
+ *p;
+
+ SignatureInfo
+ *signature_info;
+
+ size_t
+ length;
+
+ StringInfo
+ *signature;
+
+ unsigned char
+ *pixels;
+
+ CacheView
+ *image_view;
+
+ /*
+ Compute image digital signature.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ quantum_info=AcquireQuantumInfo((const ImageInfo *) NULL,image);
+ if (quantum_info == (QuantumInfo *) NULL)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ quantum_type=RGBQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=RGBAQuantum;
+ if (image->colorspace == CMYKColorspace)
+ {
+ quantum_type=CMYKQuantum;
+ if (image->matte != MagickFalse)
+ quantum_type=CMYKAQuantum;
+ }
+ signature_info=AcquireSignatureInfo();
+ signature=AcquireStringInfo(quantum_info->extent);
+ pixels=GetQuantumPixels(quantum_info);
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ length=ExportQuantumPixels(image,image_view,quantum_info,quantum_type,
+ pixels,&image->exception);
+ SetStringInfoLength(signature,length);
+ SetStringInfoDatum(signature,pixels);
+ UpdateSignature(signature_info,signature);
+ }
+ image_view=DestroyCacheView(image_view);
+ quantum_info=DestroyQuantumInfo(quantum_info);
+ FinalizeSignature(signature_info);
+ hex_signature=StringInfoToHexString(GetSignatureDigest(signature_info));
+ (void) DeleteImageProperty(image,"signature");
+ (void) SetImageProperty(image,"signature",hex_signature);
+ /*
+ Free resources.
+ */
+ hex_signature=DestroyString(hex_signature);
+ signature=DestroyStringInfo(signature);
+ signature_info=DestroySignatureInfo(signature_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ T r a n s f o r m S i g n a t u r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformSignature() transforms the Signature message accumulator.
+%
+% The format of the TransformSignature method is:
+%
+% TransformSignature(SignatureInfo *signature_info)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the address of a structure of type SignatureInfo.
+%
+*/
+
+static inline unsigned int Ch(unsigned int x,unsigned int y,unsigned int z)
+{
+ return((x & y) ^ (~x & z));
+}
+
+static inline unsigned int Maj(unsigned int x,unsigned int y,unsigned int z)
+{
+ return((x & y) ^ (x & z) ^ (y & z));
+}
+
+static inline unsigned int Trunc32(unsigned int x)
+{
+ return((unsigned int) (x & 0xffffffffU));
+}
+
+static unsigned int RotateRight(unsigned int x,unsigned int n)
+{
+ return(Trunc32((x >> n) | (x << (32-n))));
+}
+
+static void TransformSignature(SignatureInfo *signature_info)
+{
+#define Sigma0(x) (RotateRight(x,7) ^ RotateRight(x,18) ^ Trunc32((x) >> 3))
+#define Sigma1(x) (RotateRight(x,17) ^ RotateRight(x,19) ^ Trunc32((x) >> 10))
+#define Suma0(x) (RotateRight(x,2) ^ RotateRight(x,13) ^ RotateRight(x,22))
+#define Suma1(x) (RotateRight(x,6) ^ RotateRight(x,11) ^ RotateRight(x,25))
+
+ long
+ j;
+
+ register long
+ i;
+
+ register unsigned char
+ *p;
+
+ static unsigned int
+ K[64] =
+ {
+ 0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU,
+ 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U, 0xd807aa98U, 0x12835b01U,
+ 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U,
+ 0xc19bf174U, 0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU,
+ 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU, 0x983e5152U,
+ 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U,
+ 0x06ca6351U, 0x14292967U, 0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU,
+ 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
+ 0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U,
+ 0xd6990624U, 0xf40e3585U, 0x106aa070U, 0x19a4c116U, 0x1e376c08U,
+ 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU,
+ 0x682e6ff3U, 0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U,
+ 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
+ }; /* 32-bit fractional part of the cube root of the first 64 primes */
+
+ unsigned int
+ A,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ shift,
+ T,
+ T1,
+ T2,
+ W[64];
+
+ shift=32;
+ p=GetStringInfoDatum(signature_info->message);
+ if (signature_info->lsb_first == MagickFalse)
+ {
+ if (sizeof(unsigned int) <= 4)
+ for (i=0; i < 16; i++)
+ {
+ T=(*((unsigned int *) p));
+ p+=4;
+ W[i]=Trunc32(T);
+ }
+ else
+ for (i=0; i < 16; i+=2)
+ {
+ T=(*((unsigned int *) p));
+ p+=8;
+ W[i]=Trunc32(T >> shift);
+ W[i+1]=Trunc32(T);
+ }
+ }
+ else
+ if (sizeof(unsigned int) <= 4)
+ for (i=0; i < 16; i++)
+ {
+ T=(*((unsigned int *) p));
+ p+=4;
+ W[i]=((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
+ ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
+ }
+ else
+ for (i=0; i < 16; i+=2)
+ {
+ T=(*((unsigned int *) p));
+ p+=8;
+ W[i]=((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
+ ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
+ T>>=shift;
+ W[i+1]=((T << 24) & 0xff000000) | ((T << 8) & 0x00ff0000) |
+ ((T >> 8) & 0x0000ff00) | ((T >> 24) & 0x000000ff);
+ }
+ /*
+ Copy accumulator to registers.
+ */
+ A=signature_info->accumulator[0];
+ B=signature_info->accumulator[1];
+ C=signature_info->accumulator[2];
+ D=signature_info->accumulator[3];
+ E=signature_info->accumulator[4];
+ F=signature_info->accumulator[5];
+ G=signature_info->accumulator[6];
+ H=signature_info->accumulator[7];
+ for (i=16; i < 64; i++)
+ W[i]=Trunc32(Sigma1(W[i-2])+W[i-7]+Sigma0(W[i-15])+W[i-16]);
+ for (j=0; j < 64; j++)
+ {
+ T1=Trunc32(H+Suma1(E)+Ch(E,F,G)+K[j]+W[j]);
+ T2=Trunc32(Suma0(A)+Maj(A,B,C));
+ H=G;
+ G=F;
+ F=E;
+ E=Trunc32(D+T1);
+ D=C;
+ C=B;
+ B=A;
+ A=Trunc32(T1+T2);
+ }
+ /*
+ Add registers back to accumulator.
+ */
+ signature_info->accumulator[0]=Trunc32(signature_info->accumulator[0]+A);
+ signature_info->accumulator[1]=Trunc32(signature_info->accumulator[1]+B);
+ signature_info->accumulator[2]=Trunc32(signature_info->accumulator[2]+C);
+ signature_info->accumulator[3]=Trunc32(signature_info->accumulator[3]+D);
+ signature_info->accumulator[4]=Trunc32(signature_info->accumulator[4]+E);
+ signature_info->accumulator[5]=Trunc32(signature_info->accumulator[5]+F);
+ signature_info->accumulator[6]=Trunc32(signature_info->accumulator[6]+G);
+ signature_info->accumulator[7]=Trunc32(signature_info->accumulator[7]+H);
+ /*
+ Reset working registers.
+ */
+ A=0;
+ B=0;
+ C=0;
+ D=0;
+ E=0;
+ F=0;
+ G=0;
+ H=0;
+ T=0;
+ T1=0;
+ T2=0;
+ (void) ResetMagickMemory(W,0,sizeof(W));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ U p d a t e S i g n a t u r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UpdateSignature() updates the Signature message accumulator.
+%
+% The format of the UpdateSignature method is:
+%
+% UpdateSignature(SignatureInfo *signature_info,const StringInfo *message)
+%
+% A description of each parameter follows:
+%
+% o signature_info: the address of a structure of type SignatureInfo.
+%
+% o message: the message.
+%
+*/
+MagickExport void UpdateSignature(SignatureInfo *signature_info,
+ const StringInfo *message)
+{
+ register size_t
+ i;
+
+ register unsigned char
+ *p;
+
+ size_t
+ n;
+
+ unsigned int
+ length;
+
+ /*
+ Update the Signature accumulator.
+ */
+ assert(signature_info != (SignatureInfo *) NULL);
+ assert(signature_info->signature == MagickSignature);
+ n=GetStringInfoLength(message);
+ length=Trunc32((unsigned int) (signature_info->low_order+(n << 3)));
+ if (length < signature_info->low_order)
+ signature_info->high_order++;
+ signature_info->low_order=length;
+ signature_info->high_order+=(unsigned int) (n >> 29);
+ p=GetStringInfoDatum(message);
+ if (signature_info->offset != 0)
+ {
+ i=GetStringInfoLength(signature_info->message)-signature_info->offset;
+ if (i > n)
+ i=n;
+ (void) CopyMagickMemory(GetStringInfoDatum(signature_info->message)+
+ signature_info->offset,p,i);
+ n-=i;
+ p+=i;
+ signature_info->offset+=i;
+ if (signature_info->offset !=
+ GetStringInfoLength(signature_info->message))
+ return;
+ TransformSignature(signature_info);
+ }
+ while (n >= GetStringInfoLength(signature_info->message))
+ {
+ SetStringInfoDatum(signature_info->message,p);
+ p+=GetStringInfoLength(signature_info->message);
+ n-=GetStringInfoLength(signature_info->message);
+ TransformSignature(signature_info);
+ }
+ (void) CopyMagickMemory(GetStringInfoDatum(signature_info->message),p,n);
+ signature_info->offset=n;
+ /*
+ Reset working registers.
+ */
+ i=0;
+ n=0;
+ length=0;
+}
diff --git a/magick/signature.h b/magick/signature.h
new file mode 100644
index 0000000..668d509
--- /dev/null
+++ b/magick/signature.h
@@ -0,0 +1,32 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore digital signature methods.
+*/
+#ifndef _MAGICKCORE_SIGNATURE_H
+#define _MAGICKCORE_SIGNATURE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ SignatureImage(Image *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/splay-tree.c b/magick/splay-tree.c
new file mode 100644
index 0000000..523aa95
--- /dev/null
+++ b/magick/splay-tree.c
@@ -0,0 +1,1619 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS PPPP L AAA Y Y %
+% SS P P L A A Y Y %
+% SSS PPPP L AAAAA Y %
+% SS P L A A Y %
+% SSSSS P LLLLL A A Y %
+% %
+% TTTTT RRRR EEEEE EEEEE %
+% T R R E E %
+% T RRRR EEE EEE %
+% T R R E E %
+% T R R EEEEE EEEEE %
+% %
+% %
+% MagickCore Self-adjusting Binary Search Tree Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This module implements the standard handy splay-tree methods for storing and
+% retrieving large numbers of data elements. It is loosely based on the Java
+% implementation of these algorithms.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/splay-tree.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+
+/*
+ Define declarations.
+*/
+#define MaxSplayTreeDepth 1024
+
+/*
+ Typedef declarations.
+*/
+typedef struct _NodeInfo
+{
+ void
+ *key;
+
+ void
+ *value;
+
+ struct _NodeInfo
+ *left,
+ *right;
+} NodeInfo;
+
+struct _SplayTreeInfo
+{
+ NodeInfo
+ *root;
+
+ int
+ (*compare)(const void *,const void *);
+
+ void
+ *(*relinquish_key)(void *),
+ *(*relinquish_value)(void *);
+
+ MagickBooleanType
+ balance;
+
+ void
+ *key,
+ *next;
+
+ unsigned long
+ nodes;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Forward declarations.
+*/
+static int
+ IterateOverSplayTree(SplayTreeInfo *,int (*)(NodeInfo *,const void *),
+ const void *);
+
+static void
+ SplaySplayTree(SplayTreeInfo *,const void *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d d V a l u e T o S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AddValueToSplayTree() adds a value to the splay-tree.
+%
+% The format of the AddValueToSplayTree method is:
+%
+% MagickBooleanType AddValueToSplayTree(SplayTreeInfo *splay_tree,
+% const void *key,const void *value)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o key: the key.
+%
+% o value: the value.
+%
+*/
+MagickExport MagickBooleanType AddValueToSplayTree(SplayTreeInfo *splay_tree,
+ const void *key,const void *value)
+{
+ int
+ compare;
+
+ register NodeInfo
+ *node;
+
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,key);
+ compare=0;
+ if (splay_tree->root != (NodeInfo *) NULL)
+ {
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare == 0)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(
+ splay_tree->root->key);
+ splay_tree->root->key=(void *) key;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ splay_tree->root->value=(void *) value;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+ }
+ }
+ node=(NodeInfo *) AcquireMagickMemory(sizeof(*node));
+ if (node == (NodeInfo *) NULL)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickFalse);
+ }
+ node->key=(void *) key;
+ node->value=(void *) value;
+ if (splay_tree->root == (NodeInfo *) NULL)
+ {
+ node->left=(NodeInfo *) NULL;
+ node->right=(NodeInfo *) NULL;
+ }
+ else
+ if (compare < 0)
+ {
+ node->left=splay_tree->root;
+ node->right=node->left->right;
+ node->left->right=(NodeInfo *) NULL;
+ }
+ else
+ {
+ node->right=splay_tree->root;
+ node->left=node->right->left;
+ node->right->left=(NodeInfo *) NULL;
+ }
+ splay_tree->root=node;
+ splay_tree->key=(void *) NULL;
+ splay_tree->nodes++;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B a l a n c e S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BalanceSplayTree() balances the splay-tree.
+%
+% The format of the BalanceSplayTree method is:
+%
+% void *BalanceSplayTree(SplayTreeInfo *splay_tree,const void *key)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o key: the key.
+%
+*/
+
+static NodeInfo *LinkSplayTreeNodes(NodeInfo **nodes,const unsigned long low,
+ const unsigned long high)
+{
+ register NodeInfo
+ *node;
+
+ unsigned long
+ bisect;
+
+ bisect=low+(high-low)/2;
+ node=nodes[bisect];
+ if ((low+1) > bisect)
+ node->left=(NodeInfo *) NULL;
+ else
+ node->left=LinkSplayTreeNodes(nodes,low,bisect-1);
+ if ((bisect+1) > high)
+ node->right=(NodeInfo *) NULL;
+ else
+ node->right=LinkSplayTreeNodes(nodes,bisect+1,high);
+ return(node);
+}
+
+static int SplayTreeToNodeArray(NodeInfo *node,const void *nodes)
+{
+ register const NodeInfo
+ ***p;
+
+ p=(const NodeInfo ***) nodes;
+ *(*p)=node;
+ (*p)++;
+ return(0);
+}
+
+static void BalanceSplayTree(SplayTreeInfo *splay_tree)
+{
+ NodeInfo
+ **node,
+ **nodes;
+
+ if (splay_tree->nodes <= 2)
+ {
+ splay_tree->balance=MagickFalse;
+ return;
+ }
+ nodes=(NodeInfo **) AcquireQuantumMemory((size_t) splay_tree->nodes,
+ sizeof(*nodes));
+ if (nodes == (NodeInfo **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ node=nodes;
+ (void) IterateOverSplayTree(splay_tree,SplayTreeToNodeArray,
+ (const void *) &node);
+ splay_tree->root=LinkSplayTreeNodes(nodes,0,splay_tree->nodes-1);
+ splay_tree->balance=MagickFalse;
+ nodes=(NodeInfo **) RelinquishMagickMemory(nodes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneSplayTree() clones the splay tree.
+%
+% The format of the CloneSplayTree method is:
+%
+% SplayTreeInfo *CloneSplayTree(SplayTreeInfo *splay_tree,
+% void *(*clone_key)(void *),void *(*cline_value)(void *))
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+% o clone_key: the key clone method, typically ConstantString(), called
+% whenever a key is added to the splay-tree.
+%
+% o clone_value: the value clone method; typically ConstantString(), called
+% whenever a value object is added to the splay-tree.
+%
+*/
+MagickExport SplayTreeInfo *CloneSplayTree(SplayTreeInfo *splay_tree,
+ void *(*clone_key)(void *),void *(*clone_value)(void *))
+{
+ SplayTreeInfo
+ *clone_tree;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ clone_tree=NewSplayTree(splay_tree->compare,splay_tree->relinquish_key,
+ splay_tree->relinquish_value);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ if (splay_tree->root != (NodeInfo *) NULL)
+ {
+ register NodeInfo
+ *active,
+ *next,
+ *node;
+
+ void
+ *key,
+ *value;
+
+ key=splay_tree->root->key;
+ if ((clone_key != (void *(*)(void *)) NULL) && (key != (void *) NULL))
+ key=clone_key(key);
+ value=splay_tree->root->value;
+ if ((clone_value != (void *(*)(void *)) NULL) && (value != (void *) NULL))
+ value=clone_value(value);
+ (void) AddValueToSplayTree(clone_tree,key,value);
+ for (node=splay_tree->root; node != (NodeInfo *) NULL; )
+ {
+ active=node;
+ for (node=(NodeInfo *) NULL; active != (NodeInfo *) NULL; )
+ {
+ next=(NodeInfo *) NULL;
+ if (active->left != (NodeInfo *) NULL)
+ {
+ next=node;
+ key=active->left->key;
+ if ((clone_key != (void *(*)(void *)) NULL) &&
+ (key != (void *) NULL))
+ key=clone_key(key);
+ value=active->left->value;
+ if ((clone_value != (void *(*)(void *)) NULL) &&
+ (value != (void *) NULL))
+ value=clone_value(value);
+ (void) AddValueToSplayTree(clone_tree,key,value);
+ node=active->left;
+ }
+ if (active->right != (NodeInfo *) NULL)
+ {
+ next=node;
+ key=active->right->key;
+ if ((clone_key != (void *(*)(void *)) NULL) &&
+ (key != (void *) NULL))
+ key=clone_key(key);
+ value=active->right->value;
+ if ((clone_value != (void *(*)(void *)) NULL) &&
+ (value != (void *) NULL))
+ value=clone_value(value);
+ (void) AddValueToSplayTree(clone_tree,key,value);
+ node=active->right;
+ }
+ active=next;
+ }
+ }
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(clone_tree);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e S p l a y T r e e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareSplayTreeString() method finds a node in a splay-tree based on the
+% contents of a string.
+%
+% The format of the CompareSplayTreeString method is:
+%
+% int CompareSplayTreeString(const void *target,const void *source)
+%
+% A description of each parameter follows:
+%
+% o target: the target string.
+%
+% o source: the source string.
+%
+*/
+MagickExport int CompareSplayTreeString(const void *target,const void *source)
+{
+ const char
+ *p,
+ *q;
+
+ p=(const char *) target;
+ q=(const char *) source;
+ return(LocaleCompare(p,q));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e S p l a y T r e e S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareSplayTreeStringInfo() finds a node in a splay-tree based on the
+% contents of a string.
+%
+% The format of the CompareSplayTreeStringInfo method is:
+%
+% int CompareSplayTreeStringInfo(const void *target,const void *source)
+%
+% A description of each parameter follows:
+%
+% o target: the target string.
+%
+% o source: the source string.
+%
+*/
+MagickExport int CompareSplayTreeStringInfo(const void *target,
+ const void *source)
+{
+ const StringInfo
+ *p,
+ *q;
+
+ p=(const StringInfo *) target;
+ q=(const StringInfo *) source;
+ return(CompareStringInfo(p,q));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e N o d e B y V a l u e F r o m S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteNodeByValueFromSplayTree() deletes a node by value from the
+% splay-tree.
+%
+% The format of the DeleteNodeByValueFromSplayTree method is:
+%
+% MagickBooleanType DeleteNodeByValueFromSplayTree(
+% SplayTreeInfo *splay_tree,const void *value)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o value: the value.
+%
+*/
+
+static void *GetFirstSplayTreeNode(SplayTreeInfo *splay_tree)
+{
+ register NodeInfo
+ *node;
+
+ node=splay_tree->root;
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return((NodeInfo *) NULL);
+ while (node->left != (NodeInfo *) NULL)
+ node=node->left;
+ return(node->key);
+}
+
+MagickExport MagickBooleanType DeleteNodeByValueFromSplayTree(
+ SplayTreeInfo *splay_tree,const void *value)
+{
+ register NodeInfo
+ *next,
+ *node;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ if (splay_tree->root == (NodeInfo *) NULL)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickFalse);
+ }
+ next=(NodeInfo *) GetFirstSplayTreeNode(splay_tree);
+ while (next != (NodeInfo *) NULL)
+ {
+ SplaySplayTree(splay_tree,next);
+ next=(NodeInfo *) NULL;
+ node=splay_tree->root->right;
+ if (node != (NodeInfo *) NULL)
+ {
+ while (node->left != (NodeInfo *) NULL)
+ node=node->left;
+ next=(NodeInfo *) node->key;
+ }
+ if (splay_tree->root->value == value)
+ {
+ int
+ compare;
+
+ register NodeInfo
+ *left,
+ *right;
+
+ void
+ *key;
+
+ /*
+ We found the node that matches the value; now delete it.
+ */
+ key=splay_tree->root->key;
+ SplaySplayTree(splay_tree,key);
+ splay_tree->key=(void *) NULL;
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare != 0)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickFalse);
+ }
+ left=splay_tree->root->left;
+ right=splay_tree->root->right;
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(
+ splay_tree->root->key);
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ splay_tree->root=(NodeInfo *) RelinquishMagickMemory(splay_tree->root);
+ splay_tree->nodes--;
+ if (left == (NodeInfo *) NULL)
+ {
+ splay_tree->root=right;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+ }
+ splay_tree->root=left;
+ if (right != (NodeInfo *) NULL)
+ {
+ while (left->right != (NodeInfo *) NULL)
+ left=left->right;
+ left->right=right;
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+ }
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e l e t e N o d e F r o m S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DeleteNodeFromSplayTree() deletes a node from the splay-tree.
+%
+% The format of the DeleteNodeFromSplayTree method is:
+%
+% MagickBooleanType DeleteNodeFromSplayTree(SplayTreeInfo *splay_tree,
+% const void *key)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o key: the key.
+%
+*/
+MagickExport MagickBooleanType DeleteNodeFromSplayTree(
+ SplayTreeInfo *splay_tree,const void *key)
+{
+ int
+ compare;
+
+ register NodeInfo
+ *left,
+ *right;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return(MagickFalse);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,key);
+ splay_tree->key=(void *) NULL;
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare != 0)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickFalse);
+ }
+ left=splay_tree->root->left;
+ right=splay_tree->root->right;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(splay_tree->root->key);
+ splay_tree->root=(NodeInfo *) RelinquishMagickMemory(splay_tree->root);
+ splay_tree->nodes--;
+ if (left == (NodeInfo *) NULL)
+ {
+ splay_tree->root=right;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+ }
+ splay_tree->root=left;
+ if (right != (NodeInfo *) NULL)
+ {
+ while (left->right != (NodeInfo *) NULL)
+ left=left->right;
+ left->right=right;
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroySplayTree() destroys the splay-tree.
+%
+% The format of the DestroySplayTree method is:
+%
+% SplayTreeInfo *DestroySplayTree(SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+*/
+MagickExport SplayTreeInfo *DestroySplayTree(SplayTreeInfo *splay_tree)
+{
+ NodeInfo
+ *node;
+
+ register NodeInfo
+ *active,
+ *pend;
+
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ if (splay_tree->root != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(splay_tree->root->key);
+ splay_tree->root->key=(void *) NULL;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ for (pend=splay_tree->root; pend != (NodeInfo *) NULL; )
+ {
+ active=pend;
+ for (pend=(NodeInfo *) NULL; active != (NodeInfo *) NULL; )
+ {
+ if (active->left != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (active->left->key != (void *) NULL))
+ active->left->key=splay_tree->relinquish_key(active->left->key);
+ active->left->key=(void *) pend;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (active->left->value != (void *) NULL))
+ active->left->value=splay_tree->relinquish_value(
+ active->left->value);
+ pend=active->left;
+ }
+ if (active->right != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (active->right->key != (void *) NULL))
+ active->right->key=splay_tree->relinquish_key(
+ active->right->key);
+ active->right->key=(void *) pend;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (active->right->value != (void *) NULL))
+ active->right->value=splay_tree->relinquish_value(
+ active->right->value);
+ pend=active->right;
+ }
+ node=active;
+ active=(NodeInfo *) node->key;
+ node=(NodeInfo *) RelinquishMagickMemory(node);
+ }
+ }
+ }
+ splay_tree->signature=(~MagickSignature);
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ DestroySemaphoreInfo(&splay_tree->semaphore);
+ splay_tree=(SplayTreeInfo *) RelinquishMagickMemory(splay_tree);
+ return(splay_tree);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t K e y I n S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextKeyInSplayTree() gets the next key in the splay-tree.
+%
+% The format of the GetNextKeyInSplayTree method is:
+%
+% void *GetNextKeyInSplayTree(SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+% o key: the key.
+%
+*/
+MagickExport void *GetNextKeyInSplayTree(SplayTreeInfo *splay_tree)
+{
+ register NodeInfo
+ *node;
+
+ void
+ *key;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((splay_tree->root == (NodeInfo *) NULL) ||
+ (splay_tree->next == (void *) NULL))
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,splay_tree->next);
+ splay_tree->next=(void *) NULL;
+ node=splay_tree->root->right;
+ if (node != (NodeInfo *) NULL)
+ {
+ while (node->left != (NodeInfo *) NULL)
+ node=node->left;
+ splay_tree->next=node->key;
+ }
+ key=splay_tree->root->key;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(key);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t V a l u e I n S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextValueInSplayTree() gets the next value in the splay-tree.
+%
+% The format of the GetNextValueInSplayTree method is:
+%
+% void *GetNextValueInSplayTree(SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+% o key: the key.
+%
+*/
+MagickExport void *GetNextValueInSplayTree(SplayTreeInfo *splay_tree)
+{
+ register NodeInfo
+ *node;
+
+ void
+ *value;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if ((splay_tree->root == (NodeInfo *) NULL) ||
+ (splay_tree->next == (void *) NULL))
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,splay_tree->next);
+ splay_tree->next=(void *) NULL;
+ node=splay_tree->root->right;
+ if (node != (NodeInfo *) NULL)
+ {
+ while (node->left != (NodeInfo *) NULL)
+ node=node->left;
+ splay_tree->next=node->key;
+ }
+ value=splay_tree->root->value;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t V a l u e F r o m S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetValueFromSplayTree() gets a value from the splay-tree by its key.
+%
+% The format of the GetValueFromSplayTree method is:
+%
+% void *GetValueFromSplayTree(SplayTreeInfo *splay_tree,const void *key)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+% o key: the key.
+%
+*/
+MagickExport void *GetValueFromSplayTree(SplayTreeInfo *splay_tree,
+ const void *key)
+{
+ int
+ compare;
+
+ void
+ *value;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return((void *) NULL);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,key);
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare != 0)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return((void *) NULL);
+ }
+ value=splay_tree->root->value;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N u m b e r O f N o d e s I n S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNumberOfNodesInSplayTree() returns the number of nodes in the splay-tree.
+%
+% The format of the GetNumberOfNodesInSplayTree method is:
+%
+% unsigned long GetNumberOfNodesInSplayTree(
+% const SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+*/
+MagickExport unsigned long GetNumberOfNodesInSplayTree(
+ const SplayTreeInfo *splay_tree)
+{
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(splay_tree->nodes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I t e r a t e O v e r S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IterateOverSplayTree() iterates over the splay-tree.
+%
+% The format of the IterateOverSplayTree method is:
+%
+% int IterateOverSplayTree(SplayTreeInfo *splay_tree,
+% int (*method)(NodeInfo *,void *),const void *value)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o method: the method.
+%
+% o value: the value.
+%
+*/
+static int IterateOverSplayTree(SplayTreeInfo *splay_tree,
+ int (*method)(NodeInfo *,const void *),const void *value)
+{
+ typedef enum
+ {
+ LeftTransition,
+ RightTransition,
+ DownTransition,
+ UpTransition
+ } TransitionType;
+
+ int
+ status;
+
+ MagickBooleanType
+ final_transition;
+
+ NodeInfo
+ **nodes;
+
+ register long
+ i;
+
+ register NodeInfo
+ *node;
+
+ TransitionType
+ transition;
+
+ unsigned char
+ *transitions;
+
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return(0);
+ nodes=(NodeInfo **) AcquireQuantumMemory((size_t) splay_tree->nodes,
+ sizeof(*nodes));
+ transitions=(unsigned char *) AcquireQuantumMemory((size_t) splay_tree->nodes,
+ sizeof(*transitions));
+ if ((nodes == (NodeInfo **) NULL) || (transitions == (unsigned char *) NULL))
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ status=0;
+ final_transition=MagickFalse;
+ nodes[0]=splay_tree->root;
+ transitions[0]=(unsigned char) LeftTransition;
+ for (i=0; final_transition == MagickFalse; )
+ {
+ node=nodes[i];
+ transition=(TransitionType) transitions[i];
+ switch (transition)
+ {
+ case LeftTransition:
+ {
+ transitions[i]=(unsigned char) DownTransition;
+ if (node->left == (NodeInfo *) NULL)
+ break;
+ i++;
+ nodes[i]=node->left;
+ transitions[i]=(unsigned char) LeftTransition;
+ break;
+ }
+ case RightTransition:
+ {
+ transitions[i]=(unsigned char) UpTransition;
+ if (node->right == (NodeInfo *) NULL)
+ break;
+ i++;
+ nodes[i]=node->right;
+ transitions[i]=(unsigned char) LeftTransition;
+ break;
+ }
+ case DownTransition:
+ default:
+ {
+ transitions[i]=(unsigned char) RightTransition;
+ status=(*method)(node,value);
+ if (status != 0)
+ final_transition=MagickTrue;
+ break;
+ }
+ case UpTransition:
+ {
+ if (i == 0)
+ {
+ final_transition=MagickTrue;
+ break;
+ }
+ i--;
+ break;
+ }
+ }
+ }
+ nodes=(NodeInfo **) RelinquishMagickMemory(nodes);
+ transitions=(unsigned char *) RelinquishMagickMemory(transitions);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewSplayTree() returns a pointer to a SplayTreeInfo structure initialized
+% to default values.
+%
+% The format of the NewSplayTree method is:
+%
+% SplayTreeInfo *NewSplayTree(int (*compare)(const void *,const void *),
+% void *(*relinquish_key)(void *),void *(*relinquish_value)(void *))
+%
+% A description of each parameter follows:
+%
+% o compare: the compare method.
+%
+% o relinquish_key: the key deallocation method, typically
+% RelinquishMagickMemory(), called whenever a key is removed from the
+% splay-tree.
+%
+% o relinquish_value: the value deallocation method; typically
+% RelinquishMagickMemory(), called whenever a value object is removed from
+% the splay-tree.
+%
+*/
+MagickExport SplayTreeInfo *NewSplayTree(
+ int (*compare)(const void *,const void *),void *(*relinquish_key)(void *),
+ void *(*relinquish_value)(void *))
+{
+ SplayTreeInfo
+ *splay_tree;
+
+ splay_tree=(SplayTreeInfo *) AcquireMagickMemory(sizeof(*splay_tree));
+ if (splay_tree == (SplayTreeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(splay_tree,0,sizeof(*splay_tree));
+ splay_tree->root=(NodeInfo *) NULL;
+ splay_tree->compare=compare;
+ splay_tree->relinquish_key=relinquish_key;
+ splay_tree->relinquish_value=relinquish_value;
+ splay_tree->balance=MagickFalse;
+ splay_tree->key=(void *) NULL;
+ splay_tree->next=(void *) NULL;
+ splay_tree->nodes=0;
+ splay_tree->debug=IsEventLogging();
+ splay_tree->semaphore=AllocateSemaphoreInfo();
+ splay_tree->signature=MagickSignature;
+ return(splay_tree);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e N o d e B y V a l u e F r o m S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveNodeByValueFromSplayTree() removes a node by value from the splay-tree
+% and returns its key.
+%
+% The format of the RemoveNodeByValueFromSplayTree method is:
+%
+% void *RemoveNodeByValueFromSplayTree(SplayTreeInfo *splay_tree,
+% const void *value)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o value: the value.
+%
+*/
+MagickExport void *RemoveNodeByValueFromSplayTree(SplayTreeInfo *splay_tree,
+ const void *value)
+{
+ register NodeInfo
+ *next,
+ *node;
+
+ void
+ *key;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ key=(void *) NULL;
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return(key);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ next=(NodeInfo *) GetFirstSplayTreeNode(splay_tree);
+ while (next != (NodeInfo *) NULL)
+ {
+ SplaySplayTree(splay_tree,next);
+ next=(NodeInfo *) NULL;
+ node=splay_tree->root->right;
+ if (node != (NodeInfo *) NULL)
+ {
+ while (node->left != (NodeInfo *) NULL)
+ node=node->left;
+ next=(NodeInfo *) node->key;
+ }
+ if (splay_tree->root->value == value)
+ {
+ int
+ compare;
+
+ register NodeInfo
+ *left,
+ *right;
+
+ /*
+ We found the node that matches the value; now remove it.
+ */
+ key=splay_tree->root->key;
+ SplaySplayTree(splay_tree,key);
+ splay_tree->key=(void *) NULL;
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare != 0)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(key);
+ }
+ left=splay_tree->root->left;
+ right=splay_tree->root->right;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ splay_tree->root=(NodeInfo *) RelinquishMagickMemory(splay_tree->root);
+ splay_tree->nodes--;
+ if (left == (NodeInfo *) NULL)
+ {
+ splay_tree->root=right;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(key);
+ }
+ splay_tree->root=left;
+ if (right != (NodeInfo *) NULL)
+ {
+ while (left->right != (NodeInfo *) NULL)
+ left=left->right;
+ left->right=right;
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(key);
+ }
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(key);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e m o v e N o d e F r o m S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RemoveNodeFromSplayTree() removes a node from the splay-tree and returns its
+% value.
+%
+% The format of the RemoveNodeFromSplayTree method is:
+%
+% void *RemoveNodeFromSplayTree(SplayTreeInfo *splay_tree,const void *key)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o key: the key.
+%
+*/
+MagickExport void *RemoveNodeFromSplayTree(SplayTreeInfo *splay_tree,
+ const void *key)
+{
+ int
+ compare;
+
+ register NodeInfo
+ *left,
+ *right;
+
+ void
+ *value;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ value=(void *) NULL;
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return(value);
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ SplaySplayTree(splay_tree,key);
+ splay_tree->key=(void *) NULL;
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->root->key > key) ? 1 :
+ ((splay_tree->root->key < key) ? -1 : 0);
+ if (compare != 0)
+ {
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(value);
+ }
+ left=splay_tree->root->left;
+ right=splay_tree->root->right;
+ value=splay_tree->root->value;
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(splay_tree->root->key);
+ splay_tree->root=(NodeInfo *) RelinquishMagickMemory(splay_tree->root);
+ splay_tree->nodes--;
+ if (left == (NodeInfo *) NULL)
+ {
+ splay_tree->root=right;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(value);
+ }
+ splay_tree->root=left;
+ if (right != (NodeInfo *) NULL)
+ {
+ while (left->right != (NodeInfo *) NULL)
+ left=left->right;
+ left->right=right;
+ }
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetSplayTree() resets the splay-tree. That is, it deletes all the nodes
+% from the tree.
+%
+% The format of the ResetSplayTree method is:
+%
+% ResetSplayTree(SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+*/
+MagickExport void ResetSplayTree(SplayTreeInfo *splay_tree)
+{
+ NodeInfo
+ *node;
+
+ register NodeInfo
+ *active,
+ *pend;
+
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ if (splay_tree->root != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->key != (void *) NULL))
+ splay_tree->root->key=splay_tree->relinquish_key(splay_tree->root->key);
+ splay_tree->root->key=(void *) NULL;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (splay_tree->root->value != (void *) NULL))
+ splay_tree->root->value=splay_tree->relinquish_value(
+ splay_tree->root->value);
+ for (pend=splay_tree->root; pend != (NodeInfo *) NULL; )
+ {
+ active=pend;
+ for (pend=(NodeInfo *) NULL; active != (NodeInfo *) NULL; )
+ {
+ if (active->left != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (active->left->key != (void *) NULL))
+ active->left->key=splay_tree->relinquish_key(active->left->key);
+ active->left->key=(void *) pend;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (active->left->value != (void *) NULL))
+ active->left->value=splay_tree->relinquish_value(
+ active->left->value);
+ pend=active->left;
+ }
+ if (active->right != (NodeInfo *) NULL)
+ {
+ if ((splay_tree->relinquish_key != (void *(*)(void *)) NULL) &&
+ (active->right->key != (void *) NULL))
+ active->right->key=splay_tree->relinquish_key(
+ active->right->key);
+ active->right->key=(void *) pend;
+ if ((splay_tree->relinquish_value != (void *(*)(void *)) NULL) &&
+ (active->right->value != (void *) NULL))
+ active->right->value=splay_tree->relinquish_value(
+ active->right->value);
+ pend=active->right;
+ }
+ node=active;
+ active=(NodeInfo *) node->key;
+ node=(NodeInfo *) RelinquishMagickMemory(node);
+ }
+ }
+ }
+ splay_tree->root=(NodeInfo *) NULL;
+ splay_tree->key=(void *) NULL;
+ splay_tree->next=(void *) NULL;
+ splay_tree->nodes=0;
+ splay_tree->balance=MagickFalse;
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t S p l a y T r e e I t e r a t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetSplayTreeIterator() resets the splay-tree iterator. Use it in
+% conjunction with GetNextValueInSplayTree() to iterate over all the nodes in
+% the splay-tree.
+%
+% The format of the ResetSplayTreeIterator method is:
+%
+% ResetSplayTreeIterator(SplayTreeInfo *splay_tree)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay tree.
+%
+*/
+MagickExport void ResetSplayTreeIterator(SplayTreeInfo *splay_tree)
+{
+ assert(splay_tree != (SplayTreeInfo *) NULL);
+ assert(splay_tree->signature == MagickSignature);
+ if (splay_tree->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ (void) LockSemaphoreInfo(splay_tree->semaphore);
+ splay_tree->next=GetFirstSplayTreeNode(splay_tree);
+ (void) UnlockSemaphoreInfo(splay_tree->semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l a y S p l a y T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SplaySplayTree() splays the splay-tree.
+%
+% The format of the SplaySplayTree method is:
+%
+% void SplaySplayTree(SplayTreeInfo *splay_tree,const void *key,
+% NodeInfo **node,NodeInfo **parent,NodeInfo **grandparent)
+%
+% A description of each parameter follows:
+%
+% o splay_tree: the splay-tree info.
+%
+% o key: the key.
+%
+% o node: the node.
+%
+% o parent: the parent node.
+%
+% o grandparent: the grandparent node.
+%
+*/
+
+static NodeInfo *Splay(SplayTreeInfo *splay_tree,const unsigned long depth,
+ const void *key,NodeInfo **node,NodeInfo **parent,NodeInfo **grandparent)
+{
+ int
+ compare;
+
+ NodeInfo
+ **next;
+
+ register NodeInfo
+ *n,
+ *p;
+
+ n=(*node);
+ if (n == (NodeInfo *) NULL)
+ return(*parent);
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(n->key,key);
+ else
+ compare=(n->key > key) ? 1 : ((n->key < key) ? -1 : 0);
+ next=(NodeInfo **) NULL;
+ if (compare > 0)
+ next=(&n->left);
+ else
+ if (compare < 0)
+ next=(&n->right);
+ if (next != (NodeInfo **) NULL)
+ {
+ if (depth >= MaxSplayTreeDepth)
+ {
+ splay_tree->balance=MagickTrue;
+ return(n);
+ }
+ n=Splay(splay_tree,depth+1,key,next,node,parent);
+ if ((n != *node) || (splay_tree->balance != MagickFalse))
+ return(n);
+ }
+ if (parent == (NodeInfo **) NULL)
+ return(n);
+ if (grandparent == (NodeInfo **) NULL)
+ {
+ if (n == (*parent)->left)
+ {
+ *node=n->right;
+ n->right=(*parent);
+ }
+ else
+ {
+ *node=n->left;
+ n->left=(*parent);
+ }
+ *parent=n;
+ return(n);
+ }
+ if ((n == (*parent)->left) && (*parent == (*grandparent)->left))
+ {
+ p=(*parent);
+ (*grandparent)->left=p->right;
+ p->right=(*grandparent);
+ p->left=n->right;
+ n->right=p;
+ *grandparent=n;
+ return(n);
+ }
+ if ((n == (*parent)->right) && (*parent == (*grandparent)->right))
+ {
+ p=(*parent);
+ (*grandparent)->right=p->left;
+ p->left=(*grandparent);
+ p->right=n->left;
+ n->left=p;
+ *grandparent=n;
+ return(n);
+ }
+ if (n == (*parent)->left)
+ {
+ (*parent)->left=n->right;
+ n->right=(*parent);
+ (*grandparent)->right=n->left;
+ n->left=(*grandparent);
+ *grandparent=n;
+ return(n);
+ }
+ (*parent)->right=n->left;
+ n->left=(*parent);
+ (*grandparent)->left=n->right;
+ n->right=(*grandparent);
+ *grandparent=n;
+ return(n);
+}
+
+static void SplaySplayTree(SplayTreeInfo *splay_tree,const void *key)
+{
+ if (splay_tree->root == (NodeInfo *) NULL)
+ return;
+ if (splay_tree->key != (void *) NULL)
+ {
+ int
+ compare;
+
+ if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
+ compare=splay_tree->compare(splay_tree->root->key,key);
+ else
+ compare=(splay_tree->key > key) ? 1 :
+ ((splay_tree->key < key) ? -1 : 0);
+ if (compare == 0)
+ return;
+ }
+ (void) Splay(splay_tree,0UL,key,&splay_tree->root,(NodeInfo **) NULL,
+ (NodeInfo **) NULL);
+ if (splay_tree->balance != MagickFalse)
+ {
+ BalanceSplayTree(splay_tree);
+ (void) Splay(splay_tree,0UL,key,&splay_tree->root,(NodeInfo **) NULL,
+ (NodeInfo **) NULL);
+ if (splay_tree->balance != MagickFalse)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ }
+ splay_tree->key=(void *) key;
+}
diff --git a/magick/splay-tree.h b/magick/splay-tree.h
new file mode 100644
index 0000000..c407875
--- /dev/null
+++ b/magick/splay-tree.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore splay-tree methods.
+*/
+#ifndef _MAGICKCORE_SPLAY_H
+#define _MAGICKCORE_SPLAY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _SplayTreeInfo
+ SplayTreeInfo;
+
+extern MagickExport MagickBooleanType
+ AddValueToSplayTree(SplayTreeInfo *,const void *,const void *),
+ DeleteNodeByValueFromSplayTree(SplayTreeInfo *,const void *),
+ DeleteNodeFromSplayTree(SplayTreeInfo *,const void *);
+
+extern MagickExport int
+ CompareSplayTreeString(const void *,const void *),
+ CompareSplayTreeStringInfo(const void *,const void *);
+
+extern MagickExport SplayTreeInfo
+ *CloneSplayTree(SplayTreeInfo *,void *(*)(void *),void *(*)(void *)),
+ *DestroySplayTree(SplayTreeInfo *),
+ *NewSplayTree(int (*)(const void *,const void *),void *(*)(void *),
+ void *(*)(void *));
+
+extern MagickExport unsigned long
+ GetNumberOfNodesInSplayTree(const SplayTreeInfo *);
+
+extern MagickExport void
+ *GetNextKeyInSplayTree(SplayTreeInfo *),
+ *GetNextValueInSplayTree(SplayTreeInfo *),
+ *GetValueFromSplayTree(SplayTreeInfo *,const void *),
+ *RemoveNodeByValueFromSplayTree(SplayTreeInfo *,const void *),
+ *RemoveNodeFromSplayTree(SplayTreeInfo *,const void *),
+ ResetSplayTree(SplayTreeInfo *),
+ ResetSplayTreeIterator(SplayTreeInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/stamp-h.in b/magick/stamp-h.in
new file mode 100644
index 0000000..9788f70
--- /dev/null
+++ b/magick/stamp-h.in
@@ -0,0 +1 @@
+timestamp
diff --git a/magick/stamp-h1 b/magick/stamp-h1
new file mode 100644
index 0000000..7bb7372
--- /dev/null
+++ b/magick/stamp-h1
@@ -0,0 +1 @@
+timestamp for magick/magick-config.h
diff --git a/magick/static.c b/magick/static.c
new file mode 100644
index 0000000..63130fe
--- /dev/null
+++ b/magick/static.c
@@ -0,0 +1,469 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS TTTTT AAA TTTTT IIIII CCCC %
+% SS T A A T I C %
+% SSS T AAAAA T I C %
+% SS T A A T I C %
+% SSSSS T A A T IIIII CCCC %
+% %
+% %
+% MagickCore Static Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/module.h"
+#include "magick/policy.h"
+#include "magick/static.h"
+#include "magick/string_.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n v o k e S t a t i c I m a g e F i l t e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InvokeStaticImageFilter() invokes a static image filter.
+%
+% The format of the InvokeStaticImageFilter method is:
+%
+% MagickBooleanType InvokeStaticImageFilter(const char *tag,Image **image,
+% const int argc,const char **argv)
+%
+% A description of each parameter follows:
+%
+% o tag: the module tag.
+%
+% o image: the image.
+%
+% o argc: the number of elements in the argument vector.
+%
+% o argv: A text array containing the command line arguments.
+%
+% o argv: A text array containing the command line arguments.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+#if defined(MAGICKCORE_MODULES_SUPPORT)
+MagickExport MagickBooleanType InvokeStaticImageFilter(const char *tag,
+ Image **image,const int argc,const char **argv,ExceptionInfo *exception)
+{
+ PolicyRights
+ rights;
+
+ assert(image != (Image **) NULL);
+ assert((*image)->signature == MagickSignature);
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
+ rights=ReadPolicyRights;
+ if (IsRightsAuthorized(FilterPolicyDomain,rights,tag) == MagickFalse)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),PolicyError,
+ "NotAuthorized","`%s'",tag);
+ return(MagickFalse);
+ }
+#if defined(MAGICKCORE_BUILD_MODULES)
+ (void) tag;
+ (void) argc;
+ (void) argv;
+ (void) exception;
+#else
+ {
+ extern unsigned long
+ analyzeImage(Image **,const int,char **,ExceptionInfo *);
+
+ ImageFilterHandler
+ *image_filter;
+
+ image_filter=(ImageFilterHandler *) NULL;
+ if (LocaleCompare("analyze",tag) == 0)
+ image_filter=analyzeImage;
+ if (image_filter != (ImageFilterHandler *) NULL)
+ {
+ unsigned long
+ signature;
+
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+ "Invoking \"%s\" static image filter",tag);
+ signature=image_filter(image,argc,argv,exception);
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(CoderEvent,GetMagickModule(),"\"%s\" completes",
+ tag);
+ if (signature != MagickImageFilterSignature)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),ModuleError,
+ "ImageFilterSignatureMismatch","`%s': %8lx != %8lx",tag,signature,
+ MagickImageFilterSignature);
+ return(MagickFalse);
+ }
+ }
+ }
+#endif
+ return(MagickTrue);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e g i s t e r S t a t i c M o d u l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% (void) RegisterStaticModules() statically registers all the available module
+% handlers.
+%
+% The format of the RegisterStaticModules method is:
+%
+% (void) RegisterStaticModules(void)
+%
+*/
+MagickExport void RegisterStaticModules(void)
+{
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ (void) RegisterARTImage();
+ (void) RegisterAVIImage();
+ (void) RegisterAVSImage();
+ (void) RegisterBMPImage();
+ (void) RegisterCAPTIONImage();
+ (void) RegisterCINImage();
+ (void) RegisterCIPImage();
+ (void) RegisterCLIPImage();
+#if defined(MAGICKCORE_WINGDI32_DELEGATE)
+ (void) RegisterCLIPBOARDImage();
+#endif
+ (void) RegisterCMYKImage();
+ (void) RegisterCUTImage();
+ (void) RegisterDCMImage();
+ (void) RegisterDDSImage();
+ (void) RegisterDIBImage();
+#if defined(MAGICKCORE_DJVU_DELEGATE)
+ (void) RegisterDJVUImage();
+#endif
+ (void) RegisterDNGImage();
+ (void) RegisterDPSImage();
+ (void) RegisterDPXImage();
+#if defined(MAGICKCORE_WINGDI32_DELEGATE)
+ (void) RegisterEMFImage();
+#endif
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+ (void) RegisterEPTImage();
+#endif
+#if defined(MAGICKCORE_OPENEXR_DELEGATE)
+ (void) RegisterEXRImage();
+#endif
+ (void) RegisterFAXImage();
+ (void) RegisterFITSImage();
+#if defined(MAGICKCORE_FPX_DELEGATE)
+ (void) RegisterFPXImage();
+#endif
+ (void) RegisterGIFImage();
+ (void) RegisterGRAYImage();
+ (void) RegisterGRADIENTImage();
+ (void) RegisterHISTOGRAMImage();
+ (void) RegisterHRZImage();
+ (void) RegisterHTMLImage();
+ (void) RegisterICONImage();
+ (void) RegisterINFOImage();
+ (void) RegisterINLINEImage();
+ (void) RegisterIPLImage();
+#if defined(MAGICKCORE_JBIG_DELEGATE)
+ (void) RegisterJBIGImage();
+#endif
+#if defined(MAGICKCORE_JPEG_DELEGATE)
+ (void) RegisterJPEGImage();
+#endif
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ (void) RegisterJP2Image();
+#endif
+ (void) RegisterLABELImage();
+ (void) RegisterMAGICKImage();
+ (void) RegisterMAPImage();
+ (void) RegisterMATImage();
+ (void) RegisterMATTEImage();
+ (void) RegisterMETAImage();
+ (void) RegisterMIFFImage();
+ (void) RegisterMONOImage();
+ (void) RegisterMPCImage();
+ (void) RegisterMPEGImage();
+ (void) RegisterMPRImage();
+ (void) RegisterMSLImage();
+ (void) RegisterMTVImage();
+ (void) RegisterMVGImage();
+ (void) RegisterNULLImage();
+ (void) RegisterOTBImage();
+ (void) RegisterPALMImage();
+ (void) RegisterPATTERNImage();
+ (void) RegisterPCDImage();
+ (void) RegisterPCLImage();
+ (void) RegisterPCXImage();
+ (void) RegisterPDBImage();
+ (void) RegisterPDFImage();
+ (void) RegisterPICTImage();
+ (void) RegisterPIXImage();
+ (void) RegisterPLASMAImage();
+#if defined(MAGICKCORE_PNG_DELEGATE)
+ (void) RegisterPNGImage();
+#endif
+ (void) RegisterPNMImage();
+ (void) RegisterPREVIEWImage();
+ (void) RegisterPSImage();
+ (void) RegisterPS2Image();
+ (void) RegisterPS3Image();
+ (void) RegisterPSDImage();
+ (void) RegisterPWPImage();
+ (void) RegisterRAWImage();
+ (void) RegisterRGBImage();
+ (void) RegisterRLAImage();
+ (void) RegisterRLEImage();
+ (void) RegisterSCRImage();
+ (void) RegisterSCTImage();
+ (void) RegisterSFWImage();
+ (void) RegisterSGIImage();
+ (void) RegisterSTEGANOImage();
+ (void) RegisterSUNImage();
+ (void) RegisterSVGImage();
+ (void) RegisterTGAImage();
+ (void) RegisterTHUMBNAILImage();
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+ (void) RegisterTIFFImage();
+#endif
+ (void) RegisterTILEImage();
+ (void) RegisterTIMImage();
+ (void) RegisterTTFImage();
+ (void) RegisterTXTImage();
+ (void) RegisterUILImage();
+ (void) RegisterURLImage();
+ (void) RegisterUYVYImage();
+ (void) RegisterVICARImage();
+ (void) RegisterVIDImage();
+ (void) RegisterVIFFImage();
+ (void) RegisterWBMPImage();
+#if defined(MAGICKCORE_WMF_DELEGATE) || defined(MAGICKCORE_WMFLITE_DELEGATE)
+ (void) RegisterWMFImage();
+#endif
+ (void) RegisterWPGImage();
+#if defined(MAGICKCORE_X11_DELEGATE)
+ (void) RegisterXImage();
+#endif
+ (void) RegisterXBMImage();
+ (void) RegisterXCImage();
+ (void) RegisterXCFImage();
+ (void) RegisterXPMImage();
+ (void) RegisterXPSImage();
+#if defined(_VISUALC_)
+ (void) RegisterXTRNImage();
+#endif
+#if defined(MAGICKCORE_X11_DELEGATE)
+ (void) RegisterXWDImage();
+#endif
+ (void) RegisterYCBCRImage();
+ (void) RegisterYUVImage();
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U n r e g i s t e r S t a t i c M o d u l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UnregisterStaticModules() statically unregisters all the available module
+% handlers.
+%
+% The format of the UnregisterStaticModules method is:
+%
+% UnregisterStaticModules(void)
+%
+*/
+MagickExport void UnregisterStaticModules(void)
+{
+#if !defined(MAGICKCORE_BUILD_MODULES)
+ UnregisterARTImage();
+ UnregisterAVIImage();
+ UnregisterAVSImage();
+ UnregisterBMPImage();
+ UnregisterBRAILLEImage();
+ UnregisterCAPTIONImage();
+ UnregisterCINImage();
+ UnregisterCIPImage();
+ UnregisterCLIPImage();
+#if defined(MAGICKCORE_WINGDI32_DELEGATE)
+ UnregisterCLIPBOARDImage();
+#endif
+ UnregisterCMYKImage();
+ UnregisterCUTImage();
+ UnregisterDCMImage();
+ UnregisterDDSImage();
+ UnregisterDIBImage();
+#if defined(MAGICKCORE_DJVU_DELEGATE)
+ UnregisterDJVUImage();
+#endif
+ UnregisterDNGImage();
+ UnregisterDPSImage();
+ UnregisterDPXImage();
+#if defined(MAGICKCORE_WINGDI32_DELEGATE)
+ UnregisterEMFImage();
+#endif
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+ UnregisterEPTImage();
+#endif
+#if defined(MAGICKCORE_OPENEXR_DELEGATE)
+ UnregisterEXRImage();
+#endif
+ UnregisterFAXImage();
+ UnregisterFITSImage();
+#if defined(MAGICKCORE_FPX_DELEGATE)
+ UnregisterFPXImage();
+#endif
+ UnregisterGIFImage();
+ UnregisterGRAYImage();
+ UnregisterGRADIENTImage();
+ UnregisterHISTOGRAMImage();
+ UnregisterHRZImage();
+ UnregisterHTMLImage();
+ UnregisterICONImage();
+ UnregisterINFOImage();
+ UnregisterINLINEImage();
+ UnregisterIPLImage();
+#if defined(MAGICKCORE_JBIG_DELEGATE)
+ UnregisterJBIGImage();
+#endif
+#if defined(MAGICKCORE_JPEG_DELEGATE)
+ UnregisterJPEGImage();
+#endif
+#if defined(MAGICKCORE_JP2_DELEGATE)
+ UnregisterJP2Image();
+#endif
+ UnregisterLABELImage();
+ UnregisterMAGICKImage();
+ UnregisterMAPImage();
+ UnregisterMATImage();
+ UnregisterMATTEImage();
+ UnregisterMETAImage();
+ UnregisterMIFFImage();
+ UnregisterMONOImage();
+ UnregisterMPCImage();
+ UnregisterMPEGImage();
+ UnregisterMPRImage();
+ UnregisterMSLImage();
+ UnregisterMTVImage();
+ UnregisterMVGImage();
+ UnregisterNULLImage();
+ UnregisterOTBImage();
+ UnregisterPALMImage();
+ UnregisterPATTERNImage();
+ UnregisterPCDImage();
+ UnregisterPCLImage();
+ UnregisterPCXImage();
+ UnregisterPDBImage();
+ UnregisterPDFImage();
+ UnregisterPICTImage();
+ UnregisterPIXImage();
+ UnregisterPLASMAImage();
+#if defined(MAGICKCORE_PNG_DELEGATE)
+ UnregisterPNGImage();
+#endif
+ UnregisterPNMImage();
+ UnregisterPREVIEWImage();
+ UnregisterPSImage();
+ UnregisterPS2Image();
+ UnregisterPS3Image();
+ UnregisterPSDImage();
+ UnregisterPWPImage();
+ UnregisterRAWImage();
+ UnregisterRGBImage();
+ UnregisterRLAImage();
+ UnregisterRLEImage();
+ UnregisterSCRImage();
+ UnregisterSCTImage();
+ UnregisterSFWImage();
+ UnregisterSGIImage();
+ UnregisterSTEGANOImage();
+ UnregisterSUNImage();
+ UnregisterSVGImage();
+ UnregisterTGAImage();
+ UnregisterTHUMBNAILImage();
+#if defined(MAGICKCORE_TIFF_DELEGATE)
+ UnregisterTIFFImage();
+#endif
+ UnregisterTILEImage();
+ UnregisterTIMImage();
+ UnregisterTTFImage();
+ UnregisterTXTImage();
+ UnregisterUILImage();
+ UnregisterURLImage();
+ UnregisterUYVYImage();
+ UnregisterVICARImage();
+ UnregisterVIDImage();
+ UnregisterVIFFImage();
+ UnregisterWBMPImage();
+#if defined(MAGICKCORE_WMF_DELEGATE) || defined(MAGICKCORE_WMFLITE_DELEGATE)
+ UnregisterWMFImage();
+#endif
+ UnregisterWPGImage();
+#if defined(MAGICKCORE_X11_DELEGATE)
+ UnregisterXImage();
+#endif
+ UnregisterXBMImage();
+ UnregisterXCImage();
+ UnregisterXCFImage();
+ UnregisterXPMImage();
+ UnregisterXPSImage();
+#if defined(_VISUALC_)
+ UnregisterXTRNImage();
+#endif
+#if defined(MAGICKCORE_X11_DELEGATE)
+ UnregisterXWDImage();
+#endif
+ UnregisterYCBCRImage();
+ UnregisterYUVImage();
+#endif
+}
diff --git a/magick/static.h b/magick/static.h
new file mode 100644
index 0000000..d4aa1b1
--- /dev/null
+++ b/magick/static.h
@@ -0,0 +1,324 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore static coder registration methods.
+*/
+#ifndef _MAGICKCORE_STATIC_H
+#define _MAGICKCORE_STATIC_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport MagickBooleanType
+ InvokeStaticImageFilter(const char *,Image **,const int,const char **,
+ ExceptionInfo *);
+
+extern ModuleExport unsigned long
+ RegisterARTImage(void),
+ RegisterAVIImage(void),
+ RegisterAVSImage(void),
+ RegisterBIEImage(void),
+ RegisterBMPImage(void),
+ RegisterBRAILLEImage(void),
+ RegisterCAPTIONImage(void),
+ RegisterCINImage(void),
+ RegisterCIPImage(void),
+ RegisterCLIPImage(void),
+ RegisterCLIPBOARDImage(void),
+ RegisterCMYKImage(void),
+ RegisterCUTImage(void),
+ RegisterDCMImage(void),
+ RegisterDCXImage(void),
+ RegisterDDSImage(void),
+ RegisterDIBImage(void),
+ RegisterDJVUImage(void),
+ RegisterDNGImage(void),
+ RegisterDPSImage(void),
+ RegisterDPXImage(void),
+ RegisterEMFImage(void),
+ RegisterEPDFImage(void),
+ RegisterEPIImage(void),
+ RegisterEPSImage(void),
+ RegisterEPS2Image(void),
+ RegisterEPSFImage(void),
+ RegisterEPSIImage(void),
+ RegisterEPTImage(void),
+ RegisterEXRImage(void),
+ RegisterFAXImage(void),
+ RegisterFITSImage(void),
+ RegisterFPXImage(void),
+ RegisterG3Image(void),
+ RegisterGIFImage(void),
+ RegisterGIF87Image(void),
+ RegisterGRADIENTImage(void),
+ RegisterGRANITEImage(void),
+ RegisterGRAYImage(void),
+ RegisterHImage(void),
+ RegisterHISTOGRAMImage(void),
+ RegisterHRZImage(void),
+ RegisterHTMLImage(void),
+ RegisterICBImage(void),
+ RegisterICONImage(void),
+ RegisterINFOImage(void),
+ RegisterINLINEImage(void),
+ RegisterIPLImage(void),
+ RegisterJBGImage(void),
+ RegisterJBIGImage(void),
+ RegisterJPGImage(void),
+ RegisterJPEGImage(void),
+ RegisterJP2Image(void),
+ RegisterLABELImage(void),
+ RegisterMAGICKImage(void),
+ RegisterMAPImage(void),
+ RegisterMATImage(void),
+ RegisterMATTEImage(void),
+ RegisterMETAImage(void),
+ RegisterMIFFImage(void),
+ RegisterMNGImage(void),
+ RegisterMONOImage(void),
+ RegisterMPCImage(void),
+ RegisterMPEGImage(void),
+ RegisterMPRImage(void),
+ RegisterMSLImage(void),
+ RegisterMTVImage(void),
+ RegisterMVGImage(void),
+ RegisterNETSCAPEImage(void),
+ RegisterNULLImage(void),
+ RegisterP7Image(void),
+ RegisterPBMImage(void),
+ RegisterOTBImage(void),
+ RegisterPALMImage(void),
+ RegisterPATTERNImage(void),
+ RegisterPCDImage(void),
+ RegisterPCDSImage(void),
+ RegisterPCLImage(void),
+ RegisterPCTImage(void),
+ RegisterPCXImage(void),
+ RegisterPDBImage(void),
+ RegisterPDFImage(void),
+ RegisterPICImage(void),
+ RegisterPICTImage(void),
+ RegisterPIXImage(void),
+ RegisterPLASMAImage(void),
+ RegisterPGMImage(void),
+ RegisterPMImage(void),
+ RegisterPNGImage(void),
+ RegisterPNMImage(void),
+ RegisterPPMImage(void),
+ RegisterPREVIEWImage(void),
+ RegisterPSImage(void),
+ RegisterPS2Image(void),
+ RegisterPS3Image(void),
+ RegisterPSDImage(void),
+ RegisterPTIFImage(void),
+ RegisterPWPImage(void),
+ RegisterRASImage(void),
+ RegisterRAWImage(void),
+ RegisterRGBImage(void),
+ RegisterRGBAImage(void),
+ RegisterRLAImage(void),
+ RegisterRLEImage(void),
+ RegisterSCRImage(void),
+ RegisterSCTImage(void),
+ RegisterSFWImage(void),
+ RegisterSGIImage(void),
+ RegisterSHTMLImage(void),
+ RegisterSTEGANOImage(void),
+ RegisterSUNImage(void),
+ RegisterSVGImage(void),
+ RegisterTEXTImage(void),
+ RegisterTGAImage(void),
+ RegisterTHUMBNAILImage(void),
+ RegisterTIFImage(void),
+ RegisterTIFFImage(void),
+ RegisterTILEImage(void),
+ RegisterTIMImage(void),
+ RegisterTTFImage(void),
+ RegisterTXTImage(void),
+ RegisterUILImage(void),
+ RegisterURLImage(void),
+ RegisterUYVYImage(void),
+ RegisterVDAImage(void),
+ RegisterVICARImage(void),
+ RegisterVIDImage(void),
+ RegisterVIFFImage(void),
+ RegisterVSTImage(void),
+ RegisterWBMPImage(void),
+ RegisterWMFImage(void),
+ RegisterWPGImage(void),
+ RegisterXImage(void),
+ RegisterXBMImage(void),
+ RegisterXCImage(void),
+ RegisterXCFImage(void),
+ RegisterXPMImage(void),
+ RegisterXPSImage(void),
+ RegisterXTRNImage(void),
+ RegisterXVImage(void),
+ RegisterXWDImage(void),
+ RegisterYCBCRImage(void),
+ RegisterYUVImage(void);
+
+extern ModuleExport void
+ UnregisterARTImage(void),
+ UnregisterAVIImage(void),
+ UnregisterAVSImage(void),
+ UnregisterBIEImage(void),
+ UnregisterBMPImage(void),
+ UnregisterBRAILLEImage(void),
+ UnregisterCAPTIONImage(void),
+ UnregisterCINImage(void),
+ UnregisterCIPImage(void),
+ UnregisterCLIPImage(void),
+ UnregisterCLIPBOARDImage(void),
+ UnregisterCMYKImage(void),
+ UnregisterCUTImage(void),
+ UnregisterDCMImage(void),
+ UnregisterDCXImage(void),
+ UnregisterDDSImage(void),
+ UnregisterDIBImage(void),
+ UnregisterDJVUImage(void),
+ UnregisterDNGImage(void),
+ UnregisterDPSImage(void),
+ UnregisterDPXImage(void),
+ UnregisterEMFImage(void),
+ UnregisterEPDFImage(void),
+ UnregisterEPIImage(void),
+ UnregisterEPSImage(void),
+ UnregisterEPS2Image(void),
+ UnregisterEPSFImage(void),
+ UnregisterEPSIImage(void),
+ UnregisterEPTImage(void),
+ UnregisterEXRImage(void),
+ UnregisterFAXImage(void),
+ UnregisterFITSImage(void),
+ UnregisterFPXImage(void),
+ UnregisterG3Image(void),
+ UnregisterGIFImage(void),
+ UnregisterGIF87Image(void),
+ UnregisterGRADIENTImage(void),
+ UnregisterGRANITEImage(void),
+ UnregisterGRAYImage(void),
+ UnregisterHImage(void),
+ UnregisterHISTOGRAMImage(void),
+ UnregisterHRZImage(void),
+ UnregisterHTMLImage(void),
+ UnregisterICBImage(void),
+ UnregisterICONImage(void),
+ UnregisterINFOImage(void),
+ UnregisterINLINEImage(void),
+ UnregisterIPLImage(void),
+ UnregisterJBGImage(void),
+ UnregisterJBIGImage(void),
+ UnregisterJPGImage(void),
+ UnregisterJPEGImage(void),
+ UnregisterJP2Image(void),
+ UnregisterLABELImage(void),
+ UnregisterLOCALEImage(void),
+ UnregisterMAGICKImage(void),
+ UnregisterMAPImage(void),
+ UnregisterMATImage(void),
+ UnregisterMATTEImage(void),
+ UnregisterMETAImage(void),
+ UnregisterMIFFImage(void),
+ UnregisterMNGImage(void),
+ UnregisterMONOImage(void),
+ UnregisterMPCImage(void),
+ UnregisterMPEGImage(void),
+ UnregisterMPRImage(void),
+ UnregisterMSLImage(void),
+ UnregisterMTVImage(void),
+ UnregisterMVGImage(void),
+ UnregisterNETSCAPEImage(void),
+ UnregisterNULLImage(void),
+ UnregisterP7Image(void),
+ UnregisterPBMImage(void),
+ UnregisterOTBImage(void),
+ UnregisterPALMImage(void),
+ UnregisterPATTERNImage(void),
+ UnregisterPCDImage(void),
+ UnregisterPCDSImage(void),
+ UnregisterPCLImage(void),
+ UnregisterPCTImage(void),
+ UnregisterPCXImage(void),
+ UnregisterPDBImage(void),
+ UnregisterPDFImage(void),
+ UnregisterPICImage(void),
+ UnregisterPICTImage(void),
+ UnregisterPIXImage(void),
+ UnregisterPLASMAImage(void),
+ UnregisterPGMImage(void),
+ UnregisterPMImage(void),
+ UnregisterPNGImage(void),
+ UnregisterPNMImage(void),
+ UnregisterPPMImage(void),
+ UnregisterPREVIEWImage(void),
+ UnregisterPSImage(void),
+ UnregisterPS2Image(void),
+ UnregisterPS3Image(void),
+ UnregisterPSDImage(void),
+ UnregisterPTIFImage(void),
+ UnregisterPWPImage(void),
+ UnregisterRASImage(void),
+ UnregisterRAWImage(void),
+ UnregisterRGBImage(void),
+ UnregisterRGBAImage(void),
+ UnregisterRLAImage(void),
+ UnregisterRLEImage(void),
+ UnregisterSCRImage(void),
+ UnregisterSCTImage(void),
+ UnregisterSFWImage(void),
+ UnregisterSGIImage(void),
+ UnregisterSHTMLImage(void),
+ UnregisterSTEGANOImage(void),
+ UnregisterSUNImage(void),
+ UnregisterSVGImage(void),
+ UnregisterTEXTImage(void),
+ UnregisterTGAImage(void),
+ UnregisterTHUMBNAILImage(void),
+ UnregisterTIFImage(void),
+ UnregisterTIFFImage(void),
+ UnregisterTILEImage(void),
+ UnregisterTIMImage(void),
+ UnregisterTTFImage(void),
+ UnregisterTXTImage(void),
+ UnregisterUILImage(void),
+ UnregisterURLImage(void),
+ UnregisterUYVYImage(void),
+ UnregisterVDAImage(void),
+ UnregisterVICARImage(void),
+ UnregisterVIDImage(void),
+ UnregisterVIFFImage(void),
+ UnregisterVSTImage(void),
+ UnregisterWBMPImage(void),
+ UnregisterWMFImage(void),
+ UnregisterWPGImage(void),
+ UnregisterXImage(void),
+ UnregisterXBMImage(void),
+ UnregisterXCImage(void),
+ UnregisterXCFImage(void),
+ UnregisterXPMImage(void),
+ UnregisterXPSImage(void),
+ UnregisterXTRNImage(void),
+ UnregisterXVImage(void),
+ UnregisterXWDImage(void),
+ UnregisterYCBCRImage(void),
+ UnregisterYUVImage(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/statistic.c b/magick/statistic.c
new file mode 100644
index 0000000..ac227ac
--- /dev/null
+++ b/magick/statistic.c
@@ -0,0 +1,1404 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS TTTTT AAA TTTTT IIIII SSSSS TTTTT IIIII CCCC %
+% SS T A A T I SS T I C %
+% SSS T AAAAA T I SSS T I C %
+% SS T A A T I SS T I C %
+% SSSSS T A A T IIIII SSSSS T IIIII CCCC %
+% %
+% %
+% MagickCore Image Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/animate.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/cache-view.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/composite-private.h"
+#include "magick/compress.h"
+#include "magick/constitute.h"
+#include "magick/deprecate.h"
+#include "magick/display.h"
+#include "magick/draw.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/image-private.h"
+#include "magick/magic.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/module.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/paint.h"
+#include "magick/pixel-private.h"
+#include "magick/profile.h"
+#include "magick/quantize.h"
+#include "magick/random_.h"
+#include "magick/segment.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/thread-private.h"
+#include "magick/timer.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e B o u n d i n g B o x %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageBoundingBox() returns the bounding box of an image canvas.
+%
+% The format of the GetImageBoundingBox method is:
+%
+% RectangleInfo GetImageBoundingBox(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o bounds: Method GetImageBoundingBox returns the bounding box of an
+% image canvas.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ target[3],
+ zero;
+
+ RectangleInfo
+ bounds;
+
+ register const PixelPacket
+ *p;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ bounds.width=0;
+ bounds.height=0;
+ bounds.x=(long) image->columns;
+ bounds.y=(long) image->rows;
+ GetMagickPixelPacket(image,&target[0]);
+ image_view=AcquireCacheView(image);
+ p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ image_view=DestroyCacheView(image_view);
+ return(bounds);
+ }
+ SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
+ &target[0]);
+ GetMagickPixelPacket(image,&target[1]);
+ p=GetCacheViewVirtualPixels(image_view,(long) image->columns-1,0,1,1,
+ exception);
+ SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
+ &target[1]);
+ GetMagickPixelPacket(image,&target[2]);
+ p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-1,1,1,exception);
+ SetMagickPixelPacket(image,p,GetCacheViewAuthenticIndexQueue(image_view),
+ &target[2]);
+ status=MagickTrue;
+ GetMagickPixelPacket(image,&zero);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickPixelPacket
+ pixel;
+
+ RectangleInfo
+ bounding_box;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ if (status == MagickFalse)
+ continue;
+#if defined(HAVE_OPENMP)
+# pragma omp critical (MagickCore_GetImageBoundingBox)
+#endif
+ bounding_box=bounds;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ pixel=zero;
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if ((x < bounding_box.x) &&
+ (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
+ bounding_box.x=x;
+ if ((x > (long) bounding_box.width) &&
+ (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
+ bounding_box.width=(unsigned long) x;
+ if ((y < bounding_box.y) &&
+ (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
+ bounding_box.y=y;
+ if ((y > (long) bounding_box.height) &&
+ (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
+ bounding_box.height=(unsigned long) y;
+ p++;
+ }
+#if defined(HAVE_OPENMP)
+# pragma omp critical (MagickCore_GetImageBoundingBox)
+#endif
+ {
+ if (bounding_box.x < bounds.x)
+ bounds.x=bounding_box.x;
+ if (bounding_box.y < bounds.y)
+ bounds.y=bounding_box.y;
+ if (bounding_box.width > bounds.width)
+ bounds.width=bounding_box.width;
+ if (bounding_box.height > bounds.height)
+ bounds.height=bounding_box.height;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if ((bounds.width == 0) || (bounds.height == 0))
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "GeometryDoesNotContainImage","`%s'",image->filename);
+ else
+ {
+ bounds.width-=(bounds.x-1);
+ bounds.height-=(bounds.y-1);
+ }
+ return(bounds);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelDepth() returns the depth of a particular image channel.
+%
+% The format of the GetImageChannelDepth method is:
+%
+% unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception)
+% unsigned long GetImageChannelDepth(const Image *image,
+% const ChannelType channel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport unsigned long GetImageDepth(const Image *image,
+ ExceptionInfo *exception)
+{
+ return(GetImageChannelDepth(image,AllChannels,exception));
+}
+
+MagickExport unsigned long GetImageChannelDepth(const Image *image,
+ const ChannelType channel,ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ register long
+ id;
+
+ unsigned long
+ *current_depth,
+ depth,
+ number_threads;
+
+ CacheView
+ *image_view;
+
+ /*
+ Compute image depth.
+ */
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ number_threads=GetOpenMPMaximumThreads();
+ current_depth=(unsigned long *) AcquireQuantumMemory(number_threads,
+ sizeof(*current_depth));
+ if (current_depth == (unsigned long *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ status=MagickTrue;
+ for (id=0; id < (long) number_threads; id++)
+ current_depth[id]=1;
+ if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ i;
+
+ p=image->colormap;
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if (status == MagickFalse)
+ continue;
+ id=GetOpenMPThreadId();
+ while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
+ {
+ MagickStatusType
+ status;
+
+ QuantumAny
+ range;
+
+ status=0;
+ range=GetQuantumRange(current_depth[id]);
+ if ((channel & RedChannel) != 0)
+ status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,
+ range),range);
+ if ((channel & GreenChannel) != 0)
+ status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
+ range),range);
+ if ((channel & BlueChannel) != 0)
+ status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
+ range),range);
+ if (status == 0)
+ break;
+ current_depth[id]++;
+ }
+ p++;
+ }
+ depth=current_depth[0];
+ for (id=1; id < (long) number_threads; id++)
+ if (depth < current_depth[id])
+ depth=current_depth[id];
+ current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
+ return(depth);
+ }
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ id,
+ x;
+
+ if (status == MagickFalse)
+ continue;
+ id=GetOpenMPThreadId();
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ continue;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
+ {
+ MagickStatusType
+ status;
+
+ QuantumAny
+ range;
+
+ status=0;
+ range=GetQuantumRange(current_depth[id]);
+ if ((channel & RedChannel) != 0)
+ status|=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
+ range);
+ if ((channel & GreenChannel) != 0)
+ status|=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
+ range),range);
+ if ((channel & BlueChannel) != 0)
+ status|=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),
+ range);
+ if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
+ status|=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,
+ range),range);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ status|=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],
+ range),range);
+ if (status == 0)
+ break;
+ current_depth[id]++;
+ }
+ p++;
+ }
+ if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
+ status=MagickFalse;
+ }
+ image_view=DestroyCacheView(image_view);
+ depth=current_depth[0];
+ for (id=1; id < (long) number_threads; id++)
+ if (depth < current_depth[id])
+ depth=current_depth[id];
+ current_depth=(unsigned long *) RelinquishMagickMemory(current_depth);
+ return(depth);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t I m a g e C h a n n e l E x t r e m a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelExtrema() returns the extrema of one or more image channels.
+%
+% The format of the GetImageChannelExtrema method is:
+%
+% MagickBooleanType GetImageChannelExtrema(const Image *image,
+% const ChannelType channel,unsigned long *minima,unsigned long *maxima,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o minima: the minimum value in the channel.
+%
+% o maxima: the maximum value in the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType GetImageExtrema(const Image *image,
+ unsigned long *minima,unsigned long *maxima,ExceptionInfo *exception)
+{
+ return(GetImageChannelExtrema(image,AllChannels,minima,maxima,exception));
+}
+
+MagickExport MagickBooleanType GetImageChannelExtrema(const Image *image,
+ const ChannelType channel,unsigned long *minima,unsigned long *maxima,
+ ExceptionInfo *exception)
+{
+ double
+ max,
+ min;
+
+ MagickBooleanType
+ status;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ status=GetImageChannelRange(image,channel,&min,&max,exception);
+ *minima=(unsigned long) (min+0.5);
+ *maxima=(unsigned long) (max+0.5);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l M e a n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelMean() returns the mean and standard deviation of one or more
+% image channels.
+%
+% The format of the GetImageChannelMean method is:
+%
+% MagickBooleanType GetImageChannelMean(const Image *image,
+% const ChannelType channel,double *mean,double *standard_deviation,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o mean: the average value in the channel.
+%
+% o standard_deviation: the standard deviation of the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType GetImageMean(const Image *image,double *mean,
+ double *standard_deviation,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetImageChannelMean(image,AllChannels,mean,standard_deviation,
+ exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType GetImageChannelMean(const Image *image,
+ const ChannelType channel,double *mean,double *standard_deviation,
+ ExceptionInfo *exception)
+{
+ double
+ area;
+
+ long
+ y;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ *mean=0.0;
+ *standard_deviation=0.0;
+ area=0.0;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ *mean+=p->red;
+ *standard_deviation+=(double) p->red*p->red;
+ area++;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ *mean+=p->green;
+ *standard_deviation+=(double) p->green*p->green;
+ area++;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ *mean+=p->blue;
+ *standard_deviation+=(double) p->blue*p->blue;
+ area++;
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ *mean+=p->opacity;
+ *standard_deviation+=(double) p->opacity*p->opacity;
+ area++;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ *mean+=indexes[x];
+ *standard_deviation+=(double) indexes[x]*indexes[x];
+ area++;
+ }
+ p++;
+ }
+ }
+ if (y < (long) image->rows)
+ return(MagickFalse);
+ if (area != 0)
+ {
+ *mean/=area;
+ *standard_deviation/=area;
+ }
+ *standard_deviation=sqrt(*standard_deviation-(*mean*(*mean)));
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l K u r t o s i s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelKurtosis() returns the kurtosis and skewness of one or more
+% image channels.
+%
+% The format of the GetImageChannelKurtosis method is:
+%
+% MagickBooleanType GetImageChannelKurtosis(const Image *image,
+% const ChannelType channel,double *kurtosis,double *skewness,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o kurtosis: the kurtosis of the channel.
+%
+% o skewness: the skewness of the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType GetImageKurtosis(const Image *image,
+ double *kurtosis,double *skewness,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=GetImageChannelKurtosis(image,AllChannels,kurtosis,skewness,
+ exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
+ const ChannelType channel,double *kurtosis,double *skewness,
+ ExceptionInfo *exception)
+{
+ double
+ area,
+ mean,
+ standard_deviation,
+ sum_squares,
+ sum_cubes,
+ sum_fourth_power;
+
+ long
+ y;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ *kurtosis=0.0;
+ *skewness=0.0;
+ area=0.0;
+ mean=0.0;
+ standard_deviation=0.0;
+ sum_squares=0.0;
+ sum_cubes=0.0;
+ sum_fourth_power=0.0;
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ mean+=p->red;
+ sum_squares+=(double) p->red*p->red;
+ sum_cubes+=(double) p->red*p->red*p->red;
+ sum_fourth_power+=(double) p->red*p->red*p->red*p->red;
+ area++;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ mean+=p->green;
+ sum_squares+=(double) p->green*p->green;
+ sum_cubes+=(double) p->green*p->green*p->green;
+ sum_fourth_power+=(double) p->green*p->green*p->green*p->green;
+ area++;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ mean+=p->blue;
+ sum_squares+=(double) p->blue*p->blue;
+ sum_cubes+=(double) p->blue*p->blue*p->blue;
+ sum_fourth_power+=(double) p->blue*p->blue*p->blue*p->blue;
+ area++;
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ mean+=p->opacity;
+ sum_squares+=(double) p->opacity*p->opacity;
+ sum_cubes+=(double) p->opacity*p->opacity*p->opacity;
+ sum_fourth_power+=(double) p->opacity*p->opacity*p->opacity*
+ p->opacity;
+ area++;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ mean+=indexes[x];
+ sum_squares+=(double) indexes[x]*indexes[x];
+ sum_cubes+=(double) indexes[x]*indexes[x]*indexes[x];
+ sum_fourth_power+=(double) indexes[x]*indexes[x]*indexes[x]*
+ indexes[x];
+ area++;
+ }
+ p++;
+ }
+ }
+ if (y < (long) image->rows)
+ return(MagickFalse);
+ if (area != 0.0)
+ {
+ mean/=area;
+ sum_squares/=area;
+ sum_cubes/=area;
+ sum_fourth_power/=area;
+ }
+ standard_deviation=sqrt(sum_squares-(mean*mean));
+ if (standard_deviation != 0.0)
+ {
+ *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
+ 3.0*mean*mean*mean*mean;
+ *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
+ standard_deviation;
+ *kurtosis-=3.0;
+ *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
+ *skewness/=standard_deviation*standard_deviation*standard_deviation;
+ }
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l R a n g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelRange() returns the range of one or more image channels.
+%
+% The format of the GetImageChannelRange method is:
+%
+% MagickBooleanType GetImageChannelRange(const Image *image,
+% const ChannelType channel,double *minima,double *maxima,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o minima: the minimum value in the channel.
+%
+% o maxima: the maximum value in the channel.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType GetImageRange(const Image *image,
+ double *minima,double *maxima,ExceptionInfo *exception)
+{
+ return(GetImageChannelRange(image,AllChannels,minima,maxima,exception));
+}
+
+MagickExport MagickBooleanType GetImageChannelRange(const Image *image,
+ const ChannelType channel,double *minima,double *maxima,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickPixelPacket
+ pixel;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ *maxima=(-1.0E-37);
+ *minima=1.0E+37;
+ GetMagickPixelPacket(image,&pixel);
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ SetMagickPixelPacket(image,p,indexes+x,&pixel);
+ if ((channel & RedChannel) != 0)
+ {
+ if (pixel.red < *minima)
+ *minima=(double) pixel.red;
+ if (pixel.red > *maxima)
+ *maxima=(double) pixel.red;
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ if (pixel.green < *minima)
+ *minima=(double) pixel.green;
+ if (pixel.green > *maxima)
+ *maxima=(double) pixel.green;
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ if (pixel.blue < *minima)
+ *minima=(double) pixel.blue;
+ if (pixel.blue > *maxima)
+ *maxima=(double) pixel.blue;
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (pixel.opacity < *minima)
+ *minima=(double) pixel.opacity;
+ if (pixel.opacity > *maxima)
+ *maxima=(double) pixel.opacity;
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ if ((double) indexes[x] < *minima)
+ *minima=(double) indexes[x];
+ if ((double) indexes[x] > *maxima)
+ *maxima=(double) indexes[x];
+ }
+ p++;
+ }
+ }
+ return(y == (long) image->rows ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e C h a n n e l S t a t i s t i c s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageChannelStatistics() returns statistics for each channel in the
+% image. The statistics include the channel depth, its minima, maxima, mean,
+% standard deviation, kurtosis and skewness. You can access the red channel
+% mean, for example, like this:
+%
+% channel_statistics=GetImageChannelStatistics(image,excepton);
+% red_mean=channel_statistics[RedChannel].mean;
+%
+% Use MagickRelinquishMemory() to free the statistics buffer.
+%
+% The format of the GetImageChannelStatistics method is:
+%
+% ChannelStatistics *GetImageChannelStatistics(const Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline double MagickMax(const double x,const double y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline double MagickMin(const double x,const double y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport ChannelStatistics *GetImageChannelStatistics(const Image *image,
+ ExceptionInfo *exception)
+{
+ ChannelStatistics
+ *channel_statistics;
+
+ double
+ area,
+ sum_squares,
+ sum_cubes;
+
+ long
+ y;
+
+ MagickStatusType
+ status;
+
+ QuantumAny
+ range;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ unsigned long
+ channels,
+ depth;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ length=AllChannels+1UL;
+ channel_statistics=(ChannelStatistics *) AcquireQuantumMemory(length,
+ sizeof(*channel_statistics));
+ if (channel_statistics == (ChannelStatistics *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(channel_statistics,0,length*
+ sizeof(*channel_statistics));
+ for (i=0; i <= AllChannels; i++)
+ {
+ channel_statistics[i].depth=1;
+ channel_statistics[i].maxima=(-1.0E-37);
+ channel_statistics[i].minima=1.0E+37;
+ channel_statistics[i].mean=0.0;
+ channel_statistics[i].standard_deviation=0.0;
+ channel_statistics[i].kurtosis=0.0;
+ channel_statistics[i].skewness=0.0;
+ }
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ p=GetVirtualPixels(image,0,y,image->columns,1,exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(image);
+ for (x=0; x < (long) image->columns; )
+ {
+ if (channel_statistics[RedChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
+ {
+ depth=channel_statistics[RedChannel].depth;
+ range=GetQuantumRange(depth);
+ status=p->red != ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),
+ range) ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ channel_statistics[RedChannel].depth++;
+ continue;
+ }
+ }
+ if (channel_statistics[GreenChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
+ {
+ depth=channel_statistics[GreenChannel].depth;
+ range=GetQuantumRange(depth);
+ status=p->green != ScaleAnyToQuantum(ScaleQuantumToAny(p->green,
+ range),range) ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ channel_statistics[GreenChannel].depth++;
+ continue;
+ }
+ }
+ if (channel_statistics[BlueChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
+ {
+ depth=channel_statistics[BlueChannel].depth;
+ range=GetQuantumRange(depth);
+ status=p->blue != ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,
+ range),range) ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ channel_statistics[BlueChannel].depth++;
+ continue;
+ }
+ }
+ if (image->matte != MagickFalse)
+ {
+ if (channel_statistics[OpacityChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
+ {
+ depth=channel_statistics[OpacityChannel].depth;
+ range=GetQuantumRange(depth);
+ status=p->opacity != ScaleAnyToQuantum(ScaleQuantumToAny(
+ p->opacity,range),range) ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ channel_statistics[OpacityChannel].depth++;
+ continue;
+ }
+ }
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ if (channel_statistics[BlackChannel].depth != MAGICKCORE_QUANTUM_DEPTH)
+ {
+ depth=channel_statistics[BlackChannel].depth;
+ range=GetQuantumRange(depth);
+ status=indexes[x] != ScaleAnyToQuantum(ScaleQuantumToAny(
+ indexes[x],range),range) ? MagickTrue : MagickFalse;
+ if (status != MagickFalse)
+ {
+ channel_statistics[BlackChannel].depth++;
+ continue;
+ }
+ }
+ }
+ if ((double) p->red < channel_statistics[RedChannel].minima)
+ channel_statistics[RedChannel].minima=(double) p->red;
+ if ((double) p->red > channel_statistics[RedChannel].maxima)
+ channel_statistics[RedChannel].maxima=(double) p->red;
+ channel_statistics[RedChannel].mean+=p->red;
+ channel_statistics[RedChannel].standard_deviation+=(double) p->red*p->red;
+ channel_statistics[RedChannel].kurtosis+=(double) p->red*p->red*
+ p->red*p->red;
+ channel_statistics[RedChannel].skewness+=(double) p->red*p->red*p->red;
+ if ((double) p->green < channel_statistics[GreenChannel].minima)
+ channel_statistics[GreenChannel].minima=(double) p->green;
+ if ((double) p->green > channel_statistics[GreenChannel].maxima)
+ channel_statistics[GreenChannel].maxima=(double) p->green;
+ channel_statistics[GreenChannel].mean+=p->green;
+ channel_statistics[GreenChannel].standard_deviation+=(double) p->green*
+ p->green;
+ channel_statistics[GreenChannel].kurtosis+=(double) p->green*p->green*
+ p->green*p->green;
+ channel_statistics[GreenChannel].skewness+=(double) p->green*p->green*
+ p->green;
+ if ((double) p->blue < channel_statistics[BlueChannel].minima)
+ channel_statistics[BlueChannel].minima=(double) p->blue;
+ if ((double) p->blue > channel_statistics[BlueChannel].maxima)
+ channel_statistics[BlueChannel].maxima=(double) p->blue;
+ channel_statistics[BlueChannel].mean+=p->blue;
+ channel_statistics[BlueChannel].standard_deviation+=(double) p->blue*
+ p->blue;
+ channel_statistics[BlueChannel].kurtosis+=(double) p->blue*p->blue*
+ p->blue*p->blue;
+ channel_statistics[BlueChannel].skewness+=(double) p->blue*p->blue*
+ p->blue;
+ if (image->matte != MagickFalse)
+ {
+ if ((double) p->opacity < channel_statistics[OpacityChannel].minima)
+ channel_statistics[OpacityChannel].minima=(double) p->opacity;
+ if ((double) p->opacity > channel_statistics[OpacityChannel].maxima)
+ channel_statistics[OpacityChannel].maxima=(double) p->opacity;
+ channel_statistics[OpacityChannel].mean+=p->opacity;
+ channel_statistics[OpacityChannel].standard_deviation+=(double)
+ p->opacity*p->opacity;
+ channel_statistics[OpacityChannel].kurtosis+=(double) p->opacity*
+ p->opacity*p->opacity*p->opacity;
+ channel_statistics[OpacityChannel].skewness+=(double) p->opacity*
+ p->opacity*p->opacity;
+ }
+ if (image->colorspace == CMYKColorspace)
+ {
+ if ((double) indexes[x] < channel_statistics[BlackChannel].minima)
+ channel_statistics[BlackChannel].minima=(double) indexes[x];
+ if ((double) indexes[x] > channel_statistics[BlackChannel].maxima)
+ channel_statistics[BlackChannel].maxima=(double) indexes[x];
+ channel_statistics[BlackChannel].mean+=indexes[x];
+ channel_statistics[BlackChannel].standard_deviation+=(double)
+ indexes[x]*indexes[x];
+ channel_statistics[BlackChannel].kurtosis+=(double) indexes[x]*
+ indexes[x]*indexes[x]*indexes[x];
+ channel_statistics[BlackChannel].skewness+=(double) indexes[x]*
+ indexes[x]*indexes[x];
+ }
+ x++;
+ p++;
+ }
+ }
+ area=(double) image->columns*image->rows;
+ for (i=0; i < AllChannels; i++)
+ {
+ channel_statistics[i].mean/=area;
+ channel_statistics[i].standard_deviation/=area;
+ channel_statistics[i].kurtosis/=area;
+ channel_statistics[i].skewness/=area;
+ }
+ for (i=0; i < AllChannels; i++)
+ {
+ channel_statistics[AllChannels].depth=(unsigned long) MagickMax((double)
+ channel_statistics[AllChannels].depth,(double)
+ channel_statistics[i].depth);
+ channel_statistics[AllChannels].minima=MagickMin(
+ channel_statistics[AllChannels].minima,channel_statistics[i].minima);
+ channel_statistics[AllChannels].maxima=MagickMax(
+ channel_statistics[AllChannels].maxima,channel_statistics[i].maxima);
+ channel_statistics[AllChannels].mean+=channel_statistics[i].mean;
+ channel_statistics[AllChannels].standard_deviation+=
+ channel_statistics[i].standard_deviation;
+ channel_statistics[AllChannels].kurtosis+=channel_statistics[i].kurtosis;
+ channel_statistics[AllChannels].skewness+=channel_statistics[i].skewness;
+ }
+ channels=4;
+ if (image->colorspace == CMYKColorspace)
+ channels++;
+ channel_statistics[AllChannels].mean/=channels;
+ channel_statistics[AllChannels].standard_deviation/=channels;
+ channel_statistics[AllChannels].kurtosis/=channels;
+ channel_statistics[AllChannels].skewness/=channels;
+ for (i=0; i <= AllChannels; i++)
+ {
+ sum_squares=0.0;
+ sum_squares=channel_statistics[i].standard_deviation;
+ sum_cubes=0.0;
+ sum_cubes=channel_statistics[i].skewness;
+ channel_statistics[i].standard_deviation=sqrt(
+ channel_statistics[i].standard_deviation-
+ (channel_statistics[i].mean*channel_statistics[i].mean));
+ if (channel_statistics[i].standard_deviation == 0.0)
+ {
+ channel_statistics[i].kurtosis=0.0;
+ channel_statistics[i].skewness=0.0;
+ }
+ else
+ {
+ channel_statistics[i].skewness=(channel_statistics[i].skewness-
+ 3.0*channel_statistics[i].mean*sum_squares+
+ 2.0*channel_statistics[i].mean*channel_statistics[i].mean*
+ channel_statistics[i].mean)/
+ (channel_statistics[i].standard_deviation*
+ channel_statistics[i].standard_deviation*
+ channel_statistics[i].standard_deviation);
+ channel_statistics[i].kurtosis=(channel_statistics[i].kurtosis-
+ 4.0*channel_statistics[i].mean*sum_cubes+
+ 6.0*channel_statistics[i].mean*channel_statistics[i].mean*sum_squares-
+ 3.0*channel_statistics[i].mean*channel_statistics[i].mean*
+ 1.0*channel_statistics[i].mean*channel_statistics[i].mean)/
+ (channel_statistics[i].standard_deviation*
+ channel_statistics[i].standard_deviation*
+ channel_statistics[i].standard_deviation*
+ channel_statistics[i].standard_deviation)-3.0;
+ }
+ }
+ return(channel_statistics);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t I m a g e Q u a n t u m D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetImageQuantumDepth() returns the depth of the image rounded to a legal
+% quantum depth: 8, 16, or 32.
+%
+% The format of the GetImageQuantumDepth method is:
+%
+% unsigned long GetImageQuantumDepth(const Image *image,
+% const MagickBooleanType constrain)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o constrain: A value other than MagickFalse, constrains the depth to
+% a maximum of MAGICKCORE_QUANTUM_DEPTH.
+%
+*/
+MagickExport unsigned long GetImageQuantumDepth(const Image *image,
+ const MagickBooleanType constrain)
+{
+ unsigned long
+ depth;
+
+ depth=image->depth;
+ if (depth <= 8)
+ depth=8;
+ else
+ if (depth <= 16)
+ depth=16;
+ else
+ if (depth <= 32)
+ depth=32;
+ else
+ if (depth <= 64)
+ depth=64;
+ if (constrain != MagickFalse)
+ depth=(unsigned long) MagickMin((double) depth,(double)
+ MAGICKCORE_QUANTUM_DEPTH);
+ return(depth);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t I m a g e C h a n n e l D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetImageChannelDepth() sets the depth of the image.
+%
+% The format of the SetImageChannelDepth method is:
+%
+% MagickBooleanType SetImageDepth(Image *image,const unsigned long depth)
+% MagickBooleanType SetImageChannelDepth(Image *image,
+% const ChannelType channel,const unsigned long depth)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel.
+%
+% o depth: the image depth.
+%
+*/
+
+MagickExport MagickBooleanType SetImageDepth(Image *image,
+ const unsigned long depth)
+{
+ return(SetImageChannelDepth(image,AllChannels,depth));
+}
+
+MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
+ const ChannelType channel,const unsigned long depth)
+{
+ ExceptionInfo
+ *exception;
+
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ QuantumAny
+ range;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(image->signature == MagickSignature);
+ if (GetImageDepth(image,&image->exception) <= (unsigned long)
+ MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH))
+ {
+ image->depth=depth;
+ return(MagickTrue);
+ }
+ /*
+ Scale pixels to desired depth.
+ */
+ status=MagickTrue;
+ range=GetQuantumRange(depth);
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=ScaleAnyToQuantum(ScaleQuantumToAny(q->red,range),range);
+ if ((channel & GreenChannel) != 0)
+ q->green=ScaleAnyToQuantum(ScaleQuantumToAny(q->green,range),range);
+ if ((channel & BlueChannel) != 0)
+ q->blue=ScaleAnyToQuantum(ScaleQuantumToAny(q->blue,range),range);
+ if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
+ q->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(q->opacity,range),range);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=ScaleAnyToQuantum(ScaleQuantumToAny(indexes[x],range),range);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ {
+ status=MagickFalse;
+ continue;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ if (image->storage_class == PseudoClass)
+ {
+ QuantumAny
+ range;
+
+ register long
+ i;
+
+ register PixelPacket
+ *__restrict p;
+
+ p=image->colormap;
+ range=GetQuantumRange(depth);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (i=0; i < (long) image->colors; i++)
+ {
+ if ((channel & RedChannel) != 0)
+ p->red=ScaleAnyToQuantum(ScaleQuantumToAny(p->red,range),range);
+ if ((channel & GreenChannel) != 0)
+ p->green=ScaleAnyToQuantum(ScaleQuantumToAny(p->green,range),range);
+ if ((channel & BlueChannel) != 0)
+ p->blue=ScaleAnyToQuantum(ScaleQuantumToAny(p->blue,range),range);
+ if ((channel & OpacityChannel) != 0)
+ p->opacity=ScaleAnyToQuantum(ScaleQuantumToAny(p->opacity,range),
+ range);
+ p++;
+ }
+ }
+ image->depth=depth;
+ return(status);
+}
diff --git a/magick/statistic.h b/magick/statistic.h
new file mode 100644
index 0000000..8369e6f
--- /dev/null
+++ b/magick/statistic.h
@@ -0,0 +1,71 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image methods.
+*/
+#ifndef _MAGICKCORE_STATISTIC_H
+#define _MAGICKCORE_STATISTIC_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _ChannelStatistics
+{
+ unsigned long
+ depth;
+
+ double
+ minima,
+ maxima,
+ mean,
+ standard_deviation,
+ kurtosis,
+ skewness;
+} ChannelStatistics;
+
+extern MagickExport ChannelStatistics
+ *GetImageChannelStatistics(const Image *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ GetImageChannelExtrema(const Image *,const ChannelType,unsigned long *,
+ unsigned long *,ExceptionInfo *),
+ GetImageChannelMean(const Image *,const ChannelType,double *,double *,
+ ExceptionInfo *),
+ GetImageChannelKurtosis(const Image *,const ChannelType,double *,double *,
+ ExceptionInfo *),
+ GetImageChannelRange(const Image *,const ChannelType,double *,double *,
+ ExceptionInfo *),
+ GetImageExtrema(const Image *,unsigned long *,unsigned long *,
+ ExceptionInfo *),
+ GetImageRange(const Image *,double *,double *,ExceptionInfo *),
+ GetImageMean(const Image *,double *,double *,ExceptionInfo *),
+ GetImageKurtosis(const Image *,double *,double *,ExceptionInfo *),
+ SetImageChannelDepth(Image *,const ChannelType,const unsigned long),
+ SetImageDepth(Image *,const unsigned long);
+
+extern MagickExport RectangleInfo
+ GetImageBoundingBox(const Image *,ExceptionInfo *exception);
+
+extern MagickExport unsigned long
+ GetImageChannelDepth(const Image *,const ChannelType,ExceptionInfo *),
+ GetImageDepth(const Image *,ExceptionInfo *),
+ GetImageQuantumDepth(const Image *,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/stream-private.h b/magick/stream-private.h
new file mode 100644
index 0000000..7e6b164
--- /dev/null
+++ b/magick/stream-private.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image stream private methods.
+*/
+#ifndef _MAGICKCORE_STREAM_PRIVATE_H
+#define _MAGICKCORE_STREAM_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _StreamInfo
+ StreamInfo;
+
+extern MagickExport const void
+ *GetStreamInfoClientData(StreamInfo *);
+
+extern MagickExport Image
+ *StreamImage(const ImageInfo *,StreamInfo *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ OpenStream(const ImageInfo *,StreamInfo *,const char *,ExceptionInfo *);
+
+extern MagickExport StreamInfo
+ *AcquireStreamInfo(const ImageInfo *),
+ *DestroyStreamInfo(StreamInfo *);
+
+extern MagickExport void
+ SetStreamInfoClientData(StreamInfo *,const void *),
+ SetStreamInfoMap(StreamInfo *,const char *),
+ SetStreamInfoStorageType(StreamInfo *,const StorageType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/stream.c b/magick/stream.c
new file mode 100644
index 0000000..6dd1ff1
--- /dev/null
+++ b/magick/stream.c
@@ -0,0 +1,2693 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS TTTTT RRRR EEEEE AAA M M %
+% SS T R R E A A MM MM %
+% SSS T RRRR EEE AAAAA M M M %
+% SS T R R E A A M M %
+% SSSSS T R R EEEEE A A M M %
+% %
+% %
+% MagickCore Pixel Stream Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2000 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/cache.h"
+#include "magick/cache-private.h"
+#include "magick/color-private.h"
+#include "magick/composite-private.h"
+#include "magick/constitute.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/memory_.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/semaphore.h"
+#include "magick/stream.h"
+#include "magick/stream-private.h"
+#include "magick/string_.h"
+
+/*
+ Typedef declaractions.
+*/
+struct _StreamInfo
+{
+ const ImageInfo
+ *image_info;
+
+ const Image
+ *image;
+
+ Image
+ *stream;
+
+ QuantumInfo
+ *quantum_info;
+
+ char
+ *map;
+
+ StorageType
+ storage_type;
+
+ unsigned char
+ *pixels;
+
+ RectangleInfo
+ extract_info;
+
+ long
+ y;
+
+ ExceptionInfo
+ *exception;
+
+ const void
+ *client_data;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Declare pixel cache interfaces.
+*/
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static const PixelPacket
+ *GetVirtualPixelStream(const Image *,const VirtualPixelMethod,const long,
+ const long,const unsigned long,const unsigned long,ExceptionInfo *);
+
+static MagickBooleanType
+ StreamImagePixels(const StreamInfo *,const Image *,ExceptionInfo *),
+ SyncAuthenticPixelsStream(Image *,ExceptionInfo *);
+
+static PixelPacket
+ *QueueAuthenticPixelsStream(Image *,const long,const long,const unsigned long,
+ const unsigned long,ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ A c q u i r e S t r e a m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireStreamInfo() allocates the StreamInfo structure.
+%
+% The format of the AcquireStreamInfo method is:
+%
+% StreamInfo *AcquireStreamInfo(const ImageInfo *image_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+*/
+MagickExport StreamInfo *AcquireStreamInfo(const ImageInfo *image_info)
+{
+ StreamInfo
+ *stream_info;
+
+ stream_info=(StreamInfo *) AcquireMagickMemory(sizeof(*stream_info));
+ if (stream_info == (StreamInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(stream_info,0,sizeof(*stream_info));
+ stream_info->pixels=(unsigned char *) AcquireMagickMemory(
+ sizeof(*stream_info->pixels));
+ if (stream_info->pixels == (unsigned char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ stream_info->map=ConstantString("RGB");
+ stream_info->storage_type=CharPixel;
+ stream_info->stream=AcquireImage(image_info);
+ stream_info->signature=MagickSignature;
+ return(stream_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y P i x e l S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyPixelStream() deallocates memory associated with the pixel stream.
+%
+% The format of the DestroyPixelStream() method is:
+%
+% void DestroyPixelStream(Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+
+static inline void RelinquishStreamPixels(CacheInfo *cache_info)
+{
+ assert(cache_info != (CacheInfo *) NULL);
+ if (cache_info->mapped == MagickFalse)
+ (void) RelinquishMagickMemory(cache_info->pixels);
+ else
+ (void) UnmapBlob(cache_info->pixels,(size_t) cache_info->length);
+ cache_info->pixels=(PixelPacket *) NULL;
+ cache_info->indexes=(IndexPacket *) NULL;
+ cache_info->length=0;
+ cache_info->mapped=MagickFalse;
+}
+
+static void DestroyPixelStream(Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ destroy;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ destroy=MagickFalse;
+ (void) LockSemaphoreInfo(cache_info->semaphore);
+ cache_info->reference_count--;
+ if (cache_info->reference_count == 0)
+ destroy=MagickTrue;
+ (void) UnlockSemaphoreInfo(cache_info->semaphore);
+ if (destroy == MagickFalse)
+ return;
+ RelinquishStreamPixels(cache_info);
+ if (cache_info->nexus_info != (NexusInfo **) NULL)
+ cache_info->nexus_info=DestroyPixelCacheNexus(cache_info->nexus_info,
+ cache_info->number_threads);
+ if (cache_info->disk_semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&cache_info->disk_semaphore);
+ if (cache_info->semaphore != (SemaphoreInfo *) NULL)
+ DestroySemaphoreInfo(&cache_info->semaphore);
+ cache_info=(CacheInfo *) RelinquishMagickMemory(cache_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y S t r e a m I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyStreamInfo() destroys memory associated with the StreamInfo
+% structure.
+%
+% The format of the DestroyStreamInfo method is:
+%
+% StreamInfo *DestroyStreamInfo(StreamInfo *stream_info)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+*/
+MagickExport StreamInfo *DestroyStreamInfo(StreamInfo *stream_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ if (stream_info->map != (char *) NULL)
+ stream_info->map=DestroyString(stream_info->map);
+ if (stream_info->pixels != (unsigned char *) NULL)
+ stream_info->pixels=(unsigned char *) RelinquishMagickMemory(
+ stream_info->pixels);
+ if (stream_info->stream != (Image *) NULL)
+ {
+ (void) CloseBlob(stream_info->stream);
+ stream_info->stream=DestroyImage(stream_info->stream);
+ }
+ if (stream_info->quantum_info != (QuantumInfo *) NULL)
+ stream_info->quantum_info=DestroyQuantumInfo(stream_info->quantum_info);
+ stream_info->signature=(~MagickSignature);
+ stream_info=(StreamInfo *) RelinquishMagickMemory(stream_info);
+ return(stream_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c I n d e x e s F r o m S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticIndexesFromStream() returns the indexes associated with the
+% last call to QueueAuthenticPixelsStream() or GetAuthenticPixelsStream().
+%
+% The format of the GetAuthenticIndexesFromStream() method is:
+%
+% IndexPacket *GetAuthenticIndexesFromStream(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static IndexPacket *GetAuthenticIndexesFromStream(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ return(cache_info->indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c P i x e l S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelsStream() gets pixels from the in-memory or disk pixel
+% cache as defined by the geometry parameters. A pointer to the pixels is
+% returned if the pixels are transferred, otherwise a NULL is returned. For
+% streams this method is a no-op.
+%
+% The format of the GetAuthenticPixelsStream() method is:
+%
+% PixelPacket *GetAuthenticPixelsStream(Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static PixelPacket *GetAuthenticPixelsStream(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ PixelPacket
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ pixels=QueueAuthenticPixelsStream(image,x,y,columns,rows,exception);
+ return(pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t A u t h e n t i c P i x e l F r o m S t e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetAuthenticPixelsFromStream() returns the pixels associated with the last
+% call to QueueAuthenticPixelsStream() or GetAuthenticPixelsStream().
+%
+% The format of the GetAuthenticPixelsFromStream() method is:
+%
+% PixelPacket *GetAuthenticPixelsFromStream(const Image image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static PixelPacket *GetAuthenticPixelsFromStream(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ return(cache_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t O n e A u t h e n t i c P i x e l F r o m S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneAuthenticPixelFromStream() returns a single pixel at the specified
+% (x,y) location. The image background color is returned if an error occurs.
+%
+% The format of the GetOneAuthenticPixelFromStream() method is:
+%
+% MagickBooleanType GetOneAuthenticPixelFromStream(const Image image,
+% const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType GetOneAuthenticPixelFromStream(Image *image,
+ const long x,const long y,PixelPacket *pixel,ExceptionInfo *exception)
+{
+ register PixelPacket
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *pixel=image->background_color;
+ pixels=GetAuthenticPixelsStream(image,x,y,1,1,exception);
+ if (pixels != (PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t O n e V i r t u a l P i x e l F r o m S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetOneVirtualPixelFromStream() returns a single pixel at the specified
+% (x.y) location. The image background color is returned if an error occurs.
+%
+% The format of the GetOneVirtualPixelFromStream() method is:
+%
+% MagickBooleanType GetOneVirtualPixelFromStream(const Image image,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,PixelPacket *pixel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y: These values define the location of the pixel to return.
+%
+% o pixel: return a pixel at the specified (x,y) location.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType GetOneVirtualPixelFromStream(const Image *image,
+ const VirtualPixelMethod virtual_pixel_method,const long x,const long y,
+ PixelPacket *pixel,ExceptionInfo *exception)
+{
+ const PixelPacket
+ *pixels;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ *pixel=image->background_color;
+ pixels=GetVirtualPixelStream(image,virtual_pixel_method,x,y,1,1,exception);
+ if (pixels != (const PixelPacket *) NULL)
+ return(MagickFalse);
+ *pixel=(*pixels);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t S t r e a m I n f o C l i e n t D a t a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetStreamInfoClientData() gets the stream info client data.
+%
+% The format of the SetStreamInfoClientData method is:
+%
+% const void *GetStreamInfoClientData(StreamInfo *stream_info)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+*/
+MagickExport const void *GetStreamInfoClientData(StreamInfo *stream_info)
+{
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ return(stream_info->client_data);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l s F r o m S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelsStream() returns the pixels associated with the last
+% call to QueueAuthenticPixelsStream() or GetVirtualPixelStream().
+%
+% The format of the GetVirtualPixelsStream() method is:
+%
+% const IndexPacket *GetVirtualPixelsStream(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o pixels: return the pixels associated with the last call to
+% QueueAuthenticPixelsStream() or GetVirtualPixelStream().
+%
+% o image: the image.
+%
+*/
+static const PixelPacket *GetVirtualPixelsStream(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ return(cache_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l I n d e x e s F r o m S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualIndexesFromStream() returns the indexes associated with the last
+% call to QueueAuthenticPixelsStream() or GetVirtualPixelStream().
+%
+% The format of the GetVirtualIndexesFromStream() method is:
+%
+% const IndexPacket *GetVirtualIndexesFromStream(const Image *image)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+*/
+static const IndexPacket *GetVirtualIndexesFromStream(const Image *image)
+{
+ CacheInfo
+ *cache_info;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ return(cache_info->indexes);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t V i r t u a l P i x e l S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetVirtualPixelStream() gets pixels from the in-memory or disk pixel cache as
+% defined by the geometry parameters. A pointer to the pixels is returned if
+% the pixels are transferred, otherwise a NULL is returned. For streams this
+% method is a no-op.
+%
+% The format of the GetVirtualPixelStream() method is:
+%
+% const PixelPacket *GetVirtualPixelStream(const Image *image,
+% const VirtualPixelMethod virtual_pixel_method,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o virtual_pixel_method: the virtual pixel method.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType AcquireStreamPixels(CacheInfo *cache_info,
+ ExceptionInfo *exception)
+{
+ if (cache_info->length != (MagickSizeType) ((size_t) cache_info->length))
+ return(MagickFalse);
+ cache_info->mapped=MagickFalse;
+ cache_info->pixels=(PixelPacket *) AcquireMagickMemory((size_t)
+ cache_info->length);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ cache_info->mapped=MagickTrue;
+ cache_info->pixels=(PixelPacket *) MapBlob(-1,IOMode,0,(size_t)
+ cache_info->length);
+ }
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",
+ cache_info->filename);
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+static const PixelPacket *GetVirtualPixelStream(const Image *image,
+ const VirtualPixelMethod magick_unused(virtual_pixel_method),const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickBooleanType
+ status;
+
+ MagickSizeType
+ number_pixels;
+
+ size_t
+ length;
+
+ /*
+ Validate pixel cache geometry.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if ((x < 0) || (y < 0) || ((x+(long) columns) > (long) image->columns) ||
+ ((y+(long) rows) > (long) image->rows) || (columns == 0) || (rows == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),StreamError,
+ "ImageDoesNotContainTheStreamGeometry","`%s'",image->filename);
+ return((PixelPacket *) NULL);
+ }
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ /*
+ Pixels are stored in a temporary buffer until they are synced to the cache.
+ */
+ number_pixels=(MagickSizeType) columns*rows;
+ length=(size_t) number_pixels*sizeof(PixelPacket);
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ length+=number_pixels*sizeof(IndexPacket);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ cache_info->length=length;
+ status=AcquireStreamPixels(cache_info,exception);
+ if (status == MagickFalse)
+ return((PixelPacket *) NULL);
+ }
+ else
+ if (cache_info->length != length)
+ {
+ RelinquishStreamPixels(cache_info);
+ cache_info->length=length;
+ status=AcquireStreamPixels(cache_info,exception);
+ if (status == MagickFalse)
+ return((PixelPacket *) NULL);
+ }
+ cache_info->indexes=(IndexPacket *) NULL;
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ cache_info->indexes=(IndexPacket *) (cache_info->pixels+number_pixels);
+ return(cache_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ O p e n S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenStream() opens a stream for writing by the StreamImage() method.
+%
+% The format of the OpenStream method is:
+%
+% MagickBooleanType OpenStream(const ImageInfo *image_info,
+% StreamInfo *stream_info,const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o stream_info: the stream info.
+%
+% o filename: the stream filename.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType OpenStream(const ImageInfo *image_info,
+ StreamInfo *stream_info,const char *filename,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ (void) CopyMagickString(stream_info->stream->filename,filename,MaxTextExtent);
+ status=OpenBlob(image_info,stream_info->stream,WriteBinaryBlobMode,exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ Q u e u e A u t h e n t i c P i x e l s S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% QueueAuthenticPixelsStream() allocates an area to store image pixels as
+% defined by the region rectangle and returns a pointer to the area. This
+% area is subsequently transferred from the pixel cache with method
+% SyncAuthenticPixelsStream(). A pointer to the pixels is returned if the
+% pixels are transferred, otherwise a NULL is returned.
+%
+% The format of the QueueAuthenticPixelsStream() method is:
+%
+% PixelPacket *QueueAuthenticPixelsStream(Image *image,const long x,
+% const long y,const unsigned long columns,const unsigned long rows,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x,y,columns,rows: These values define the perimeter of a region of
+% pixels.
+%
+*/
+static PixelPacket *QueueAuthenticPixelsStream(Image *image,const long x,
+ const long y,const unsigned long columns,const unsigned long rows,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ MagickSizeType
+ number_pixels;
+
+ size_t
+ length;
+
+ StreamHandler
+ stream_handler;
+
+ /*
+ Validate pixel cache geometry.
+ */
+ assert(image != (Image *) NULL);
+ if ((x < 0) || (y < 0) || ((x+(long) columns) > (long) image->columns) ||
+ ((y+(long) rows) > (long) image->rows) || (columns == 0) || (rows == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),StreamError,
+ "ImageDoesNotContainTheStreamGeometry","`%s'",image->filename);
+ return((PixelPacket *) NULL);
+ }
+ stream_handler=GetBlobStreamHandler(image);
+ if (stream_handler == (StreamHandler) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),StreamError,
+ "NoStreamHandlerIsDefined","`%s'",image->filename);
+ return((PixelPacket *) NULL);
+ }
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ if ((image->storage_class != GetPixelCacheStorageClass(image->cache)) ||
+ (image->colorspace != GetPixelCacheColorspace(image->cache)))
+ {
+ if (GetPixelCacheStorageClass(image->cache) == UndefinedClass)
+ (void) stream_handler(image,(const void *) NULL,(size_t)
+ cache_info->columns);
+ cache_info->storage_class=image->storage_class;
+ cache_info->colorspace=image->colorspace;
+ cache_info->columns=image->columns;
+ cache_info->rows=image->rows;
+ image->cache=cache_info;
+ }
+ /*
+ Pixels are stored in a temporary buffer until they are synced to the cache.
+ */
+ cache_info->columns=columns;
+ cache_info->rows=rows;
+ number_pixels=(MagickSizeType) columns*rows;
+ length=(size_t) number_pixels*sizeof(PixelPacket);
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ length+=number_pixels*sizeof(IndexPacket);
+ if (cache_info->pixels == (PixelPacket *) NULL)
+ {
+ cache_info->pixels=(PixelPacket *) AcquireMagickMemory(length);
+ cache_info->length=(MagickSizeType) length;
+ }
+ else
+ if (cache_info->length < (MagickSizeType) length)
+ {
+ cache_info->pixels=(PixelPacket *) ResizeMagickMemory(
+ cache_info->pixels,length);
+ cache_info->length=(MagickSizeType) length;
+ }
+ if (cache_info->pixels == (void *) NULL)
+ return((PixelPacket *) NULL);
+ cache_info->indexes=(IndexPacket *) NULL;
+ if ((image->storage_class == PseudoClass) ||
+ (image->colorspace == CMYKColorspace))
+ cache_info->indexes=(IndexPacket *) (cache_info->pixels+number_pixels);
+ return(cache_info->pixels);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e a d S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ReadStream() makes the image pixels available to a user supplied callback
+% method immediately upon reading a scanline with the ReadImage() method.
+%
+% The format of the ReadStream() method is:
+%
+% Image *ReadStream(const ImageInfo *image_info,StreamHandler stream,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o stream: a callback method.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ReadStream(const ImageInfo *image_info,StreamHandler stream,
+ ExceptionInfo *exception)
+{
+ CacheMethods
+ cache_methods;
+
+ Image
+ *image;
+
+ ImageInfo
+ *read_info;
+
+ /*
+ Stream image pixels.
+ */
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ read_info=CloneImageInfo(image_info);
+ read_info->cache=AcquirePixelCache(0);
+ GetPixelCacheMethods(&cache_methods);
+ cache_methods.get_virtual_pixel_handler=GetVirtualPixelStream;
+ cache_methods.get_virtual_indexes_from_handler=GetVirtualIndexesFromStream;
+ cache_methods.get_virtual_pixels_handler=GetVirtualPixelsStream;
+ cache_methods.get_authentic_pixels_handler=GetAuthenticPixelsStream;
+ cache_methods.queue_authentic_pixels_handler=QueueAuthenticPixelsStream;
+ cache_methods.sync_authentic_pixels_handler=SyncAuthenticPixelsStream;
+ cache_methods.get_authentic_pixels_from_handler=GetAuthenticPixelsFromStream;
+ cache_methods.get_authentic_indexes_from_handler=
+ GetAuthenticIndexesFromStream;
+ cache_methods.get_one_virtual_pixel_from_handler=GetOneVirtualPixelFromStream;
+ cache_methods.get_one_authentic_pixel_from_handler=
+ GetOneAuthenticPixelFromStream;
+ cache_methods.destroy_pixel_handler=DestroyPixelStream;
+ SetPixelCacheMethods(read_info->cache,&cache_methods);
+ read_info->stream=stream;
+ image=ReadImage(read_info,exception);
+ read_info=DestroyImageInfo(read_info);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t S t r e a m I n f o C l i e n t D a t a %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStreamInfoClientData() sets the stream info client data.
+%
+% The format of the SetStreamInfoClientData method is:
+%
+% void SetStreamInfoClientData(StreamInfo *stream_info,
+% const void *client_data)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+% o client_data: the client data.
+%
+*/
+MagickExport void SetStreamInfoClientData(StreamInfo *stream_info,
+ const void *client_data)
+{
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ stream_info->client_data=client_data;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t S t r e a m I n f o M a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStreamInfoMap() sets the stream info map member.
+%
+% The format of the SetStreamInfoMap method is:
+%
+% void SetStreamInfoMap(StreamInfo *stream_info,const char *map)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+% o map: the map.
+%
+*/
+MagickExport void SetStreamInfoMap(StreamInfo *stream_info,const char *map)
+{
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ (void) CloneString(&stream_info->map,map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S e t S t r e a m I n f o S t o r a g e T y p e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStreamInfoStorageType() sets the stream info storage type member.
+%
+% The format of the SetStreamInfoStorageType method is:
+%
+% void SetStreamInfoStorageType(StreamInfo *stream_info,
+% const StoreageType *storage_type)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+% o storage_type: the storage type.
+%
+*/
+MagickExport void SetStreamInfoStorageType(StreamInfo *stream_info,
+ const StorageType storage_type)
+{
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ stream_info->storage_type=storage_type;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S t r e a m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StreamImage() streams pixels from an image and writes them in a user
+% defined format and storage type (e.g. RGBA as 8-bit unsigned char).
+%
+% The format of he wStreamImage() method is:
+%
+% Image *StreamImage(const ImageInfo *image_info,
+% StreamInfo *stream_info,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o stream_info: the stream info.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static size_t WriteStreamImage(const Image *image,const void *pixels,
+ const size_t columns)
+{
+ RectangleInfo
+ extract_info;
+
+ size_t
+ length,
+ packet_size;
+
+ ssize_t
+ count;
+
+ StreamInfo
+ *stream_info;
+
+ stream_info=(StreamInfo *) image->client_data;
+ switch (stream_info->storage_type)
+ {
+ default: packet_size=sizeof(char); break;
+ case CharPixel: packet_size=sizeof(char); break;
+ case DoublePixel: packet_size=sizeof(double); break;
+ case FloatPixel: packet_size=sizeof(float); break;
+ case IntegerPixel: packet_size=sizeof(int); break;
+ case LongPixel: packet_size=sizeof(long); break;
+ case QuantumPixel: packet_size=sizeof(Quantum); break;
+ case ShortPixel: packet_size=sizeof(unsigned short); break;
+ }
+ packet_size*=strlen(stream_info->map);
+ length=packet_size*image->columns;
+ if (image != stream_info->image)
+ {
+ ImageInfo
+ *write_info;
+
+ /*
+ Prepare stream for writing.
+ */
+ stream_info->pixels=(unsigned char *) ResizeQuantumMemory(
+ stream_info->pixels,length,sizeof(*stream_info->pixels));
+ if (pixels == (unsigned char *) NULL)
+ return(0);
+ stream_info->image=image;
+ write_info=CloneImageInfo(stream_info->image_info);
+ (void) SetImageInfo(write_info,MagickFalse,stream_info->exception);
+ if (write_info->extract != (char *) NULL)
+ (void) ParseAbsoluteGeometry(write_info->extract,
+ &stream_info->extract_info);
+ stream_info->y=0;
+ write_info=DestroyImageInfo(write_info);
+ }
+ extract_info=stream_info->extract_info;
+ if ((extract_info.width == 0) ||
+ (extract_info.height == 0))
+ {
+ /*
+ Write all pixels to stream.
+ */
+ (void) StreamImagePixels(stream_info,image,stream_info->exception);
+ count=WriteBlob(stream_info->stream,length,stream_info->pixels);
+ stream_info->y++;
+ return(count == 0 ? 0 : columns);
+ }
+ if ((stream_info->y < extract_info.y) ||
+ (stream_info->y >= (long) (extract_info.y+extract_info.height)))
+ {
+ stream_info->y++;
+ return(columns);
+ }
+ /*
+ Write a portion of the pixel row to the stream.
+ */
+ (void) StreamImagePixels(stream_info,image,stream_info->exception);
+ length=packet_size*extract_info.width;
+ count=WriteBlob(stream_info->stream,length,stream_info->pixels+
+ packet_size*extract_info.x);
+ stream_info->y++;
+ return(count == 0 ? 0 : columns);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport Image *StreamImage(const ImageInfo *image_info,
+ StreamInfo *stream_info,ExceptionInfo *exception)
+{
+ Image
+ *image;
+
+ ImageInfo
+ *read_info;
+
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ assert(exception != (ExceptionInfo *) NULL);
+ read_info=CloneImageInfo(image_info);
+ stream_info->image_info=image_info;
+ stream_info->exception=exception;
+ read_info->client_data=(void *) stream_info;
+ image=ReadStream(read_info,&WriteStreamImage,exception);
+ read_info=DestroyImageInfo(read_info);
+ stream_info->quantum_info=AcquireQuantumInfo(image_info,image);
+ if (stream_info->quantum_info == (QuantumInfo *) NULL)
+ image=DestroyImage(image);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S t r e a m I m a g e P i x e l s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StreamImagePixels() extracts pixel data from an image and returns it in the
+% stream_info->pixels structure in the format as defined by
+% stream_info->quantum_info->map and stream_info->quantum_info->storage_type.
+%
+% The format of the StreamImagePixels method is:
+%
+% MagickBooleanType StreamImagePixels(const StreamInfo *stream_info,
+% const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o stream_info: the stream info.
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType StreamImagePixels(const StreamInfo *stream_info,
+ const Image *image,ExceptionInfo *exception)
+{
+ QuantumInfo
+ *quantum_info;
+
+ QuantumType
+ *quantum_map;
+
+ register long
+ i,
+ x;
+
+ register const PixelPacket
+ *p;
+
+ register IndexPacket
+ *indexes;
+
+ size_t
+ length;
+
+ assert(stream_info != (StreamInfo *) NULL);
+ assert(stream_info->signature == MagickSignature);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ length=strlen(stream_info->map);
+ quantum_map=(QuantumType *) AcquireQuantumMemory(length,sizeof(*quantum_map));
+ if (quantum_map == (QuantumType *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ for (i=0; i < (long) length; i++)
+ {
+ switch (stream_info->map[i])
+ {
+ case 'A':
+ case 'a':
+ {
+ quantum_map[i]=AlphaQuantum;
+ break;
+ }
+ case 'B':
+ case 'b':
+ {
+ quantum_map[i]=BlueQuantum;
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ quantum_map[i]=CyanQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",stream_info->map);
+ return(MagickFalse);
+ }
+ case 'g':
+ case 'G':
+ {
+ quantum_map[i]=GreenQuantum;
+ break;
+ }
+ case 'I':
+ case 'i':
+ {
+ quantum_map[i]=IndexQuantum;
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ quantum_map[i]=BlackQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",stream_info->map);
+ return(MagickFalse);
+ }
+ case 'M':
+ case 'm':
+ {
+ quantum_map[i]=MagentaQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",stream_info->map);
+ return(MagickFalse);
+ }
+ case 'o':
+ case 'O':
+ {
+ quantum_map[i]=OpacityQuantum;
+ break;
+ }
+ case 'P':
+ case 'p':
+ {
+ quantum_map[i]=UndefinedQuantum;
+ break;
+ }
+ case 'R':
+ case 'r':
+ {
+ quantum_map[i]=RedQuantum;
+ break;
+ }
+ case 'Y':
+ case 'y':
+ {
+ quantum_map[i]=YellowQuantum;
+ if (image->colorspace == CMYKColorspace)
+ break;
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),ImageError,
+ "ColorSeparatedImageRequired","`%s'",stream_info->map);
+ return(MagickFalse);
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnrecognizedPixelMap","`%s'",stream_info->map);
+ return(MagickFalse);
+ }
+ }
+ }
+ quantum_info=stream_info->quantum_info;
+ switch (stream_info->storage_type)
+ {
+ case CharPixel:
+ {
+ register unsigned char
+ *q;
+
+ q=(unsigned char *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar((Quantum) 0);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToChar(p->red);
+ *q++=ScaleQuantumToChar(p->green);
+ *q++=ScaleQuantumToChar(p->blue);
+ *q++=ScaleQuantumToChar((Quantum) 0);
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToChar(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToChar(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToChar(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToChar((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToChar(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToChar(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToChar(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case DoublePixel:
+ {
+ register double
+ *q;
+
+ q=(double *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*((Quantum) (QuantumRange-p->opacity)))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=0.0;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*PixelIntensityToQuantum(p))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*((Quantum) (QuantumRange-p->opacity)))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=0.0;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(double) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(double) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(double) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(double) ((QuantumScale*((Quantum) (QuantumRange-
+ p->opacity)))*quantum_info->scale+quantum_info->minimum);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(double) ((QuantumScale*p->opacity)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(double) ((QuantumScale*indexes[x])*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(double) ((QuantumScale*PixelIntensityToQuantum(p))*
+ quantum_info->scale+quantum_info->minimum);
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case FloatPixel:
+ {
+ register float
+ *q;
+
+ q=(float *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*(Quantum) (QuantumRange-p->opacity))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=0.0;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*PixelIntensityToQuantum(p))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*((Quantum) (QuantumRange-p->opacity)))*
+ quantum_info->scale+quantum_info->minimum);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ *q++=0.0;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(float) ((QuantumScale*p->red)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(float) ((QuantumScale*p->green)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(float) ((QuantumScale*p->blue)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(float) ((QuantumScale*((Quantum) (QuantumRange-
+ p->opacity)))*quantum_info->scale+quantum_info->minimum);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(float) ((QuantumScale*p->opacity)*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(float) ((QuantumScale*indexes[x])*quantum_info->scale+
+ quantum_info->minimum);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(float) ((QuantumScale*PixelIntensityToQuantum(p))*
+ quantum_info->scale+quantum_info->minimum);
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case IntegerPixel:
+ {
+ register unsigned int
+ *q;
+
+ q=(unsigned int *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong((Quantum) (QuantumRange-
+ p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=0U;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(
+ PixelIntensityToQuantum(p));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=(unsigned int) ScaleQuantumToLong((Quantum)
+ (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=(unsigned int) ScaleQuantumToLong(p->red);
+ *q++=(unsigned int) ScaleQuantumToLong(p->green);
+ *q++=(unsigned int) ScaleQuantumToLong(p->blue);
+ *q++=0U;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong((Quantum) (QuantumRange-
+ p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=(unsigned int) ScaleQuantumToLong(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=(unsigned int) ScaleQuantumToLong(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(unsigned int)
+ ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case LongPixel:
+ {
+ register unsigned long
+ *q;
+
+ q=(unsigned long *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=0;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToLong(p->red);
+ *q++=ScaleQuantumToLong(p->green);
+ *q++=ScaleQuantumToLong(p->blue);
+ *q++=0;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToLong(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToLong(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToLong(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToLong((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToLong(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToLong(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToLong(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case QuantumPixel:
+ {
+ register Quantum
+ *q;
+
+ q=(Quantum *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ *q++=(Quantum) (QuantumRange-p->opacity);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->blue;
+ *q++=p->green;
+ *q++=p->red;
+ *q++=0;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=PixelIntensityToQuantum(p);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ *q++=(Quantum) (QuantumRange-p->opacity);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=p->red;
+ *q++=p->green;
+ *q++=p->blue;
+ *q++=0U;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=(Quantum) 0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=p->red;
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=p->green;
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=p->blue;
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=(Quantum) (QuantumRange-p->opacity);
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=p->opacity;
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=indexes[x];
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ *q=0;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ case ShortPixel:
+ {
+ register unsigned short
+ *q;
+
+ q=(unsigned short *) stream_info->pixels;
+ if (LocaleCompare(stream_info->map,"BGR") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"BGRP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=0;
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"I") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGB") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBA") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ p++;
+ }
+ break;
+ }
+ if (LocaleCompare(stream_info->map,"RGBP") == 0)
+ {
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ *q++=ScaleQuantumToShort(p->red);
+ *q++=ScaleQuantumToShort(p->green);
+ *q++=ScaleQuantumToShort(p->blue);
+ *q++=0;
+ p++;
+ }
+ break;
+ }
+ p=GetAuthenticPixelQueue(image);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=0; x < (long) GetImageExtent(image); x++)
+ {
+ for (i=0; i < (long) length; i++)
+ {
+ *q=0;
+ switch (quantum_map[i])
+ {
+ case RedQuantum:
+ case CyanQuantum:
+ {
+ *q=ScaleQuantumToShort(p->red);
+ break;
+ }
+ case GreenQuantum:
+ case MagentaQuantum:
+ {
+ *q=ScaleQuantumToShort(p->green);
+ break;
+ }
+ case BlueQuantum:
+ case YellowQuantum:
+ {
+ *q=ScaleQuantumToShort(p->blue);
+ break;
+ }
+ case AlphaQuantum:
+ {
+ *q=ScaleQuantumToShort((Quantum) (QuantumRange-p->opacity));
+ break;
+ }
+ case OpacityQuantum:
+ {
+ *q=ScaleQuantumToShort(p->opacity);
+ break;
+ }
+ case BlackQuantum:
+ {
+ if (image->colorspace == CMYKColorspace)
+ *q=ScaleQuantumToShort(indexes[x]);
+ break;
+ }
+ case IndexQuantum:
+ {
+ *q=ScaleQuantumToShort(PixelIntensityToQuantum(p));
+ break;
+ }
+ default:
+ break;
+ }
+ q++;
+ }
+ p++;
+ }
+ break;
+ }
+ default:
+ {
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "UnrecognizedPixelMap","`%s'",stream_info->map);
+ break;
+ }
+ }
+ quantum_map=(QuantumType *) RelinquishMagickMemory(quantum_map);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S y n c A u t h e n t i c P i x e l s S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SyncAuthenticPixelsStream() calls the user supplied callback method with
+% the latest stream of pixels.
+%
+% The format of the SyncAuthenticPixelsStream method is:
+%
+% MagickBooleanType SyncAuthenticPixelsStream(Image *image,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType SyncAuthenticPixelsStream(Image *image,
+ ExceptionInfo *exception)
+{
+ CacheInfo
+ *cache_info;
+
+ size_t
+ length;
+
+ StreamHandler
+ stream_handler;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ cache_info=(CacheInfo *) image->cache;
+ assert(cache_info->signature == MagickSignature);
+ stream_handler=GetBlobStreamHandler(image);
+ if (stream_handler == (StreamHandler) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),StreamError,
+ "NoStreamHandlerIsDefined","`%s'",image->filename);
+ return(MagickFalse);
+ }
+ length=stream_handler(image,cache_info->pixels,(size_t) cache_info->columns);
+ return(length == cache_info->columns ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W r i t e S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WriteStream() makes the image pixels available to a user supplied callback
+% method immediately upon writing pixel data with the WriteImage() method.
+%
+% The format of the WriteStream() method is:
+%
+% MagickBooleanType WriteStream(const ImageInfo *image_info,Image *,
+% StreamHandler stream)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o stream: A callback method.
+%
+*/
+MagickExport MagickBooleanType WriteStream(const ImageInfo *image_info,
+ Image *image,StreamHandler stream)
+{
+ ImageInfo
+ *write_info;
+
+ MagickBooleanType
+ status;
+
+ assert(image_info != (ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ write_info=CloneImageInfo(image_info);
+ write_info->stream=stream;
+ status=WriteImage(write_info,image);
+ write_info=DestroyImageInfo(write_info);
+ return(status);
+}
diff --git a/magick/stream.h b/magick/stream.h
new file mode 100644
index 0000000..631f79f
--- /dev/null
+++ b/magick/stream.h
@@ -0,0 +1,38 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image stream methods.
+*/
+#ifndef _MAGICKCORE_STREAM_H
+#define _MAGICKCORE_STREAM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef size_t
+ (*StreamHandler)(const Image *,const void *,const size_t);
+
+extern MagickExport Image
+ *ReadStream(const ImageInfo *,StreamHandler,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ WriteStream(const ImageInfo *,Image *,StreamHandler);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/string.c b/magick/string.c
new file mode 100644
index 0000000..beca1aa
--- /dev/null
+++ b/magick/string.c
@@ -0,0 +1,2491 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% SSSSS TTTTT RRRR IIIII N N GGGG %
+% SS T R R I NN N G %
+% SSS T RRRR I N N N G GGG %
+% SS T R R I N NN G G %
+% SSSSS T R R IIIII N N GGGG %
+% %
+% %
+% MagickCore String Methods %
+% %
+% Software Design %
+% John Cristy %
+% August 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/blob-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/property.h"
+#include "magick/resource_.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+
+/*
+ Static declarations.
+*/
+#if !defined(MAGICKCORE_HAVE_STRCASECMP) || !defined(MAGICKCORE_HAVE_STRNCASECMP)
+static const unsigned char
+ AsciiMap[] =
+ {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
+ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
+ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
+ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xe1, 0xe2, 0xe3, 0xe4, 0xc5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb,
+ 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
+ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
+ 0xfc, 0xfd, 0xfe, 0xff,
+ };
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireString() allocates memory for a string and copies the source string
+% to that memory location (and returns it).
+%
+% The format of the AcquireString method is:
+%
+% char *AcquireString(const char *source)
+%
+% A description of each parameter follows:
+%
+% o source: A character string.
+%
+*/
+MagickExport char *AcquireString(const char *source)
+{
+ char
+ *destination;
+
+ size_t
+ length;
+
+ length=0;
+ if (source != (char *) NULL)
+ length+=strlen(source);
+ destination=(char *) NULL;
+ if (~length >= MaxTextExtent)
+ destination=(char *) AcquireQuantumMemory(length+MaxTextExtent,
+ sizeof(*destination));
+ if (destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ *destination='\0';
+ if (source != (char *) NULL)
+ (void) CopyMagickString(destination,source,(length+1)*sizeof(*destination));
+ return(destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireStringInfo() allocates the StringInfo structure.
+%
+% The format of the AcquireStringInfo method is:
+%
+% StringInfo *AcquireStringInfo(const size_t length)
+%
+% A description of each parameter follows:
+%
+% o length: the string length.
+%
+*/
+MagickExport StringInfo *AcquireStringInfo(const size_t length)
+{
+ StringInfo
+ *string_info;
+
+ string_info=(StringInfo *) AcquireMagickMemory(sizeof(*string_info));
+ if (string_info == (StringInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(string_info,0,sizeof(*string_info));
+ string_info->signature=MagickSignature;
+ string_info->length=length;
+ if (string_info->length != 0)
+ {
+ string_info->datum=(unsigned char *) NULL;
+ if (~string_info->length >= MaxTextExtent)
+ string_info->datum=(unsigned char *) AcquireQuantumMemory(
+ string_info->length+MaxTextExtent,sizeof(*string_info->datum));
+ if (string_info->datum == (unsigned char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ }
+ return(string_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneString() allocates memory for the destination string and copies
+% the source string to that memory location.
+%
+% The format of the CloneString method is:
+%
+% char *CloneString(char **destination,const char *source)
+%
+% A description of each parameter follows:
+%
+% o destination: A pointer to a character string.
+%
+% o source: A character string.
+%
+*/
+MagickExport char *CloneString(char **destination,const char *source)
+{
+ size_t
+ length;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(destination != (char **) NULL);
+ if (source == (const char *) NULL)
+ {
+ if (*destination != (char *) NULL)
+ *destination=DestroyString(*destination);
+ return(*destination);
+ }
+ if (*destination == (char *) NULL)
+ {
+ *destination=AcquireString(source);
+ return(*destination);
+ }
+ length=strlen(source);
+ if (~length < MaxTextExtent)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ *destination=(char *) ResizeQuantumMemory(*destination,length+MaxTextExtent,
+ sizeof(*destination));
+ if (*destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ (void) CopyMagickString(*destination,source,(length+1)*sizeof(*destination));
+ return(*destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C l o n e S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CloneStringInfo() clones a copy of the StringInfo structure.
+%
+% The format of the CloneStringInfo method is:
+%
+% StringInfo *CloneStringInfo(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport StringInfo *CloneStringInfo(const StringInfo *string_info)
+{
+ StringInfo
+ *clone_info;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ clone_info=AcquireStringInfo(string_info->length);
+ if (string_info->length != 0)
+ (void) CopyMagickMemory(clone_info->datum,string_info->datum,
+ string_info->length+1);
+ return(clone_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o m p a r e S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CompareStringInfo() compares the two datums target and source. It returns
+% an integer less than, equal to, or greater than zero if target is found,
+% respectively, to be less than, to match, or be greater than source.
+%
+% The format of the CompareStringInfo method is:
+%
+% int CompareStringInfo(const StringInfo *target,const StringInfo *source)
+%
+% A description of each parameter follows:
+%
+% o target: the target string.
+%
+% o source: the source string.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport int CompareStringInfo(const StringInfo *target,
+ const StringInfo *source)
+{
+ int
+ status;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(target != (StringInfo *) NULL);
+ assert(target->signature == MagickSignature);
+ assert(source != (StringInfo *) NULL);
+ assert(source->signature == MagickSignature);
+ status=memcmp(target->datum,source->datum,MagickMin(target->length,
+ source->length));
+ if (status != 0)
+ return(status);
+ if (target->length == source->length)
+ return(0);
+ return(target->length < source->length ? -1 : 1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n c a t e n a t e M a g i c k S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConcatenateMagickString() concatenates the source string to the destination
+% string. The destination buffer is always null-terminated even if the
+% string must be truncated.
+%
+% The format of the ConcatenateMagickString method is:
+%
+% size_t ConcatenateMagickString(char *destination,const char *source,
+% const size_t length)
+%
+% A description of each parameter follows:
+%
+% o destination: the destination string.
+%
+% o source: the source string.
+%
+% o length: the length of the destination string.
+%
+*/
+MagickExport size_t ConcatenateMagickString(char *destination,
+ const char *source,const size_t length)
+{
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ register size_t
+ i;
+
+ size_t
+ count;
+
+ assert(destination != (char *) NULL);
+ assert(source != (const char *) NULL);
+ assert(length >= 1);
+ p=source;
+ q=destination;
+ i=length;
+ while ((i-- != 0) && (*q != '\0'))
+ q++;
+ count=(size_t) (q-destination);
+ i=length-count;
+ if (i == 0)
+ return(count+strlen(p));
+ while (*p != '\0')
+ {
+ if (i != 1)
+ {
+ *q++=(*p);
+ i--;
+ }
+ p++;
+ }
+ *q='\0';
+ return(count+(p-source));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n c a t e n a t e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConcatenateString() appends a copy of string source, including the
+% terminating null character, to the end of string destination.
+%
+% The format of the ConcatenateString method is:
+%
+% MagickBooleanType ConcatenateString(char **destination,
+% const char *source)
+%
+% A description of each parameter follows:
+%
+% o destination: A pointer to a character string.
+%
+% o source: A character string.
+%
+*/
+MagickExport MagickBooleanType ConcatenateString(char **destination,
+ const char *source)
+{
+ size_t
+ length,
+ source_length;
+
+ assert(destination != (char **) NULL);
+ if (source == (const char *) NULL)
+ return(MagickTrue);
+ if (*destination == (char *) NULL)
+ {
+ *destination=AcquireString(source);
+ return(MagickTrue);
+ }
+ length=strlen(*destination);
+ source_length=strlen(source);
+ if (~length < source_length)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConcatenateString");
+ length+=source_length;
+ if (~length < MaxTextExtent)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConcatenateString");
+ *destination=(char *) ResizeQuantumMemory(*destination,length+MaxTextExtent,
+ sizeof(*destination));
+ if (*destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConcatenateString");
+ (void) ConcatenateMagickString(*destination,source,(length+1)*
+ sizeof(*destination));
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n c a t e n a t e S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConcatenateStringInfo() concatenates the source string to the destination
+% string.
+%
+% The format of the ConcatenateStringInfo method is:
+%
+% void ConcatenateStringInfo(StringInfo *string_info,
+% const StringInfo *source)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+% o source: the source string.
+%
+*/
+MagickExport void ConcatenateStringInfo(StringInfo *string_info,
+ const StringInfo *source)
+{
+ size_t
+ length;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ assert(source != (const StringInfo *) NULL);
+ length=string_info->length;
+ if (~length < source->length)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConcatenateString");
+ SetStringInfoLength(string_info,length+source->length);
+ (void) CopyMagickMemory(string_info->datum+length,source->datum,
+ source->length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n f i g u r e F i l e T o S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConfigureFileToStringInfo() returns the contents of a configure file as a
+% string.
+%
+% The format of the ConfigureFileToStringInfo method is:
+%
+% StringInfo *ConfigureFileToStringInfo(const char *filename)
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the filename.
+%
+*/
+MagickExport StringInfo *ConfigureFileToStringInfo(const char *filename)
+{
+ char
+ *string;
+
+ int
+ file;
+
+ MagickOffsetType
+ offset;
+
+ size_t
+ length;
+
+ StringInfo
+ *string_info;
+
+ void
+ *map;
+
+ assert(filename != (const char *) NULL);
+ file=open(filename,O_RDONLY | O_BINARY);
+ if (file == -1)
+ return((StringInfo *) NULL);
+ offset=(MagickOffsetType) MagickSeek(file,0,SEEK_END);
+ if ((offset < 0) || (offset != (MagickOffsetType) ((ssize_t) offset)))
+ {
+ file=close(file)-1;
+ return((StringInfo *) NULL);
+ }
+ length=(size_t) offset;
+ string=(char *) NULL;
+ if (~length > MaxTextExtent)
+ string=(char *) AcquireQuantumMemory(length+MaxTextExtent,sizeof(*string));
+ if (string == (char *) NULL)
+ {
+ file=close(file)-1;
+ return((StringInfo *) NULL);
+ }
+ map=MapBlob(file,ReadMode,0,length);
+ if (map != (void *) NULL)
+ {
+ (void) CopyMagickMemory(string,map,length);
+ (void) UnmapBlob(map,length);
+ }
+ else
+ {
+ register size_t
+ i;
+
+ ssize_t
+ count;
+
+ (void) MagickSeek(file,0,SEEK_SET);
+ for (i=0; i < length; i+=count)
+ {
+ count=read(file,string+i,(size_t) MagickMin(length-i,(size_t)
+ SSIZE_MAX));
+ if (count <= 0)
+ {
+ count=0;
+ if (errno != EINTR)
+ break;
+ }
+ }
+ if (i < length)
+ {
+ file=close(file)-1;
+ string=DestroyString(string);
+ return((StringInfo *) NULL);
+ }
+ }
+ string[length]='\0';
+ file=close(file)-1;
+ string_info=AcquireStringInfo(0);
+ (void) CopyMagickString(string_info->path,filename,MaxTextExtent);
+ string_info->length=length;
+ string_info->datum=(unsigned char *) string;
+ return(string_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n s t a n t S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConstantString() allocates memory for a string and copies the source string
+% to that memory location (and returns it). Use it for strings that you do
+% do not expect to change over its lifetime.
+%
+% The format of the ConstantString method is:
+%
+% char *ConstantString(const char *source)
+%
+% A description of each parameter follows:
+%
+% o source: A character string.
+%
+*/
+MagickExport char *ConstantString(const char *source)
+{
+ char
+ *destination;
+
+ size_t
+ length;
+
+ length=0;
+ if (source != (char *) NULL)
+ length+=strlen(source);
+ destination=(char *) NULL;
+ if (~length >= 1UL)
+ destination=(char *) AcquireQuantumMemory(length+1UL,sizeof(*destination));
+ if (destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ *destination='\0';
+ if (source != (char *) NULL)
+ (void) CopyMagickString(destination,source,(length+1)*sizeof(*destination));
+ return(destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o p y M a g i c k S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CopyMagickString() copies the source string to the destination string. The
+% destination buffer is always null-terminated even if the string must be
+% truncated. The return value is the minimum of the source string length
+% or the length parameter.
+%
+% The format of the CopyMagickString method is:
+%
+% size_t CopyMagickString(const char *destination,char *source,
+% const size_t length)
+%
+% A description of each parameter follows:
+%
+% o destination: the destination string.
+%
+% o source: the source string.
+%
+% o length: the length of the destination string.
+%
+*/
+MagickExport size_t CopyMagickString(char *destination,const char *source,
+ const size_t length)
+{
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ register size_t
+ n;
+
+ p=source;
+ q=destination;
+ for (n=length; n > 4; n-=4)
+ {
+ *q=(*p++);
+ if (*q == '\0')
+ return((size_t) (p-source-1));
+ q++;
+ *q=(*p++);
+ if (*q == '\0')
+ return((size_t) (p-source-1));
+ q++;
+ *q=(*p++);
+ if (*q == '\0')
+ return((size_t) (p-source-1));
+ q++;
+ *q=(*p++);
+ if (*q == '\0')
+ return((size_t) (p-source-1));
+ q++;
+ }
+ if (n != 0)
+ for (n--; n != 0; n--)
+ {
+ *q=(*p++);
+ if (*q == '\0')
+ return((size_t) (p-source-1));
+ q++;
+ }
+ if (length != 0)
+ *q='\0';
+ return((size_t) (p-source-1));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyString() destroys memory associated with a string.
+%
+% The format of the DestroyString method is:
+%
+% char *DestroyString(char *string)
+%
+% A description of each parameter follows:
+%
+% o string: the string.
+%
+*/
+MagickExport char *DestroyString(char *string)
+{
+ return((char *) RelinquishMagickMemory(string));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyStringInfo() destroys memory associated with the StringInfo structure.
+%
+% The format of the DestroyStringInfo method is:
+%
+% StringInfo *DestroyStringInfo(StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport StringInfo *DestroyStringInfo(StringInfo *string_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ if (string_info->datum != (unsigned char *) NULL)
+ string_info->datum=(unsigned char *) RelinquishMagickMemory(
+ string_info->datum);
+ string_info->signature=(~MagickSignature);
+ string_info=(StringInfo *) RelinquishMagickMemory(string_info);
+ return(string_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y S t r i n g L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyStringList() zeros memory associated with a string list.
+%
+% The format of the DestroyStringList method is:
+%
+% char **DestroyStringList(char **list)
+%
+% A description of each parameter follows:
+%
+% o list: the string list.
+%
+*/
+MagickExport char **DestroyStringList(char **list)
+{
+ register long
+ i;
+
+ assert(list != (char **) NULL);
+ for (i=0; list[i] != (char *) NULL; i++)
+ list[i]=DestroyString(list[i]);
+ list=(char **) RelinquishMagickMemory(list);
+ return(list);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E s c a p e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% EscapeString() allocates memory for a backslash-escaped version of a
+% source text string, copies the escaped version of the text to that
+% memory location while adding backslash characters, and returns the
+% escaped string.
+%
+% The format of the EscapeString method is:
+%
+% char *EscapeString(const char *source,const char escape)
+%
+% A description of each parameter follows:
+%
+% o allocate_string: Method EscapeString returns the escaped string.
+%
+% o source: A character string.
+%
+% o escape: the quoted string termination character to escape (e.g. '"').
+%
+*/
+MagickExport char *EscapeString(const char *source,const char escape)
+{
+ char
+ *destination;
+
+ register char
+ *q;
+
+ register const char
+ *p;
+
+ size_t
+ length;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(source != (const char *) NULL);
+ length=strlen(source);
+ for (p=source; *p != '\0'; p++)
+ if ((*p == '\\') || (*p == escape))
+ {
+ if (~length < 1)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
+ length++;
+ }
+ destination=(char *) NULL;
+ if (~length >= MaxTextExtent)
+ destination=(char *) AcquireQuantumMemory(length+MaxTextExtent,
+ sizeof(*destination));
+ if (destination == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToEscapeString");
+ *destination='\0';
+ if (source != (char *) NULL)
+ {
+ q=destination;
+ for (p=source; *p != '\0'; p++)
+ {
+ if ((*p == '\\') || (*p == escape))
+ *q++='\\';
+ *q++=(*p);
+ }
+ *q='\0';
+ }
+ return(destination);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F i l e T o S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FileToString() returns the contents of a file as a string.
+%
+% The format of the FileToString method is:
+%
+% char *FileToString(const char *filename,const size_t extent,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the filename.
+%
+% o extent: Maximum length of the string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport char *FileToString(const char *filename,const size_t extent,
+ ExceptionInfo *exception)
+{
+ size_t
+ length;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ return((char *) FileToBlob(filename,extent,&length,exception));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F i l e T o S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FileToStringInfo() returns the contents of a file as a string.
+%
+% The format of the FileToStringInfo method is:
+%
+% StringInfo *FileToStringInfo(const char *filename,const size_t extent,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the filename.
+%
+% o extent: Maximum length of the string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport StringInfo *FileToStringInfo(const char *filename,
+ const size_t extent,ExceptionInfo *exception)
+{
+ StringInfo
+ *string_info;
+
+ assert(filename != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ string_info=AcquireStringInfo(0);
+ (void) CopyMagickString(string_info->path,filename,MaxTextExtent);
+ string_info->datum=FileToBlob(filename,extent,&string_info->length,exception);
+ if (string_info->datum == (unsigned char *) NULL)
+ {
+ string_info=DestroyStringInfo(string_info);
+ return((StringInfo *) NULL);
+ }
+ return(string_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t M a g i c k S i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatMagickSize() converts a size to a human readable format, for example,
+% 14kb, 234mb, 2.7gb, or 3.0tb. Scaling is done by repetitively dividing by
+% 1024.
+%
+% The format of the FormatMagickSize method is:
+%
+% long FormatMagickSize(const MagickSizeType size,char *format)
+%
+% A description of each parameter follows:
+%
+% o size: convert this size to a human readable format.
+%
+% o format: human readable format.
+%
+*/
+MagickExport long FormatMagickSize(const MagickSizeType size,char *format)
+{
+ double
+ length;
+
+ long
+ count;
+
+ register long
+ i,
+ j;
+
+ static const char
+ *units[] = { "b", "kb", "mb", "gb", "tb", "pb", "eb", (char *) NULL };
+
+#if defined(_MSC_VER) && (_MSC_VER == 1200)
+ length=(double) ((MagickOffsetType) size);
+#else
+ length=(double) size;
+#endif
+ for (i=0; (length >= 1024.0) && (units[i+1] != (const char *) NULL); i++)
+ length/=1024.0;
+ for (j=2; j < 10; j++)
+ {
+ count=FormatMagickString(format,MaxTextExtent,"%.*g%s",(int) (i+j),length,
+ units[i]);
+ if (strchr(format,'+') == (char *) NULL)
+ break;
+ }
+ return(count);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t M a g i c k S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatMagickString() prints formatted output of a variable argument list.
+%
+% The format of the FormatMagickString method is:
+%
+% long FormatMagickString(char *string,const size_t length,
+% const char *format,...)
+%
+% A description of each parameter follows.
+%
+% o string: FormatMagickString() returns the formatted string in this
+% character buffer.
+%
+% o length: the maximum length of the string.
+%
+% o format: A string describing the format to use to write the remaining
+% arguments.
+%
+*/
+
+MagickExport long FormatMagickStringList(char *string,const size_t length,
+ const char *format,va_list operands)
+{
+ int
+ n;
+
+#if defined(MAGICKCORE_HAVE_VSNPRINTF)
+ n=vsnprintf(string,length,format,operands);
+#else
+ n=vsprintf(string,format,operands);
+#endif
+ if (n < 0)
+ string[length-1]='\0';
+ return((long) n);
+}
+
+MagickExport long FormatMagickString(char *string,const size_t length,
+ const char *format,...)
+{
+ long
+ n;
+
+ va_list
+ operands;
+
+ va_start(operands,format);
+ n=(long) FormatMagickStringList(string,length,format,operands);
+ va_end(operands);
+ return(n);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F o r m a t M a g i c k T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FormatMagickTime() returns the specified time in the Internet date/time
+% format and the length of the timestamp.
+%
+% The format of the FormatMagickTime method is:
+%
+% long FormatMagickTime(const time_t time,const size_t length,
+% char *timestamp)
+%
+% A description of each parameter follows.
+%
+% o time: the time since the Epoch (00:00:00 UTC, January 1, 1970),
+% measured in seconds.
+%
+% o length: the maximum length of the string.
+%
+% o timestamp: Return the Internet date/time here.
+%
+*/
+MagickExport long FormatMagickTime(const time_t time,const size_t length,
+ char *timestamp)
+{
+ long
+ count;
+
+ struct tm
+ gm_time,
+ local_time;
+
+ time_t
+ timezone;
+
+ assert(timestamp != (char *) NULL);
+ (void) ResetMagickMemory(&local_time,0,sizeof(local_time));
+ (void) ResetMagickMemory(&gm_time,0,sizeof(gm_time));
+#if defined(MAGICKCORE_HAVE_LOCALTIME_R)
+ (void) localtime_r(&time,&local_time);
+#else
+ {
+ struct tm
+ *my_time;
+
+ my_time=localtime(&time);
+ if (my_time != (struct tm *) NULL)
+ (void) memcpy(&local_time,my_time,sizeof(local_time));
+ }
+#endif
+#if defined(MAGICKCORE_HAVE_GMTIME_R)
+ (void) gmtime_r(&time,&gm_time);
+#else
+ {
+ struct tm
+ *my_time;
+
+ my_time=gmtime(&time);
+ if (my_time != (struct tm *) NULL)
+ (void) memcpy(&gm_time,my_time,sizeof(gm_time));
+ }
+#endif
+ timezone=(time_t) ((local_time.tm_min-gm_time.tm_min)/60+
+ local_time.tm_hour-gm_time.tm_hour+24*((local_time.tm_year-
+ gm_time.tm_year) != 0 ? (local_time.tm_year-gm_time.tm_year) :
+ (local_time.tm_yday-gm_time.tm_yday)));
+ count=FormatMagickString(timestamp,length,
+ "%04d-%02d-%02dT%02d:%02d:%02d%+03ld:00",local_time.tm_year+1900,
+ local_time.tm_mon+1,local_time.tm_mday,local_time.tm_hour,
+ local_time.tm_min,local_time.tm_sec,(long) timezone);
+ return(count);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t E n v i r o n m e n t V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetEnvironmentValue() returns the environment string that matches the
+% specified name.
+%
+% The format of the GetEnvironmentValue method is:
+%
+% char *GetEnvironmentValue(const char *name)
+%
+% A description of each parameter follows:
+%
+% o name: the environment name.
+%
+*/
+MagickExport char *GetEnvironmentValue(const char *name)
+{
+ const char
+ *environment;
+
+ environment=getenv(name);
+ if (environment == (const char *) NULL)
+ return((char *) NULL);
+ return(ConstantString(environment));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t S t r i n g I n f o D a t u m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetStringInfoDatum() returns the datum associated with the string.
+%
+% The format of the GetStringInfoDatum method is:
+%
+% unsigned char *GetStringInfoDatum(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport unsigned char *GetStringInfoDatum(const StringInfo *string_info)
+{
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ return(string_info->datum);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t S t r i n g I n f o L e n g t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetStringInfoLength() returns the string length.
+%
+% The format of the GetStringInfoLength method is:
+%
+% size_t GetStringInfoLength(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport size_t GetStringInfoLength(const StringInfo *string_info)
+{
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ return(string_info->length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t S t r i n g I n f o P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetStringInfoPath() returns the path associated with the string.
+%
+% The format of the GetStringInfoPath method is:
+%
+% const char *GetStringInfoPath(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport const char *GetStringInfoPath(const StringInfo *string_info)
+{
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ return(string_info->path);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o c a l e C o m p a r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LocaleCompare() performs a case-insensitive comparison of two strings
+% byte-by-byte, according to the ordering of the current locale encoding.
+% LocaleCompare returns an integer greater than, equal to, or less than 0,
+% if the string pointed to by p is greater than, equal to, or less than the
+% string pointed to by q respectively. The sign of a non-zero return value
+% is determined by the sign of the difference between the values of the first< % pair of bytes that differ in the strings being compared.
+%
+% The format of the LocaleCompare method is:
+%
+% long LocaleCompare(const char *p,const char *q)
+%
+% A description of each parameter follows:
+%
+% o p: A pointer to a character string.
+%
+% o q: A pointer to a character string to compare to p.
+%
+*/
+MagickExport long LocaleCompare(const char *p,const char *q)
+{
+ if ((p == (char *) NULL) && (q == (char *) NULL))
+ return(0);
+ if (p == (char *) NULL)
+ return(-1);
+ if (q == (char *) NULL)
+ return(1);
+#if defined(MAGICKCORE_HAVE_STRCASECMP)
+ return((long) strcasecmp(p,q));
+#else
+ {
+ register unsigned char
+ c,
+ d;
+
+ for ( ; ; )
+ {
+ c=(unsigned char) *p;
+ d=(unsigned char) *q;
+ if ((c == '\0') || (AsciiMap[c] != AsciiMap[d]))
+ break;
+ p++;
+ q++;
+ }
+ return((long) AsciiMap[c]-AsciiMap[d]);
+ }
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o c a l e L o w e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LocaleLower() transforms all of the characters in the supplied
+% null-terminated string, changing all uppercase letters to lowercase.
+%
+% The format of the LocaleLower method is:
+%
+% void LocaleLower(char *string)
+%
+% A description of each parameter follows:
+%
+% o string: A pointer to the string to convert to lower-case Locale.
+%
+*/
+MagickExport void LocaleLower(char *string)
+{
+ register char
+ *q;
+
+ assert(string != (char *) NULL);
+ for (q=string; *q != '\0'; q++)
+ *q=(char) tolower((int) *q);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o c a l e N C o m p a r e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LocaleNCompare() performs a case-insensitive comparison of two
+% strings byte-by-byte, according to the ordering of the current locale
+% encoding. LocaleNCompare returns an integer greater than, equal to, or
+% less than 0, if the string pointed to by p is greater than, equal to, or
+% less than the string pointed to by q respectively. The sign of a non-zero
+% return value is determined by the sign of the difference between the
+% values of the first pair of bytes that differ in the strings being
+% compared. The LocaleNCompare method makes the same comparison as
+% LocaleCompare but looks at a maximum of n bytes. Bytes following a
+% null byte are not compared.
+%
+% The format of the LocaleNCompare method is:
+%
+% long LocaleNCompare(const char *p,const char *q,const size_t n)
+%
+% A description of each parameter follows:
+%
+% o p: A pointer to a character string.
+%
+% o q: A pointer to a character string to compare to p.
+%
+% o length: the number of characters to compare in strings p & q.
+%
+*/
+MagickExport long LocaleNCompare(const char *p,const char *q,
+ const size_t length)
+{
+ if (p == (char *) NULL)
+ return(-1);
+ if (q == (char *) NULL)
+ return(1);
+#if defined(MAGICKCORE_HAVE_STRNCASECMP)
+ return((long) strncasecmp(p,q,length));
+#else
+ {
+ register size_t
+ n;
+
+ register unsigned char
+ c,
+ d;
+
+ for (n=length; n != 0; n--)
+ {
+ c=(unsigned char) *p;
+ d=(unsigned char) *q;
+ if (AsciiMap[c] != AsciiMap[d])
+ return(AsciiMap[c]-AsciiMap[d]);
+ if (c == '\0')
+ return(0L);
+ p++;
+ q++;
+ }
+ return(0L);
+ }
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o c a l e U p p e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LocaleUpper() transforms all of the characters in the supplied
+% null-terminated string, changing all lowercase letters to uppercase.
+%
+% The format of the LocaleUpper method is:
+%
+% void LocaleUpper(char *string)
+%
+% A description of each parameter follows:
+%
+% o string: A pointer to the string to convert to upper-case Locale.
+%
+*/
+MagickExport void LocaleUpper(char *string)
+{
+ register char
+ *q;
+
+ assert(string != (char *) NULL);
+ for (q=string; *q != '\0'; q++)
+ *q=(char) toupper((int) *q);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r i n t S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PrintStringInfo() prints the string.
+%
+% The format of the PrintStringInfo method is:
+%
+% void PrintStringInfo(FILE *file,const char *id,
+% const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o file: the file, typically stdout.
+%
+% o id: the string id.
+%
+% o string_info: the string info.
+%
+*/
+MagickExport void PrintStringInfo(FILE *file,const char *id,
+ const StringInfo *string_info)
+{
+ register const char
+ *p;
+
+ register size_t
+ i,
+ j;
+
+ assert(id != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",id);
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ p=(char *) string_info->datum;
+ for (i=0; i < string_info->length; i++)
+ {
+ if (((int) ((unsigned char) *p) < 32) &&
+ (isspace((int) ((unsigned char) *p)) == 0))
+ break;
+ p++;
+ }
+ if (i == string_info->length)
+ {
+ (void) fputs((char *) string_info->datum,file);
+ (void) fputc('\n',file);
+ return;
+ }
+ /*
+ Convert string to a HEX list.
+ */
+ p=(char *) string_info->datum;
+ for (i=0; i < string_info->length; i+=0x14)
+ {
+ (void) fprintf(file,"0x%08lx: ",(unsigned long) (0x14*i));
+ for (j=1; j <= MagickMin(string_info->length-i,0x14); j++)
+ {
+ (void) fprintf(file,"%02lx",(unsigned long) (*(p+j)) & 0xff);
+ if ((j % 0x04) == 0)
+ (void) fputc(' ',file);
+ }
+ for ( ; j <= 0x14; j++)
+ {
+ (void) fputc(' ',file);
+ (void) fputc(' ',file);
+ if ((j % 0x04) == 0)
+ (void) fputc(' ',file);
+ }
+ (void) fputc(' ',file);
+ for (j=1; j <= MagickMin(string_info->length-i,0x14); j++)
+ {
+ if (isprint((int) ((unsigned char) *p)) != 0)
+ (void) fputc(*p,file);
+ else
+ (void) fputc('-',file);
+ p++;
+ }
+ (void) fputc('\n',file);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetStringInfo() reset the string to all null bytes.
+%
+% The format of the ResetStringInfo method is:
+%
+% void ResetStringInfo(StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport void ResetStringInfo(StringInfo *string_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ (void) ResetMagickMemory(string_info->datum,0,string_info->length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStringInfo() copies the source string to the destination string.
+%
+% The format of the SetStringInfo method is:
+%
+% void SetStringInfo(StringInfo *string_info,const StringInfo *source)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+% o source: the source string.
+%
+*/
+MagickExport void SetStringInfo(StringInfo *string_info,
+ const StringInfo *source)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ assert(source != (StringInfo *) NULL);
+ assert(source->signature == MagickSignature);
+ if (string_info->length == 0)
+ return;
+ (void) ResetMagickMemory(string_info->datum,0,string_info->length);
+ (void) CopyMagickMemory(string_info->datum,source->datum,MagickMin(
+ string_info->length,source->length));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t S t r i n g I n f o D a t u m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStringInfoDatum() copies bytes from the source string for the length of
+% the destination string.
+%
+% The format of the SetStringInfoDatum method is:
+%
+% void SetStringInfoDatum(StringInfo *string_info,
+% const unsigned char *source)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+% o source: the source string.
+%
+*/
+MagickExport void SetStringInfoDatum(StringInfo *string_info,
+ const unsigned char *source)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ if (string_info->length != 0)
+ (void) CopyMagickMemory(string_info->datum,source,string_info->length);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t S t r i n g I n f o L e n g t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStringInfoLength() set the string length to the specified value.
+%
+% The format of the SetStringInfoLength method is:
+%
+% void SetStringInfoLength(StringInfo *string_info,const size_t length)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+% o length: the string length.
+%
+*/
+MagickExport void SetStringInfoLength(StringInfo *string_info,
+ const size_t length)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ if (~length < MaxTextExtent)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ string_info->length=length;
+ if (string_info->datum == (unsigned char *) NULL)
+ string_info->datum=(unsigned char *) AcquireQuantumMemory(length+
+ MaxTextExtent,sizeof(*string_info->datum));
+ else
+ string_info->datum=(unsigned char *) ResizeQuantumMemory(string_info->datum,
+ length+MaxTextExtent,sizeof(*string_info->datum));
+ if (string_info->datum == (unsigned char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t S t r i n g I n f o D a t u m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetStringInfoPath() sets the path associated with the string.
+%
+% The format of the SetStringInfoPath method is:
+%
+% void SetStringInfoPath(StringInfo *string_info,const char *path)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+% o path: the path.
+%
+*/
+MagickExport void SetStringInfoPath(StringInfo *string_info,const char *path)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ assert(path != (const char *) NULL);
+ (void) CopyMagickString(string_info->path,path,MaxTextExtent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l i t S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SplitStringInfo() splits a string into two and returns it.
+%
+% The format of the SplitStringInfo method is:
+%
+% StringInfo *SplitStringInfo(StringInfo *string_info,const size_t offset)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string info.
+%
+*/
+MagickExport StringInfo *SplitStringInfo(StringInfo *string_info,
+ const size_t offset)
+{
+ StringInfo
+ *split_info;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string_info != (StringInfo *) NULL);
+ assert(string_info->signature == MagickSignature);
+ if (offset > string_info->length)
+ return((StringInfo *) NULL);
+ split_info=AcquireStringInfo(offset);
+ SetStringInfo(split_info,string_info);
+ (void) CopyMagickMemory(string_info->datum,string_info->datum+offset,
+ string_info->length-offset+MaxTextExtent);
+ SetStringInfoLength(string_info,string_info->length-offset);
+ return(split_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g I n f o T o S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringInfoToString() converts a string info string to a C string.
+%
+% The format of the StringInfoToString method is:
+%
+% char *StringInfoToString(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string.
+%
+*/
+MagickExport char *StringInfoToString(const StringInfo *string_info)
+{
+ char
+ *string;
+
+ size_t
+ length;
+
+ string=(char *) NULL;
+ length=string_info->length;
+ if (~length >= MaxTextExtent)
+ string=(char *) AcquireQuantumMemory(length+MaxTextExtent,sizeof(*string));
+ if (string != (char *) NULL)
+ (void) CopyMagickString(string,(char *) string_info->datum,
+ (length+1)*sizeof(*string));
+ return(string);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g T o A r g v %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringToArgv() converts a text string into command line arguments.
+%
+% The format of the StringToArgv method is:
+%
+% char **StringToArgv(const char *text,int *argc)
+%
+% A description of each parameter follows:
+%
+% o argv: Method StringToArgv returns the string list unless an error
+% occurs, otherwise NULL.
+%
+% o text: Specifies the string to segment into a list.
+%
+% o argc: This integer pointer returns the number of arguments in the
+% list.
+%
+*/
+MagickExport char **StringToArgv(const char *text,int *argc)
+{
+ char
+ **argv;
+
+ register const char
+ *p,
+ *q;
+
+ register long
+ i;
+
+ *argc=0;
+ if (text == (char *) NULL)
+ return((char **) NULL);
+ /*
+ Determine the number of arguments.
+ */
+ for (p=text; *p != '\0'; )
+ {
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ (*argc)++;
+ if (*p == '"')
+ for (p++; (*p != '"') && (*p != '\0'); p++) ;
+ if (*p == '\'')
+ for (p++; (*p != '\'') && (*p != '\0'); p++) ;
+ while ((isspace((int) ((unsigned char) *p)) == 0) && (*p != '\0'))
+ p++;
+ }
+ (*argc)++;
+ argv=(char **) AcquireQuantumMemory((size_t) (*argc+1UL),sizeof(*argv));
+ if (argv == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConvertStringToARGV");
+ /*
+ Convert string to an ASCII list.
+ */
+ argv[0]=AcquireString("magick");
+ p=text;
+ for (i=1; i < (long) *argc; i++)
+ {
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ q=p;
+ if (*q == '"')
+ {
+ p++;
+ for (q++; (*q != '"') && (*q != '\0'); q++) ;
+ }
+ else
+ if (*q == '\'')
+ {
+ for (q++; (*q != '\'') && (*q != '\0'); q++) ;
+ q++;
+ }
+ else
+ while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
+ q++;
+ argv[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
+ sizeof(**argv));
+ if (argv[i] == (char *) NULL)
+ {
+ for (i--; i >= 0; i--)
+ argv[i]=DestroyString(argv[i]);
+ argv=(char **) RelinquishMagickMemory(argv);
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToConvertStringToARGV");
+ }
+ (void) CopyMagickString(argv[i],p,(size_t) (q-p+1));
+ p=q;
+ while ((isspace((int) ((unsigned char) *p)) == 0) && (*p != '\0'))
+ p++;
+ }
+ argv[i]=(char *) NULL;
+ return(argv);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g T o D o u b l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringToDouble() converts a text string to a double. If the string contains
+% a percent sign (e.g. 50%) that percentage of the interval is returned.
+%
+% The format of the StringToDouble method is:
+%
+% double StringToDouble(const char *string,const double interval)
+%
+% A description of each parameter follows:
+%
+% o string: Specifies the string representing a value.
+%
+% o interval: Specifies the interval; used for obtaining a percentage.
+%
+*/
+MagickExport double StringToDouble(const char *string,const double interval)
+{
+ char
+ *q;
+
+ double
+ value;
+
+ assert(string != (char *) NULL);
+ value=strtod(string,&q);
+ switch (tolower((int) ((unsigned char) *q)))
+ {
+ case '%': value*=pow(1024.0,0)*interval/100.0; break;
+ case 'k': value*=pow(1024.0,1); break;
+ case 'm': value*=pow(1024.0,2); break;
+ case 'g': value*=pow(1024.0,3); break;
+ case 't': value*=pow(1024.0,4); break;
+ case 'p': value*=pow(1024.0,5); break;
+ case 'e': value*=pow(1024.0,6); break;
+ case 'z': value*=pow(1024.0,7); break;
+ case 'y': value*=pow(1024.0,8); break;
+ default: break;
+ }
+ return(value);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g I n f o T o H e x S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringInfoToHexString() converts a string info string to a C string.
+%
+% The format of the StringInfoToHexString method is:
+%
+% char *StringInfoToHexString(const StringInfo *string_info)
+%
+% A description of each parameter follows:
+%
+% o string_info: the string.
+%
+*/
+MagickExport char *StringInfoToHexString(const StringInfo *string_info)
+{
+ char
+ *string;
+
+ register const unsigned char
+ *p;
+
+ register long
+ i;
+
+ register unsigned char
+ *q;
+
+ size_t
+ length;
+
+ unsigned char
+ hex_digits[16];
+
+ length=string_info->length;
+ if (~length < MaxTextExtent)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ string=(char *) AcquireQuantumMemory(length+MaxTextExtent,2*sizeof(*string));
+ if (string == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ hex_digits[0]='0';
+ hex_digits[1]='1';
+ hex_digits[2]='2';
+ hex_digits[3]='3';
+ hex_digits[4]='4';
+ hex_digits[5]='5';
+ hex_digits[6]='6';
+ hex_digits[7]='7';
+ hex_digits[8]='8';
+ hex_digits[9]='9';
+ hex_digits[10]='a';
+ hex_digits[11]='b';
+ hex_digits[12]='c';
+ hex_digits[13]='d';
+ hex_digits[14]='e';
+ hex_digits[15]='f';
+ p=string_info->datum;
+ q=(unsigned char *) string;
+ for (i=0; i < (long) string_info->length; i++)
+ {
+ *q++=hex_digits[(*p >> 4) & 0x0f];
+ *q++=hex_digits[*p & 0x0f];
+ p++;
+ }
+ *q='\0';
+ return(string);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g T o k e n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringToken() extracts a token a from the string.
+%
+% The format of the StringToken method is:
+%
+% char *StringToken(const char *delimiters,char **string)
+%
+% A description of each parameter follows:
+%
+% o delimiters: one or more delimiters.
+%
+% o string: return the first token in the string. If none is found, return
+% NULL.
+%
+*/
+MagickExport char *StringToken(const char *delimiters,char **string)
+{
+ char
+ *q;
+
+ register char
+ *p;
+
+ register const char
+ *r;
+
+ register int
+ c,
+ d;
+
+ p=(*string);
+ if (p == (char *) NULL)
+ return((char *) NULL);
+ for (q=p; ; )
+ {
+ c=(*p++);
+ r=delimiters;
+ do
+ {
+ d=(*r++);
+ if (c == d)
+ {
+ if (c == '\0')
+ p=(char *) NULL;
+ else
+ p[-1]='\0';
+ *string=p;
+ return(q);
+ }
+ } while (d != '\0');
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g T o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringToList() converts a text string into a list by segmenting the text
+% string at each carriage return discovered. The list is converted to HEX
+% characters if any control characters are discovered within the text string.
+%
+% The format of the StringToList method is:
+%
+% char **StringToList(const char *text)
+%
+% A description of each parameter follows:
+%
+% o list: Method StringToList returns the string list unless an error
+% occurs, otherwise NULL.
+%
+% o text: Specifies the string to segment into a list.
+%
+*/
+MagickExport char **StringToList(const char *text)
+{
+ char
+ **textlist;
+
+ register const char
+ *p;
+
+ register long
+ i;
+
+ unsigned long
+ lines;
+
+ if (text == (char *) NULL)
+ return((char **) NULL);
+ for (p=text; *p != '\0'; p++)
+ if (((int) ((unsigned char) *p) < 32) &&
+ (isspace((int) ((unsigned char) *p)) == 0))
+ break;
+ if (*p == '\0')
+ {
+ register const char
+ *q;
+
+ /*
+ Convert string to an ASCII list.
+ */
+ lines=1;
+ for (p=text; *p != '\0'; p++)
+ if (*p == '\n')
+ lines++;
+ textlist=(char **) AcquireQuantumMemory((size_t) lines+1UL,
+ sizeof(*textlist));
+ if (textlist == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
+ p=text;
+ for (i=0; i < (long) lines; i++)
+ {
+ for (q=p; *q != '\0'; q++)
+ if ((*q == '\r') || (*q == '\n'))
+ break;
+ textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
+ sizeof(*textlist));
+ if (textlist[i] == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
+ (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
+ if (*q == '\r')
+ q++;
+ p=q+1;
+ }
+ }
+ else
+ {
+ char
+ hex_string[MaxTextExtent];
+
+ register char
+ *q;
+
+ register long
+ j;
+
+ /*
+ Convert string to a HEX list.
+ */
+ lines=(unsigned long) (strlen(text)/0x14)+1;
+ textlist=(char **) AcquireQuantumMemory((size_t) lines+1UL,
+ sizeof(*textlist));
+ if (textlist == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
+ p=text;
+ for (i=0; i < (long) lines; i++)
+ {
+ textlist[i]=(char *) AcquireQuantumMemory(2UL*MaxTextExtent,
+ sizeof(*textlist));
+ if (textlist[i] == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
+ (void) FormatMagickString(textlist[i],MaxTextExtent,"0x%08lx: ",0x14*i);
+ q=textlist[i]+strlen(textlist[i]);
+ for (j=1; j <= (long) MagickMin(strlen(p),0x14); j++)
+ {
+ (void) FormatMagickString(hex_string,MaxTextExtent,"%02x",*(p+j));
+ (void) CopyMagickString(q,hex_string,MaxTextExtent);
+ q+=2;
+ if ((j % 0x04) == 0)
+ *q++=' ';
+ }
+ for ( ; j <= 0x14; j++)
+ {
+ *q++=' ';
+ *q++=' ';
+ if ((j % 0x04) == 0)
+ *q++=' ';
+ }
+ *q++=' ';
+ for (j=1; j <= (long) MagickMin(strlen(p),0x14); j++)
+ {
+ if (isprint((int) ((unsigned char) *p)) != 0)
+ *q++=(*p);
+ else
+ *q++='-';
+ p++;
+ }
+ *q='\0';
+ }
+ }
+ textlist[i]=(char *) NULL;
+ return(textlist);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i n g T o S t r i n g I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StringToStringInfo() returns the contents of a file as a string.
+%
+% The format of the StringToStringInfo method is:
+%
+% StringInfo *StringToStringInfo(const char *string)
+%
+% A description of each parameter follows:
+%
+% o string: The string.
+%
+*/
+MagickExport StringInfo *StringToStringInfo(const char *string)
+{
+ StringInfo
+ *string_info;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(string != (const char *) NULL);
+ string_info=AcquireStringInfo(strlen(string)+1);
+ SetStringInfoDatum(string_info,(const unsigned char *) string);
+ return(string_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S t r i p S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StripString() strips any whitespace or quotes from the beginning and end of
+% a string of characters.
+%
+% The format of the StripString method is:
+%
+% void StripString(char *message)
+%
+% A description of each parameter follows:
+%
+% o message: Specifies an array of characters.
+%
+*/
+MagickExport void StripString(char *message)
+{
+ register char
+ *p,
+ *q;
+
+ size_t
+ length;
+
+ assert(message != (char *) NULL);
+ if (*message == '\0')
+ return;
+ length=strlen(message);
+ p=message;
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if ((*p == '\'') || (*p == '"'))
+ p++;
+ q=message+length-1;
+ while ((isspace((int) ((unsigned char) *q)) != 0) && (q > p))
+ q--;
+ if (q > p)
+ if ((*q == '\'') || (*q == '"'))
+ q--;
+ (void) CopyMagickMemory(message,p,(size_t) (q-p+1));
+ message[q-p+1]='\0';
+ for (p=message; *p != '\0'; p++)
+ if (*p == '\n')
+ *p=' ';
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S u b s t i t u t e S t r i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SubstituteString() performs string substitution on a buffer, replacing the
+% buffer with the substituted version. Buffer must be allocate from the heap.
+%
+% The format of the SubstituteString method is:
+%
+% MagickBooleanType SubstituteString(char **buffer,const char *search,
+% const char *replace)
+%
+% A description of each parameter follows:
+%
+% o buffer: the buffer to perform replacements on. Replaced with new
+% allocation if a replacement is made.
+%
+% o search: String to search for.
+%
+% o replace: Replacement string.
+%
+*/
+MagickExport MagickBooleanType SubstituteString(char **buffer,
+ const char *search,const char *replace)
+{
+ char
+ *result;
+
+ const char
+ *match,
+ *source;
+
+ MagickOffsetType
+ destination_offset;
+
+ size_t
+ copy_length,
+ length,
+ replace_length,
+ result_length,
+ search_length;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(buffer != (char **) NULL);
+ assert(*buffer != (char *) NULL);
+ assert(search != (const char *) NULL);
+ assert(replace != (const char *) NULL);
+ if (strcmp(search,replace) == 0)
+ return(MagickTrue);
+ source=(*buffer);
+ match=strstr(source,search);
+ if (match == (char *) NULL)
+ return(MagickFalse);
+ result=(char *) NULL;
+ length=strlen(source);
+ if (~length >= MaxTextExtent)
+ result=(char *) AcquireQuantumMemory(length+MaxTextExtent,sizeof(*result));
+ if (result == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ *result='\0';
+ result_length=0;
+ destination_offset=0;
+ replace_length=strlen(replace);
+ for (search_length=strlen(search); match != (char *) NULL; )
+ {
+ /*
+ Copy portion before match.
+ */
+ copy_length=(size_t) (match-source);
+ if (copy_length != 0)
+ {
+ result_length+=copy_length;
+ if ((result_length+MaxTextExtent) >= length)
+ {
+ length+=copy_length;
+ result=(char *) ResizeQuantumMemory(result,length+MaxTextExtent,
+ sizeof(*result));
+ if (result == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAcquireString");
+ }
+ (void) CopyMagickString(result+destination_offset,source,copy_length+1);
+ destination_offset+=copy_length;
+ }
+ /*
+ Copy replacement.
+ */
+ result_length+=replace_length;
+ if ((result_length+MaxTextExtent) >= length)
+ {
+ length+=replace_length;
+ result=(char *) ResizeQuantumMemory(result,length+MaxTextExtent,
+ sizeof(*result));
+ if (result == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ }
+ (void) ConcatenateMagickString(result+destination_offset,replace,length);
+ destination_offset+=replace_length;
+ /*
+ Find next match.
+ */
+ source=match;
+ source+=search_length;
+ match=strstr(source,search);
+ }
+ /*
+ Copy remaining string.
+ */
+ copy_length=strlen(source);
+ result_length+=copy_length;
+ if ((result_length+MaxTextExtent) >= length)
+ {
+ length+=copy_length;
+ result=(char *) ResizeQuantumMemory(result,length+MaxTextExtent,
+ sizeof(*result));
+ if (result == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ }
+ (void) ConcatenateMagickString(result+destination_offset,source,(size_t)
+ (length+MaxTextExtent-destination_offset));
+ (void) RelinquishMagickMemory(*buffer);
+ *buffer=result;
+ return(MagickTrue);
+}
diff --git a/magick/string_.h b/magick/string_.h
new file mode 100644
index 0000000..be346fb
--- /dev/null
+++ b/magick/string_.h
@@ -0,0 +1,115 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore string methods.
+*/
+#ifndef _MAGICKCORE_STRING_H_
+#define _MAGICKCORE_STRING_H_
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <stdarg.h>
+#include <time.h>
+#include "magick/exception.h"
+
+typedef struct _StringInfo
+{
+ char
+ path[MaxTextExtent];
+
+ unsigned char
+ *datum;
+
+ size_t
+ length;
+
+ unsigned long
+ signature;
+} StringInfo;
+
+extern MagickExport char
+ *AcquireString(const char *),
+ *CloneString(char **,const char *),
+ *ConstantString(const char *),
+ *DestroyString(char *),
+ **DestroyStringList(char **),
+ *EscapeString(const char *,const char),
+ *FileToString(const char *,const size_t,ExceptionInfo *),
+ *GetEnvironmentValue(const char *),
+ *StringInfoToHexString(const StringInfo *),
+ *StringInfoToString(const StringInfo *),
+ **StringToArgv(const char *,int *),
+ *StringToken(const char *,char **),
+ **StringToList(const char *);
+
+extern MagickExport const char
+ *GetStringInfoPath(const StringInfo *);
+
+extern MagickExport double
+ StringToDouble(const char *,const double);
+
+extern MagickExport long
+ FormatMagickSize(const MagickSizeType,char *),
+ FormatMagickString(char *,const size_t,const char *,...)
+ magick_attribute((format (printf,3,4))),
+ FormatMagickStringList(char *,const size_t,const char *,va_list)
+ magick_attribute((format (printf,3,0))),
+ FormatMagickTime(const time_t,const size_t,char *),
+ LocaleCompare(const char *,const char *),
+ LocaleNCompare(const char *,const char *,const size_t);
+
+extern MagickExport MagickBooleanType
+ ConcatenateString(char **,const char *),
+ SubstituteString(char **,const char *,const char *);
+
+extern MagickExport int
+ CompareStringInfo(const StringInfo *,const StringInfo *);
+
+extern MagickExport size_t
+ ConcatenateMagickString(char *,const char *,const size_t),
+ CopyMagickString(char *,const char *,const size_t),
+ GetStringInfoLength(const StringInfo *);
+
+extern MagickExport StringInfo
+ *AcquireStringInfo(const size_t),
+ *CloneStringInfo(const StringInfo *),
+ *ConfigureFileToStringInfo(const char *),
+ *DestroyStringInfo(StringInfo *),
+ *FileToStringInfo(const char *,const size_t,ExceptionInfo *),
+ *SplitStringInfo(StringInfo *,const size_t),
+ *StringToStringInfo(const char *);
+
+extern MagickExport unsigned char
+ *GetStringInfoDatum(const StringInfo *);
+
+extern MagickExport void
+ ConcatenateStringInfo(StringInfo *,const StringInfo *),
+ LocaleLower(char *),
+ LocaleUpper(char *),
+ PrintStringInfo(FILE *file,const char *,const StringInfo *),
+ ResetStringInfo(StringInfo *),
+ SetStringInfo(StringInfo *,const StringInfo *),
+ SetStringInfoDatum(StringInfo *,const unsigned char *),
+ SetStringInfoLength(StringInfo *,const size_t),
+ SetStringInfoPath(StringInfo *,const char *),
+ StripString(char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/studio.h b/magick/studio.h
new file mode 100644
index 0000000..c43512b
--- /dev/null
+++ b/magick/studio.h
@@ -0,0 +1,450 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private application programming interface declarations.
+*/
+#ifndef _MAGICKCORE_STUDIO_H
+#define _MAGICKCORE_STUDIO_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(__CYGWIN32__)
+# if !defined(__CYGWIN__)
+# define __CYGWIN__ __CYGWIN32__
+# endif
+#endif
+
+#if defined(_WIN32) || defined(WIN32)
+# if !defined(__WINDOWS__)
+# if defined(_WIN32)
+# define __WINDOWS__ _WIN32
+# else
+# if defined(WIN32)
+# define __WINDOWS__ WIN32
+# endif
+# endif
+# endif
+#endif
+
+#if defined(_WIN64) || defined(WIN64)
+# if !defined(__WINDOWS__)
+# if defined(_WIN64)
+# define __WINDOWS__ _WIN64
+# else
+# if defined(WIN64)
+# define __WINDOWS__ WIN64
+# endif
+# endif
+# endif
+#endif
+
+#if !defined(vms) && !defined(macintosh) && !defined(__WINDOWS__)
+# define MAGICKCORE_POSIX_SUPPORT
+#endif
+
+#define MAGICKCORE_IMPLEMENTATION 1
+
+#if !defined(_MAGICKCORE_CONFIG_H)
+# define _MAGICKCORE_CONFIG_H
+# if !defined(vms) && !defined(macintosh)
+# include "magick/magick-config.h"
+# else
+# include "magick-config.h"
+# endif
+#if defined(MAGICKCORE__FILE_OFFSET_BITS) && !defined(_FILE_OFFSET_BITS)
+# define _FILE_OFFSET_BITS MAGICKCORE__FILE_OFFSET_BITS
+#endif
+#if defined(_magickcore_const) && !defined(const)
+# define const _magickcore_const
+#endif
+#if defined(_magickcore_inline) && !defined(inline)
+# define inline _magickcore_inline
+#endif
+# if defined(__cplusplus) || defined(c_plusplus)
+# undef inline
+# endif
+#if defined(_magickcore_restrict) && !defined(__restrict)
+# define __restrict _magickcore_restrict
+#endif
+#endif
+
+#if defined(MAGICKCORE_NAMESPACE_PREFIX)
+# include "magick/methods.h"
+#endif
+
+#if !defined(const)
+# define STDC
+#endif
+
+#if defined(__BORLANDC__) && defined(_DLL)
+# pragma message("BCBMagick lib DLL export interface")
+# define _MAGICKDLL_
+# define _MAGICKLIB_
+# define MAGICKCORE_MODULES_SUPPORT
+# undef MAGICKCORE_BUILD_MODULES
+#endif
+
+#if defined(__WINDOWS__)
+# if defined(_MT) && defined(_DLL) && !defined(_MAGICKDLL_) && !defined(_LIB)
+# define _MAGICKDLL_
+# endif
+# if defined(_MAGICKDLL_)
+# if defined(_VISUALC_)
+# pragma warning( disable: 4273 ) /* Disable the dll linkage warnings */
+# endif
+# if !defined(_MAGICKLIB_)
+# define MagickExport __declspec(dllimport)
+# if defined(_VISUALC_)
+# pragma message( "MagickCore lib DLL import interface" )
+# endif
+# else
+# define MagickExport __declspec(dllexport)
+# if defined(_VISUALC_)
+# pragma message( "MagickCore lib DLL export interface" )
+# endif
+# endif
+# else
+# define MagickExport
+# if defined(_VISUALC_)
+# pragma message( "MagickCore lib static interface" )
+# endif
+# endif
+
+# if defined(_DLL) && !defined(_LIB)
+# define ModuleExport __declspec(dllexport)
+# if defined(_VISUALC_)
+# pragma message( "MagickCore module DLL export interface" )
+# endif
+# else
+# define ModuleExport
+# if defined(_VISUALC_)
+# pragma message( "MagickCore module static interface" )
+# endif
+
+# endif
+# define MagickGlobal __declspec(thread)
+# if defined(_VISUALC_)
+# pragma warning(disable : 4018)
+# pragma warning(disable : 4068)
+# pragma warning(disable : 4244)
+# pragma warning(disable : 4142)
+# pragma warning(disable : 4800)
+# pragma warning(disable : 4786)
+# pragma warning(disable : 4996)
+# endif
+#else
+# define MagickExport
+# define ModuleExport
+# define MagickGlobal
+#endif
+
+#define MagickSignature 0xabacadabUL
+#if !defined(MaxTextExtent)
+# define MaxTextExtent 4096
+#endif
+
+#include <stdarg.h>
+#include <stdio.h>
+#if defined(__WINDOWS__) && defined(_DEBUG)
+#define _CRTDBG_MAP_ALLOC
+#endif
+#include <stdlib.h>
+#if !defined(__WINDOWS__)
+# include <unistd.h>
+#else
+# include <direct.h>
+# if !defined(MAGICKCORE_HAVE_STRERROR)
+# define HAVE_STRERROR
+# endif
+#endif
+
+#if defined(MAGICKCORE_HAVE_STRINGS_H)
+# include <strings.h>
+#endif
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include <limits.h>
+#include <signal.h>
+#include <assert.h>
+
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+# include <pthread.h>
+#elif defined(__WINDOWS__)
+# define MAGICKORE_HAVE_WINTHREADS 1
+#include <windows.h>
+#endif
+#if defined(MAGICKCORE_HAVE_SYS_SYSLIMITS_H)
+# include <sys/syslimits.h>
+#endif
+#if defined(MAGICKCORE_HAVE_ARM_LIMITS_H)
+# include <arm/limits.h>
+#endif
+
+#if defined(_OPENMP) && (_OPENMP >= 200203)
+# include <omp.h>
+# define MAGICKCORE_OPENMP_SUPPORT 1
+#endif
+
+#if defined(MAGICKCORE_HAVE_PREAD) && defined(MAGICKCORE_HAVE_DECL_PREAD) && !MAGICKCORE_HAVE_DECL_PREAD
+ssize_t pread(int,void *,size_t,off_t);
+#endif
+
+#if defined(MAGICKCORE_HAVE_PWRITE) && defined(MAGICKCORE_HAVE_DECL_PWRITE) && !MAGICKCORE_HAVE_DECL_PWRITE
+ssize_t pwrite(int,const void *,size_t,off_t);
+#endif
+
+#if defined(MAGICKCORE_HAVE_STRLCPY) && defined(MAGICKCORE_HAVE_DECL_STRLCPY) && !MAGICKCORE_HAVE_DECL_STRLCPY
+extern size_t strlcpy(char *,const char *,size_t);
+#endif
+
+#if defined(MAGICKCORE_HAVE_VSNPRINTF) && defined(MAGICKCORE_HAVE_DECL_VSNPRINTF) && !MAGICKCORE_HAVE_DECL_VSNPRINTF
+extern int vsnprintf(char *,size_t,const char *,va_list);
+#endif
+
+#if !defined(magick_attribute)
+# if !defined(__GNUC__)
+# define magick_attribute(x) /* nothing */
+# else
+# define magick_attribute __attribute__
+# endif
+#endif
+
+#if !defined(magick_unused)
+# if defined(__GNUC__)
+# define magick_unused(x) magick_unused_ ## x __attribute__((unused))
+# elif defined(__LCLINT__)
+# define magick_unused(x) /*@unused@*/ x
+# else
+# define magick_unused(x) x
+# endif
+#endif
+
+#if defined(__WINDOWS__) || defined(MAGICKCORE_POSIX_SUPPORT)
+# include <sys/types.h>
+# include <sys/stat.h>
+# if defined(MAGICKCORE_HAVE_FTIME)
+# include <sys/timeb.h>
+# endif
+# if defined(MAGICKCORE_POSIX_SUPPORT)
+# if defined(MAGICKCORE_HAVE_SYS_NDIR_H) || defined(MAGICKCORE_HAVE_SYS_DIR_H) || defined(MAGICKCORE_HAVE_NDIR_H)
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if defined(MAGICKCORE_HAVE_SYS_NDIR_H)
+# include <sys/ndir.h>
+# endif
+# if defined(MAGICKCORE_HAVE_SYS_DIR_H)
+# include <sys/dir.h>
+# endif
+# if defined(MAGICKCORE_HAVE_NDIR_H)
+# include <ndir.h>
+# endif
+# else
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+# endif
+# include <sys/wait.h>
+# include <pwd.h>
+# endif
+# if !defined(S_ISDIR)
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+# endif
+# if !defined(S_ISREG)
+# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+# endif
+# include "magick/magick-type.h"
+# if !defined(__WINDOWS__)
+# include <sys/time.h>
+# if defined(MAGICKCORE_HAVE_SYS_TIMES_H)
+# include <sys/times.h>
+# endif
+# if defined(MAGICKCORE_HAVE_SYS_RESOURCE_H)
+# include <sys/resource.h>
+# endif
+#endif
+#else
+# include <types.h>
+# include <stat.h>
+# if defined(macintosh)
+# if !defined(DISABLE_SIOUX)
+# include <SIOUX.h>
+# include <console.h>
+# endif
+# include <unix.h>
+# endif
+# include "magick/magick-type.h"
+#endif
+
+#if defined(S_IRUSR) && defined(S_IWUSR)
+# define S_MODE (S_IRUSR | S_IWUSR)
+#elif defined (__WINDOWS__)
+# define S_MODE (_S_IREAD | _S_IWRITE)
+#else
+# define S_MODE 0600
+#endif
+
+#if defined(__WINDOWS__)
+# include "magick/nt-base.h"
+#endif
+#if defined(macintosh)
+# include "magick/mac.h"
+#endif
+#if defined(vms)
+# include "magick/vms.h"
+#endif
+
+#undef HAVE_CONFIG_H
+#undef gamma
+#undef index
+#undef pipe
+#undef y1
+
+/*
+ Review these platform specific definitions.
+*/
+#if defined(MAGICKCORE_POSIX_SUPPORT) && !defined(__OS2__)
+# define DirectorySeparator "/"
+# define DirectoryListSeparator ':'
+# define EditorOptions " -title \"Edit Image Comment\" -e vi"
+# define Exit exit
+# define IsBasenameSeparator(c) ((c) == '/' ? MagickTrue : MagickFalse)
+# define X11_PREFERENCES_PATH "~/."
+# define ProcessPendingEvents(text)
+# define ReadCommandlLine(argc,argv)
+# define SetNotifyHandlers
+#else
+# if defined(vms)
+# define X11_APPLICATION_PATH "decw$system_defaults:"
+# define DirectorySeparator ""
+# define DirectoryListSeparator ';'
+# define EditorOptions ""
+# define Exit exit
+# define IsBasenameSeparator(c) \
+ (((c) == ']') || ((c) == ':') || ((c) == '/') ? MagickTrue : MagickFalse)
+# define MAGICKCORE_LIBRARY_PATH "sys$login:"
+# define MAGICKCORE_CODER_PATH "sys$login:"
+# define MAGICKCORE_FILTER_PATH "sys$login:"
+# define MAGICKCORE_SHARE_PATH "sys$login:"
+# define X11_PREFERENCES_PATH "decw$user_defaults:"
+# define ProcessPendingEvents(text)
+# define ReadCommandlLine(argc,argv)
+# define SetNotifyHandlers
+# endif
+# if defined(__OS2__)
+# define DirectorySeparator "\\"
+# define DirectoryListSeparator ';'
+# define EditorOptions " -title \"Edit Image Comment\" -e vi"
+# define Exit exit
+# define IsBasenameSeparator(c) \
+ (((c) == '/') || ((c) == '\\') ? MagickTrue : MagickFalse)
+# define PreferencesDefaults "~\."
+# define ProcessPendingEvents(text)
+# define ReadCommandlLine(argc,argv)
+# define SetNotifyHandlers
+#endif
+# if defined(macintosh)
+# define X11_APPLICATION_PATH "/usr/lib/X11/app-defaults/"
+# define DirectorySeparator ":"
+# define DirectoryListSeparator ';'
+# define EditorOptions ""
+# define IsBasenameSeparator(c) ((c) == ':' ? MagickTrue : MagickFalse)
+# define MAGICKCORE_LIBRARY_PATH ""
+# define MAGICKCORE_CODER_PATH ""
+# define MAGICKCORE_FILTER_PATH ""
+# define MAGICKCORE_SHARE_PATH ""
+# define X11_PREFERENCES_PATH "~/."
+# if defined(DISABLE_SIOUX)
+# define ReadCommandlLine(argc,argv)
+# define SetNotifyHandlers \
+ SetFatalErrorHandler(MacFatalErrorHandler); \
+ SetErrorHandler(MACErrorHandler); \
+ SetWarningHandler(MACWarningHandler)
+# else
+# define ReadCommandlLine(argc,argv) argc=ccommand(argv); puts(MagickVersion);
+# define SetNotifyHandlers \
+ SetErrorHandler(MACErrorHandler); \
+ SetWarningHandler(MACWarningHandler)
+# endif
+# endif
+# if defined(__WINDOWS__)
+# define DirectorySeparator "\\"
+# define DirectoryListSeparator ';'
+# define EditorOptions ""
+# define IsBasenameSeparator(c) \
+ (((c) == '/') || ((c) == '\\') ? MagickTrue : MagickFalse)
+# define ProcessPendingEvents(text)
+# if !defined(X11_PREFERENCES_PATH)
+# define X11_PREFERENCES_PATH "~\\."
+# endif
+# define ReadCommandlLine(argc,argv)
+# define SetNotifyHandlers \
+ SetErrorHandler(NTErrorHandler); \
+ SetWarningHandler(NTWarningHandler)
+# undef sleep
+# define sleep(seconds) Sleep(seconds*1000)
+# if !defined(MAGICKCORE_HAVE_TIFFCONF_H)
+# define HAVE_TIFFCONF_H
+# endif
+# endif
+
+#endif
+
+/*
+ Define system symbols if not already defined.
+*/
+#if !defined(STDIN_FILENO)
+#define STDIN_FILENO 0x00
+#endif
+
+#if !defined(O_BINARY)
+#define O_BINARY 0x00
+#endif
+
+#if defined(MAGICKCORE_LTDL_DELEGATE) || (defined(__WINDOWS__) && defined(_DLL) && !defined(_LIB))
+# define MAGICKCORE_MODULES_SUPPORT
+#endif
+
+#if defined(_MAGICKMOD_)
+# undef MAGICKCORE_BUILD_MODULES
+# define MAGICKCORE_BUILD_MODULES
+#endif
+
+/*
+ I/O defines.
+*/
+#if defined(__WINDOWS__) && !defined(Windows95) && !defined(__BORLANDC__)
+#define MagickSeek(file,offset,whence) _lseeki64(file,offset,whence)
+#define MagickTell(file) _telli64(file)
+#else
+#define MagickSeek(file,offset,whence) lseek(file,offset,whence)
+#define MagickTell(file) tell(file)
+#endif
+
+/*
+ Magick defines.
+*/
+#define Swap(x,y) ((x)^=(y), (y)^=(x), (x)^=(y))
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/thread-private.h b/magick/thread-private.h
new file mode 100644
index 0000000..2bec784
--- /dev/null
+++ b/magick/thread-private.h
@@ -0,0 +1,118 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private methods for internal threading.
+*/
+#ifndef _MAGICKCORE_THREAD_PRIVATE_H
+#define _MAGICKCORE_THREAD_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/thread_.h>
+
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ typedef pthread_mutex_t MagickMutexType;
+#elif defined(__WINDOWS__)
+ typedef CRITICAL_SECTION MagickMutexType;
+#else
+ typedef unsigned long MagickMutexType;
+#endif
+
+static inline MagickThreadType GetMagickThreadId(void)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ return(pthread_self());
+#elif defined(__WINDOWS__)
+ return(GetCurrentThreadId());
+#else
+ return(getpid());
+#endif
+}
+
+static inline unsigned long GetMagickThreadSignature(void)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ {
+ union
+ {
+ pthread_t
+ id;
+
+ unsigned long
+ signature;
+ } magick_thread;
+
+ magick_thread.signature=0UL;
+ magick_thread.id=pthread_self();
+ return(magick_thread.signature);
+ }
+#elif defined(__WINDOWS__)
+ return((unsigned long) GetCurrentThreadId());
+#else
+ return((unsigned long) getpid());
+#endif
+}
+
+static inline MagickBooleanType IsMagickThreadEqual(const MagickThreadType id)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ if (pthread_equal(id,pthread_self()) != 0)
+ return(MagickTrue);
+#elif defined(__WINDOWS__)
+ if (id == GetCurrentThreadId())
+ return(MagickTrue);
+#else
+ if (id == getpid())
+ return(MagickTrue);
+#endif
+ return(MagickFalse);
+}
+
+/*
+ Lightweight OpenMP methods.
+*/
+static inline unsigned long GetOpenMPMaximumThreads(void)
+{
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ return((unsigned long) omp_get_max_threads());
+#endif
+ return(1UL);
+}
+
+static inline long GetOpenMPThreadId(void)
+{
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ return(omp_get_thread_num());
+#else
+ return(0);
+#endif
+}
+
+static inline void SetOpenMPMaximumThreads(const unsigned long threads)
+{
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ omp_set_num_threads(threads);
+#else
+ (void) threads;
+#endif
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/thread.c b/magick/thread.c
new file mode 100644
index 0000000..56f2cd5
--- /dev/null
+++ b/magick/thread.c
@@ -0,0 +1,181 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% TTTTT H H RRRR EEEEE AAA DDDD %
+% T H H R R E A A D D %
+% T HHHHH RRRR EEE AAAAA D D %
+% T H H R R E A A D D %
+% T H H R R EEEEE A A DDDD %
+% %
+% %
+% MagickCore Thread Methods %
+% %
+% Software Design %
+% John Cristy %
+% March 2003 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/memory_.h"
+#include "magick/thread_.h"
+#include "magick/thread-private.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k C r e a t e T h r e a d K e y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickCreateThreadKey() creates a thread key and returns it.
+%
+% The format of the MagickCreateThreadKey method is:
+%
+% MagickThreadKey MagickCreateThreadKey(MagickThreadKey *key)
+%
+*/
+MagickExport MagickBooleanType MagickCreateThreadKey(MagickThreadKey *key)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ return(pthread_key_create(key,NULL) == 0 ? MagickTrue : MagickFalse);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ *key=TlsAlloc();
+ return(*key != TLS_OUT_OF_INDEXES ? MagickTrue : MagickFalse);
+#else
+ *key=AcquireMagickMemory(sizeof(key));
+ return(*key != (void *) NULL ? MagickTrue : MagickFalse);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k D e l e t e T h r e a d K e y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickDeleteThreadKey() deletes a thread key.
+%
+% The format of the AcquireAESInfo method is:
+%
+% MagickBooleanType MagickDeleteThreadKey(MagickThreadKey key)
+%
+% A description of each parameter follows:
+%
+% o key: the thread key.
+%
+*/
+MagickExport MagickBooleanType MagickDeleteThreadKey(MagickThreadKey key)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ return(pthread_key_delete(key) == 0 ? MagickTrue : MagickFalse);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ return(TlsFree(key) != 0 ? MagickTrue : MagickFalse);
+#else
+ key=(MagickThreadKey) RelinquishMagickMemory(key);
+ return(MagickTrue);
+#endif
+
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k G e t T h r e a d V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickGetThreadValue() returns a value associated with the thread key.
+%
+% The format of the MagickGetThreadValue method is:
+%
+% void *MagickGetThreadValue(MagickThreadKey key)
+%
+% A description of each parameter follows:
+%
+% o key: the thread key.
+%
+*/
+MagickExport void *MagickGetThreadValue(MagickThreadKey key)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ return(pthread_getspecific(key));
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ return(TlsGetValue(key));
+#else
+ return((void *) (*key));
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M a g i c k S e t T h r e a d V a l u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MagickSetThreadValue() associates a value with the thread key.
+%
+% The format of the MagickSetThreadValue method is:
+%
+% MagickBooleanType MagickSetThreadValue(MagickThreadKey key,
+% const void *value)
+%
+% A description of each parameter follows:
+%
+% o key: the thread key.
+%
+% o value: the value
+%
+*/
+MagickExport MagickBooleanType MagickSetThreadValue(MagickThreadKey key,
+ const void *value)
+{
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+ return(pthread_setspecific(key,value) == 0 ? MagickTrue : MagickFalse);
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+ return(TlsSetValue(key,(void *) value) != 0 ? MagickTrue : MagickFalse);
+#else
+ *key=(unsigned long) value;
+ return(MagickTrue);
+#endif
+}
diff --git a/magick/thread_.h b/magick/thread_.h
new file mode 100644
index 0000000..1003a66
--- /dev/null
+++ b/magick/thread_.h
@@ -0,0 +1,53 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private methods for internal threading.
+*/
+#ifndef _MAGICKCORE_THREAD_H
+#define _MAGICKCORE_THREAD_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+typedef pthread_t MagickThreadType;
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+typedef DWORD MagickThreadType;
+#else
+typedef pid_t MagickThreadType;
+#endif
+
+#if defined(MAGICKCORE_HAVE_PTHREAD)
+typedef pthread_key_t MagickThreadKey;
+#elif defined(MAGICKORE_HAVE_WINTHREADS)
+typedef DWORD MagickThreadKey;
+#else
+typedef unsigned long *MagickThreadKey;
+#endif
+
+extern MagickExport MagickBooleanType
+ MagickCreateThreadKey(MagickThreadKey *),
+ MagickDeleteThreadKey(MagickThreadKey),
+ MagickSetThreadValue(MagickThreadKey,const void *);
+
+extern MagickExport void
+ *MagickGetThreadValue(MagickThreadKey);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/threshold.c b/magick/threshold.c
new file mode 100644
index 0000000..88c78e4
--- /dev/null
+++ b/magick/threshold.c
@@ -0,0 +1,1908 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% TTTTT H H RRRR EEEEE SSSSS H H OOO L DDDD %
+% T H H R R E SS H H O O L D D %
+% T HHHHH RRRR EEE SSS HHHHH O O L D D %
+% T H H R R E SS H H O O L D D %
+% T H H R R EEEEE SSSSS H H OOO LLLLL DDDD %
+% %
+% %
+% MagickCore Image Threshold Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 1996 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace.h"
+#include "magick/configure.h"
+#include "magick/constitute.h"
+#include "magick/decorate.h"
+#include "magick/draw.h"
+#include "magick/enhance.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/effect.h"
+#include "magick/fx.h"
+#include "magick/gem.h"
+#include "magick/geometry.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/montage.h"
+#include "magick/pixel-private.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/random_.h"
+#include "magick/random-private.h"
+#include "magick/resize.h"
+#include "magick/resource_.h"
+#include "magick/segment.h"
+#include "magick/shear.h"
+#include "magick/signature-private.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+#include "magick/threshold.h"
+#include "magick/option.h"
+#include "magick/xml-tree.h"
+
+/*
+ Define declarations.
+*/
+#define ThresholdsFilename "thresholds.xml"
+
+/*
+ Typedef declarations.
+*/
+struct _ThresholdMap
+{
+ char
+ *map_id,
+ *description;
+
+ unsigned long
+ width,
+ height;
+
+ long
+ divisor,
+ *levels;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d a p t i v e T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AdaptiveThresholdImage() selects an individual threshold for each pixel
+% based on the range of intensity values in its local neighborhood. This
+% allows for thresholding of an image whose global intensity histogram
+% doesn't contain distinctive peaks.
+%
+% The format of the AdaptiveThresholdImage method is:
+%
+% Image *AdaptiveThresholdImage(const Image *image,
+% const unsigned long width,const unsigned long height,
+% const long offset,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o width: the width of the local neighborhood.
+%
+% o height: the height of the local neighborhood.
+%
+% o offset: the mean offset.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *AdaptiveThresholdImage(const Image *image,
+ const unsigned long width,const unsigned long height,const long offset,
+ ExceptionInfo *exception)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ Image
+ *threshold_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ zero;
+
+ MagickRealType
+ number_pixels;
+
+ CacheView
+ *image_view,
+ *threshold_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if ((image->columns < width) || (image->rows < height))
+ ThrowImageException(OptionError,"ImageSmallerThanRadius");
+ threshold_image=CloneImage(image,0,0,MagickTrue,exception);
+ if (threshold_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(threshold_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&threshold_image->exception);
+ threshold_image=DestroyImage(threshold_image);
+ return((Image *) NULL);
+ }
+ /*
+ Local adaptive threshold.
+ */
+ status=MagickTrue;
+ progress=0;
+ GetMagickPixelPacket(image,&zero);
+ number_pixels=(MagickRealType) width*height;
+ image_view=AcquireCacheView(image);
+ threshold_view=AcquireCacheView(threshold_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict threshold_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-height/2L,
+ image->columns+width,height,exception);
+ q=GetCacheViewAuthenticPixels(threshold_view,0,y,threshold_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ threshold_indexes=GetCacheViewAuthenticIndexQueue(threshold_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ long
+ v;
+
+ MagickPixelPacket
+ mean,
+ pixel;
+
+ register const PixelPacket
+ *r;
+
+ register long
+ u;
+
+ pixel=zero;
+ mean=zero;
+ r=p;
+ for (v=0; v < (long) height; v++)
+ {
+ for (u=0; u < (long) width; u++)
+ {
+ pixel.red+=r[u].red;
+ pixel.green+=r[u].green;
+ pixel.blue+=r[u].blue;
+ pixel.opacity+=r[u].opacity;
+ if (image->colorspace == CMYKColorspace)
+ pixel.index=(MagickRealType) indexes[x+(r-p)+u];
+ }
+ r+=image->columns+width;
+ }
+ mean.red=(MagickRealType) (pixel.red/number_pixels+offset);
+ mean.green=(MagickRealType) (pixel.green/number_pixels+offset);
+ mean.blue=(MagickRealType) (pixel.blue/number_pixels+offset);
+ mean.opacity=(MagickRealType) (pixel.opacity/number_pixels+offset);
+ if (image->colorspace == CMYKColorspace)
+ mean.index=(MagickRealType) (pixel.index/number_pixels+offset);
+ q->red=(Quantum) (((MagickRealType) q->red <= mean.red) ?
+ 0 : QuantumRange);
+ q->green=(Quantum) (((MagickRealType) q->green <= mean.green) ?
+ 0 : QuantumRange);
+ q->blue=(Quantum) (((MagickRealType) q->blue <= mean.blue) ?
+ 0 : QuantumRange);
+ q->opacity=(Quantum) (((MagickRealType) q->opacity <= mean.opacity) ?
+ 0 : QuantumRange);
+ if (image->colorspace == CMYKColorspace)
+ threshold_indexes[x]=(IndexPacket) (((MagickRealType)
+ threshold_indexes[x] <= mean.index) ? 0 : QuantumRange);
+ p++;
+ q++;
+ }
+ sync=SyncCacheViewAuthenticPixels(threshold_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_AdaptiveThresholdImage)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ threshold_view=DestroyCacheView(threshold_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ threshold_image=DestroyImage(threshold_image);
+ return(threshold_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B i l e v e l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BilevelImage() changes the value of individual pixels based on the
+% intensity of each pixel channel. The result is a high-contrast image.
+%
+% More precisely each channel value of the image is 'thresholded' so that if
+% it is equal to or less than the given value it is set to zero, while any
+% value greater than that give is set to it maximum or QuantumRange.
+%
+% This function is what is used to implement the "-threshold" operator for
+% the command line API.
+%
+% If the default channel setting is given the image is thresholded using just
+% the gray 'intensity' of the image, rather than the individual channels.
+%
+% The format of the BilevelImageChannel method is:
+%
+% MagickBooleanType BilevelImage(Image *image,const double threshold)
+% MagickBooleanType BilevelImageChannel(Image *image,
+% const ChannelType channel,const double threshold)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel type.
+%
+% o threshold: define the threshold values.
+%
+% Aside: You can get the same results as operator using LevelImageChannels()
+% with the 'threshold' value for both the black_point and the white_point.
+%
+*/
+
+MagickExport MagickBooleanType BilevelImage(Image *image,const double threshold)
+{
+ MagickBooleanType
+ status;
+
+ status=BilevelImageChannel(image,DefaultChannels,threshold);
+ return(status);
+}
+
+MagickExport MagickBooleanType BilevelImageChannel(Image *image,
+ const ChannelType channel,const double threshold)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ ExceptionInfo
+ *exception;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ /*
+ Bilevel threshold image.
+ */
+ status=MagickTrue;
+ progress=0;
+ exception=(&image->exception);
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ if (channel == DefaultChannels)
+ {
+ for (x=0; x < (long) image->columns; x++)
+ {
+ q->red=(Quantum) ((MagickRealType) PixelIntensityToQuantum(q) <=
+ threshold ? 0 : QuantumRange);
+ q->green=q->red;
+ q->blue=q->red;
+ q++;
+ }
+ }
+ else
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ q->red=(Quantum) ((MagickRealType) q->red <= threshold ? 0 :
+ QuantumRange);
+ if ((channel & GreenChannel) != 0)
+ q->green=(Quantum) ((MagickRealType) q->green <= threshold ? 0 :
+ QuantumRange);
+ if ((channel & BlueChannel) != 0)
+ q->blue=(Quantum) ((MagickRealType) q->blue <= threshold ? 0 :
+ QuantumRange);
+ if ((channel & OpacityChannel) != 0)
+ {
+ if (image->matte == MagickFalse)
+ q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
+ 0 : QuantumRange);
+ else
+ q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold ?
+ OpaqueOpacity : TransparentOpacity);
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <= threshold ?
+ 0 : QuantumRange);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_BilevelImageChannel)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B l a c k T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% BlackThresholdImage() is like ThresholdImage() but forces all pixels below
+% the threshold into black while leaving all pixels above the threshold
+% unchanged.
+%
+% The format of the BlackThresholdImage method is:
+%
+% MagickBooleanType BlackThresholdImage(Image *image,const char *threshold)
+% MagickBooleanType BlackThresholdImageChannel(Image *image,
+% const ChannelType channel,const char *threshold,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o threshold: Define the threshold value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType BlackThresholdImage(Image *image,
+ const char *threshold)
+{
+ MagickBooleanType
+ status;
+
+ status=BlackThresholdImageChannel(image,DefaultChannels,threshold,
+ &image->exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType BlackThresholdImageChannel(Image *image,
+ const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ GeometryInfo
+ geometry_info;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ threshold;
+
+ MagickStatusType
+ flags;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (thresholds == (const char *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ GetMagickPixelPacket(image,&threshold);
+ flags=ParseGeometry(thresholds,&geometry_info);
+ threshold.red=geometry_info.rho;
+ threshold.green=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ threshold.green=threshold.red;
+ threshold.blue=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ threshold.blue=threshold.red;
+ threshold.opacity=geometry_info.psi;
+ if ((flags & PsiValue) == 0)
+ threshold.opacity=threshold.red;
+ threshold.index=geometry_info.chi;
+ if ((flags & ChiValue) == 0)
+ threshold.index=threshold.red;
+ if ((flags & PercentValue) != 0)
+ {
+ threshold.red*=(QuantumRange/100.0);
+ threshold.green*=(QuantumRange/100.0);
+ threshold.blue*=(QuantumRange/100.0);
+ threshold.opacity*=(QuantumRange/100.0);
+ threshold.index*=(QuantumRange/100.0);
+ }
+ /*
+ Black threshold image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ if (channel == DefaultChannels)
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((MagickRealType) q->red < threshold.red)
+ q->red=(Quantum) 0;
+ if ((MagickRealType) q->green < threshold.green)
+ q->green=(Quantum) 0;
+ if ((MagickRealType) q->blue < threshold.blue)
+ q->blue=(Quantum) 0;
+ if ((image->colorspace == CMYKColorspace) &&
+ ((MagickRealType) indexes[x] < threshold.index))
+ indexes[x]=(Quantum) 0;
+ q++;
+ }
+ else
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if (((channel & RedChannel) != 0) &&
+ ((MagickRealType) q->red < threshold.red))
+ q->red=(Quantum) 0;
+ if (((channel & GreenChannel) != 0) &&
+ ((MagickRealType) q->green < threshold.green))
+ q->green=(Quantum) 0;
+ if (((channel & BlueChannel) != 0) &&
+ ((MagickRealType) q->blue < threshold.blue))
+ q->blue=(Quantum) 0;
+ if (((channel & OpacityChannel) != 0) &&
+ ((MagickRealType) q->opacity < threshold.opacity))
+ q->opacity=(Quantum) 0;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ ((MagickRealType) indexes[x] < threshold.index))
+ indexes[x]=(Quantum) 0;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_BlackThresholdImageChannel)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y T h r e s h o l d M a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyThresholdMap() de-allocate the given ThresholdMap
+%
+% The format of the ListThresholdMaps method is:
+%
+% ThresholdMap *DestroyThresholdMap(Threshold *map)
+%
+% A description of each parameter follows.
+%
+% o map: Pointer to the Threshold map to destroy
+%
+*/
+MagickExport ThresholdMap *DestroyThresholdMap(ThresholdMap *map)
+{
+ assert(map != (ThresholdMap *) NULL);
+ if (map->map_id != (char *) NULL)
+ map->map_id=DestroyString(map->map_id);
+ if (map->description != (char *) NULL)
+ map->description=DestroyString(map->description);
+ if (map->levels != (long *) NULL)
+ map->levels=(long *) RelinquishMagickMemory(map->levels);
+ map=(ThresholdMap *) RelinquishMagickMemory(map);
+ return(map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t T h r e s h o l d M a p F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetThresholdMapFile() look for a given threshold map name or alias in the
+% given XML file data, and return the allocated the map when found.
+%
+% The format of the ListThresholdMaps method is:
+%
+% ThresholdMap *GetThresholdMap(const char *xml,const char *filename,
+% const char *map_id,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o xml: The threshold map list in XML format.
+%
+% o filename: The threshold map XML filename.
+%
+% o map_id: ID of the map to look for in XML list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ThresholdMap *GetThresholdMapFile(const char *xml,
+ const char *filename,const char *map_id,ExceptionInfo *exception)
+{
+ const char
+ *attr,
+ *content;
+
+ double
+ value;
+
+ ThresholdMap
+ *map;
+
+ XMLTreeInfo
+ *description,
+ *levels,
+ *threshold,
+ *thresholds;
+
+ map = (ThresholdMap *)NULL;
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading threshold map file \"%s\" ...",filename);
+ thresholds=NewXMLTree(xml,exception);
+ if ( thresholds == (XMLTreeInfo *)NULL )
+ return(map);
+
+ for( threshold = GetXMLTreeChild(thresholds,"threshold");
+ threshold != (XMLTreeInfo *)NULL;
+ threshold = GetNextXMLTreeTag(threshold) ) {
+ attr = GetXMLTreeAttribute(threshold, "map");
+ if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
+ break;
+ attr = GetXMLTreeAttribute(threshold, "alias");
+ if ( (attr != (char *)NULL) && (LocaleCompare(map_id,attr) == 0) )
+ break;
+ }
+ if ( threshold == (XMLTreeInfo *)NULL ) {
+ return(map);
+ }
+ description = GetXMLTreeChild(threshold,"description");
+ if ( description == (XMLTreeInfo *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingElement", "<description>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ return(map);
+ }
+ levels = GetXMLTreeChild(threshold,"levels");
+ if ( levels == (XMLTreeInfo *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingElement", "<levels>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ return(map);
+ }
+
+ /* The map has been found -- Allocate a Threshold Map to return */
+ map = (ThresholdMap *)AcquireMagickMemory(sizeof(ThresholdMap));
+ if ( map == (ThresholdMap *)NULL )
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
+ map->map_id = (char *)NULL;
+ map->description = (char *)NULL;
+ map->levels = (long *) NULL;
+
+ /* Assign Basic Attributes */
+ attr = GetXMLTreeAttribute(threshold, "map");
+ if ( attr != (char *)NULL )
+ map->map_id = ConstantString(attr);
+
+ content = GetXMLTreeContent(description);
+ if ( content != (char *)NULL )
+ map->description = ConstantString(content);
+
+ attr = GetXMLTreeAttribute(levels, "width");
+ if ( attr == (char *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingAttribute", "<levels width>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ map->width = (unsigned long) atoi(attr);
+ if ( map->width == 0 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidAttribute", "<levels width>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+
+ attr = GetXMLTreeAttribute(levels, "height");
+ if ( attr == (char *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingAttribute", "<levels height>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ map->height = (unsigned long) atoi(attr);
+ if ( map->height == 0 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidAttribute", "<levels height>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+
+ attr = GetXMLTreeAttribute(levels, "divisor");
+ if ( attr == (char *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingAttribute", "<levels divisor>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ map->divisor = atoi(attr);
+ if ( map->divisor < 2 ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidAttribute", "<levels divisor>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+
+ /* Allocate theshold levels array */
+ content = GetXMLTreeContent(levels);
+ if ( content == (char *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingContent", "<levels>, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ map->levels=(long *) AcquireQuantumMemory((size_t) map->width,map->height*
+ sizeof(*map->levels));
+ if ( map->levels == (long *)NULL )
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireThresholdMap");
+ { /* parse levels into integer array */
+ int i;
+ char *p;
+ for( i=0; i< (long) (map->width*map->height); i++) {
+ map->levels[i] = (int)strtol(content, &p, 10);
+ if ( p == content ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidContent", "<level> too few values, map \"%s\"", map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ if ( map->levels[i] < 0 || map->levels[i] > map->divisor ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidContent", "<level> %ld out of range, map \"%s\"",
+ map->levels[i], map_id);
+ thresholds = DestroyXMLTree(thresholds);
+ map = DestroyThresholdMap(map);
+ return(map);
+ }
+ content = p;
+ }
+ value=(double) strtol(content,&p,10);
+ if (p != content)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlInvalidContent", "<level> too many values, map \"%s\"", map_id);
+ thresholds=DestroyXMLTree(thresholds);
+ map=DestroyThresholdMap(map);
+ return(map);
+ }
+ }
+
+ thresholds = DestroyXMLTree(thresholds);
+ return(map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t T h r e s h o l d M a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetThresholdMap() load and search one or more threshold map files for the
+% a map matching the given name or aliase.
+%
+% The format of the GetThresholdMap method is:
+%
+% ThresholdMap *GetThresholdMap(const char *map_id,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o map_id: ID of the map to look for.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport ThresholdMap *GetThresholdMap(const char *map_id,
+ ExceptionInfo *exception)
+{
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ ThresholdMap
+ *map;
+
+ map=(ThresholdMap *)NULL;
+ options=GetConfigureOptions(ThresholdsFilename,exception);
+ while (( option=(const StringInfo *) GetNextValueInLinkedList(options) )
+ != (const StringInfo *) NULL && map == (ThresholdMap *)NULL )
+ map=GetThresholdMapFile((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),map_id,exception);
+ options=DestroyConfigureOptions(options);
+ return(map);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L i s t T h r e s h o l d M a p F i l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListThresholdMapFile() lists the threshold maps and their descriptions
+% in the given XML file data.
+%
+% The format of the ListThresholdMaps method is:
+%
+% MagickBooleanType ListThresholdMaps(FILE *file,const char*xml,
+% const char *filename,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to the output FILE.
+%
+% o xml: The threshold map list in XML format.
+%
+% o filename: The threshold map XML filename.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickBooleanType ListThresholdMapFile(FILE *file,const char *xml,
+ const char *filename,ExceptionInfo *exception)
+{
+ XMLTreeInfo *thresholds,*threshold,*description;
+ const char *map,*alias,*content;
+
+ assert( xml != (char *)NULL );
+ assert( file != (FILE *)NULL );
+
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading threshold map file \"%s\" ...",filename);
+ thresholds=NewXMLTree(xml,exception);
+ if ( thresholds == (XMLTreeInfo *)NULL )
+ return(MagickFalse);
+
+ (void) fprintf(file,"%-16s %-12s %s\n", "Map", "Alias", "Description");
+ (void) fprintf(file,"----------------------------------------------------\n");
+
+ for( threshold = GetXMLTreeChild(thresholds,"threshold");
+ threshold != (XMLTreeInfo *)NULL;
+ threshold = GetNextXMLTreeTag(threshold) )
+ {
+ map = GetXMLTreeAttribute(threshold, "map");
+ if (map == (char *) NULL) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingAttribute", "<map>");
+ thresholds=DestroyXMLTree(thresholds);
+ return(MagickFalse);
+ }
+ alias = GetXMLTreeAttribute(threshold, "alias");
+ /* alias is optional, no if test needed */
+ description=GetXMLTreeChild(threshold,"description");
+ if ( description == (XMLTreeInfo *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingElement", "<description>, map \"%s\"", map);
+ thresholds=DestroyXMLTree(thresholds);
+ return(MagickFalse);
+ }
+ content=GetXMLTreeContent(description);
+ if ( content == (char *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "XmlMissingContent", "<description>, map \"%s\"", map);
+ thresholds=DestroyXMLTree(thresholds);
+ return(MagickFalse);
+ }
+ (void) fprintf(file,"%-16s %-12s %s\n",map,alias ? alias : "", content);
+ }
+ thresholds=DestroyXMLTree(thresholds);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t T h r e s h o l d M a p s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListThresholdMaps() lists the threshold maps and their descriptions
+% as defined by "threshold.xml" to a file.
+%
+% The format of the ListThresholdMaps method is:
+%
+% MagickBooleanType ListThresholdMaps(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to the output FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListThresholdMaps(FILE *file,
+ ExceptionInfo *exception)
+{
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ if ( file == (FILE *)NULL )
+ file = stdout;
+ options=GetConfigureOptions(ThresholdsFilename,exception);
+
+ (void) fprintf(file, "\n Threshold Maps for Ordered Dither Operations\n");
+
+ while ( ( option=(const StringInfo *) GetNextValueInLinkedList(options) )
+ != (const StringInfo *) NULL)
+ {
+ (void) fprintf(file,"\nPATH: %s\n\n",GetStringInfoPath(option));
+ status|=ListThresholdMapFile(file,(const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),exception);
+ }
+ options=DestroyConfigureOptions(options);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O r d e r e d D i t h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OrderedDitherImage() uses the ordered dithering technique of reducing color
+% images to monochrome using positional information to retain as much
+% information as possible.
+%
+% WARNING: This function is deprecated, and is now just a call to
+% the more more powerful OrderedPosterizeImage(); function.
+%
+% The format of the OrderedDitherImage method is:
+%
+% MagickBooleanType OrderedDitherImage(Image *image)
+% MagickBooleanType OrderedDitherImageChannel(Image *image,
+% const ChannelType channel,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType OrderedDitherImage(Image *image)
+{
+ MagickBooleanType
+ status;
+
+ status=OrderedDitherImageChannel(image,DefaultChannels,&image->exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType OrderedDitherImageChannel(Image *image,
+ const ChannelType channel,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ /*
+ Call the augumented function OrderedPosterizeImage()
+ */
+ status=OrderedPosterizeImageChannel(image,channel,"o8x8",exception);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O r d e r e d P o s t e r i z e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OrderedPosterizeImage() will perform a ordered dither based on a number
+% of pre-defined dithering threshold maps, but over multiple intensity
+% levels, which can be different for different channels, according to the
+% input argument.
+%
+% The format of the OrderedPosterizeImage method is:
+%
+% MagickBooleanType OrderedPosterizeImage(Image *image,
+% const char *threshold_map,ExceptionInfo *exception)
+% MagickBooleanType OrderedPosterizeImageChannel(Image *image,
+% const ChannelType channel,const char *threshold_map,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o threshold_map: A string containing the name of the threshold dither
+% map to use, followed by zero or more numbers representing the number
+% of color levels tho dither between.
+%
+% Any level number less than 2 will be equivelent to 2, and means only
+% binary dithering will be applied to each color channel.
+%
+% No numbers also means a 2 level (bitmap) dither will be applied to all
+% channels, while a single number is the number of levels applied to each
+% channel in sequence. More numbers will be applied in turn to each of
+% the color channels.
+%
+% For example: "o3x3,6" will generate a 6 level posterization of the
+% image with a ordered 3x3 diffused pixel dither being applied between
+% each level. While checker,8,8,4 will produce a 332 colormaped image
+% with only a single checkerboard hash pattern (50% grey) between each
+% color level, to basically double the number of color levels with
+% a bare minimim of dithering.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType OrderedPosterizeImage(Image *image,
+ const char *threshold_map,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=OrderedPosterizeImageChannel(image,DefaultChannels,threshold_map,
+ exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType OrderedPosterizeImageChannel(Image *image,
+ const ChannelType channel,const char *threshold_map,ExceptionInfo *exception)
+{
+#define DitherImageTag "Dither/Image"
+
+ long
+ progress,
+ y;
+
+ LongPixelPacket
+ levels;
+
+ MagickBooleanType
+ status;
+
+ ThresholdMap
+ *map;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (threshold_map == (const char *) NULL)
+ return(MagickTrue);
+ {
+ char
+ token[MaxTextExtent];
+
+ register const char
+ *p;
+
+ p=(char *)threshold_map;
+ while (((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) &&
+ (*p != '\0'))
+ p++;
+ threshold_map=p;
+ while (((isspace((int) ((unsigned char) *p)) == 0) && (*p != ',')) &&
+ (*p != '\0')) {
+ if ((p-threshold_map) >= MaxTextExtent)
+ break;
+ token[p-threshold_map] = *p;
+ p++;
+ }
+ token[p-threshold_map] = '\0';
+ map = GetThresholdMap(token, exception);
+ if ( map == (ThresholdMap *)NULL ) {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
+ "InvalidArgument","%s : '%s'","ordered-dither",threshold_map);
+ return(MagickFalse);
+ }
+ }
+ /* Set channel levels from extra comma seperated arguments
+ Default to 2, the single value given, or individual channel values
+ */
+#if 1
+ { /* parse directly as a comma seperated list of integers */
+ char *p;
+
+ p = strchr((char *) threshold_map,',');
+ if ( p != (char *)NULL && isdigit((int) ((unsigned char) *(++p))) )
+ levels.index = (unsigned long) strtol(p, &p, 10);
+ else
+ levels.index = 2;
+
+ levels.red = ((channel & RedChannel ) != 0) ? levels.index : 0;
+ levels.green = ((channel & GreenChannel) != 0) ? levels.index : 0;
+ levels.blue = ((channel & BlueChannel) != 0) ? levels.index : 0;
+ levels.opacity = ((channel & OpacityChannel) != 0) ? levels.index : 0;
+ levels.index = ((channel & IndexChannel) != 0
+ && (image->colorspace == CMYKColorspace)) ? levels.index : 0;
+
+ /* if more than a single number, each channel has a separate value */
+ if ( p != (char *) NULL && *p == ',' ) {
+ p=strchr((char *) threshold_map,',');
+ p++;
+ if ((channel & RedChannel) != 0)
+ levels.red = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
+ if ((channel & GreenChannel) != 0)
+ levels.green = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
+ if ((channel & BlueChannel) != 0)
+ levels.blue = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
+ if ((channel & IndexChannel) != 0 && image->colorspace == CMYKColorspace)
+ levels.index=(unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
+ if ((channel & OpacityChannel) != 0)
+ levels.opacity = (unsigned long) strtol(p, &p, 10), (void)(*p == ',' && p++);
+ }
+ }
+#else
+ /* Parse level values as a geometry */
+ /* This difficult!
+ * How to map GeometryInfo structure elements into
+ * LongPixelPacket structure elements, but according to channel?
+ * Note the channels list may skip elements!!!!
+ * EG -channel BA -ordered-dither map,2,3
+ * will need to map g.rho -> l.blue, and g.sigma -> l.opacity
+ * A simpler way is needed, probably converting geometry to a temporary
+ * array, then using channel to advance the index into long pixel packet.
+ */
+#endif
+
+#if 0
+printf("DEBUG levels r=%ld g=%ld b=%ld a=%ld i=%ld\n",
+ levels.red, levels.green, levels.blue, levels.opacity, levels.index);
+#endif
+
+ { /* Do the posterized ordered dithering of the image */
+ int
+ d;
+
+ /* d = number of psuedo-level divisions added between color levels */
+ d = map->divisor-1;
+
+ /* reduce levels to levels - 1 */
+ levels.red = levels.red ? levels.red-1 : 0;
+ levels.green = levels.green ? levels.green-1 : 0;
+ levels.blue = levels.blue ? levels.blue-1 : 0;
+ levels.opacity = levels.opacity ? levels.opacity-1 : 0;
+ levels.index = levels.index ? levels.index-1 : 0;
+
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&image->exception);
+ return(MagickFalse);
+ }
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ register int
+ threshold,
+ t,
+ l;
+
+ /*
+ Figure out the dither threshold for this pixel
+ This must be a integer from 1 to map->divisor-1
+ */
+ threshold = map->levels[(x%map->width) +map->width*(y%map->height)];
+
+ /* Dither each channel in the image as appropriate
+ Notes on the integer Math...
+ total number of divisions = (levels-1)*(divisor-1)+1)
+ t1 = this colors psuedo_level =
+ q->red * total_divisions / (QuantumRange+1)
+ l = posterization level 0..levels
+ t = dither threshold level 0..divisor-1 NB: 0 only on last
+ Each color_level is of size QuantumRange / (levels-1)
+ NB: All input levels and divisor are already had 1 subtracted
+ Opacity is inverted so 'off' represents transparent.
+ */
+ if (levels.red) {
+ t = (int) (QuantumScale*q->red*(levels.red*d+1));
+ l = t/d; t = t-l*d;
+ q->red=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.red);
+ }
+ if (levels.green) {
+ t = (int) (QuantumScale*q->green*(levels.green*d+1));
+ l = t/d; t = t-l*d;
+ q->green=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.green);
+ }
+ if (levels.blue) {
+ t = (int) (QuantumScale*q->blue*(levels.blue*d+1));
+ l = t/d; t = t-l*d;
+ q->blue=(Quantum) ((l+(t >= threshold))*QuantumRange/levels.blue);
+ }
+ if (levels.opacity) {
+ t = (int) ((1.0-QuantumScale*q->opacity)*(levels.opacity*d+1));
+ l = t/d; t = t-l*d;
+ q->opacity=(Quantum) ((1.0-l-(t >= threshold))*QuantumRange/
+ levels.opacity);
+ }
+ if (levels.index) {
+ t = (int) (QuantumScale*indexes[x]*(levels.index*d+1));
+ l = t/d; t = t-l*d;
+ indexes[x]=(IndexPacket) ((l+(t>=threshold))*QuantumRange/
+ levels.index);
+ }
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_OrderedPosterizeImageChannel)
+#endif
+ proceed=SetImageProgress(image,DitherImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ }
+ map=DestroyThresholdMap(map);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R a n d o m T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RandomThresholdImage() changes the value of individual pixels based on the
+% intensity of each pixel compared to a random threshold. The result is a
+% low-contrast, two color image.
+%
+% The format of the RandomThresholdImage method is:
+%
+% MagickBooleanType RandomThresholdImageChannel(Image *image,
+% const char *thresholds,ExceptionInfo *exception)
+% MagickBooleanType RandomThresholdImageChannel(Image *image,
+% const ChannelType channel,const char *thresholds,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o thresholds: a geometry string containing low,high thresholds. If the
+% string contains 2x2, 3x3, or 4x4, an ordered dither of order 2, 3, or 4
+% is performed instead.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+MagickExport MagickBooleanType RandomThresholdImage(Image *image,
+ const char *thresholds,ExceptionInfo *exception)
+{
+ MagickBooleanType
+ status;
+
+ status=RandomThresholdImageChannel(image,DefaultChannels,thresholds,
+ exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType RandomThresholdImageChannel(Image *image,
+ const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ threshold;
+
+ MagickRealType
+ min_threshold,
+ max_threshold;
+
+ RandomInfo
+ **random_info;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ if (thresholds == (const char *) NULL)
+ return(MagickTrue);
+ GetMagickPixelPacket(image,&threshold);
+ min_threshold=0.0;
+ max_threshold=(MagickRealType) QuantumRange;
+ flags=ParseGeometry(thresholds,&geometry_info);
+ min_threshold=geometry_info.rho;
+ max_threshold=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ max_threshold=min_threshold;
+ if (strchr(thresholds,'%') != (char *) NULL)
+ {
+ max_threshold*=(MagickRealType) (0.01*QuantumRange);
+ min_threshold*=(MagickRealType) (0.01*QuantumRange);
+ }
+ else
+ if (((max_threshold == min_threshold) || (max_threshold == 1)) &&
+ (min_threshold <= 8))
+ {
+ /*
+ Backward Compatibility -- ordered-dither -- IM v 6.2.9-6.
+ */
+ status=OrderedPosterizeImageChannel(image,channel,thresholds,exception);
+ return(status);
+ }
+ /*
+ Random threshold image.
+ */
+ status=MagickTrue;
+ progress=0;
+ if (channel == AllChannels)
+ {
+ if (AcquireImageColormap(image,2) == MagickFalse)
+ ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
+ image->filename);
+ random_info=AcquireRandomInfoThreadSet();
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ IndexPacket
+ index;
+
+ MagickRealType
+ intensity;
+
+ intensity=(MagickRealType) PixelIntensityToQuantum(q);
+ if (intensity < min_threshold)
+ threshold.index=min_threshold;
+ else if (intensity > max_threshold)
+ threshold.index=max_threshold;
+ else
+ threshold.index=(MagickRealType)(QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ index=(IndexPacket) (intensity <= threshold.index ? 0 : 1);
+ indexes[x]=index;
+ *q++=image->colormap[(long) index];
+ }
+ sync=SyncCacheViewAuthenticPixels(image_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RandomThresholdImageChannel)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ return(status);
+ }
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&image->exception);
+ return(MagickFalse);
+ }
+ random_info=AcquireRandomInfoThreadSet();
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ id,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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);
+ id=GetOpenMPThreadId();
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((channel & RedChannel) != 0)
+ {
+ if ((MagickRealType) q->red < min_threshold)
+ threshold.red=min_threshold;
+ else
+ if ((MagickRealType) q->red > max_threshold)
+ threshold.red=max_threshold;
+ else
+ threshold.red=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ }
+ if ((channel & GreenChannel) != 0)
+ {
+ if ((MagickRealType) q->green < min_threshold)
+ threshold.green=min_threshold;
+ else
+ if ((MagickRealType) q->green > max_threshold)
+ threshold.green=max_threshold;
+ else
+ threshold.green=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ }
+ if ((channel & BlueChannel) != 0)
+ {
+ if ((MagickRealType) q->blue < min_threshold)
+ threshold.blue=min_threshold;
+ else
+ if ((MagickRealType) q->blue > max_threshold)
+ threshold.blue=max_threshold;
+ else
+ threshold.blue=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ }
+ if ((channel & OpacityChannel) != 0)
+ {
+ if ((MagickRealType) q->opacity < min_threshold)
+ threshold.opacity=min_threshold;
+ else
+ if ((MagickRealType) q->opacity > max_threshold)
+ threshold.opacity=max_threshold;
+ else
+ threshold.opacity=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ }
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ {
+ if ((MagickRealType) indexes[x] < min_threshold)
+ threshold.index=min_threshold;
+ else
+ if ((MagickRealType) indexes[x] > max_threshold)
+ threshold.index=max_threshold;
+ else
+ threshold.index=(MagickRealType) (QuantumRange*
+ GetPseudoRandomValue(random_info[id]));
+ }
+ if ((channel & RedChannel) != 0)
+ q->red=(Quantum) ((MagickRealType) q->red <= threshold.red ? 0 :
+ QuantumRange);
+ if ((channel & GreenChannel) != 0)
+ q->green=(Quantum) ((MagickRealType) q->green <= threshold.green ? 0 :
+ QuantumRange);
+ if ((channel & BlueChannel) != 0)
+ q->blue=(Quantum) ((MagickRealType) q->blue <= threshold.blue ? 0 :
+ QuantumRange);
+ if ((channel & OpacityChannel) != 0)
+ q->opacity=(Quantum) ((MagickRealType) q->opacity <= threshold.opacity ?
+ 0 : QuantumRange);
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace))
+ indexes[x]=(IndexPacket) ((MagickRealType) indexes[x] <=
+ threshold.index ? 0 : QuantumRange);
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_RandomThresholdImageChannel)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ random_info=DestroyRandomInfoThreadSet(random_info);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% W h i t e T h r e s h o l d I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% WhiteThresholdImage() is like ThresholdImage() but forces all pixels above
+% the threshold into white while leaving all pixels below the threshold
+% unchanged.
+%
+% The format of the WhiteThresholdImage method is:
+%
+% MagickBooleanType WhiteThresholdImage(Image *image,const char *threshold)
+% MagickBooleanType WhiteThresholdImageChannel(Image *image,
+% const ChannelType channel,const char *threshold,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o channel: the channel or channels to be thresholded.
+%
+% o threshold: Define the threshold value.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType WhiteThresholdImage(Image *image,
+ const char *threshold)
+{
+ MagickBooleanType
+ status;
+
+ status=WhiteThresholdImageChannel(image,DefaultChannels,threshold,
+ &image->exception);
+ return(status);
+}
+
+MagickExport MagickBooleanType WhiteThresholdImageChannel(Image *image,
+ const ChannelType channel,const char *thresholds,ExceptionInfo *exception)
+{
+#define ThresholdImageTag "Threshold/Image"
+
+ GeometryInfo
+ geometry_info;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ MagickPixelPacket
+ threshold;
+
+ MagickStatusType
+ flags;
+
+ CacheView
+ *image_view;
+
+ assert(image != (Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (thresholds == (const char *) NULL)
+ return(MagickTrue);
+ if (SetImageStorageClass(image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ flags=ParseGeometry(thresholds,&geometry_info);
+ GetMagickPixelPacket(image,&threshold);
+ threshold.red=geometry_info.rho;
+ threshold.green=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ threshold.green=threshold.red;
+ threshold.blue=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ threshold.blue=threshold.red;
+ threshold.opacity=geometry_info.psi;
+ if ((flags & PsiValue) == 0)
+ threshold.opacity=threshold.red;
+ threshold.index=geometry_info.chi;
+ if ((flags & ChiValue) == 0)
+ threshold.index=threshold.red;
+ if ((flags & PercentValue) != 0)
+ {
+ threshold.red*=(QuantumRange/100.0);
+ threshold.green*=(QuantumRange/100.0);
+ threshold.blue*=(QuantumRange/100.0);
+ threshold.opacity*=(QuantumRange/100.0);
+ threshold.index*=(QuantumRange/100.0);
+ }
+ /*
+ White threshold image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ 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 < (long) image->columns; x++)
+ {
+ if (((channel & RedChannel) != 0) &&
+ ((MagickRealType) q->red > threshold.red))
+ q->red=(Quantum) QuantumRange;
+ if (((channel & GreenChannel) != 0) &&
+ ((MagickRealType) q->green > threshold.green))
+ q->green=(Quantum) QuantumRange;
+ if (((channel & BlueChannel) != 0) &&
+ ((MagickRealType) q->blue > threshold.blue))
+ q->blue=(Quantum) QuantumRange;
+ if (((channel & OpacityChannel) != 0) &&
+ ((MagickRealType) q->opacity > threshold.opacity))
+ q->opacity=(Quantum) QuantumRange;
+ if (((channel & IndexChannel) != 0) &&
+ (image->colorspace == CMYKColorspace) &&
+ ((MagickRealType) indexes[x] > threshold.index))
+ indexes[x]=(Quantum) QuantumRange;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_WhiteThresholdImageChannel)
+#endif
+ proceed=SetImageProgress(image,ThresholdImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ image_view=DestroyCacheView(image_view);
+ return(status);
+}
diff --git a/magick/threshold.h b/magick/threshold.h
new file mode 100644
index 0000000..76baa6b
--- /dev/null
+++ b/magick/threshold.h
@@ -0,0 +1,59 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image threshold methods.
+*/
+#ifndef _MAGICKCORE_THRESHOLD_H
+#define _MAGICKCORE_THRESHOLD_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _ThresholdMap
+ ThresholdMap;
+
+extern MagickExport Image
+ *AdaptiveThresholdImage(const Image *,const unsigned long,const unsigned long,
+ const long,ExceptionInfo *);
+
+extern MagickExport ThresholdMap
+ *DestroyThresholdMap(ThresholdMap *),
+ *GetThresholdMap(const char *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ BilevelImage(Image *,const double),
+ BilevelImageChannel(Image *,const ChannelType,const double),
+ BlackThresholdImage(Image *,const char *),
+ BlackThresholdImageChannel(Image *,const ChannelType,const char *,
+ ExceptionInfo *),
+ ListThresholdMaps(FILE *,ExceptionInfo *),
+ OrderedDitherImage(Image *), /* deprecated */
+ OrderedDitherImageChannel(Image *,const ChannelType,ExceptionInfo *),
+ OrderedPosterizeImage(Image *,const char *,ExceptionInfo *),
+ OrderedPosterizeImageChannel(Image *,const ChannelType,const char *,
+ ExceptionInfo *),
+ RandomThresholdImage(Image *,const char *,ExceptionInfo *),
+ RandomThresholdImageChannel(Image *,const ChannelType,const char *,
+ ExceptionInfo *),
+ WhiteThresholdImage(Image *,const char *),
+ WhiteThresholdImageChannel(Image *,const ChannelType,const char *,
+ ExceptionInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/timer.c b/magick/timer.c
new file mode 100644
index 0000000..06e3b3d
--- /dev/null
+++ b/magick/timer.c
@@ -0,0 +1,460 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% TTTTT IIIII M M EEEEE RRRR %
+% T I MM MM E R R %
+% T I M M M EEE RRRR %
+% T I M M E R R %
+% T IIIII M M EEEEE R R %
+% %
+% %
+% MagickCore Timing Methods %
+% %
+% Software Design %
+% John Cristy %
+% January 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Contributed by Bill Radcliffe and Bob Friesenhahn.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/timer.h"
+
+/*
+ Define declarations.
+*/
+#if defined(macintosh)
+#define CLK_TCK CLOCKS_PER_SEC
+#endif
+#if !defined(CLK_TCK)
+#define CLK_TCK sysconf(_SC_CLK_TCK)
+#endif
+
+/*
+ Forward declarations.
+*/
+static double
+ UserTime(void);
+
+static void
+ StopTimer(TimerInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e T i m e r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireTimerInfo() initializes the TimerInfo structure. It effectively
+% creates a stopwatch and starts it.
+%
+% The format of the AcquireTimerInfo method is:
+%
+% TimerInfo *AcquireTimerInfo(void)
+%
+*/
+MagickExport TimerInfo *AcquireTimerInfo(void)
+{
+ TimerInfo
+ *timer_info;
+
+ timer_info=(TimerInfo *) AcquireMagickMemory(sizeof(*timer_info));
+ if (timer_info == (TimerInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ (void) ResetMagickMemory(timer_info,0,sizeof(*timer_info));
+ timer_info->signature=MagickSignature;
+ GetTimerInfo(timer_info);
+ return(timer_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C o n t i n u e T i m e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ContinueTimer() resumes a stopped stopwatch. The stopwatch continues
+% counting from the last StartTimer() onwards.
+%
+% The format of the ContinueTimer method is:
+%
+% MagickBooleanType ContinueTimer(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Time statistics structure.
+%
+*/
+MagickExport MagickBooleanType ContinueTimer(TimerInfo *time_info)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ if (time_info->state == UndefinedTimerState)
+ return(MagickFalse);
+ if (time_info->state == StoppedTimerState)
+ {
+ time_info->user.total-=time_info->user.stop-time_info->user.start;
+ time_info->elapsed.total-=time_info->elapsed.stop-
+ time_info->elapsed.start;
+ }
+ time_info->state=RunningTimerState;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y T i m e r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyTimerInfo() zeros memory associated with the TimerInfo structure.
+%
+% The format of the DestroyTimerInfo method is:
+%
+% TimerInfo *DestroyTimerInfo(TimerInfo *timer_info)
+%
+% A description of each parameter follows:
+%
+% o timer_info: The cipher context.
+%
+*/
+MagickExport TimerInfo *DestroyTimerInfo(TimerInfo *timer_info)
+{
+ assert(timer_info != (TimerInfo *) NULL);
+ assert(timer_info->signature == MagickSignature);
+ timer_info->signature=(~MagickSignature);
+ timer_info=(TimerInfo *) RelinquishMagickMemory(timer_info);
+ return(timer_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ E l a p s e d T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ElapsedTime() returns the elapsed time (in seconds) since the last call to
+% StartTimer().
+%
+% The format of the ElapsedTime method is:
+%
+% double ElapsedTime()
+%
+*/
+static double ElapsedTime(void)
+{
+#if defined(MAGICKCORE_HAVE_TIMES)
+ struct tms
+ timer;
+
+ return((double) (times(&timer)/CLK_TCK));
+#else
+#if defined(__WINDOWS__)
+ return(NTElapsedTime());
+#else
+ return((double) clock()/CLK_TCK);
+#endif
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t E l a p s e d T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetElapsedTime() returns the elapsed time (in seconds) passed between the
+% start and stop events. If the stopwatch is still running, it is stopped
+% first.
+%
+% The format of the GetElapsedTime method is:
+%
+% double GetElapsedTime(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+*/
+MagickExport double GetElapsedTime(TimerInfo *time_info)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ if (time_info->state == UndefinedTimerState)
+ return(0.0);
+ if (time_info->state == RunningTimerState)
+ StopTimer(time_info);
+ return(time_info->elapsed.total);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t T i m e r I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTimerInfo() initializes the TimerInfo structure.
+%
+% The format of the GetTimerInfo method is:
+%
+% void GetTimerInfo(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+*/
+MagickExport void GetTimerInfo(TimerInfo *time_info)
+{
+ /*
+ Create a stopwatch and start it.
+ */
+ assert(time_info != (TimerInfo *) NULL);
+ (void) ResetMagickMemory(time_info,0,sizeof(*time_info));
+ time_info->state=UndefinedTimerState;
+ time_info->signature=MagickSignature;
+ StartTimer(time_info,MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t U s e r T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetUserTime() returns the User time (user and system) by the operating
+% system (in seconds) between the start and stop events. If the stopwatch is
+% still running, it is stopped first.
+%
+% The format of the GetUserTime method is:
+%
+% double GetUserTime(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+*/
+MagickExport double GetUserTime(TimerInfo *time_info)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ if (time_info->state == UndefinedTimerState)
+ return(0.0);
+ if (time_info->state == RunningTimerState)
+ StopTimer(time_info);
+ return(time_info->user.total);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R e s e t T i m e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ResetTimer() resets the stopwatch.
+%
+% The format of the ResetTimer method is:
+%
+% void ResetTimer(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+*/
+MagickExport void ResetTimer(TimerInfo *time_info)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ StopTimer(time_info);
+ time_info->elapsed.stop=0.0;
+ time_info->user.stop=0.0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S t a r t T i m e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StartTimer() starts the stopwatch.
+%
+% The format of the StartTimer method is:
+%
+% void StartTimer(TimerInfo *time_info,const MagickBooleanType reset)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+% o reset: If reset is MagickTrue, then the stopwatch is reset prior to
+% starting. If reset is MagickFalse, then timing is continued without
+% resetting the stopwatch.
+%
+*/
+MagickExport void StartTimer(TimerInfo *time_info,const MagickBooleanType reset)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ if (reset != MagickFalse)
+ {
+ /*
+ Reset the stopwatch before starting it.
+ */
+ time_info->user.total=0.0;
+ time_info->elapsed.total=0.0;
+ }
+ if (time_info->state != RunningTimerState)
+ {
+ time_info->elapsed.start=ElapsedTime();
+ time_info->user.start=UserTime();
+ }
+ time_info->state=RunningTimerState;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ S t o p T i m e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% StopTimer() stops the stopwatch.
+%
+% The format of the StopTimer method is:
+%
+% void StopTimer(TimerInfo *time_info)
+%
+% A description of each parameter follows.
+%
+% o time_info: Timer statistics structure.
+%
+*/
+static void StopTimer(TimerInfo *time_info)
+{
+ assert(time_info != (TimerInfo *) NULL);
+ assert(time_info->signature == MagickSignature);
+ time_info->elapsed.stop=ElapsedTime();
+ time_info->user.stop=UserTime();
+ if (time_info->state == RunningTimerState)
+ {
+ time_info->user.total+=time_info->user.stop-
+ time_info->user.start+MagickEpsilon;
+ time_info->elapsed.total+=time_info->elapsed.stop-
+ time_info->elapsed.start+MagickEpsilon;
+ }
+ time_info->state=StoppedTimerState;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ U s e r T i m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% UserTime() returns the total time the process has been scheduled (in
+% seconds) since the last call to StartTimer().
+%
+% The format of the UserTime method is:
+%
+% double UserTime()
+%
+*/
+static double UserTime(void)
+{
+#if defined(MAGICKCORE_HAVE_TIMES)
+ struct tms
+ timer;
+
+ (void) times(&timer);
+ return((double) (timer.tms_utime+timer.tms_stime)/CLK_TCK);
+#else
+#if defined(__WINDOWS__)
+ return(NTUserTime());
+#else
+ return((double) clock()/CLK_TCK);
+#endif
+#endif
+}
diff --git a/magick/timer.h b/magick/timer.h
new file mode 100644
index 0000000..e108f24
--- /dev/null
+++ b/magick/timer.h
@@ -0,0 +1,73 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore timer methods.
+*/
+#ifndef _MAGICKCORE_TIMER_H
+#define _MAGICKCORE_TIMER_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedTimerState,
+ StoppedTimerState,
+ RunningTimerState
+} TimerState;
+
+typedef struct _Timer
+{
+ double
+ start,
+ stop,
+ total;
+} Timer;
+
+typedef struct _TimerInfo
+{
+ Timer
+ user,
+ elapsed;
+
+ TimerState
+ state;
+
+ unsigned long
+ signature;
+} TimerInfo;
+
+extern MagickExport double
+ GetElapsedTime(TimerInfo *),
+ GetUserTime(TimerInfo *);
+
+extern MagickExport MagickBooleanType
+ ContinueTimer(TimerInfo *);
+
+extern MagickExport TimerInfo
+ *AcquireTimerInfo(void),
+ *DestroyTimerInfo(TimerInfo *);
+
+extern MagickExport void
+ GetTimerInfo(TimerInfo *),
+ ResetTimer(TimerInfo *),
+ StartTimer(TimerInfo *,const MagickBooleanType);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/token-private.h b/magick/token-private.h
new file mode 100644
index 0000000..e6e623d
--- /dev/null
+++ b/magick/token-private.h
@@ -0,0 +1,151 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore private token methods.
+*/
+#ifndef _MAGICKCORE_TOKEN_PRIVATE_H
+#define _MAGICKCORE_TOKEN_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#ifndef EILSEQ
+ #define EILSEQ ENOENT
+#endif
+
+#define MaxMultibyteCodes 6
+
+typedef struct
+{
+ unsigned long
+ code_mask,
+ code_value,
+ utf_mask,
+ utf_value;
+} UTFInfo;
+
+static UTFInfo
+ utf_info[MaxMultibyteCodes] =
+ {
+ { 0x80, 0x00, 0x000007f, 0x0000000 }, /* 1 byte sequence */
+ { 0xE0, 0xC0, 0x00007ff, 0x0000080 }, /* 2 byte sequence */
+ { 0xF0, 0xE0, 0x000ffff, 0x0000800 }, /* 3 byte sequence */
+ { 0xF8, 0xF0, 0x01fffff, 0x0010000 }, /* 4 byte sequence */
+ { 0xFC, 0xF8, 0x03fffff, 0x0200000 }, /* 5 byte sequence */
+ { 0xFE, 0xFC, 0x7ffffff, 0x4000000 }, /* 6 byte sequence */
+ };
+
+static inline unsigned long GetNextUTFCode(const char *text,size_t *octets)
+{
+ register long
+ i;
+
+ register unsigned long
+ c,
+ unicode;
+
+ unsigned long
+ code;
+
+ *octets=0;
+ if (text == (const char *) NULL)
+ {
+ errno=EINVAL;
+ return(0);
+ }
+ code=(unsigned long) (*text++) & 0xff;
+ unicode=code;
+ for (i=0; i < MaxMultibyteCodes; i++)
+ {
+ if ((code & utf_info[i].code_mask) == utf_info[i].code_value)
+ {
+ unicode&=utf_info[i].utf_mask;
+ if (unicode < utf_info[i].utf_value)
+ {
+ errno=EILSEQ;
+ return(0);
+ }
+ *octets=(size_t) (i+1);
+ return(unicode);
+ }
+ c=(unsigned long) (*text++ ^ 0x80) & 0xff;
+ if ((c & 0xc0) != 0)
+ {
+ errno=EILSEQ;
+ return(0);
+ }
+ unicode=(unicode << 6) | c;
+ }
+ errno=EILSEQ;
+ return(0);
+}
+
+static unsigned long GetUTFCode(const char *text)
+{
+ size_t
+ octets;
+
+ return(GetNextUTFCode(text,&octets));
+}
+
+static size_t GetUTFOctets(const char *text)
+{
+ size_t
+ octets;
+
+ (void) GetNextUTFCode(text,&octets);
+ return(octets);
+}
+
+static inline MagickBooleanType IsUTFSpace(unsigned long code)
+{
+ if (((code >= 0x0009) && (code <= 0x000d)) || (code == 0x0020) ||
+ (code == 0x0085) || (code == 0x00a0) || (code == 0x1680) ||
+ (code == 0x180e) || ((code >= 0x2000) && (code <= 0x200a)) ||
+ (code == 0x2028) || (code == 0x2029) || (code == 0x202f) ||
+ (code == 0x205f) || (code == 0x3000))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+static inline MagickBooleanType IsUTFValid(unsigned long code)
+{
+ unsigned long
+ mask;
+
+ mask=(unsigned long) 0x7fffffff;
+ if (((code & ~mask) != 0) && ((code < 0xd800) || (code > 0xdfff)) &&
+ (code != 0xfffe) && (code != 0xffff))
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+static inline MagickBooleanType IsUTFAscii(unsigned long code)
+{
+ unsigned long
+ mask;
+
+ mask=(unsigned long) 0x7f;
+ if ((code & ~mask) != 0)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/token.c b/magick/token.c
new file mode 100644
index 0000000..26d918f
--- /dev/null
+++ b/magick/token.c
@@ -0,0 +1,962 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% TTTTT OOO K K EEEEE N N %
+% T O O K K E NN N %
+% T O O KKK EEE N N N %
+% T O O K K E N NN %
+% T OOO K K EEEEE N N %
+% %
+% %
+% MagickCore Token Methods %
+% %
+% Software Design %
+% John Cristy %
+% January 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/token-private.h"
+#include "magick/utility.h"
+
+/*
+ Typedef declaractions.
+*/
+struct _TokenInfo
+{
+ int
+ state;
+
+ MagickStatusType
+ flag;
+
+ long
+ offset;
+
+ char
+ quote;
+
+ unsigned long
+ signature;
+};
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e T o k e n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireTokenInfo() allocates the TokenInfo structure.
+%
+% The format of the AcquireTokenInfo method is:
+%
+% TokenInfo *AcquireTokenInfo()
+%
+*/
+MagickExport TokenInfo *AcquireTokenInfo(void)
+{
+ TokenInfo
+ *token_info;
+
+ token_info=(TokenInfo *) AcquireMagickMemory(sizeof(*token_info));
+ if (token_info == (TokenInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ token_info->signature=MagickSignature;
+ return(token_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y T o k e n I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyTokenInfo() deallocates memory associated with an TokenInfo
+% structure.
+%
+% The format of the DestroyTokenInfo method is:
+%
+% TokenInfo *DestroyTokenInfo(TokenInfo *token_info)
+%
+% A description of each parameter follows:
+%
+% o token_info: Specifies a pointer to an TokenInfo structure.
+%
+*/
+MagickExport TokenInfo *DestroyTokenInfo(TokenInfo *token_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(token_info != (TokenInfo *) NULL);
+ assert(token_info->signature == MagickSignature);
+ token_info->signature=(~MagickSignature);
+ token_info=(TokenInfo *) RelinquishMagickMemory(token_info);
+ return(token_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t M a g i c k T o k e n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickToken() gets a token from the token stream. A token is defined as a
+% sequence of characters delimited by whitespace (e.g. clip-path), a sequence
+% delimited with quotes (.e.g "Quote me"), or a sequence enclosed in
+% parenthesis (e.g. rgb(0,0,0)).
+%
+% The format of the GetMagickToken method is:
+%
+% void GetMagickToken(const char *start,const char **end,char *token)
+%
+% A description of each parameter follows:
+%
+% o start: the start of the token sequence.
+%
+% o end: point to the end of the token sequence.
+%
+% o token: copy the token to this buffer.
+%
+*/
+MagickExport void GetMagickToken(const char *start,const char **end,char *token)
+{
+ double
+ value;
+
+ register const char
+ *p;
+
+ register long
+ i;
+
+ i=0;
+ for (p=start; *p != '\0'; )
+ {
+ while ((isspace((int) ((unsigned char) *p)) != 0) && (*p != '\0'))
+ p++;
+ if (*p == '\0')
+ break;
+ switch (*p)
+ {
+ case '"':
+ case '\'':
+ case '`':
+ case '{':
+ {
+ register char
+ escape;
+
+ switch (*p)
+ {
+ case '"': escape='"'; break;
+ case '\'': escape='\''; break;
+ case '`': escape='\''; break;
+ case '{': escape='}'; break;
+ default: escape=(*p); break;
+ }
+ for (p++; *p != '\0'; p++)
+ {
+ if ((*p == '\\') && ((*(p+1) == escape) || (*(p+1) == '\\')))
+ p++;
+ else
+ if (*p == escape)
+ {
+ p++;
+ break;
+ }
+ token[i++]=(*p);
+ }
+ break;
+ }
+ case '/':
+ {
+ token[i++]=(*p++);
+ if ((*p == '>') || (*p == '/'))
+ token[i++]=(*p++);
+ break;
+ }
+ default:
+ {
+ char
+ *q;
+
+ value=strtod(p,&q);
+ if ((p != q) && (*p != ','))
+ {
+ for ( ; (p < q) && (*p != ','); p++)
+ token[i++]=(*p);
+ if (*p == '%')
+ token[i++]=(*p++);
+ break;
+ }
+ if ((isalpha((int) ((unsigned char) *p)) == 0) &&
+ (*p != *DirectorySeparator) && (*p != '#') && (*p != '<'))
+ {
+ token[i++]=(*p++);
+ break;
+ }
+ for ( ; *p != '\0'; p++)
+ {
+ if (((isspace((int) ((unsigned char) *p)) != 0) || (*p == '=') ||
+ (*p == ',') || (*p == ':')) && (*(p-1) != '\\'))
+ break;
+ if ((i > 0) && (*p == '<'))
+ break;
+ token[i++]=(*p);
+ if (*p == '>')
+ break;
+ if (*p == '(')
+ for (p++; *p != '\0'; p++)
+ {
+ token[i++]=(*p);
+ if ((*p == ')') && (*(p-1) != '\\'))
+ break;
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ token[i]='\0';
+ if (LocaleNCompare(token,"url(",4) == 0)
+ {
+ ssize_t
+ offset;
+
+ offset=4;
+ if (token[offset] == '#')
+ offset++;
+ i=(long) strlen(token);
+ (void) CopyMagickString(token,token+offset,MaxTextExtent);
+ token[i-offset-1]='\0';
+ }
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ if (end != (const char **) NULL)
+ *end=(const char *) p;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G l o b E x p r e s s i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GlobExpression() returns MagickTrue if the expression matches the pattern.
+%
+% The format of the GlobExpression function is:
+%
+% MagickBooleanType GlobExpression(const char *expression,
+% const char *pattern,const MagickBooleanType case_insensitive)
+%
+% A description of each parameter follows:
+%
+% o expression: Specifies a pointer to a text string containing a file name.
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o case_insensitive: set to MagickTrue to ignore the case when matching
+% an expression.
+%
+*/
+MagickExport MagickBooleanType GlobExpression(const char *expression,
+ const char *pattern,const MagickBooleanType case_insensitive)
+{
+ MagickBooleanType
+ done,
+ match;
+
+ register const char
+ *p;
+
+ /*
+ Return on empty pattern or '*'.
+ */
+ if (pattern == (char *) NULL)
+ return(MagickTrue);
+ if (GetUTFCode(pattern) == 0)
+ return(MagickTrue);
+ if (LocaleCompare(pattern,"*") == 0)
+ return(MagickTrue);
+ p=pattern+strlen(pattern)-1;
+ if ((GetUTFCode(p) == ']') && (strchr(pattern,'[') != (char *) NULL))
+ {
+ ExceptionInfo
+ *exception;
+
+ ImageInfo
+ *image_info;
+
+ /*
+ Determine if pattern is a scene, i.e. img0001.pcd[2].
+ */
+ image_info=AcquireImageInfo();
+ (void) CopyMagickString(image_info->filename,pattern,MaxTextExtent);
+ exception=AcquireExceptionInfo();
+ (void) SetImageInfo(image_info,MagickTrue,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (LocaleCompare(image_info->filename,pattern) != 0)
+ {
+ image_info=DestroyImageInfo(image_info);
+ return(MagickFalse);
+ }
+ image_info=DestroyImageInfo(image_info);
+ }
+ /*
+ Evaluate glob expression.
+ */
+ done=MagickFalse;
+ while ((GetUTFCode(pattern) != 0) && (done == MagickFalse))
+ {
+ if (GetUTFCode(expression) == 0)
+ if ((GetUTFCode(pattern) != '{') && (GetUTFCode(pattern) != '*'))
+ break;
+ switch (GetUTFCode(pattern))
+ {
+ case '\\':
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) != 0)
+ pattern+=GetUTFOctets(pattern);
+ break;
+ }
+ case '*':
+ {
+ MagickBooleanType
+ status;
+
+ status=MagickFalse;
+ pattern+=GetUTFOctets(pattern);
+ while ((GetUTFCode(expression) != 0) && (status == MagickFalse))
+ {
+ status=GlobExpression(expression,pattern,case_insensitive);
+ expression+=GetUTFOctets(expression);
+ }
+ if (status != MagickFalse)
+ {
+ while (GetUTFCode(expression) != 0)
+ expression+=GetUTFOctets(expression);
+ while (GetUTFCode(pattern) != 0)
+ pattern+=GetUTFOctets(pattern);
+ }
+ break;
+ }
+ case '[':
+ {
+ unsigned long
+ c;
+
+ pattern+=GetUTFOctets(pattern);
+ for ( ; ; )
+ {
+ if ((GetUTFCode(pattern) == 0) || (GetUTFCode(pattern) == ']'))
+ {
+ done=MagickTrue;
+ break;
+ }
+ if (GetUTFCode(pattern) == '\\')
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == 0)
+ {
+ done=MagickTrue;
+ break;
+ }
+ }
+ if (GetUTFCode(pattern+GetUTFOctets(pattern)) == '-')
+ {
+ c=GetUTFCode(pattern);
+ pattern+=GetUTFOctets(pattern);
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == ']')
+ {
+ done=MagickTrue;
+ break;
+ }
+ if (GetUTFCode(pattern) == '\\')
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == 0)
+ {
+ done=MagickTrue;
+ break;
+ }
+ }
+ if ((GetUTFCode(expression) < c) ||
+ (GetUTFCode(expression) > GetUTFCode(pattern)))
+ {
+ pattern+=GetUTFOctets(pattern);
+ continue;
+ }
+ }
+ else
+ if (GetUTFCode(pattern) != GetUTFCode(expression))
+ {
+ pattern+=GetUTFOctets(pattern);
+ continue;
+ }
+ pattern+=GetUTFOctets(pattern);
+ while ((GetUTFCode(pattern) != ']') && (GetUTFCode(pattern) != 0))
+ {
+ if ((GetUTFCode(pattern) == '\\') &&
+ (GetUTFCode(pattern+GetUTFOctets(pattern)) > 0))
+ pattern+=GetUTFOctets(pattern);
+ pattern+=GetUTFOctets(pattern);
+ }
+ if (GetUTFCode(pattern) != 0)
+ {
+ pattern+=GetUTFOctets(pattern);
+ expression+=GetUTFOctets(expression);
+ }
+ break;
+ }
+ break;
+ }
+ case '?':
+ {
+ pattern+=GetUTFOctets(pattern);
+ expression+=GetUTFOctets(expression);
+ break;
+ }
+ case '{':
+ {
+ register const char
+ *p;
+
+ pattern+=GetUTFOctets(pattern);
+ while ((GetUTFCode(pattern) != '}') && (GetUTFCode(pattern) != 0))
+ {
+ p=expression;
+ match=MagickTrue;
+ while ((GetUTFCode(p) != 0) && (GetUTFCode(pattern) != 0) &&
+ (GetUTFCode(pattern) != ',') && (GetUTFCode(pattern) != '}') &&
+ (match != MagickFalse))
+ {
+ if (GetUTFCode(pattern) == '\\')
+ pattern+=GetUTFOctets(pattern);
+ match=(GetUTFCode(pattern) == GetUTFCode(p)) ? MagickTrue :
+ MagickFalse;
+ p+=GetUTFOctets(p);
+ pattern+=GetUTFOctets(pattern);
+ }
+ if (GetUTFCode(pattern) == 0)
+ {
+ match=MagickFalse;
+ done=MagickTrue;
+ break;
+ }
+ else
+ if (match != MagickFalse)
+ {
+ expression=p;
+ while ((GetUTFCode(pattern) != '}') &&
+ (GetUTFCode(pattern) != 0))
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == '\\')
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == '}')
+ pattern+=GetUTFOctets(pattern);
+ }
+ }
+ }
+ else
+ {
+ while ((GetUTFCode(pattern) != '}') &&
+ (GetUTFCode(pattern) != ',') &&
+ (GetUTFCode(pattern) != 0))
+ {
+ pattern+=GetUTFOctets(pattern);
+ if (GetUTFCode(pattern) == '\\')
+ {
+ pattern+=GetUTFOctets(pattern);
+ if ((GetUTFCode(pattern) == '}') ||
+ (GetUTFCode(pattern) == ','))
+ pattern+=GetUTFOctets(pattern);
+ }
+ }
+ }
+ if (GetUTFCode(pattern) != 0)
+ pattern+=GetUTFOctets(pattern);
+ }
+ break;
+ }
+ default:
+ {
+ if (case_insensitive != MagickFalse)
+ {
+ if (tolower((int) GetUTFCode(expression)) !=
+ tolower((int) GetUTFCode(pattern)))
+ {
+ done=MagickTrue;
+ break;
+ }
+ }
+ else
+ if (GetUTFCode(expression) != GetUTFCode(pattern))
+ {
+ done=MagickTrue;
+ break;
+ }
+ expression+=GetUTFOctets(expression);
+ pattern+=GetUTFOctets(pattern);
+ }
+ }
+ }
+ while (GetUTFCode(pattern) == '*')
+ pattern+=GetUTFOctets(pattern);
+ match=(GetUTFCode(expression) == 0) && (GetUTFCode(pattern) == 0) ?
+ MagickTrue : MagickFalse;
+ return(match);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s G l o b %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsGlob() returns MagickTrue if the path specification contains a globbing
+% pattern.
+%
+% The format of the IsGlob method is:
+%
+% MagickBooleanType IsGlob(const char *geometry)
+%
+% A description of each parameter follows:
+%
+% o path: the path.
+%
+*/
+MagickExport MagickBooleanType IsGlob(const char *path)
+{
+ MagickBooleanType
+ status;
+
+ if (IsPathAccessible(path) != MagickFalse)
+ return(MagickFalse);
+ status=(strchr(path,'*') != (char *) NULL) ||
+ (strchr(path,'?') != (char *) NULL) ||
+ (strchr(path,'{') != (char *) NULL) ||
+ (strchr(path,'}') != (char *) NULL) ||
+ (strchr(path,'[') != (char *) NULL) ||
+ (strchr(path,']') != (char *) NULL) ? MagickTrue : MagickFalse;
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T o k e n i z e r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Tokenizer() is a generalized, finite state token parser. It extracts tokens
+% one at a time from a string of characters. The characters used for white
+% space, for break characters, and for quotes can be specified. Also,
+% characters in the string can be preceded by a specifiable escape character
+% which removes any special meaning the character may have.
+%
+% Here is some terminology:
+%
+% o token: A single unit of information in the form of a group of
+% characters.
+%
+% o white space: Apace that gets ignored (except within quotes or when
+% escaped), like blanks and tabs. in addition, white space terminates a
+% non-quoted token.
+%
+% o break set: One or more characters that separates non-quoted tokens.
+% Commas are a common break character. The usage of break characters to
+% signal the end of a token is the same as that of white space, except
+% multiple break characters with nothing or only white space between
+% generate a null token for each two break characters together.
+%
+% For example, if blank is set to be the white space and comma is set to
+% be the break character, the line
+%
+% A, B, C , , DEF
+%
+% ... consists of 5 tokens:
+%
+% 1) "A"
+% 2) "B"
+% 3) "C"
+% 4) "" (the null string)
+% 5) "DEF"
+%
+% o Quote character: A character that, when surrounding a group of other
+% characters, causes the group of characters to be treated as a single
+% token, no matter how many white spaces or break characters exist in
+% the group. Also, a token always terminates after the closing quote.
+% For example, if ' is the quote character, blank is white space, and
+% comma is the break character, the following string
+%
+% A, ' B, CD'EF GHI
+%
+% ... consists of 4 tokens:
+%
+% 1) "A"
+% 2) " B, CD" (note the blanks & comma)
+% 3) "EF"
+% 4) "GHI"
+%
+% The quote characters themselves do not appear in the resultant
+% tokens. The double quotes are delimiters i use here for
+% documentation purposes only.
+%
+% o Escape character: A character which itself is ignored but which
+% causes the next character to be used as is. ^ and \ are often used
+% as escape characters. An escape in the last position of the string
+% gets treated as a "normal" (i.e., non-quote, non-white, non-break,
+% and non-escape) character. For example, assume white space, break
+% character, and quote are the same as in the above examples, and
+% further, assume that ^ is the escape character. Then, in the string
+%
+% ABC, ' DEF ^' GH' I ^ J K^ L ^
+%
+% ... there are 7 tokens:
+%
+% 1) "ABC"
+% 2) " DEF ' GH"
+% 3) "I"
+% 4) " " (a lone blank)
+% 5) "J"
+% 6) "K L"
+% 7) "^" (passed as is at end of line)
+%
+% The format of the Tokenizer method is:
+%
+% int Tokenizer(TokenInfo *token_info,const unsigned flag,char *token,
+% const size_t max_token_length,const char *line,const char *white,
+% const char *break_set,const char *quote,const char escape,
+% char *breaker,int *next,char *quoted)
+%
+% A description of each parameter follows:
+%
+% o flag: right now, only the low order 3 bits are used.
+%
+% 1 => convert non-quoted tokens to upper case
+% 2 => convert non-quoted tokens to lower case
+% 0 => do not convert non-quoted tokens
+%
+% o token: a character string containing the returned next token
+%
+% o max_token_length: the maximum size of "token". Characters beyond
+% "max_token_length" are truncated.
+%
+% o string: the string to be parsed.
+%
+% o white: a string of the valid white spaces. example:
+%
+% char whitesp[]={" \t"};
+%
+% blank and tab will be valid white space.
+%
+% o break: a string of the valid break characters. example:
+%
+% char breakch[]={";,"};
+%
+% semicolon and comma will be valid break characters.
+%
+% o quote: a string of the valid quote characters. An example would be
+%
+% char whitesp[]={"'\"");
+%
+% (this causes single and double quotes to be valid) Note that a
+% token starting with one of these characters needs the same quote
+% character to terminate it.
+%
+% for example:
+%
+% "ABC '
+%
+% is unterminated, but
+%
+% "DEF" and 'GHI'
+%
+% are properly terminated. Note that different quote characters
+% can appear on the same line; only for a given token do the quote
+% characters have to be the same.
+%
+% o escape: the escape character (NOT a string ... only one
+% allowed). Use zero if none is desired.
+%
+% o breaker: the break character used to terminate the current
+% token. If the token was quoted, this will be the quote used. If
+% the token is the last one on the line, this will be zero.
+%
+% o next: this variable points to the first character of the
+% next token. it gets reset by "tokenizer" as it steps through the
+% string. Set it to 0 upon initialization, and leave it alone
+% after that. You can change it if you want to jump around in the
+% string or re-parse from the beginning, but be careful.
+%
+% o quoted: set to True if the token was quoted and MagickFalse
+% if not. You may need this information (for example: in C, a
+% string with quotes around it is a character string, while one
+% without is an identifier).
+%
+% o result: 0 if we haven't reached EOS (end of string), and 1
+% if we have.
+%
+*/
+
+#define IN_WHITE 0
+#define IN_TOKEN 1
+#define IN_QUOTE 2
+#define IN_OZONE 3
+
+static long sindex(int c,const char *string)
+{
+ register const char
+ *p;
+
+ for (p=string; *p != '\0'; p++)
+ if (c == (int) (*p))
+ return(p-string);
+ return(-1);
+}
+
+static void StoreToken(TokenInfo *token_info,char *string,
+ size_t max_token_length,int c)
+{
+ register long
+ i;
+
+ if ((token_info->offset < 0) ||
+ ((size_t) token_info->offset >= (max_token_length-1)))
+ return;
+ i=token_info->offset++;
+ string[i]=(char) c;
+ if (token_info->state == IN_QUOTE)
+ return;
+ switch (token_info->flag & 0x03)
+ {
+ case 1:
+ {
+ string[i]=(char) toupper(c);
+ break;
+ }
+ case 2:
+ {
+ string[i]=(char) tolower(c);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+MagickExport int Tokenizer(TokenInfo *token_info,const unsigned flag,
+ char *token,const size_t max_token_length,const char *line,const char *white,
+ const char *break_set,const char *quote,const char escape,char *breaker,
+ int *next,char *quoted)
+{
+ int
+ c;
+
+ register long
+ i;
+
+ *breaker='\0';
+ *quoted='\0';
+ if (line[*next] == '\0')
+ return(1);
+ token_info->state=IN_WHITE;
+ token_info->quote=(char) MagickFalse;
+ token_info->flag=flag;
+ for (token_info->offset=0; (int) line[*next] != 0; (*next)++)
+ {
+ c=(int) line[*next];
+ i=sindex(c,break_set);
+ if (i >= 0)
+ {
+ switch (token_info->state)
+ {
+ case IN_WHITE:
+ case IN_TOKEN:
+ case IN_OZONE:
+ {
+ (*next)++;
+ *breaker=break_set[i];
+ token[token_info->offset]='\0';
+ return(0);
+ }
+ case IN_QUOTE:
+ {
+ StoreToken(token_info,token,max_token_length,c);
+ break;
+ }
+ }
+ continue;
+ }
+ i=sindex(c,quote);
+ if (i >= 0)
+ {
+ switch (token_info->state)
+ {
+ case IN_WHITE:
+ {
+ token_info->state=IN_QUOTE;
+ token_info->quote=quote[i];
+ *quoted=(char) MagickTrue;
+ break;
+ }
+ case IN_QUOTE:
+ {
+ if (quote[i] != token_info->quote)
+ StoreToken(token_info,token,max_token_length,c);
+ else
+ {
+ token_info->state=IN_OZONE;
+ token_info->quote='\0';
+ }
+ break;
+ }
+ case IN_TOKEN:
+ case IN_OZONE:
+ {
+ *breaker=(char) c;
+ token[token_info->offset]='\0';
+ return(0);
+ }
+ }
+ continue;
+ }
+ i=sindex(c,white);
+ if (i >= 0)
+ {
+ switch (token_info->state)
+ {
+ case IN_WHITE:
+ case IN_OZONE:
+ break;
+ case IN_TOKEN:
+ {
+ token_info->state=IN_OZONE;
+ break;
+ }
+ case IN_QUOTE:
+ {
+ StoreToken(token_info,token,max_token_length,c);
+ break;
+ }
+ }
+ continue;
+ }
+ if (c == (int) escape)
+ {
+ if (line[(*next)+1] == '\0')
+ {
+ *breaker='\0';
+ StoreToken(token_info,token,max_token_length,c);
+ (*next)++;
+ token[token_info->offset]='\0';
+ return(0);
+ }
+ switch (token_info->state)
+ {
+ case IN_WHITE:
+ {
+ (*next)--;
+ token_info->state=IN_TOKEN;
+ break;
+ }
+ case IN_TOKEN:
+ case IN_QUOTE:
+ {
+ (*next)++;
+ c=(int) line[*next];
+ StoreToken(token_info,token,max_token_length,c);
+ break;
+ }
+ case IN_OZONE:
+ {
+ token[token_info->offset]='\0';
+ return(0);
+ }
+ }
+ continue;
+ }
+ switch (token_info->state)
+ {
+ case IN_WHITE:
+ token_info->state=IN_TOKEN;
+ case IN_TOKEN:
+ case IN_QUOTE:
+ {
+ StoreToken(token_info,token,max_token_length,c);
+ break;
+ }
+ case IN_OZONE:
+ {
+ token[token_info->offset]='\0';
+ return(0);
+ }
+ }
+ }
+ token[token_info->offset]='\0';
+ return(0);
+}
diff --git a/magick/token.h b/magick/token.h
new file mode 100644
index 0000000..69993e6
--- /dev/null
+++ b/magick/token.h
@@ -0,0 +1,50 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore token methods.
+*/
+#ifndef _MAGICKCORE_TOKEN_H
+#define _MAGICKCORE_TOKEN_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ Typedef declarations.
+*/
+typedef struct _TokenInfo
+ TokenInfo;
+
+extern MagickExport int
+ Tokenizer(TokenInfo *,const unsigned int,char *,const size_t,const char *,
+ const char *,const char *,const char *,const char,char *,int *,char *);
+
+extern MagickExport MagickBooleanType
+ GlobExpression(const char *,const char *,const MagickBooleanType),
+ IsGlob(const char *);
+
+extern MagickExport TokenInfo
+ *AcquireTokenInfo(void),
+ *DestroyTokenInfo(TokenInfo *);
+
+extern MagickExport void
+ GetMagickToken(const char *,const char **,char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/transform.c b/magick/transform.c
new file mode 100644
index 0000000..d956cc0
--- /dev/null
+++ b/magick/transform.c
@@ -0,0 +1,2132 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% TTTTT RRRR AAA N N SSSSS FFFFF OOO RRRR M M %
+% T R R A A NN N SS F O O R R MM MM %
+% T RRRR AAAAA N N N SSS FFF O O RRRR M M M %
+% T R R A A N NN SS F O O R R M M %
+% T R R A A N N SSSSS F OOO R R M M %
+% %
+% %
+% MagickCore Image Transform Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/cache.h"
+#include "magick/cache-view.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/colorspace-private.h"
+#include "magick/composite.h"
+#include "magick/effect.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/image.h"
+#include "magick/memory_.h"
+#include "magick/layer.h"
+#include "magick/list.h"
+#include "magick/monitor.h"
+#include "magick/monitor-private.h"
+#include "magick/pixel-private.h"
+#include "magick/resource_.h"
+#include "magick/resize.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C h o p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Chop() removes a region of an image and collapses the image to occupy the
+% removed portion.
+%
+% The format of the ChopImage method is:
+%
+% Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o chop_info: Define the region of the image to chop.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
+ ExceptionInfo *exception)
+{
+#define ChopImageTag "Chop/Image"
+
+ Image
+ *chop_image;
+
+ long
+ j,
+ y;
+
+ MagickBooleanType
+ proceed;
+
+ RectangleInfo
+ extent;
+
+ register long
+ i;
+
+ CacheView
+ *chop_view,
+ *image_view;
+
+ /*
+ Check chop geometry.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ assert(chop_info != (RectangleInfo *) NULL);
+ if (((chop_info->x+(long) chop_info->width) < 0) ||
+ ((chop_info->y+(long) chop_info->height) < 0) ||
+ (chop_info->x > (long) image->columns) ||
+ (chop_info->y > (long) image->rows))
+ ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
+ extent=(*chop_info);
+ if ((extent.x+(long) extent.width) > (long) image->columns)
+ extent.width=(unsigned long) ((long) image->columns-extent.x);
+ if ((extent.y+(long) extent.height) > (long) image->rows)
+ extent.height=(unsigned long) ((long) image->rows-extent.y);
+ if (extent.x < 0)
+ {
+ extent.width-=(unsigned long) (-extent.x);
+ extent.x=0;
+ }
+ if (extent.y < 0)
+ {
+ extent.height-=(unsigned long) (-extent.y);
+ extent.y=0;
+ }
+ chop_image=CloneImage(image,image->columns-extent.width,image->rows-
+ extent.height,MagickTrue,exception);
+ if (chop_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Extract chop image.
+ */
+ i=0;
+ j=0;
+ image_view=AcquireCacheView(image);
+ chop_view=AcquireCacheView(chop_image);
+ for (y=0; y < (long) extent.y; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict chop_indexes,
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
+ {
+ *q=(*p);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ if (chop_indexes != (IndexPacket *) NULL)
+ *chop_indexes++=indexes[x];
+ }
+ q++;
+ }
+ p++;
+ }
+ if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ /*
+ Extract chop image.
+ */
+ i+=extent.height;
+ for (y=0; y < (long) (image->rows-(extent.y+extent.height)); y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict chop_indexes,
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetCacheViewVirtualPixels(image_view,0,i++,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(chop_view,0,j++,chop_image->columns,1,
+ exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ chop_indexes=GetCacheViewAuthenticIndexQueue(chop_view);
+ for (x=0; x < (long) image->columns; x++)
+ {
+ if ((x < extent.x) || (x >= (long) (extent.x+extent.width)))
+ {
+ *q=(*p);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ if (chop_indexes != (IndexPacket *) NULL)
+ *chop_indexes++=indexes[x];
+ }
+ q++;
+ }
+ p++;
+ }
+ if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
+ break;
+ proceed=SetImageProgress(image,ChopImageTag,y,chop_image->rows);
+ if (proceed == MagickFalse)
+ break;
+ }
+ chop_view=DestroyCacheView(chop_view);
+ image_view=DestroyCacheView(image_view);
+ chop_image->type=image->type;
+ return(chop_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ C o n s o l i d a t e C M Y K I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
+% single image.
+%
+% The format of the ConsolidateCMYKImage method is:
+%
+% Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image sequence.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ConsolidateCMYKImages(const Image *images,
+ ExceptionInfo *exception)
+{
+ Image
+ *cmyk_image,
+ *cmyk_images;
+
+ long
+ y;
+
+ register long
+ i;
+
+ /*
+ Consolidate separate C, M, Y, and K planes into a single image.
+ */
+ assert(images != (Image *) NULL);
+ assert(images->signature == MagickSignature);
+ if (images->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ cmyk_images=NewImageList();
+ for (i=0; i < (long) GetImageListLength(images); i+=4)
+ {
+ cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
+ exception);
+ if (cmyk_image == (Image *) NULL)
+ break;
+ if (SetImageStorageClass(cmyk_image,DirectClass) == MagickFalse)
+ break;
+ (void) SetImageColorspace(cmyk_image,CMYKColorspace);
+ for (y=0; y < (long) images->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetVirtualPixels(images,0,y,images->columns,1,exception);
+ q=QueueAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) images->columns; x++)
+ {
+ q->red=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
+ break;
+ }
+ images=GetNextImageInList(images);
+ if (images == (Image *) NULL)
+ break;
+ for (y=0; y < (long) images->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetVirtualPixels(images,0,y,images->columns,1,exception);
+ q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) images->columns; x++)
+ {
+ q->green=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
+ break;
+ }
+ images=GetNextImageInList(images);
+ if (images == (Image *) NULL)
+ break;
+ for (y=0; y < (long) images->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetVirtualPixels(images,0,y,images->columns,1,exception);
+ q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ for (x=0; x < (long) images->columns; x++)
+ {
+ q->blue=(Quantum) (QuantumRange-PixelIntensityToQuantum(p));
+ p++;
+ q++;
+ }
+ if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
+ break;
+ }
+ images=GetNextImageInList(images);
+ if (images == (Image *) NULL)
+ break;
+ for (y=0; y < (long) images->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ p=GetVirtualPixels(images,0,y,images->columns,1,exception);
+ q=GetAuthenticPixels(cmyk_image,0,y,cmyk_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ break;
+ indexes=GetAuthenticIndexQueue(cmyk_image);
+ for (x=0; x < (long) images->columns; x++)
+ {
+ indexes[x]=(IndexPacket) (QuantumRange-PixelIntensityToQuantum(p));
+ p++;
+ }
+ if (SyncAuthenticPixels(cmyk_image,exception) == MagickFalse)
+ break;
+ }
+ AppendImageToList(&cmyk_images,cmyk_image);
+ images=GetNextImageInList(images);
+ if (images == (Image *) NULL)
+ break;
+ }
+ return(cmyk_images);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C r o p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CropImage() extracts a region of the image starting at the offset defined
+% by geometry.
+%
+% The format of the CropImage method is:
+%
+% Image *CropImage(const Image *image,const RectangleInfo *geometry,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: Define the region of the image to crop with members
+% x, y, width, and height.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
+ ExceptionInfo *exception)
+{
+#define CropImageTag "Crop/Image"
+
+ Image
+ *crop_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ bounding_box,
+ page;
+
+ CacheView
+ *crop_view,
+ *image_view;
+
+ /*
+ Check crop geometry.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ bounding_box=image->page;
+ if ((bounding_box.width == 0) || (bounding_box.height == 0))
+ {
+ bounding_box.width=image->columns;
+ bounding_box.height=image->rows;
+ }
+ page=(*geometry);
+ if (page.width == 0)
+ page.width=bounding_box.width;
+ if (page.height == 0)
+ page.height=bounding_box.height;
+ if (((bounding_box.x-page.x) >= (long) page.width) ||
+ ((bounding_box.y-page.y) >= (long) page.height) ||
+ ((page.x-bounding_box.x) > (long) image->columns) ||
+ ((page.y-bounding_box.y) > (long) image->rows))
+ {
+ /*
+ Crop is not within virtual canvas, return 1 pixel transparent image.
+ */
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "GeometryDoesNotContainImage","`%s'",image->filename);
+ crop_image=CloneImage(image,1,1,MagickTrue,exception);
+ if (crop_image == (Image *) NULL)
+ return((Image *) NULL);
+ crop_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(crop_image);
+ crop_image->page=bounding_box;
+ crop_image->page.x=(-1);
+ crop_image->page.y=(-1);
+ if (crop_image->dispose == BackgroundDispose)
+ crop_image->dispose=NoneDispose;
+ return(crop_image);
+ }
+ if ((page.x < 0) && (bounding_box.x >= 0))
+ {
+ page.width+=page.x-bounding_box.x;
+ page.x=0;
+ }
+ else
+ {
+ page.width-=bounding_box.x-page.x;
+ page.x-=bounding_box.x;
+ if (page.x < 0)
+ page.x=0;
+ }
+ if ((page.y < 0) && (bounding_box.y >= 0))
+ {
+ page.height+=page.y-bounding_box.y;
+ page.y=0;
+ }
+ else
+ {
+ page.height-=bounding_box.y-page.y;
+ page.y-=bounding_box.y;
+ if (page.y < 0)
+ page.y=0;
+ }
+ if ((unsigned long) (page.x+page.width) > image->columns)
+ page.width=image->columns-page.x;
+ if (geometry->width != 0)
+ if (page.width > geometry->width)
+ page.width=geometry->width;
+ if ((unsigned long) (page.y+page.height) > image->rows)
+ page.height=image->rows-page.y;
+ if (geometry->height != 0)
+ if (page.height > geometry->height)
+ page.height=geometry->height;
+ bounding_box.x+=page.x;
+ bounding_box.y+=page.y;
+ if ((page.width == 0) || (page.height == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "GeometryDoesNotContainImage","`%s'",image->filename);
+ return((Image *) NULL);
+ }
+ /*
+ Initialize crop image attributes.
+ */
+ crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
+ if (crop_image == (Image *) NULL)
+ return((Image *) NULL);
+ crop_image->page.width=image->page.width;
+ crop_image->page.height=image->page.height;
+ if (((long) (bounding_box.x+bounding_box.width) > (long) image->page.width) ||
+ ((long) (bounding_box.y+bounding_box.height) > (long) image->page.height))
+ {
+ crop_image->page.width=bounding_box.width;
+ crop_image->page.height=bounding_box.height;
+ }
+ crop_image->page.x=bounding_box.x;
+ crop_image->page.y=bounding_box.y;
+ /*
+ Crop image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ crop_view=AcquireCacheView(crop_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) crop_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict crop_indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
+ 1,exception);
+ q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ (void) CopyMagickMemory(q,p,(size_t) crop_image->columns*sizeof(*q));
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ crop_indexes=GetCacheViewAuthenticIndexQueue(crop_view);
+ if (crop_indexes != (IndexPacket *) NULL)
+ (void) CopyMagickMemory(crop_indexes,indexes,(size_t)
+ crop_image->columns*sizeof(*crop_indexes));
+ }
+ if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_CropImage)
+#endif
+ proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ crop_view=DestroyCacheView(crop_view);
+ image_view=DestroyCacheView(image_view);
+ crop_image->type=image->type;
+ if (status == MagickFalse)
+ crop_image=DestroyImage(crop_image);
+ return(crop_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x c e r p t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExcerptImage() returns a excerpt of the image as defined by the geometry.
+%
+% The format of the ExcerptImage method is:
+%
+% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: Define the region of the image to extend with members
+% x, y, width, and height.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ExcerptImage(const Image *image,
+ const RectangleInfo *geometry,ExceptionInfo *exception)
+{
+#define ExcerptImageTag "Excerpt/Image"
+
+ Image
+ *excerpt_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *excerpt_view,
+ *image_view;
+
+ /*
+ Allocate excerpt image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
+ exception);
+ if (excerpt_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Excerpt each row.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ excerpt_view=AcquireCacheView(excerpt_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) excerpt_image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict excerpt_indexes,
+ *__restrict indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
+ geometry->width,1,exception);
+ q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ (void) CopyMagickMemory(q,p,(size_t) excerpt_image->columns*sizeof(*q));
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ excerpt_indexes=GetCacheViewAuthenticIndexQueue(excerpt_view);
+ if (excerpt_indexes != (IndexPacket *) NULL)
+ (void) CopyMagickMemory(excerpt_indexes,indexes,(size_t)
+ excerpt_image->columns*sizeof(*excerpt_indexes));
+ }
+ if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_ExcerptImage)
+#endif
+ proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ excerpt_view=DestroyCacheView(excerpt_view);
+ image_view=DestroyCacheView(image_view);
+ excerpt_image->type=image->type;
+ if (status == MagickFalse)
+ excerpt_image=DestroyImage(excerpt_image);
+ return(excerpt_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x t e n t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExtentImage() extends the image as defined by the geometry, gravity, and
+% image background color. Set the (x,y) offset of the geometry to move the
+% original image relative to the extended image.
+%
+% The format of the ExtentImage method is:
+%
+% Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: Define the region of the image to extend with members
+% x, y, width, and height.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ExtentImage(const Image *image,
+ const RectangleInfo *geometry,ExceptionInfo *exception)
+{
+ Image
+ *extent_image;
+
+ /*
+ Allocate extent image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
+ exception);
+ if (extent_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(extent_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&extent_image->exception);
+ extent_image=DestroyImage(extent_image);
+ return((Image *) NULL);
+ }
+ if (extent_image->background_color.opacity != OpaqueOpacity)
+ extent_image->matte=MagickTrue;
+ (void) SetImageBackgroundColor(extent_image);
+ (void) CompositeImage(extent_image,image->compose,image,geometry->x,
+ geometry->y);
+ return(extent_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F l i p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FlipImage() creates a vertical mirror image by reflecting the pixels
+% around the central x-axis.
+%
+% The format of the FlipImage method is:
+%
+% Image *FlipImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
+{
+#define FlipImageTag "Flip/Image"
+
+ Image
+ *flip_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *flip_view,
+ *image_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (flip_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Flip image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ flip_view=AcquireCacheView(flip_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) flip_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict flip_indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(flip_view,0,(long) (flip_image->rows-y-1),
+ flip_image->columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ if (indexes != (const IndexPacket *) NULL)
+ {
+ flip_indexes=GetCacheViewAuthenticIndexQueue(flip_view);
+ if (flip_indexes != (IndexPacket *) NULL)
+ (void) CopyMagickMemory(flip_indexes,indexes,(size_t) image->columns*
+ sizeof(*flip_indexes));
+ }
+ if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_FlipImage)
+#endif
+ proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ flip_view=DestroyCacheView(flip_view);
+ image_view=DestroyCacheView(image_view);
+ flip_image->type=image->type;
+ if (status == MagickFalse)
+ flip_image=DestroyImage(flip_image);
+ return(flip_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% F l o p I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% FlopImage() creates a horizontal mirror image by reflecting the pixels
+% around the central y-axis.
+%
+% The format of the FlopImage method is:
+%
+% Image *FlopImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
+{
+#define FlopImageTag "Flop/Image"
+
+ Image
+ *flop_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *flop_view,
+ *image_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (flop_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Flop each row.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ flop_view=AcquireCacheView(flop_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) flop_image->rows; y++)
+ {
+ register const IndexPacket
+ *__restrict indexes;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict flop_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
+ exception);
+ if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ q+=flop_image->columns;
+ indexes=GetCacheViewVirtualIndexQueue(image_view);
+ flop_indexes=GetCacheViewAuthenticIndexQueue(flop_view);
+ for (x=0; x < (long) flop_image->columns; x++)
+ {
+ (*--q)=(*p++);
+ if ((indexes != (const IndexPacket *) NULL) &&
+ (flop_indexes != (IndexPacket *) NULL))
+ flop_indexes[flop_image->columns-x-1]=indexes[x];
+ }
+ if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_FlopImage)
+#endif
+ proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ flop_view=DestroyCacheView(flop_view);
+ image_view=DestroyCacheView(image_view);
+ flop_image->type=image->type;
+ if (status == MagickFalse)
+ flop_image=DestroyImage(flop_image);
+ return(flop_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% R o l l I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% RollImage() offsets an image as defined by x_offset and y_offset.
+%
+% The format of the RollImage method is:
+%
+% Image *RollImage(const Image *image,const long x_offset,
+% const long y_offset,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o x_offset: the number of columns to roll in the horizontal direction.
+%
+% o y_offset: the number of rows to roll in the vertical direction.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline MagickBooleanType CopyImageRegion(Image *destination,
+ const Image *source,const unsigned long columns,const unsigned long rows,
+ const long sx,const long sy,const long dx,const long dy,
+ ExceptionInfo *exception)
+{
+ long
+ y;
+
+ MagickBooleanType
+ status;
+
+ CacheView
+ *source_view,
+ *destination_view;
+
+ status=MagickTrue;
+ source_view=AcquireCacheView(source);
+ destination_view=AcquireCacheView(destination);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(status)
+#endif
+ for (y=0; y < (long) rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes,
+ *__restrict destination_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Transfer scanline.
+ */
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
+ q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(source_view);
+ for (x=0; x < (long) columns; x++)
+ *q++=(*p++);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ destination_indexes=GetCacheViewAuthenticIndexQueue(destination_view);
+ for (x=0; x < (long) columns; x++)
+ destination_indexes[x]=indexes[x];
+ }
+ sync=SyncCacheViewAuthenticPixels(destination_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ }
+ destination_view=DestroyCacheView(destination_view);
+ source_view=DestroyCacheView(source_view);
+ return(status);
+}
+
+MagickExport Image *RollImage(const Image *image,const long x_offset,
+ const long y_offset,ExceptionInfo *exception)
+{
+#define RollImageTag "Roll/Image"
+
+ Image
+ *roll_image;
+
+ MagickStatusType
+ status;
+
+ RectangleInfo
+ offset;
+
+ /*
+ Initialize roll image attributes.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
+ if (roll_image == (Image *) NULL)
+ return((Image *) NULL);
+ offset.x=x_offset;
+ offset.y=y_offset;
+ while (offset.x < 0)
+ offset.x+=image->columns;
+ while (offset.x >= (long) image->columns)
+ offset.x-=image->columns;
+ while (offset.y < 0)
+ offset.y+=image->rows;
+ while (offset.y >= (long) image->rows)
+ offset.y-=image->rows;
+ /*
+ Roll image.
+ */
+ status=CopyImageRegion(roll_image,image,(unsigned long) offset.x,
+ (unsigned long) offset.y,(long) image->columns-offset.x,(long) image->rows-
+ offset.y,0,0,exception);
+ (void) SetImageProgress(image,RollImageTag,0,3);
+ status|=CopyImageRegion(roll_image,image,image->columns-offset.x,
+ (unsigned long) offset.y,0,(long) image->rows-offset.y,offset.x,0,
+ exception);
+ (void) SetImageProgress(image,RollImageTag,1,3);
+ status|=CopyImageRegion(roll_image,image,(unsigned long) offset.x,image->rows-
+ offset.y,(long) image->columns-offset.x,0,0,offset.y,exception);
+ (void) SetImageProgress(image,RollImageTag,2,3);
+ status|=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
+ offset.y,0,0,offset.x,offset.y,exception);
+ (void) SetImageProgress(image,RollImageTag,3,3);
+ roll_image->type=image->type;
+ if (status == MagickFalse)
+ roll_image=DestroyImage(roll_image);
+ return(roll_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S h a v e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ShaveImage() shaves pixels from the image edges. It allocates the memory
+% necessary for the new Image structure and returns a pointer to the new
+% image.
+%
+% The format of the ShaveImage method is:
+%
+% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o shave_image: Method ShaveImage returns a pointer to the shaved
+% image. A null image is returned if there is a memory shortage or
+% if the image width or height is zero.
+%
+% o image: the image.
+%
+% o shave_info: Specifies a pointer to a RectangleInfo which defines the
+% region of the image to crop.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *ShaveImage(const Image *image,
+ const RectangleInfo *shave_info,ExceptionInfo *exception)
+{
+ Image
+ *shave_image;
+
+ RectangleInfo
+ geometry;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ if (((2*shave_info->width) >= image->columns) ||
+ ((2*shave_info->height) >= image->rows))
+ ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
+ SetGeometry(image,&geometry);
+ geometry.width-=2*shave_info->width;
+ geometry.height-=2*shave_info->height;
+ geometry.x=(long) shave_info->width+image->page.x;
+ geometry.y=(long) shave_info->height+image->page.y;
+ shave_image=CropImage(image,&geometry,exception);
+ if (shave_image == (Image *) NULL)
+ return((Image *) NULL);
+ shave_image->page.width-=2*shave_info->width;
+ shave_image->page.height-=2*shave_info->height;
+ shave_image->page.x-=shave_info->width;
+ shave_image->page.y-=shave_info->height;
+ return(shave_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S p l i c e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SpliceImage() splices a solid color into the image as defined by the
+% geometry.
+%
+% The format of the SpliceImage method is:
+%
+% Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o geometry: Define the region of the image to splice with members
+% x, y, width, and height.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *SpliceImage(const Image *image,
+ const RectangleInfo *geometry,ExceptionInfo *exception)
+{
+#define SpliceImageTag "Splice/Image"
+
+ Image
+ *splice_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ proceed,
+ status;
+
+ RectangleInfo
+ splice_geometry;
+
+ register long
+ i;
+
+ CacheView
+ *image_view,
+ *splice_view;
+
+ /*
+ Allocate splice image.
+ */
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(geometry != (const RectangleInfo *) NULL);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ splice_geometry=(*geometry);
+ splice_image=CloneImage(image,image->columns+splice_geometry.width,
+ image->rows+splice_geometry.height,MagickTrue,exception);
+ if (splice_image == (Image *) NULL)
+ return((Image *) NULL);
+ if (SetImageStorageClass(splice_image,DirectClass) == MagickFalse)
+ {
+ InheritException(exception,&splice_image->exception);
+ splice_image=DestroyImage(splice_image);
+ return((Image *) NULL);
+ }
+ (void) SetImageBackgroundColor(splice_image);
+ /*
+ Respect image geometry.
+ */
+ switch (image->gravity)
+ {
+ default:
+ case UndefinedGravity:
+ case NorthWestGravity:
+ break;
+ case NorthGravity:
+ {
+ splice_geometry.x+=splice_geometry.width/2;
+ break;
+ }
+ case NorthEastGravity:
+ {
+ splice_geometry.x+=splice_geometry.width;
+ break;
+ }
+ case WestGravity:
+ {
+ splice_geometry.y+=splice_geometry.width/2;
+ break;
+ }
+ case StaticGravity:
+ case CenterGravity:
+ {
+ splice_geometry.x+=splice_geometry.width/2;
+ splice_geometry.y+=splice_geometry.height/2;
+ break;
+ }
+ case EastGravity:
+ {
+ splice_geometry.x+=splice_geometry.width;
+ splice_geometry.y+=splice_geometry.height/2;
+ break;
+ }
+ case SouthWestGravity:
+ {
+ splice_geometry.y+=splice_geometry.height;
+ break;
+ }
+ case SouthGravity:
+ {
+ splice_geometry.x+=splice_geometry.width/2;
+ splice_geometry.y+=splice_geometry.height;
+ break;
+ }
+ case SouthEastGravity:
+ {
+ splice_geometry.x+=splice_geometry.width;
+ splice_geometry.y+=splice_geometry.height;
+ break;
+ }
+ }
+ /*
+ Splice image.
+ */
+ status=MagickTrue;
+ i=0;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ splice_view=AcquireCacheView(splice_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) splice_geometry.y; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes,
+ *__restrict splice_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
+ for (x=0; x < splice_geometry.x; x++)
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=OpaqueOpacity;
+ if (image->matte != MagickFalse)
+ q->opacity=p->opacity;
+ if (image->colorspace == CMYKColorspace)
+ splice_indexes[x]=(*indexes++);
+ p++;
+ q++;
+ }
+ for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
+ q++;
+ for ( ; x < (long) splice_image->columns; x++)
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=OpaqueOpacity;
+ if (image->matte != MagickFalse)
+ q->opacity=p->opacity;
+ if (image->colorspace == CMYKColorspace)
+ splice_indexes[x]=(*indexes++);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
+ status=MagickFalse;
+ proceed=SetImageProgress(image,SpliceImageTag,y,splice_image->rows);
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransposeImage)
+#endif
+ proceed=SetImageProgress(image,SpliceImageTag,progress++,
+ splice_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=(long) (splice_geometry.y+splice_geometry.height);
+ y < (long) splice_image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict indexes,
+ *__restrict splice_indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y-splice_geometry.height,
+ image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
+ exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ splice_indexes=GetCacheViewAuthenticIndexQueue(splice_view);
+ for (x=0; x < splice_geometry.x; x++)
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=OpaqueOpacity;
+ if (image->matte != MagickFalse)
+ q->opacity=p->opacity;
+ if (image->colorspace == CMYKColorspace)
+ splice_indexes[x]=(*indexes++);
+ p++;
+ q++;
+ }
+ for ( ; x < (long) (splice_geometry.x+splice_geometry.width); x++)
+ q++;
+ for ( ; x < (long) splice_image->columns; x++)
+ {
+ q->red=p->red;
+ q->green=p->green;
+ q->blue=p->blue;
+ q->opacity=OpaqueOpacity;
+ if (image->matte != MagickFalse)
+ q->opacity=p->opacity;
+ if (image->colorspace == CMYKColorspace)
+ splice_indexes[x]=(*indexes++);
+ p++;
+ q++;
+ }
+ if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransposeImage)
+#endif
+ proceed=SetImageProgress(image,SpliceImageTag,progress++,
+ splice_image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ splice_view=DestroyCacheView(splice_view);
+ image_view=DestroyCacheView(image_view);
+ if (status == MagickFalse)
+ splice_image=DestroyImage(splice_image);
+ return(splice_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s f o r m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformImage() is a convenience method that behaves like ResizeImage() or
+% CropImage() but accepts scaling and/or cropping information as a region
+% geometry specification. If the operation fails, the original image handle
+% is returned.
+%
+% The format of the TransformImage method is:
+%
+% MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
+% const char *image_geometry)
+%
+% A description of each parameter follows:
+%
+% o image: the image The transformed image is returned as this parameter.
+%
+% o crop_geometry: A crop geometry string. This geometry defines a
+% subregion of the image to crop.
+%
+% o image_geometry: An image geometry string. This geometry defines the
+% final size of the image.
+%
+*/
+MagickExport MagickBooleanType TransformImage(Image **image,
+ const char *crop_geometry,const char *image_geometry)
+{
+ Image
+ *resize_image,
+ *transform_image;
+
+ MagickStatusType
+ flags;
+
+ RectangleInfo
+ geometry;
+
+ assert(image != (Image **) NULL);
+ assert((*image)->signature == MagickSignature);
+ if ((*image)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
+ transform_image=(*image);
+ if (crop_geometry != (const char *) NULL)
+ {
+ Image
+ *crop_image;
+
+ RectangleInfo
+ geometry;
+
+ /*
+ Crop image to a user specified size.
+ */
+ crop_image=NewImageList();
+ flags=ParseGravityGeometry(transform_image,crop_geometry,&geometry,
+ &(*image)->exception);
+ if (((geometry.width == 0) && (geometry.height == 0)) ||
+ ((flags & XValue) != 0) || ((flags & YValue) != 0))
+ {
+ crop_image=CropImage(transform_image,&geometry,&(*image)->exception);
+ if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
+ {
+ crop_image->page.width=geometry.width;
+ crop_image->page.height=geometry.height;
+ crop_image->page.x-=geometry.x;
+ crop_image->page.y-=geometry.y;
+ }
+ }
+ else
+ if ((transform_image->columns > geometry.width) ||
+ (transform_image->rows > geometry.height))
+ {
+ Image
+ *next;
+
+ long
+ y;
+
+ register long
+ x;
+
+ unsigned long
+ height,
+ width;
+
+ /*
+ Crop repeatedly to create uniform scenes.
+ */
+ if (transform_image->page.width == 0)
+ transform_image->page.width=transform_image->columns;
+ if (transform_image->page.height == 0)
+ transform_image->page.height=transform_image->rows;
+ width=geometry.width;
+ if (width == 0)
+ width=transform_image->page.width;
+ height=geometry.height;
+ if (height == 0)
+ height=transform_image->page.height;
+ next=NewImageList();
+ for (y=0; y < (long) transform_image->page.height; y+=height)
+ {
+ for (x=0; x < (long) transform_image->page.width; x+=width)
+ {
+ geometry.width=width;
+ geometry.height=height;
+ geometry.x=x;
+ geometry.y=y;
+ next=CropImage(transform_image,&geometry,&(*image)->exception);
+ if (next == (Image *) NULL)
+ break;
+ AppendImageToList(&crop_image,next);
+ }
+ if (next == (Image *) NULL)
+ break;
+ }
+ }
+ if (crop_image == (Image *) NULL)
+ transform_image=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
+ else
+ {
+ transform_image=DestroyImage(transform_image);
+ transform_image=GetFirstImageInList(crop_image);
+ }
+ *image=transform_image;
+ }
+ if (image_geometry == (const char *) NULL)
+ return(MagickTrue);
+ /*
+ Scale image to a user specified size.
+ */
+ flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,
+ &(*image)->exception);
+ if ((transform_image->columns == geometry.width) &&
+ (transform_image->rows == geometry.height))
+ return(MagickTrue);
+ resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
+ &(*image)->exception);
+ if (resize_image == (Image *) NULL)
+ return(MagickFalse);
+ transform_image=DestroyImage(transform_image);
+ transform_image=resize_image;
+ *image=transform_image;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s f o r m I m a g e s %
+% %
+% %
+% % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransformImages() calls TransformImage() on each image of a sequence.
+%
+% The format of the TransformImage method is:
+%
+% MagickBooleanType TransformImages(Image **image,
+% const char *crop_geometry,const char *image_geometry)
+%
+% A description of each parameter follows:
+%
+% o image: the image The transformed image is returned as this parameter.
+%
+% o crop_geometry: A crop geometry string. This geometry defines a
+% subregion of the image to crop.
+%
+% o image_geometry: An image geometry string. This geometry defines the
+% final size of the image.
+%
+*/
+MagickExport MagickBooleanType TransformImages(Image **images,
+ const char *crop_geometry,const char *image_geometry)
+{
+ Image
+ *image,
+ **image_list,
+ *transform_images;
+
+ MagickStatusType
+ status;
+
+ register long
+ i;
+
+ assert(images != (Image **) NULL);
+ assert((*images)->signature == MagickSignature);
+ if ((*images)->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ (*images)->filename);
+ image_list=ImageListToArray(*images,&(*images)->exception);
+ if (image_list == (Image **) NULL)
+ return(MagickFalse);
+ status=MagickTrue;
+ transform_images=NewImageList();
+ for (i=0; image_list[i] != (Image *) NULL; i++)
+ {
+ image=image_list[i];
+ status|=TransformImage(&image,crop_geometry,image_geometry);
+ AppendImageToList(&transform_images,image);
+ }
+ *images=transform_images;
+ image_list=(Image **) RelinquishMagickMemory(image_list);
+ return(status != 0 ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s p o s e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransposeImage() creates a horizontal mirror image by reflecting the pixels
+% around the central y-axis while rotating them by 90 degrees.
+%
+% The format of the TransposeImage method is:
+%
+% Image *TransposeImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
+{
+#define TransposeImageTag "Transpose/Image"
+
+ Image
+ *transpose_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ page;
+
+ CacheView
+ *image_view,
+ *transpose_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
+ exception);
+ if (transpose_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Transpose image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ transpose_view=AcquireCacheView(transpose_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict transpose_indexes,
+ *__restrict indexes;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,(long) image->rows-y-1,
+ image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(transpose_view,(long) (image->rows-y-1),0,
+ 1,transpose_image->rows,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ (void) CopyMagickMemory(q,p,(size_t) image->columns*sizeof(*q));
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ transpose_indexes=GetCacheViewAuthenticIndexQueue(transpose_view);
+ if (transpose_indexes != (IndexPacket *) NULL)
+ (void) CopyMagickMemory(transpose_indexes,indexes,(size_t)
+ image->columns*sizeof(*transpose_indexes));
+ }
+ if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransposeImage)
+#endif
+ proceed=SetImageProgress(image,TransposeImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ transpose_view=DestroyCacheView(transpose_view);
+ image_view=DestroyCacheView(image_view);
+ transpose_image->type=image->type;
+ page=transpose_image->page;
+ Swap(page.width,page.height);
+ Swap(page.x,page.y);
+ if (page.width != 0)
+ page.x=(long) (page.width-transpose_image->columns-page.x);
+ transpose_image->page=page;
+ if (status == MagickFalse)
+ transpose_image=DestroyImage(transpose_image);
+ return(transpose_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r a n s v e r s e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TransverseImage() creates a vertical mirror image by reflecting the pixels
+% around the central x-axis while rotating them by 270 degrees.
+%
+% The format of the TransverseImage method is:
+%
+% Image *TransverseImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
+{
+#define TransverseImageTag "Transverse/Image"
+
+ Image
+ *transverse_image;
+
+ long
+ progress,
+ y;
+
+ MagickBooleanType
+ status;
+
+ RectangleInfo
+ page;
+
+ CacheView
+ *image_view,
+ *transverse_view;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ assert(exception != (ExceptionInfo *) NULL);
+ assert(exception->signature == MagickSignature);
+ transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
+ exception);
+ if (transverse_image == (Image *) NULL)
+ return((Image *) NULL);
+ /*
+ Transverse image.
+ */
+ status=MagickTrue;
+ progress=0;
+ image_view=AcquireCacheView(image);
+ transverse_view=AcquireCacheView(transverse_image);
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
+#endif
+ for (y=0; y < (long) image->rows; y++)
+ {
+ MagickBooleanType
+ sync;
+
+ register const PixelPacket
+ *__restrict p;
+
+ register IndexPacket
+ *__restrict transverse_indexes,
+ *__restrict indexes;
+
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ if (status == MagickFalse)
+ continue;
+ p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
+ q=QueueCacheViewAuthenticPixels(transverse_view,(long) (image->rows-y-
+ 1),0,1,transverse_image->rows,exception);
+ if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
+ {
+ status=MagickFalse;
+ continue;
+ }
+ q+=image->columns;
+ for (x=0; x < (long) image->columns; x++)
+ *--q=(*p++);
+ indexes=GetCacheViewAuthenticIndexQueue(image_view);
+ if (indexes != (IndexPacket *) NULL)
+ {
+ transverse_indexes=GetCacheViewAuthenticIndexQueue(transverse_view);
+ if (transverse_indexes != (IndexPacket *) NULL)
+ for (x=0; x < (long) image->columns; x++)
+ transverse_indexes[image->columns-x-1]=indexes[x];
+ }
+ sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
+ if (sync == MagickFalse)
+ status=MagickFalse;
+ if (image->progress_monitor != (MagickProgressMonitor) NULL)
+ {
+ MagickBooleanType
+ proceed;
+
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+ #pragma omp critical (MagickCore_TransverseImage)
+#endif
+ proceed=SetImageProgress(image,TransverseImageTag,progress++,
+ image->rows);
+ if (proceed == MagickFalse)
+ status=MagickFalse;
+ }
+ }
+ transverse_view=DestroyCacheView(transverse_view);
+ image_view=DestroyCacheView(image_view);
+ transverse_image->type=image->type;
+ page=transverse_image->page;
+ Swap(page.width,page.height);
+ Swap(page.x,page.y);
+ if (page.height != 0)
+ page.y=(long) (page.height-transverse_image->rows-page.y);
+ transverse_image->page=page;
+ if (status == MagickFalse)
+ transverse_image=DestroyImage(transverse_image);
+ return(transverse_image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% T r i m I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% TrimImage() trims pixels from the image edges. It allocates the memory
+% necessary for the new Image structure and returns a pointer to the new
+% image.
+%
+% The format of the TrimImage method is:
+%
+% Image *TrimImage(const Image *image,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
+{
+ RectangleInfo
+ geometry;
+
+ assert(image != (const Image *) NULL);
+ assert(image->signature == MagickSignature);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ geometry=GetImageBoundingBox(image,exception);
+ if ((geometry.width == 0) || (geometry.height == 0))
+ {
+ Image
+ *crop_image;
+
+ crop_image=CloneImage(image,1,1,MagickTrue,exception);
+ if (crop_image == (Image *) NULL)
+ return((Image *) NULL);
+ crop_image->background_color.opacity=(Quantum) TransparentOpacity;
+ (void) SetImageBackgroundColor(crop_image);
+ crop_image->page=image->page;
+ crop_image->page.x=(-1);
+ crop_image->page.y=(-1);
+ return(crop_image);
+ }
+ geometry.x+=image->page.x;
+ geometry.y+=image->page.y;
+ return(CropImage(image,&geometry,exception));
+}
diff --git a/magick/transform.h b/magick/transform.h
new file mode 100644
index 0000000..6561f9e
--- /dev/null
+++ b/magick/transform.h
@@ -0,0 +1,48 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image transform methods.
+*/
+#ifndef _MAGICKCORE_TRANSFORM_H
+#define _MAGICKCORE_TRANSFORM_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+extern MagickExport Image
+ *ChopImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *ConsolidateCMYKImages(const Image *,ExceptionInfo *),
+ *CropImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *ExcerptImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *ExtentImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *FlipImage(const Image *,ExceptionInfo *),
+ *FlopImage(const Image *,ExceptionInfo *),
+ *RollImage(const Image *,const long,const long,ExceptionInfo *),
+ *ShaveImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *SpliceImage(const Image *,const RectangleInfo *,ExceptionInfo *),
+ *TransposeImage(const Image *,ExceptionInfo *),
+ *TransverseImage(const Image *,ExceptionInfo *),
+ *TrimImage(const Image *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ TransformImage(Image **,const char *,const char *),
+ TransformImages(Image **,const char *,const char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/type.c b/magick/type.c
new file mode 100644
index 0000000..fa7eaf4
--- /dev/null
+++ b/magick/type.c
@@ -0,0 +1,1351 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% TTTTT Y Y PPPP EEEEE %
+% T Y Y P P E %
+% T Y PPPP EEE %
+% T Y P E %
+% T Y P EEEEE %
+% %
+% %
+% MagickCore Image Type Methods %
+% %
+% Software Design %
+% John Cristy %
+% May 2001 %
+% %
+% %
+% Copyright 1999-2007 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/client.h"
+#include "magick/configure.h"
+#include "magick/draw.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/semaphore.h"
+#include "magick/splay-tree.h"
+#include "magick/string_.h"
+#include "magick/type.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xml-tree.h"
+#if defined(MAGICKCORE_FONTCONFIG_DELEGATE)
+# include "fontconfig/fontconfig.h"
+#if (FC_VERSION < 20209)
+#undef FC_WEIGHT_LIGHT
+#define FC_WIDTH "width" /* Int */
+#define FC_WIDTH_ULTRACONDENSED 50
+#define FC_WIDTH_EXTRACONDENSED 63
+#define FC_WIDTH_CONDENSED 75
+#define FC_WIDTH_SEMICONDENSED 87
+#define FC_WIDTH_NORMAL 100
+#define FC_WIDTH_SEMIEXPANDED 113
+#define FC_WIDTH_EXPANDED 125
+#define FC_WIDTH_EXTRAEXPANDED 150
+#define FC_WIDTH_ULTRAEXPANDED 200
+
+#define FC_WEIGHT_THIN 0
+#define FC_WEIGHT_EXTRALIGHT 40
+#define FC_WEIGHT_ULTRALIGHT FC_WEIGHT_EXTRALIGHT
+#define FC_WEIGHT_LIGHT 50
+#define FC_WEIGHT_BOOK 75
+#define FC_WEIGHT_REGULAR 80
+#define FC_WEIGHT_NORMAL FC_WEIGHT_REGULAR
+#define FC_WEIGHT_MEDIUM 100
+#define FC_WEIGHT_DEMIBOLD 180
+#define FC_WEIGHT_SEMIBOLD FC_WEIGHT_DEMIBOLD
+#define FC_WEIGHT_BOLD 200
+#define FC_WEIGHT_EXTRABOLD 205
+#define FC_WEIGHT_ULTRABOLD FC_WEIGHT_EXTRABOLD
+#define FC_WEIGHT_BLACK 210
+#define FC_WEIGHT_HEAVY FC_WEIGHT_BLACK
+#endif
+#endif
+#if defined(__WINDOWS__)
+# include "magick/nt-feature.h"
+#endif
+
+/*
+ Define declarations.
+*/
+#define MagickTypeFilename "type.xml"
+
+/*
+ Declare type map.
+*/
+static const char
+ *TypeMap = (const char *)
+ "<?xml version=\"1.0\"?>"
+ "<typemap>"
+ " <type stealth=\"True\" name=\"fixed\" family=\"helvetica\"/>"
+ " <type stealth=\"True\" name=\"helvetica\" family=\"helvetica\"/>"
+ "</typemap>";
+
+/*
+ Static declarations.
+*/
+static SemaphoreInfo
+ *type_semaphore = (SemaphoreInfo *) NULL;
+
+static volatile MagickBooleanType
+ instantiate_type = MagickFalse;
+
+static SplayTreeInfo
+ *type_list = (SplayTreeInfo *) NULL;
+
+/*
+ Forward declarations.
+*/
+static MagickBooleanType
+ InitializeTypeList(ExceptionInfo *),
+ LoadTypeLists(const char *,ExceptionInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ D e s t r o y T y p e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyTypeList() deallocates memory associated with the font list.
+%
+% The format of the DestroyTypeList method is:
+%
+% void DestroyTypeList(void)
+%
+*/
+MagickExport void DestroyTypeList(void)
+{
+ AcquireSemaphoreInfo(&type_semaphore);
+ if (type_list != (SplayTreeInfo *) NULL)
+ type_list=DestroySplayTree(type_list);
+ instantiate_type=MagickFalse;
+ RelinquishSemaphoreInfo(type_semaphore);
+ DestroySemaphoreInfo(&type_semaphore);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t T y p e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTypeInfo searches the type list for the specified name and if found
+% returns attributes for that type.
+%
+% The format of the GetTypeInfo method is:
+%
+% const TypeInfo *GetTypeInfo(const char *name,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o name: the type name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport const TypeInfo *GetTypeInfo(const char *name,
+ ExceptionInfo *exception)
+{
+ assert(exception != (ExceptionInfo *) NULL);
+ if ((type_list == (SplayTreeInfo *) NULL) ||
+ (instantiate_type == MagickFalse))
+ if (InitializeTypeList(exception) == MagickFalse)
+ return((const TypeInfo *) NULL);
+ if ((type_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(type_list) == 0))
+ return((const TypeInfo *) NULL);
+ if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
+ {
+ ResetSplayTreeIterator(type_list);
+ return((const TypeInfo *) GetNextValueInSplayTree(type_list));
+ }
+ return((const TypeInfo *) GetValueFromSplayTree(type_list,name));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ G e t T y p e I n f o B y F a m i l y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTypeInfoByFamily() searches the type list for the specified family and if
+% found returns attributes for that type.
+%
+% Type substitution and scoring algorithm contributed by Bob Friesenhahn.
+%
+% The format of the GetTypeInfoByFamily method is:
+%
+% const TypeInfo *GetTypeInfoByFamily(const char *family,
+% const StyleType style,const StretchType stretch,
+% const unsigned long weight,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o family: the type family.
+%
+% o style: the type style.
+%
+% o stretch: the type stretch.
+%
+% o weight: the type weight.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static inline unsigned long MagickMax(const unsigned long x,
+ const unsigned long y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline unsigned long MagickMin(const unsigned long x,
+ const unsigned long y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport const TypeInfo *GetTypeInfoByFamily(const char *family,
+ const StyleType style,const StretchType stretch,const unsigned long weight,
+ ExceptionInfo *exception)
+{
+ typedef struct _Fontmap
+ {
+ const char
+ *name,
+ *substitute;
+ } Fontmap;
+
+ const TypeInfo
+ *type_info;
+
+ long
+ range;
+
+ register const TypeInfo
+ *p;
+
+ register long
+ i;
+
+ static Fontmap
+ fontmap[] =
+ {
+ { "fixed", "courier" },
+ { "modern","courier" },
+ { "monotype corsiva", "courier" },
+ { "news gothic", "helvetica" },
+ { "system", "courier" },
+ { "terminal", "courier" },
+ { "wingdings", "symbol" },
+ { NULL, NULL }
+ };
+
+ unsigned long
+ max_score,
+ score;
+
+ /*
+ Check for an exact type match.
+ */
+ (void) GetTypeInfo("*",exception);
+ if (type_list == (SplayTreeInfo *) NULL)
+ return((TypeInfo *) NULL);
+ AcquireSemaphoreInfo(&type_semaphore);
+ ResetSplayTreeIterator(type_list);
+ type_info=(const TypeInfo *) NULL;
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ while (p != (const TypeInfo *) NULL)
+ {
+ if (p->family == (char *) NULL)
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ if (family == (const char *) NULL)
+ {
+ if ((LocaleCompare(p->family,"arial") != 0) &&
+ (LocaleCompare(p->family,"helvetica") != 0))
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ }
+ else
+ if (LocaleCompare(p->family,family) != 0)
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ if ((style != UndefinedStyle) && (style != AnyStyle) && (p->style != style))
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ if ((stretch != UndefinedStretch) && (stretch != AnyStretch) &&
+ (p->stretch != stretch))
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ if ((weight != 0) && (p->weight != weight))
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ type_info=p;
+ break;
+ }
+ RelinquishSemaphoreInfo(type_semaphore);
+ if (type_info != (const TypeInfo *) NULL)
+ return(type_info);
+ /*
+ Check for types in the same family.
+ */
+ max_score=0;
+ AcquireSemaphoreInfo(&type_semaphore);
+ ResetSplayTreeIterator(type_list);
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ while (p != (const TypeInfo *) NULL)
+ {
+ if (p->family == (char *) NULL)
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ if (family == (const char *) NULL)
+ {
+ if ((LocaleCompare(p->family,"arial") != 0) &&
+ (LocaleCompare(p->family,"helvetica") != 0))
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ }
+ else
+ if (LocaleCompare(p->family,family) != 0)
+ {
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ continue;
+ }
+ score=0;
+ if ((style == UndefinedStyle) || (style == AnyStyle) || (p->style == style))
+ score+=32;
+ else
+ if (((style == ItalicStyle) || (style == ObliqueStyle)) &&
+ ((p->style == ItalicStyle) || (p->style == ObliqueStyle)))
+ score+=25;
+ if (weight == 0)
+ score+=16;
+ else
+ score+=(16*(800-((long) MagickMax(MagickMin(weight,900),p->weight)-
+ (long) MagickMin(MagickMin(weight,900),p->weight))))/800;
+ if ((stretch == UndefinedStretch) || (stretch == AnyStretch))
+ score+=8;
+ else
+ {
+ range=(long) UltraExpandedStretch-(long) NormalStretch;
+ score+=(8*(range-((long) MagickMax(stretch,p->stretch)-
+ (long) MagickMin(stretch,p->stretch))))/range;
+ }
+ if (score > max_score)
+ {
+ max_score=score;
+ type_info=p;
+ }
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ }
+ RelinquishSemaphoreInfo(type_semaphore);
+ if (type_info != (const TypeInfo *) NULL)
+ return(type_info);
+ /*
+ Check for table-based substitution match.
+ */
+ for (i=0; fontmap[i].name != (char *) NULL; i++)
+ {
+ if (family == (const char *) NULL)
+ {
+ if ((LocaleCompare(fontmap[i].name,"arial") != 0) &&
+ (LocaleCompare(fontmap[i].name,"helvetica") != 0))
+ continue;
+ }
+ else
+ if (LocaleCompare(fontmap[i].name,family) != 0)
+ continue;
+ type_info=GetTypeInfoByFamily(fontmap[i].substitute,style,stretch,weight,
+ exception);
+ break;
+ }
+ if (type_info != (const TypeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),TypeError,
+ "FontSubstitutionRequired","`%s'",type_info->family);
+ return(type_info);
+ }
+ if (family != (const char *) NULL)
+ type_info=GetTypeInfoByFamily((const char *) NULL,style,stretch,weight,
+ exception);
+ return(type_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t T y p e I n f o L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTypeInfoList() returns any fonts that match the specified pattern.
+%
+% The format of the GetTypeInfoList function is:
+%
+% const TypeInfo **GetTypeInfoList(const char *pattern,
+% unsigned long *number_fonts,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_fonts: This integer returns the number of types in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int TypeInfoCompare(const void *x,const void *y)
+{
+ const TypeInfo
+ **p,
+ **q;
+
+ p=(const TypeInfo **) x,
+ q=(const TypeInfo **) y;
+ if (LocaleCompare((*p)->path,(*q)->path) == 0)
+ return(LocaleCompare((*p)->name,(*q)->name));
+ return(LocaleCompare((*p)->path,(*q)->path));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport const TypeInfo **GetTypeInfoList(const char *pattern,
+ unsigned long *number_fonts,ExceptionInfo *exception)
+{
+ const TypeInfo
+ **fonts;
+
+ register const TypeInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate type list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_fonts != (unsigned long *) NULL);
+ *number_fonts=0;
+ p=GetTypeInfo("*",exception);
+ if (p == (const TypeInfo *) NULL)
+ return((const TypeInfo **) NULL);
+ fonts=(const TypeInfo **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(type_list)+1UL,sizeof(*fonts));
+ if (fonts == (const TypeInfo **) NULL)
+ return((const TypeInfo **) NULL);
+ /*
+ Generate type list.
+ */
+ AcquireSemaphoreInfo(&type_semaphore);
+ ResetSplayTreeIterator(type_list);
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ for (i=0; p != (const TypeInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ fonts[i++]=p;
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ }
+ RelinquishSemaphoreInfo(type_semaphore);
+ qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeInfoCompare);
+ fonts[i]=(TypeInfo *) NULL;
+ *number_fonts=(unsigned long) i;
+ return(fonts);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t T y p e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetTypeList() returns any fonts that match the specified pattern.
+%
+% The format of the GetTypeList function is:
+%
+% char **GetTypeList(const char *pattern,unsigned long *number_fonts,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_fonts: This integer returns the number of fonts in the list.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int TypeCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport char **GetTypeList(const char *pattern,unsigned long *number_fonts,
+ ExceptionInfo *exception)
+{
+ char
+ **fonts;
+
+ register const TypeInfo
+ *p;
+
+ register long
+ i;
+
+ /*
+ Allocate type list.
+ */
+ assert(pattern != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
+ assert(number_fonts != (unsigned long *) NULL);
+ *number_fonts=0;
+ p=GetTypeInfo("*",exception);
+ if (p == (const TypeInfo *) NULL)
+ return((char **) NULL);
+ fonts=(char **) AcquireQuantumMemory((size_t)
+ GetNumberOfNodesInSplayTree(type_list)+1UL,sizeof(*fonts));
+ if (fonts == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Generate type list.
+ */
+ AcquireSemaphoreInfo(&type_semaphore);
+ ResetSplayTreeIterator(type_list);
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ for (i=0; p != (const TypeInfo *) NULL; )
+ {
+ if ((p->stealth == MagickFalse) &&
+ (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
+ fonts[i++]=ConstantString(p->name);
+ p=(const TypeInfo *) GetNextValueInSplayTree(type_list);
+ }
+ RelinquishSemaphoreInfo(type_semaphore);
+ qsort((void *) fonts,(size_t) i,sizeof(*fonts),TypeCompare);
+ fonts[i]=(char *) NULL;
+ *number_fonts=(unsigned long) i;
+ return(fonts);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I n i t i a l i z e T y p e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InitializeTypeList() initializes the type list.
+%
+% The format of the InitializeTypeList method is:
+%
+% MagickBooleanType InitializeTypeList(ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+#if defined(MAGICKCORE_FONTCONFIG_DELEGATE)
+MagickExport MagickBooleanType LoadFontConfigFonts(SplayTreeInfo *type_list,
+ ExceptionInfo *exception)
+{
+ char
+ extension[MaxTextExtent],
+ name[MaxTextExtent];
+
+ FcChar8
+ *family,
+ *file,
+ *style;
+
+ FcConfig
+ *font_config;
+
+ FcFontSet
+ *font_set;
+
+ FcObjectSet
+ *object_set;
+
+ FcPattern
+ *pattern;
+
+ FcResult
+ status;
+
+ int
+ slant,
+ width,
+ weight;
+
+ register long
+ i;
+
+ TypeInfo
+ *type_info;
+
+ /*
+ Load system fonts.
+ */
+ (void) exception;
+ font_config=FcInitLoadConfigAndFonts();
+ if (font_config == (FcConfig *) NULL)
+ return(MagickFalse);
+ font_set=(FcFontSet *) NULL;
+ object_set=FcObjectSetBuild(FC_FAMILY,FC_STYLE,FC_SLANT,FC_WIDTH,FC_WEIGHT,
+ FC_FILE,(char *) NULL);
+ if (object_set != (FcObjectSet *) NULL)
+ {
+ pattern=FcPatternCreate();
+ if (pattern != (FcPattern *) NULL)
+ {
+ font_set=FcFontList(0,pattern,object_set);
+ FcPatternDestroy(pattern);
+ }
+ FcObjectSetDestroy(object_set);
+ }
+ if (font_set == (FcFontSet *) NULL)
+ {
+ FcConfigDestroy(font_config);
+ return(MagickFalse);
+ }
+ for (i=0; i < (long) font_set->nfont; i++)
+ {
+ status=FcPatternGetString(font_set->fonts[i],FC_FAMILY,0,&family);
+ if (status != FcResultMatch)
+ continue;
+ status=FcPatternGetString(font_set->fonts[i],FC_FILE,0,&file);
+ if (status != FcResultMatch)
+ continue;
+ *extension='\0';
+ GetPathComponent((const char *) file,ExtensionPath,extension);
+ if ((*extension != '\0') && (LocaleCompare(extension,"gz") == 0))
+ continue;
+ type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info));
+ if (type_info == (TypeInfo *) NULL)
+ continue;
+ (void) ResetMagickMemory(type_info,0,sizeof(*type_info));
+ type_info->path=ConstantString("System Fonts");
+ type_info->signature=MagickSignature;
+ (void) CopyMagickString(name,(const char *) family,MaxTextExtent);
+ (void) ConcatenateMagickString(name," ",MaxTextExtent);
+ status=FcPatternGetString(font_set->fonts[i],FC_STYLE,0,&style);
+ if (status == FcResultMatch)
+ (void) ConcatenateMagickString(name,(const char *) style,MaxTextExtent);
+ type_info->name=ConstantString(name);
+ (void) SubstituteString(&type_info->name," ","-");
+ (void) SubstituteString(&type_info->name,"-L-","-");
+ (void) SubstituteString(&type_info->name,"semicondensed","SemiCondensed");
+ type_info->family=ConstantString((const char *) family);
+ (void) SubstituteString(&type_info->family," L","");
+ status=FcPatternGetInteger(font_set->fonts[i],FC_SLANT,0,&slant);
+ type_info->style=NormalStyle;
+ if (slant == FC_SLANT_ITALIC)
+ type_info->style=ItalicStyle;
+ if (slant == FC_SLANT_OBLIQUE)
+ type_info->style=ObliqueStyle;
+ status=FcPatternGetInteger(font_set->fonts[i],FC_WIDTH,0,&width);
+ type_info->stretch=NormalStretch;
+ if (width >= FC_WIDTH_ULTRACONDENSED)
+ type_info->stretch=UltraCondensedStretch;
+ if (width >= FC_WIDTH_EXTRACONDENSED)
+ type_info->stretch=ExtraCondensedStretch;
+ if (width >= FC_WIDTH_CONDENSED)
+ type_info->stretch=CondensedStretch;
+ if (width >= FC_WIDTH_SEMICONDENSED)
+ type_info->stretch=SemiCondensedStretch;
+ if (width >= FC_WIDTH_NORMAL)
+ type_info->stretch=NormalStretch;
+ if (width >= FC_WIDTH_SEMIEXPANDED)
+ type_info->stretch=SemiExpandedStretch;
+ if (width >= FC_WIDTH_EXPANDED)
+ type_info->stretch=ExpandedStretch;
+ if (width >= FC_WIDTH_EXTRAEXPANDED)
+ type_info->stretch=ExtraExpandedStretch;
+ if (width >= FC_WIDTH_ULTRAEXPANDED)
+ type_info->stretch=UltraExpandedStretch;
+ type_info->weight=400;
+ status=FcPatternGetInteger(font_set->fonts[i],FC_WEIGHT,0,&weight);
+ if (weight >= FC_WEIGHT_THIN)
+ type_info->weight=100;
+ if (weight >= FC_WEIGHT_EXTRALIGHT)
+ type_info->weight=200;
+ if (weight >= FC_WEIGHT_LIGHT)
+ type_info->weight=300;
+ if (weight >= FC_WEIGHT_NORMAL)
+ type_info->weight=400;
+ if (weight >= FC_WEIGHT_MEDIUM)
+ type_info->weight=500;
+ if (weight >= FC_WEIGHT_DEMIBOLD)
+ type_info->weight=600;
+ if (weight >= FC_WEIGHT_BOLD)
+ type_info->weight=700;
+ if (weight >= FC_WEIGHT_EXTRABOLD)
+ type_info->weight=800;
+ if (weight >= FC_WEIGHT_BLACK)
+ type_info->weight=900;
+ type_info->glyphs=ConstantString((const char *) file);
+ (void) AddValueToSplayTree(type_list,type_info->name,type_info);
+ }
+ FcFontSetDestroy(font_set);
+ FcConfigDestroy(font_config);
+ return(MagickTrue);
+}
+#endif
+
+static MagickBooleanType InitializeTypeList(ExceptionInfo *exception)
+{
+ if ((type_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_type == MagickFalse))
+ {
+ AcquireSemaphoreInfo(&type_semaphore);
+ if ((type_list == (SplayTreeInfo *) NULL) &&
+ (instantiate_type == MagickFalse))
+ {
+ (void) LoadTypeLists(MagickTypeFilename,exception);
+#if defined(__WINDOWS__)
+ (void) NTLoadTypeLists(type_list,exception);
+#endif
+#if defined(MAGICKCORE_FONTCONFIG_DELEGATE)
+ (void) LoadFontConfigFonts(type_list,exception);
+#endif
+ instantiate_type=MagickTrue;
+ }
+ RelinquishSemaphoreInfo(type_semaphore);
+ }
+ return(type_list != (SplayTreeInfo *) NULL ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t T y p e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListTypeInfo() lists the fonts to a file.
+%
+% The format of the ListTypeInfo method is:
+%
+% MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception)
+%
+% A description of each parameter follows.
+%
+% o file: An pointer to a FILE.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+MagickExport MagickBooleanType ListTypeInfo(FILE *file,ExceptionInfo *exception)
+{
+ char
+ weight[MaxTextExtent];
+
+ const char
+ *family,
+ *glyphs,
+ *name,
+ *path,
+ *stretch,
+ *style;
+
+ const TypeInfo
+ **type_info;
+
+ register long
+ i;
+
+ unsigned long
+ number_fonts;
+
+ if (file == (FILE *) NULL)
+ file=stdout;
+ number_fonts=0;
+ type_info=GetTypeInfoList("*",&number_fonts,exception);
+ if (type_info == (const TypeInfo **) NULL)
+ return(MagickFalse);
+ *weight='\0';
+ path=(const char *) NULL;
+ for (i=0; i < (long) number_fonts; i++)
+ {
+ if (type_info[i]->stealth != MagickFalse)
+ continue;
+ if (((path == (const char *) NULL) ||
+ (LocaleCompare(path,type_info[i]->path) != 0)) &&
+ (type_info[i]->path != (char *) NULL))
+ (void) fprintf(file,"\nPath: %s\n",type_info[i]->path);
+ path=type_info[i]->path;
+ name="unknown";
+ if (type_info[i]->name != (char *) NULL)
+ name=type_info[i]->name;
+ family="unknown";
+ if (type_info[i]->family != (char *) NULL)
+ family=type_info[i]->family;
+ style=MagickOptionToMnemonic(MagickStyleOptions,type_info[i]->style);
+ stretch=MagickOptionToMnemonic(MagickStretchOptions,type_info[i]->stretch);
+ glyphs="unknown";
+ if (type_info[i]->glyphs != (char *) NULL)
+ glyphs=type_info[i]->glyphs;
+ (void) FormatMagickString(weight,MaxTextExtent,"%lu",type_info[i]->weight);
+ (void) fprintf(file," Font: %s\n",name);
+ (void) fprintf(file," family: %s\n",family);
+ (void) fprintf(file," style: %s\n",style);
+ (void) fprintf(file," stretch: %s\n",stretch);
+ (void) fprintf(file," weight: %s\n",weight);
+ (void) fprintf(file," glyphs: %s\n",glyphs);
+ }
+ (void) fflush(file);
+ type_info=(const TypeInfo **) RelinquishMagickMemory((void *) type_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ L o a d T y p e L i s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadTypeList() loads the type configuration file which provides a mapping
+% between type attributes and a type name.
+%
+% The format of the LoadTypeList method is:
+%
+% MagickBooleanType LoadTypeList(const char *xml,const char *filename,
+% const unsigned long depth,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The type list in XML format.
+%
+% o filename: The type list filename.
+%
+% o depth: depth of <include /> statements.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static void *DestroyTypeNode(void *type_info)
+{
+ register TypeInfo
+ *p;
+
+ p=(TypeInfo *) type_info;
+ if (p->path != (char *) NULL)
+ p->path=DestroyString(p->path);
+ if (p->name != (char *) NULL)
+ p->name=DestroyString(p->name);
+ if (p->description != (char *) NULL)
+ p->description=DestroyString(p->description);
+ if (p->family != (char *) NULL)
+ p->family=DestroyString(p->family);
+ if (p->encoding != (char *) NULL)
+ p->encoding=DestroyString(p->encoding);
+ if (p->foundry != (char *) NULL)
+ p->foundry=DestroyString(p->foundry);
+ if (p->format != (char *) NULL)
+ p->format=DestroyString(p->format);
+ if (p->metrics != (char *) NULL)
+ p->metrics=DestroyString(p->metrics);
+ if (p->glyphs != (char *) NULL)
+ p->glyphs=DestroyString(p->glyphs);
+ return(RelinquishMagickMemory(p));
+}
+
+static MagickBooleanType LoadTypeList(const char *xml,const char *filename,
+ const unsigned long depth,ExceptionInfo *exception)
+{
+ char
+ font_path[MaxTextExtent],
+ keyword[MaxTextExtent],
+ *token;
+
+ const char
+ *q;
+
+ MagickBooleanType
+ status;
+
+ TypeInfo
+ *type_info;
+
+ /*
+ Load the type map file.
+ */
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Loading type configure file \"%s\" ...",filename);
+ if (xml == (const char *) NULL)
+ return(MagickFalse);
+ if (type_list == (SplayTreeInfo *) NULL)
+ {
+ type_list=NewSplayTree(CompareSplayTreeString,(void *(*)(void *)) NULL,
+ DestroyTypeNode);
+ if (type_list == (SplayTreeInfo *) NULL)
+ {
+ ThrowFileException(exception,ResourceLimitError,
+ "MemoryAllocationFailed",filename);
+ return(MagickFalse);
+ }
+ }
+ status=MagickTrue;
+ type_info=(TypeInfo *) NULL;
+ token=AcquireString(xml);
+#if defined(__WINDOWS__)
+ /*
+ Determine the Ghostscript font path.
+ */
+ *font_path='\0';
+ if (NTGhostscriptFonts(font_path,MaxTextExtent-2))
+ (void) ConcatenateMagickString(font_path,DirectorySeparator,MaxTextExtent);
+#endif
+ for (q=(char *) xml; *q != '\0'; )
+ {
+ /*
+ Interpret XML.
+ */
+ GetMagickToken(q,&q,token);
+ if (*token == '\0')
+ break;
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
+ {
+ /*
+ Doctype element.
+ */
+ while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleNCompare(keyword,"<!--",4) == 0)
+ {
+ /*
+ Comment element.
+ */
+ while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
+ GetMagickToken(q,&q,token);
+ continue;
+ }
+ if (LocaleCompare(keyword,"<include") == 0)
+ {
+ /*
+ Include element.
+ */
+ while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
+ {
+ (void) CopyMagickString(keyword,token,MaxTextExtent);
+ GetMagickToken(q,&q,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ if (LocaleCompare(keyword,"file") == 0)
+ {
+ if (depth > 200)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ConfigureError,"IncludeNodeNestedTooDeeply","`%s'",token);
+ else
+ {
+ char
+ path[MaxTextExtent],
+ *xml;
+
+ ExceptionInfo
+ *sans_exception;
+
+ *path='\0';
+ GetPathComponent(filename,HeadPath,path);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(path,DirectorySeparator,
+ MaxTextExtent);
+ if (*token == *DirectorySeparator)
+ (void) CopyMagickString(path,token,MaxTextExtent);
+ else
+ (void) ConcatenateMagickString(path,token,MaxTextExtent);
+ sans_exception=AcquireExceptionInfo();
+ xml=FileToString(path,~0,sans_exception);
+ sans_exception=DestroyExceptionInfo(sans_exception);
+ if (xml != (char *) NULL)
+ {
+ status=LoadTypeList(xml,path,depth+1,exception);
+ xml=(char *) RelinquishMagickMemory(xml);
+ }
+ }
+ }
+ }
+ continue;
+ }
+ if (LocaleCompare(keyword,"<type") == 0)
+ {
+ /*
+ Type element.
+ */
+ type_info=(TypeInfo *) AcquireMagickMemory(sizeof(*type_info));
+ if (type_info == (TypeInfo *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) ResetMagickMemory(type_info,0,sizeof(*type_info));
+ type_info->path=ConstantString(filename);
+ type_info->signature=MagickSignature;
+ continue;
+ }
+ if (type_info == (TypeInfo *) NULL)
+ continue;
+ if (LocaleCompare(keyword,"/>") == 0)
+ {
+ status=AddValueToSplayTree(type_list,type_info->name,type_info);
+ if (status == MagickFalse)
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'",type_info->name);
+ type_info=(TypeInfo *) NULL;
+ }
+ GetMagickToken(q,(const char **) NULL,token);
+ if (*token != '=')
+ continue;
+ GetMagickToken(q,&q,token);
+ GetMagickToken(q,&q,token);
+ switch (*keyword)
+ {
+ case 'E':
+ case 'e':
+ {
+ if (LocaleCompare((char *) keyword,"encoding") == 0)
+ {
+ type_info->encoding=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'F':
+ case 'f':
+ {
+ if (LocaleCompare((char *) keyword,"face") == 0)
+ {
+ type_info->face=(unsigned long) atol(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"family") == 0)
+ {
+ type_info->family=ConstantString(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"format") == 0)
+ {
+ type_info->format=ConstantString(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"foundry") == 0)
+ {
+ type_info->foundry=ConstantString(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"fullname") == 0)
+ {
+ type_info->description=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'G':
+ case 'g':
+ {
+ if (LocaleCompare((char *) keyword,"glyphs") == 0)
+ {
+ char
+ *path;
+
+ path=ConstantString(token);
+#if defined(__WINDOWS__)
+ if (strchr(path,'@') != (char *) NULL)
+ SubstituteString(&path,"@ghostscript_font_path@",font_path);
+#endif
+ if (IsPathAccessible(path) == MagickFalse)
+ {
+ /*
+ Relative path.
+ */
+ path=DestroyString(path);
+ GetPathComponent(filename,HeadPath,font_path);
+ (void) ConcatenateMagickString(font_path,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(font_path,token,MaxTextExtent);
+ path=ConstantString(font_path);
+ }
+ type_info->glyphs=path;
+ break;
+ }
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ if (LocaleCompare((char *) keyword,"metrics") == 0)
+ {
+ char
+ *path;
+
+ path=ConstantString(token);
+#if defined(__WINDOWS__)
+ if (strchr(path,'@') != (char *) NULL)
+ SubstituteString(&path,"@ghostscript_font_path@",font_path);
+#endif
+ if (IsPathAccessible(path) == MagickFalse)
+ {
+ /*
+ Relative path.
+ */
+ path=DestroyString(path);
+ GetPathComponent(filename,HeadPath,font_path);
+ (void) ConcatenateMagickString(font_path,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(font_path,token,MaxTextExtent);
+ path=ConstantString(font_path);
+ }
+ type_info->metrics=path;
+ break;
+ }
+ break;
+ }
+ case 'N':
+ case 'n':
+ {
+ if (LocaleCompare((char *) keyword,"name") == 0)
+ {
+ type_info->name=ConstantString(token);
+ break;
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ if (LocaleCompare((char *) keyword,"stealth") == 0)
+ {
+ type_info->stealth=IsMagickTrue(token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"stretch") == 0)
+ {
+ type_info->stretch=(StretchType) ParseMagickOption(
+ MagickStretchOptions,MagickFalse,token);
+ break;
+ }
+ if (LocaleCompare((char *) keyword,"style") == 0)
+ {
+ type_info->style=(StyleType) ParseMagickOption(MagickStyleOptions,
+ MagickFalse,token);
+ break;
+ }
+ break;
+ }
+ case 'W':
+ case 'w':
+ {
+ if (LocaleCompare((char *) keyword,"weight") == 0)
+ {
+ type_info->weight=(unsigned long) atol(token);
+ if (LocaleCompare(token,"bold") == 0)
+ type_info->weight=700;
+ if (LocaleCompare(token,"normal") == 0)
+ type_info->weight=400;
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ token=(char *) RelinquishMagickMemory(token);
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L o a d T y p e L i s t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% LoadTypeList() loads one or more type configuration files which provides a
+% mapping between type attributes and a type name.
+%
+% The format of the LoadTypeLists method is:
+%
+% MagickBooleanType LoadTypeLists(const char *filename,
+% ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o filename: the font file name.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+static MagickBooleanType LoadTypeLists(const char *filename,
+ ExceptionInfo *exception)
+{
+#if defined(MAGICKCORE_EMBEDDABLE_SUPPORT)
+ return(LoadTypeList(TypeMap,"built-in",0,exception));
+#else
+ char
+ *font_path,
+ path[MaxTextExtent];
+
+ const StringInfo
+ *option;
+
+ LinkedListInfo
+ *options;
+
+ MagickStatusType
+ status;
+
+ status=MagickFalse;
+ *path='\0';
+ options=GetConfigureOptions(filename,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ while (option != (const StringInfo *) NULL)
+ {
+ (void) CopyMagickString(path,GetStringInfoPath(option),MaxTextExtent);
+ status|=LoadTypeList((const char *) GetStringInfoDatum(option),
+ GetStringInfoPath(option),0,exception);
+ option=(const StringInfo *) GetNextValueInLinkedList(options);
+ }
+ options=DestroyConfigureOptions(options);
+ font_path=GetEnvironmentValue("MAGICK_FONT_PATH");
+ if (font_path != (char *) NULL)
+ {
+ char
+ *option;
+
+ /*
+ Search MAGICK_FONT_PATH.
+ */
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",font_path,
+ DirectorySeparator,filename);
+ option=FileToString(path,~0,exception);
+ if (option != (void *) NULL)
+ {
+ status|=LoadTypeList(option,path,0,exception);
+ option=DestroyString(option);
+ }
+ font_path=DestroyString(font_path);
+ }
+ if ((type_list == (SplayTreeInfo *) NULL) ||
+ (GetNumberOfNodesInSplayTree(type_list) == 0))
+ status|=LoadTypeList(TypeMap,"built-in",0,exception);
+ return(status != 0 ? MagickTrue : MagickFalse);
+#endif
+}
diff --git a/magick/type.h b/magick/type.h
new file mode 100644
index 0000000..d4d1db5
--- /dev/null
+++ b/magick/type.h
@@ -0,0 +1,106 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore image type methods.
+*/
+#ifndef _MAGICKCORE_TYPE_H
+#define _MAGICKCORE_TYPE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedStretch,
+ NormalStretch,
+ UltraCondensedStretch,
+ ExtraCondensedStretch,
+ CondensedStretch,
+ SemiCondensedStretch,
+ SemiExpandedStretch,
+ ExpandedStretch,
+ ExtraExpandedStretch,
+ UltraExpandedStretch,
+ AnyStretch
+} StretchType;
+
+typedef enum
+{
+ UndefinedStyle,
+ NormalStyle,
+ ItalicStyle,
+ ObliqueStyle,
+ AnyStyle
+} StyleType;
+
+typedef struct _TypeInfo
+{
+ unsigned long
+ face;
+
+ char
+ *path,
+ *name,
+ *description,
+ *family;
+
+ StyleType
+ style;
+
+ StretchType
+ stretch;
+
+ unsigned long
+ weight;
+
+ char
+ *encoding,
+ *foundry,
+ *format,
+ *metrics,
+ *glyphs;
+
+ MagickBooleanType
+ stealth;
+
+ struct _TypeInfo
+ *previous,
+ *next; /* deprecated, use GetTypeInfoList() */
+
+ unsigned long
+ signature;
+} TypeInfo;
+
+extern MagickExport char
+ **GetTypeList(const char *,unsigned long *,ExceptionInfo *);
+
+extern MagickExport MagickBooleanType
+ ListTypeInfo(FILE *,ExceptionInfo *);
+
+extern MagickExport const TypeInfo
+ *GetTypeInfo(const char *,ExceptionInfo *),
+ *GetTypeInfoByFamily(const char *,const StyleType,const StretchType,
+ const unsigned long,ExceptionInfo *),
+ **GetTypeInfoList(const char *,unsigned long *,ExceptionInfo *);
+
+MagickExport void
+ DestroyTypeList(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/utility.c b/magick/utility.c
new file mode 100644
index 0000000..c78affa
--- /dev/null
+++ b/magick/utility.c
@@ -0,0 +1,1888 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% U U TTTTT IIIII L IIIII TTTTT Y Y %
+% U U T I L I T Y Y %
+% U U T I L I T Y %
+% U U T I L I T Y %
+% UUU T IIIII LLLLL IIIII T Y %
+% %
+% %
+% MagickCore Utility Methods %
+% %
+% Software Design %
+% John Cristy %
+% January 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/property.h"
+#include "magick/blob.h"
+#include "magick/color.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/list.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/option.h"
+#include "magick/resource_.h"
+#include "magick/semaphore.h"
+#include "magick/signature-private.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#if defined(MAGICKCORE_HAVE_MACH_O_DYLD_H)
+#include <mach-o/dyld.h>
+#endif
+
+/*
+ Static declarations.
+*/
+static const char
+ Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ Forward declaration.
+*/
+static int
+ IsPathDirectory(const char *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e U n i q u e F i l e n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireUniqueFilename() replaces the contents of path by a unique path name.
+%
+% The format of the AcquireUniqueFilename method is:
+%
+% MagickBooleanType AcquireUniqueFilename(char *path)
+%
+% A description of each parameter follows.
+%
+% o path: Specifies a pointer to an array of characters. The unique path
+% name is returned in this array.
+%
+*/
+MagickExport MagickBooleanType AcquireUniqueFilename(char *path)
+{
+ int
+ file;
+
+ file=AcquireUniqueFileResource(path);
+ if (file == -1)
+ return(MagickFalse);
+ file=close(file)-1;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A c q u i r e U n i q u e S ym b o l i c L i n k %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AcquireUniqueSymbolicLink() creates a unique symbolic link to the specified
+% source path and returns MagickTrue on success otherwise MagickFalse. If the
+% symlink() method fails or is not available, a unique file name is generated
+% and the source file copied to it. When you are finished with the file, use
+% RelinquishUniqueFilename() to destroy it.
+%
+% The format of the AcquireUniqueSymbolicLink method is:
+%
+% MagickBooleanType AcquireUniqueSymbolicLink(const char *source,
+% char destination)
+%
+% A description of each parameter follows.
+%
+% o source: the source path.
+%
+% o destination: the destination path.
+%
+*/
+
+static inline size_t MagickMin(const size_t x,const size_t y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport MagickBooleanType AcquireUniqueSymbolicLink(const char *source,
+ char *destination)
+{
+ int
+ destination_file,
+ source_file;
+
+ size_t
+ length,
+ quantum;
+
+ ssize_t
+ count;
+
+ struct stat
+ attributes;
+
+ unsigned char
+ *buffer;
+
+ assert(source != (const char *) NULL);
+ assert(destination != (char *) NULL);
+#if defined(MAGICKCORE_HAVE_SYMLINK)
+ (void) AcquireUniqueFilename(destination);
+ (void) RelinquishUniqueFileResource(destination);
+ if (*source == *DirectorySeparator)
+ {
+ if (symlink(source,destination) == 0)
+ return(MagickTrue);
+ }
+ else
+ {
+ char
+ path[MaxTextExtent];
+
+ *path='\0';
+ if (getcwd(path,MaxTextExtent) == (char *) NULL)
+ return(MagickFalse);
+ (void) ConcatenateMagickString(path,DirectorySeparator,MaxTextExtent);
+ (void) ConcatenateMagickString(path,source,MaxTextExtent);
+ if (symlink(path,destination) == 0)
+ return(MagickTrue);
+ }
+#endif
+ destination_file=AcquireUniqueFileResource(destination);
+ if (destination_file == -1)
+ return(MagickFalse);
+ source_file=open(source,O_RDONLY | O_BINARY);
+ if (source_file == -1)
+ {
+ (void) close(destination_file);
+ (void) RelinquishUniqueFileResource(destination);
+ return(MagickFalse);
+ }
+ quantum=(size_t) MagickMaxBufferExtent;
+ if ((fstat(source_file,&attributes) == 0) && (attributes.st_size != 0))
+ quantum=MagickMin((size_t) attributes.st_size,MagickMaxBufferExtent);
+ buffer=(unsigned char *) AcquireQuantumMemory(quantum,sizeof(*buffer));
+ if (buffer == (unsigned char *) NULL)
+ {
+ (void) close(source_file);
+ (void) close(destination_file);
+ (void) RelinquishUniqueFileResource(destination);
+ return(MagickFalse);
+ }
+ for (length=0; ; )
+ {
+ count=(ssize_t) read(source_file,buffer,quantum);
+ if (count <= 0)
+ break;
+ length=(size_t) count;
+ count=(ssize_t) write(destination_file,buffer,length);
+ if ((size_t) count != length)
+ {
+ (void) close(destination_file);
+ (void) close(source_file);
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ (void) RelinquishUniqueFileResource(destination);
+ return(MagickFalse);
+ }
+ }
+ (void) close(destination_file);
+ (void) close(source_file);
+ buffer=(unsigned char *) RelinquishMagickMemory(buffer);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A p p e n d I m a g e F o r m a t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AppendImageFormat() appends the image format type to the filename. If an
+% extension to the file already exists, it is first removed.
+%
+% The format of the AppendImageFormat method is:
+%
+% void AppendImageFormat(const char *format,char *filename)
+%
+% A description of each parameter follows.
+%
+% o format: Specifies a pointer to an array of characters. This the
+% format of the image.
+%
+% o filename: Specifies a pointer to an array of characters. The unique
+% file name is returned in this array.
+%
+*/
+MagickExport void AppendImageFormat(const char *format,char *filename)
+{
+ char
+ root[MaxTextExtent];
+
+ assert(format != (char *) NULL);
+ assert(filename != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ if ((*format == '\0') || (*filename == '\0'))
+ return;
+ if (LocaleCompare(filename,"-") == 0)
+ {
+ char
+ message[MaxTextExtent];
+
+ (void) FormatMagickString(message,MaxTextExtent,"%s:%s",format,filename);
+ (void) CopyMagickString(filename,message,MaxTextExtent);
+ return;
+ }
+ GetPathComponent(filename,RootPath,root);
+ (void) FormatMagickString(filename,MaxTextExtent,"%s.%s",root,format);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B a s e 6 4 D e c o d e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Base64Decode() decodes Base64-encoded text and returns its binary
+% equivalent. NULL is returned if the text is not valid Base64 data, or a
+% memory allocation failure occurs.
+%
+% The format of the Base64Decode method is:
+%
+% unsigned char *Base64Decode(const char *source,length_t *length)
+%
+% A description of each parameter follows:
+%
+% o source: A pointer to a Base64-encoded string.
+%
+% o length: the number of bytes decoded.
+%
+*/
+MagickExport unsigned char *Base64Decode(const char *source,size_t *length)
+{
+ int
+ state;
+
+ register const char
+ *p,
+ *q;
+
+ register size_t
+ i;
+
+ unsigned char
+ *decode;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(source != (char *) NULL);
+ assert(length != (size_t *) NULL);
+ *length=0;
+ decode=(unsigned char *) AcquireQuantumMemory(strlen(source)/4+4,
+ 3*sizeof(*decode));
+ if (decode == (unsigned char *) NULL)
+ return((unsigned char *) NULL);
+ i=0;
+ state=0;
+ for (p=source; *p != '\0'; p++)
+ {
+ if (isspace((int) ((unsigned char) *p)) != 0)
+ continue;
+ if (*p == '=')
+ break;
+ q=strchr(Base64,*p);
+ if (q == (char *) NULL)
+ {
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL); /* non-Base64 character */
+ }
+ switch (state)
+ {
+ case 0:
+ {
+ decode[i]=(q-Base64) << 2;
+ state++;
+ break;
+ }
+ case 1:
+ {
+ decode[i++]|=(q-Base64) >> 4;
+ decode[i]=((q-Base64) & 0x0f) << 4;
+ state++;
+ break;
+ }
+ case 2:
+ {
+ decode[i++]|=(q-Base64) >> 2;
+ decode[i]=((q-Base64) & 0x03) << 6;
+ state++;
+ break;
+ }
+ case 3:
+ {
+ decode[i++]|=(q-Base64);
+ state=0;
+ break;
+ }
+ }
+ }
+ /*
+ Verify Base-64 string has proper terminal characters.
+ */
+ if (*p != '=')
+ {
+ if (state != 0)
+ {
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL);
+ }
+ }
+ else
+ {
+ p++;
+ switch (state)
+ {
+ case 0:
+ case 1:
+ {
+ /*
+ Unrecognized '=' character.
+ */
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL);
+ }
+ case 2:
+ {
+ for ( ; *p != '\0'; p++)
+ if (isspace((int) ((unsigned char) *p)) == 0)
+ break;
+ if (*p != '=')
+ {
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL);
+ }
+ p++;
+ }
+ case 3:
+ {
+ for ( ; *p != '\0'; p++)
+ if (isspace((int) ((unsigned char) *p)) == 0)
+ {
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL);
+ }
+ if ((int) decode[i] != 0)
+ {
+ decode=(unsigned char *) RelinquishMagickMemory(decode);
+ return((unsigned char *) NULL);
+ }
+ }
+ }
+ }
+ *length=i;
+ return(decode);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% B a s e 6 4 E n c o d e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% Base64Encode() encodes arbitrary binary data to Base64 encoded format as
+% described by the "Base64 Content-Transfer-Encoding" section of RFC 2045 and
+% returns the result as a null-terminated ASCII string. NULL is returned if
+% a memory allocation failure occurs.
+%
+% The format of the Base64Encode method is:
+%
+% char *Base64Encode(const unsigned char *blob,const size_t blob_length,
+% size_t *encode_length)
+%
+% A description of each parameter follows:
+%
+% o blob: A pointer to binary data to encode.
+%
+% o blob_length: the number of bytes to encode.
+%
+% o encode_length: The number of bytes encoded.
+%
+*/
+MagickExport char *Base64Encode(const unsigned char *blob,
+ const size_t blob_length,size_t *encode_length)
+{
+ char
+ *encode;
+
+ register const unsigned char
+ *p;
+
+ register size_t
+ i;
+
+ size_t
+ remainder;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(blob != (const unsigned char *) NULL);
+ assert(blob_length != 0);
+ assert(encode_length != (size_t *) NULL);
+ *encode_length=0;
+ encode=(char *) AcquireQuantumMemory(blob_length/3+4,4*sizeof(*encode));
+ if (encode == (char *) NULL)
+ return((char *) NULL);
+ i=0;
+ for (p=blob; p < (blob+blob_length-2); p+=3)
+ {
+ encode[i++]=Base64[(int) (*p >> 2)];
+ encode[i++]=Base64[(int) (((*p & 0x03) << 4)+(*(p+1) >> 4))];
+ encode[i++]=Base64[(int) (((*(p+1) & 0x0f) << 2)+(*(p+2) >> 6))];
+ encode[i++]=Base64[(int) (*(p+2) & 0x3f)];
+ }
+ remainder=blob_length % 3;
+ if (remainder != 0)
+ {
+ long
+ j;
+
+ unsigned char
+ code[3];
+
+ code[0]='\0';
+ code[1]='\0';
+ code[2]='\0';
+ for (j=0; j < (long) remainder; j++)
+ code[j]=(*p++);
+ encode[i++]=Base64[(int) (code[0] >> 2)];
+ encode[i++]=Base64[(int) (((code[0] & 0x03) << 4)+(code[1] >> 4))];
+ if (remainder == 1)
+ encode[i++]='=';
+ else
+ encode[i++]=Base64[(int) (((code[1] & 0x0f) << 2)+(code[2] >> 6))];
+ encode[i++]='=';
+ }
+ *encode_length=i;
+ encode[i++]='\0';
+ return(encode);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C h o p P a t h C o m p o n e n t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ChopPathComponents() removes the number of specified file components from a
+% path.
+%
+% The format of the ChopPathComponents method is:
+%
+% ChopPathComponents(char *path,unsigned long components)
+%
+% A description of each parameter follows:
+%
+% o path: The path.
+%
+% o components: The number of components to chop.
+%
+*/
+MagickExport void ChopPathComponents(char *path,const unsigned long components)
+{
+ register long
+ i;
+
+ for (i=0; i < (long) components; i++)
+ GetPathComponent(path,HeadPath,path);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x p a n d F i l e n a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExpandFilename() expands '~' in a path.
+%
+% The format of the ExpandFilename function is:
+%
+% ExpandFilename(char *path)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies a pointer to a character array that contains the
+% path.
+%
+*/
+MagickExport void ExpandFilename(char *path)
+{
+ char
+ expand_path[MaxTextExtent];
+
+ if (path == (char *) NULL)
+ return;
+ if (*path != '~')
+ return;
+ (void) CopyMagickString(expand_path,path,MaxTextExtent);
+ if ((*(path+1) == *DirectorySeparator) || (*(path+1) == '\0'))
+ {
+ char
+ *home;
+
+ /*
+ Substitute ~ with $HOME.
+ */
+ (void) CopyMagickString(expand_path,".",MaxTextExtent);
+ (void) ConcatenateMagickString(expand_path,path+1,MaxTextExtent);
+ home=GetEnvironmentValue("HOME");
+ if (home == (char *) NULL)
+ home=GetEnvironmentValue("USERPROFILE");
+ if (home != (char *) NULL)
+ {
+ (void) CopyMagickString(expand_path,home,MaxTextExtent);
+ (void) ConcatenateMagickString(expand_path,path+1,MaxTextExtent);
+ home=DestroyString(home);
+ }
+ }
+ else
+ {
+#if defined(MAGICKCORE_POSIX_SUPPORT) && !defined(__OS2__)
+ char
+ username[MaxTextExtent];
+
+ register char
+ *p;
+
+ struct passwd
+ *entry;
+
+ /*
+ Substitute ~ with home directory from password file.
+ */
+ (void) CopyMagickString(username,path+1,MaxTextExtent);
+ p=strchr(username,'/');
+ if (p != (char *) NULL)
+ *p='\0';
+ entry=getpwnam(username);
+ if (entry == (struct passwd *) NULL)
+ return;
+ (void) CopyMagickString(expand_path,entry->pw_dir,MaxTextExtent);
+ if (p != (char *) NULL)
+ {
+ (void) ConcatenateMagickString(expand_path,"/",MaxTextExtent);
+ (void) ConcatenateMagickString(expand_path,p+1,MaxTextExtent);
+ }
+#endif
+ }
+ (void) CopyMagickString(path,expand_path,MaxTextExtent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% E x p a n d F i l e n a m e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ExpandFilenames() checks each argument of the command line vector and
+% expands it if they have a wildcard character. For example, *.jpg might
+% expand to: bird.jpg rose.jpg tiki.jpg.
+%
+% The format of the ExpandFilenames function is:
+%
+% status=ExpandFilenames(int *number_arguments,char ***arguments)
+%
+% A description of each parameter follows:
+%
+% o number_arguments: Specifies a pointer to an integer describing the
+% number of elements in the argument vector.
+%
+% o arguments: Specifies a pointer to a text array containing the command
+% line arguments.
+%
+*/
+MagickExport MagickBooleanType ExpandFilenames(int *number_arguments,
+ char ***arguments)
+{
+ char
+ *cwd,
+ home_directory[MaxTextExtent],
+ **vector;
+
+ long
+ count,
+ parameters;
+
+ register long
+ i,
+ j;
+
+ unsigned long
+ number_files;
+
+ /*
+ Allocate argument vector.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(number_arguments != (int *) NULL);
+ assert(arguments != (char ***) NULL);
+ vector=(char **) AcquireQuantumMemory((size_t) (*number_arguments+1),
+ sizeof(*vector));
+ if (vector == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ /*
+ Expand any wildcard filenames.
+ */
+ *home_directory='\0';
+ cwd=getcwd(home_directory,MaxTextExtent);
+ count=0;
+ for (i=0; i < (long) *number_arguments; i++)
+ {
+ char
+ **filelist,
+ filename[MaxTextExtent],
+ magick[MaxTextExtent],
+ *option,
+ path[MaxTextExtent],
+ subimage[MaxTextExtent];
+
+ MagickBooleanType
+ destroy;
+
+ option=(*arguments)[i];
+ *magick='\0';
+ *path='\0';
+ *filename='\0';
+ *subimage='\0';
+ vector[count++]=ConstantString(option);
+ destroy=MagickTrue;
+ parameters=ParseMagickOption(MagickCommandOptions,MagickFalse,option);
+ if (parameters > 0)
+ {
+ /*
+ Do not expand command option parameters.
+ */
+ for (j=0; j < parameters; j++)
+ {
+ i++;
+ if (i == (long) *number_arguments)
+ break;
+ option=(*arguments)[i];
+ vector[count++]=ConstantString(option);
+ }
+ continue;
+ }
+ if ((*option == '"') || (*option == '\''))
+ continue;
+ GetPathComponent(option,TailPath,filename);
+ GetPathComponent(option,MagickPath,magick);
+ if ((LocaleCompare(magick,"CAPTION") == 0) ||
+ (LocaleCompare(magick,"LABEL") == 0) ||
+ (LocaleCompare(magick,"VID") == 0))
+ continue;
+ if ((IsGlob(filename) == MagickFalse) && (*filename != '@'))
+ continue;
+ if (*filename != '@')
+ {
+ /*
+ Generate file list from wildcard filename (e.g. *.jpg).
+ */
+ GetPathComponent(option,HeadPath,path);
+ GetPathComponent(option,SubimagePath,subimage);
+ ExpandFilename(path);
+ filelist=ListFiles(*path == '\0' ? home_directory : path,filename,
+ &number_files);
+ }
+ else
+ {
+ char
+ *files;
+
+ ExceptionInfo
+ *exception;
+
+ int
+ number_images;
+
+ /*
+ Generate file list from file list (e.g. @filelist.txt).
+ */
+ exception=AcquireExceptionInfo();
+ files=FileToString(filename+1,~0,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (files == (char *) NULL)
+ continue;
+ StripString(files);
+ filelist=StringToArgv(files,&number_images);
+ files=DestroyString(files);
+ number_files=(unsigned long) number_images;
+ if (filelist != (char **) NULL)
+ {
+ number_files--;
+ for (j=0; j < (long) number_files; j++)
+ filelist[j]=filelist[j+1];
+ }
+ }
+ if (filelist == (char **) NULL)
+ continue;
+ for (j=0; j < (long) number_files; j++)
+ if (IsPathDirectory(filelist[j]) <= 0)
+ break;
+ if (j == (long) number_files)
+ {
+ for (j=0; j < (long) number_files; j++)
+ filelist[j]=DestroyString(filelist[j]);
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ continue;
+ }
+ /*
+ Transfer file list to argument vector.
+ */
+ vector=(char **) ResizeQuantumMemory(vector,(size_t) *number_arguments+
+ count+number_files+1,sizeof(*vector));
+ if (vector == (char **) NULL)
+ return(MagickFalse);
+ for (j=0; j < (long) number_files; j++)
+ {
+ (void) CopyMagickString(filename,path,MaxTextExtent);
+ if (*path != '\0')
+ (void) ConcatenateMagickString(filename,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(filename,filelist[j],MaxTextExtent);
+ filelist[j]=DestroyString(filelist[j]);
+ if (strlen(filename) >= MaxTextExtent)
+ ThrowFatalException(OptionFatalError,"FilenameTruncated");
+ if (IsPathDirectory(filename) == 0)
+ {
+ char
+ path[MaxTextExtent];
+
+ *path='\0';
+ if (*magick != '\0')
+ {
+ (void) ConcatenateMagickString(path,magick,MaxTextExtent);
+ (void) ConcatenateMagickString(path,":",MaxTextExtent);
+ }
+ (void) ConcatenateMagickString(path,filename,MaxTextExtent);
+ if (*subimage != '\0')
+ {
+ (void) ConcatenateMagickString(path,"[",MaxTextExtent);
+ (void) ConcatenateMagickString(path,subimage,MaxTextExtent);
+ (void) ConcatenateMagickString(path,"]",MaxTextExtent);
+ }
+ if (strlen(path) >= MaxTextExtent)
+ ThrowFatalException(OptionFatalError,"FilenameTruncated");
+ if (destroy != MagickFalse)
+ {
+ count--;
+ vector[count]=DestroyString(vector[count]);
+ destroy=MagickFalse;
+ }
+ vector[count++]=ConstantString(path);
+ }
+ }
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ }
+ vector[count]=(char *) NULL;
+ if (IsEventLogging() != MagickFalse)
+ {
+ char
+ *command_line;
+
+ command_line=AcquireString(vector[0]);
+ for (i=1; i < count; i++)
+ {
+ (void) ConcatenateString(&command_line," {");
+ (void) ConcatenateString(&command_line,vector[i]);
+ (void) ConcatenateString(&command_line,"}");
+ }
+ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
+ "Command line: %s",command_line);
+ command_line=DestroyString(command_line);
+ }
+ *number_arguments=(int) count;
+ *arguments=vector;
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t E x e c u t i o n P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetExecutionPath() returns the pathname of the executable that started
+% the process. On success MagickTrue is returned, otherwise MagickFalse.
+%
+% The format of the GetExecutionPath method is:
+%
+% MagickBooleanType GetExecutionPath(char *path,const size_t extent)
+%
+% A description of each parameter follows:
+%
+% o path: the pathname of the executable that started the process.
+%
+% o extent: the maximum extent of the path.
+%
+*/
+MagickExport MagickBooleanType GetExecutionPath(char *path,const size_t extent)
+{
+ char
+ *cwd;
+
+ *path='\0';
+ cwd=getcwd(path,(unsigned long) extent);
+#if defined(MAGICKCORE_HAVE_GETPID) && defined(MAGICKCORE_HAVE_READLINK)
+ {
+ char
+ link_path[MaxTextExtent],
+ real_path[PATH_MAX+1];
+
+ ssize_t
+ count;
+
+ (void) FormatMagickString(link_path,MaxTextExtent,"/proc/%ld/exe",
+ (long) getpid());
+ count=readlink(link_path,real_path,PATH_MAX);
+ if (count == -1)
+ {
+ (void) FormatMagickString(link_path,MaxTextExtent,"/proc/%ld/file",
+ (long) getpid());
+ count=readlink(link_path,real_path,PATH_MAX);
+ }
+ if ((count > 0) && (count <= (ssize_t) PATH_MAX))
+ {
+ real_path[count]='\0';
+ (void) CopyMagickString(path,real_path,extent);
+ }
+ }
+#endif
+#if defined(MAGICKCORE_HAVE__NSGETEXECUTABLEPATH)
+ {
+ char
+ executable_path[PATH_MAX << 1],
+ real_path[PATH_MAX+1];
+
+ uint32_t
+ length;
+
+ length=sizeof(executable_path);
+ if ((_NSGetExecutablePath(executable_path,&length) == 0) &&
+ (realpath(executable_path,real_path) != (char *) NULL))
+ (void) CopyMagickString(path,real_path,extent);
+ }
+#endif
+#if defined(MAGICKCORE_HAVE_GETEXECNAME)
+ {
+ const char
+ *execution_path;
+
+ execution_path=(const char *) getexecname();
+ if (execution_path != (const char *) NULL)
+ {
+ if (*execution_path != *DirectorySeparator)
+ (void) ConcatenateMagickString(path,DirectorySeparator,extent);
+ (void) ConcatenateMagickString(path,execution_path,extent);
+ }
+ }
+#endif
+#if defined(__WINDOWS__)
+ NTGetExecutionPath(path,extent);
+#endif
+ return(IsPathAccessible(path));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P a t h A t t r i b u t e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPathAttributes() returns attributes (e.g. size of file) about a path.
+%
+% The path of the GetPathAttributes method is:
+%
+% MagickBooleanType GetPathAttributes(const char *path,void *attributes)
+%
+% A description of each parameter follows.
+%
+% o path: the file path.
+%
+% o attributes: the path attributes are returned here.
+%
+*/
+
+#if defined(MAGICKCORE_HAVE__WFOPEN)
+static size_t UTF8ToUTF16(const unsigned char *utf8,wchar_t *utf16)
+{
+ register const unsigned char
+ *p;
+
+ if (utf16 != (wchar_t *) NULL)
+ {
+ register wchar_t
+ *q;
+
+ wchar_t
+ c;
+
+ /*
+ Convert UTF-8 to UTF-16.
+ */
+ q=utf16;
+ for (p=utf8; *p != '\0'; p++)
+ {
+ if ((*p & 0x80) == 0)
+ *q=(*p);
+ else
+ if ((*p & 0xE0) == 0xC0)
+ {
+ c=(*p);
+ *q=(c & 0x1F) << 6;
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ *q|=(*p & 0x3F);
+ }
+ else
+ if ((*p & 0xF0) == 0xE0)
+ {
+ c=(*p);
+ *q=c << 12;
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ c=(*p);
+ *q|=(c & 0x3F) << 6;
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ *q|=(*p & 0x3F);
+ }
+ else
+ return(0);
+ q++;
+ }
+ *q++='\0';
+ return(q-utf16);
+ }
+ /*
+ Compute UTF-16 string length.
+ */
+ for (p=utf8; *p != '\0'; p++)
+ {
+ if ((*p & 0x80) == 0)
+ ;
+ else
+ if ((*p & 0xE0) == 0xC0)
+ {
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ }
+ else
+ if ((*p & 0xF0) == 0xE0)
+ {
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ p++;
+ if ((*p & 0xC0) != 0x80)
+ return(0);
+ }
+ else
+ return(0);
+ }
+ return(p-utf8);
+}
+
+static wchar_t *ConvertUTF8ToUTF16(const unsigned char *source)
+{
+ size_t
+ length;
+
+ wchar_t
+ *utf16;
+
+ length=UTF8ToUTF16(source,(wchar_t *) NULL);
+ if (length == 0)
+ {
+ register long
+ i;
+
+ /*
+ Not UTF-8, just copy.
+ */
+ length=strlen(source);
+ utf16=(wchar_t *) AcquireQuantumMemory(length+1,sizeof(*utf16));
+ if (utf16 == (wchar_t *) NULL)
+ return((wchar_t *) NULL);
+ for (i=0; i <= (long) length; i++)
+ utf16[i]=source[i];
+ return(utf16);
+ }
+ utf16=(wchar_t *) AcquireQuantumMemory(length+1,sizeof(*utf16));
+ if (utf16 == (wchar_t *) NULL)
+ return((wchar_t *) NULL);
+ length=UTF8ToUTF16(source,utf16);
+ return(utf16);
+}
+#endif
+
+MagickExport MagickBooleanType GetPathAttributes(const char *path,
+ void *attributes)
+{
+ MagickBooleanType
+ status;
+
+ if (path == (const char *) NULL)
+ {
+ errno=EINVAL;
+ return(MagickFalse);
+ }
+#if !defined(MAGICKCORE_HAVE__WSTAT)
+ status=stat(path,(struct stat *) attributes) == 0 ? MagickTrue : MagickFalse;
+#else
+ {
+ wchar_t
+ *unicode_path;
+
+ unicode_path=ConvertUTF8ToUTF16(path);
+ if (unicode_path == (wchar_t *) NULL)
+ return(MagickFalse);
+ status=wstat(unicode_path,(struct stat *) attributes) == 0 ? MagickTrue :
+ MagickFalse;
+ unicode_path=(wchar_t *) RelinquishMagickMemory(unicode_path);
+ }
+#endif
+ return(status);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P a t h C o m p o n e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPathComponent() returns the parent directory name, filename, basename, or
+% extension of a file path.
+%
+% The format of the GetPathComponent function is:
+%
+% GetPathComponent(const char *path,PathType type,char *component)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies a pointer to a character array that contains the
+% file path.
+%
+% o type: Specififies which file path component to return.
+%
+% o component: the selected file path component is returned here.
+%
+*/
+MagickExport void GetPathComponent(const char *path,PathType type,
+ char *component)
+{
+ char
+ magick[MaxTextExtent],
+ *q,
+ subimage[MaxTextExtent];
+
+ register char
+ *p;
+
+ assert(path != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",path);
+ assert(component != (char *) NULL);
+ if (*path == '\0')
+ {
+ *component='\0';
+ return;
+ }
+ (void) CopyMagickString(component,path,MaxTextExtent);
+ *magick='\0';
+#if defined(__OS2__)
+ if (path[1] != ":")
+#endif
+ for (p=component; *p != '\0'; p++)
+ if ((*p == ':') && (IsPathDirectory(path) < 0) &&
+ (IsPathAccessible(path) == MagickFalse))
+ {
+ /*
+ Look for image format specification (e.g. ps3:image).
+ */
+ (void) CopyMagickString(magick,component,(size_t) (p-component+1));
+ if (IsMagickConflict(magick) != MagickFalse)
+ *magick='\0';
+ else
+ for (q=component; *q != '\0'; q++)
+ *q=(*++p);
+ break;
+ }
+ *subimage='\0';
+ p=component;
+ if (*p != '\0')
+ p=component+strlen(component)-1;
+ if ((*p == ']') && (strchr(component,'[') != (char *) NULL) &&
+ (IsPathAccessible(path) == MagickFalse))
+ {
+ /*
+ Look for scene specification (e.g. img0001.pcd[4]).
+ */
+ for (q=p-1; q > component; q--)
+ if (*q == '[')
+ break;
+ if (*q == '[')
+ {
+ (void) CopyMagickString(subimage,q+1,MaxTextExtent);
+ subimage[p-q-1]='\0';
+ if ((IsSceneGeometry(subimage,MagickFalse) == MagickFalse) &&
+ (IsGeometry(subimage) == MagickFalse))
+ *subimage='\0';
+ else
+ *q='\0';
+ }
+ }
+ p=component;
+ if (*p != '\0')
+ for (p=component+strlen(component)-1; p > component; p--)
+ if (IsBasenameSeparator(*p) != MagickFalse)
+ break;
+ switch (type)
+ {
+ case MagickPath:
+ {
+ (void) CopyMagickString(component,magick,MaxTextExtent);
+ break;
+ }
+ case RootPath:
+ {
+ for (p=component+(strlen(component)-1); p > component; p--)
+ {
+ if (IsBasenameSeparator(*p) != MagickFalse)
+ break;
+ if (*p == '.')
+ break;
+ }
+ if (*p == '.')
+ *p='\0';
+ break;
+ }
+ case HeadPath:
+ {
+ *p='\0';
+ break;
+ }
+ case TailPath:
+ {
+ if (IsBasenameSeparator(*p) != MagickFalse)
+ (void) CopyMagickMemory((unsigned char *) component,
+ (const unsigned char *) (p+1),strlen(p+1)+1);
+ break;
+ }
+ case BasePath:
+ {
+ if (IsBasenameSeparator(*p) != MagickFalse)
+ (void) CopyMagickString(component,p+1,MaxTextExtent);
+ for (p=component+(strlen(component)-1); p > component; p--)
+ if (*p == '.')
+ {
+ *p='\0';
+ break;
+ }
+ break;
+ }
+ case ExtensionPath:
+ {
+ if (IsBasenameSeparator(*p) != MagickFalse)
+ (void) CopyMagickString(component,p+1,MaxTextExtent);
+ p=component;
+ if (*p != '\0')
+ for (p=component+strlen(component)-1; p > component; p--)
+ if (*p == '.')
+ break;
+ *component='\0';
+ if (*p == '.')
+ (void) CopyMagickString(component,p+1,MaxTextExtent);
+ break;
+ }
+ case SubimagePath:
+ {
+ (void) CopyMagickString(component,subimage,MaxTextExtent);
+ break;
+ }
+ case CanonicalPath:
+ case UndefinedPath:
+ break;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t P a t h C o m p o n e n t s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetPathComponents() returns a list of path components.
+%
+% The format of the GetPathComponents method is:
+%
+% char **GetPathComponents(const char *path,
+% unsigned long *number_componenets)
+%
+% A description of each parameter follows:
+%
+% o path: Specifies the string to segment into a list.
+%
+% o number_components: return the number of components in the list
+%
+*/
+MagickExport char **GetPathComponents(const char *path,
+ unsigned long *number_components)
+{
+ char
+ **components;
+
+ register const char
+ *p,
+ *q;
+
+ register long
+ i;
+
+ if (path == (char *) NULL)
+ return((char **) NULL);
+ *number_components=1;
+ for (p=path; *p != '\0'; p++)
+ if (IsBasenameSeparator(*p))
+ (*number_components)++;
+ components=(char **) AcquireQuantumMemory((size_t) *number_components+1UL,
+ sizeof(*components));
+ if (components == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ p=path;
+ for (i=0; i < (long) *number_components; i++)
+ {
+ for (q=p; *q != '\0'; q++)
+ if (IsBasenameSeparator(*q))
+ break;
+ components[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MaxTextExtent,
+ sizeof(*components));
+ if (components[i] == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) CopyMagickString(components[i],p,(size_t) (q-p+1));
+ p=q+1;
+ }
+ components[i]=(char *) NULL;
+ return(components);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s P a t h A c c e s s i b l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsPathAccessible() returns MagickTrue if the file as defined by the path is
+% accessible.
+%
+% The format of the IsPathAccessible method is:
+%
+% MagickBooleanType IsPathAccessible(const char *filename)
+%
+% A description of each parameter follows.
+%
+% o path: Specifies a path to a file.
+%
+*/
+MagickExport MagickBooleanType IsPathAccessible(const char *path)
+{
+ MagickBooleanType
+ status;
+
+ struct stat
+ attributes;
+
+ if ((path == (const char *) NULL) || (*path == '\0'))
+ return(MagickFalse);
+ status=GetPathAttributes(path,&attributes);
+ if (status == MagickFalse)
+ return(status);
+ if (S_ISREG(attributes.st_mode) == 0)
+ return(MagickFalse);
+ if (access(path,F_OK) != 0)
+ return(MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ I s P a t h D i r e c t o r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsPathDirectory() returns -1 if the directory does not exist, 1 is returned
+% if the path represents a directory otherwise 0.
+%
+% The format of the IsPathDirectory method is:
+%
+% int IsPathDirectory(const char *path)
+%
+% A description of each parameter follows.
+%
+% o path: The directory path.
+%
+*/
+static int IsPathDirectory(const char *path)
+{
+ MagickBooleanType
+ status;
+
+ struct stat
+ attributes;
+
+ if ((path == (const char *) NULL) || (*path == '\0'))
+ return(MagickFalse);
+ status=GetPathAttributes(path,&attributes);
+ if (status == MagickFalse)
+ return(-1);
+ if (S_ISDIR(attributes.st_mode) == 0)
+ return(0);
+ return(1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k T r u e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% IsMagickTrue() returns MagickTrue if the value is "true", "on", "yes" or
+% "1".
+%
+% The format of the IsMagickTrue method is:
+%
+% MagickBooleanType IsMagickTrue(const char *value)
+%
+% A description of each parameter follows:
+%
+% o option: either MagickTrue or MagickFalse depending on the value
+% parameter.
+%
+% o value: Specifies a pointer to a character array.
+%
+*/
+MagickExport MagickBooleanType IsMagickTrue(const char *value)
+{
+ if (value == (const char *) NULL)
+ return(MagickFalse);
+ if (LocaleCompare(value,"true") == 0)
+ return(MagickTrue);
+ if (LocaleCompare(value,"on") == 0)
+ return(MagickTrue);
+ if (LocaleCompare(value,"yes") == 0)
+ return(MagickTrue);
+ if (LocaleCompare(value,"1") == 0)
+ return(MagickTrue);
+ return(MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% L i s t F i l e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ListFiles() reads the directory specified and returns a list of filenames
+% contained in the directory sorted in ascending alphabetic order.
+%
+% The format of the ListFiles function is:
+%
+% char **ListFiles(const char *directory,const char *pattern,
+% long *number_entries)
+%
+% A description of each parameter follows:
+%
+% o filelist: Method ListFiles returns a list of filenames contained
+% in the directory. If the directory specified cannot be read or it is
+% a file a NULL list is returned.
+%
+% o directory: Specifies a pointer to a text string containing a directory
+% name.
+%
+% o pattern: Specifies a pointer to a text string containing a pattern.
+%
+% o number_entries: This integer returns the number of filenames in the
+% list.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int FileCompare(const void *x,const void *y)
+{
+ register const char
+ **p,
+ **q;
+
+ p=(const char **) x;
+ q=(const char **) y;
+ return(LocaleCompare(*p,*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static inline int MagickReadDirectory(DIR *directory,struct dirent *entry,
+ struct dirent **result)
+{
+#if defined(MAGICKCORE_HAVE_READDIR_R)
+ return(readdir_r(directory,entry,result));
+#else
+ (void) entry;
+ errno=0;
+ *result=readdir(directory);
+ return(errno);
+#endif
+}
+
+MagickExport char **ListFiles(const char *directory,const char *pattern,
+ unsigned long *number_entries)
+{
+ char
+ **filelist;
+
+ DIR
+ *current_directory;
+
+ struct dirent
+ *buffer,
+ *entry;
+
+ unsigned long
+ max_entries;
+
+ /*
+ Open directory.
+ */
+ assert(directory != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",directory);
+ assert(pattern != (const char *) NULL);
+ assert(number_entries != (unsigned long *) NULL);
+ *number_entries=0;
+ current_directory=opendir(directory);
+ if (current_directory == (DIR *) NULL)
+ return((char **) NULL);
+ /*
+ Allocate filelist.
+ */
+ max_entries=2048;
+ filelist=(char **) AcquireQuantumMemory((size_t) max_entries,
+ sizeof(*filelist));
+ if (filelist == (char **) NULL)
+ {
+ (void) closedir(current_directory);
+ return((char **) NULL);
+ }
+ /*
+ Save the current and change to the new directory.
+ */
+ buffer=(struct dirent *) AcquireMagickMemory(sizeof(*buffer)+FILENAME_MAX+1);
+ if (buffer == (struct dirent *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ while ((MagickReadDirectory(current_directory,buffer,&entry) == 0) &&
+ (entry != (struct dirent *) NULL))
+ {
+ if (*entry->d_name == '.')
+ continue;
+ if ((IsPathDirectory(entry->d_name) > 0) ||
+#if defined(__WINDOWS__)
+ (GlobExpression(entry->d_name,pattern,MagickTrue) != MagickFalse))
+#else
+ (GlobExpression(entry->d_name,pattern,MagickFalse) != MagickFalse))
+#endif
+ {
+ if (*number_entries >= max_entries)
+ {
+ /*
+ Extend the file list.
+ */
+ max_entries<<=1;
+ filelist=(char **) ResizeQuantumMemory(filelist,(size_t)
+ max_entries,sizeof(*filelist));
+ if (filelist == (char **) NULL)
+ break;
+ }
+#if defined(vms)
+ {
+ register char
+ *p;
+
+ p=strchr(entry->d_name,';');
+ if (p)
+ *p='\0';
+ if (*number_entries > 0)
+ if (LocaleCompare(entry->d_name,filelist[*number_entries-1]) == 0)
+ continue;
+ }
+#endif
+ filelist[*number_entries]=(char *) AcquireString(entry->d_name);
+ if (IsPathDirectory(entry->d_name) > 0)
+ (void) ConcatenateMagickString(filelist[*number_entries],
+ DirectorySeparator,MaxTextExtent);
+ (*number_entries)++;
+ }
+ }
+ buffer=(struct dirent *) RelinquishMagickMemory(buffer);
+ (void) closedir(current_directory);
+ if (filelist == (char **) NULL)
+ return((char **) NULL);
+ /*
+ Sort filelist in ascending order.
+ */
+ qsort((void *) filelist,(size_t) *number_entries,sizeof(*filelist),
+ FileCompare);
+ return(filelist);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% M u l t i l i n e C e n s u s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% MultilineCensus() returns the number of lines within a label. A line is
+% represented by a \n character.
+%
+% The format of the MultilineCenus method is:
+%
+% unsigned long MultilineCensus(const char *label)
+%
+% A description of each parameter follows.
+%
+% o label: This character string is the label.
+%
+%
+*/
+MagickExport unsigned long MultilineCensus(const char *label)
+{
+ unsigned long
+ number_lines;
+
+ /*
+ Determine the number of lines within this label.
+ */
+ if (label == (char *) NULL)
+ return(0);
+ for (number_lines=1; *label != '\0'; label++)
+ if (*label == '\n')
+ number_lines++;
+ return(number_lines);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% O p e n M a g i c k S t r e a m %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% OpenMagickStream() opens the file at the specified path and return the
+% associated stream.
+%
+% The path of the OpenMagickStream method is:
+%
+% FILE *OpenMagickStream(const char *path,const char *mode)
+%
+% A description of each parameter follows.
+%
+% o path: the file path.
+%
+% o mode: the file mode.
+%
+*/
+MagickExport FILE *OpenMagickStream(const char *path,const char *mode)
+{
+ FILE
+ *file;
+
+ if ((path == (const char *) NULL) || (mode == (const char *) NULL))
+ {
+ errno=EINVAL;
+ return((FILE *) NULL);
+ }
+ file=(FILE *) NULL;
+#if defined(MAGICKCORE_HAVE__WFOPEN)
+ {
+ wchar_t
+ *unicode_mode,
+ *unicode_path;
+
+ unicode_path=ConvertUTF8ToUTF16(path);
+ if (unicode_path == (wchar_t *) NULL)
+ return((FILE *) NULL);
+ unicode_mode=ConvertUTF8ToUTF16(mode);
+ if (unicode_mode == (wchar_t *) NULL)
+ {
+ unicode_path=(wchar_t *) RelinquishMagickMemory(unicode_path);
+ return((FILE *) NULL);
+ }
+ file=_wfopen(unicode_path,unicode_mode);
+ unicode_mode=(wchar_t *) RelinquishMagickMemory(unicode_mode);
+ unicode_path=(wchar_t *) RelinquishMagickMemory(unicode_path);
+ }
+#endif
+ if (file == (FILE *) NULL)
+ file=fopen(path,mode);
+ return(file);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S y s t e m C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SystemCommand() executes the specified command and waits until it
+% terminates. The returned value is the exit status of the command.
+%
+% The format of the SystemCommand method is:
+%
+% int SystemCommand(const MagickBooleanType verbose,const char *command)
+%
+% A description of each parameter follows:
+%
+% o verbose: A value other than 0 prints the executed command before it is
+% invoked.
+%
+% o command: This string is the command to execute.
+%
+*/
+MagickExport int SystemCommand(const MagickBooleanType verbose,
+ const char *command)
+{
+ int
+ status;
+
+ if (verbose != MagickFalse)
+ {
+ (void) fprintf(stderr,"%s\n",command);
+ (void) fflush(stderr);
+ }
+ status=(-1);
+#if defined(MAGICKCORE_POSIX_SUPPORT)
+#if !defined(MAGICKCORE_HAVE_EXECVP)
+ status=system(command);
+#else
+ if (strspn(command,"&;<>|") == 0)
+ status=system(command);
+ else
+ {
+ char
+ **arguments;
+
+ int
+ number_arguments;
+
+ arguments=StringToArgv(command,&number_arguments);
+ if (arguments == (char **) NULL)
+ status=system(command);
+ else
+ {
+ pid_t
+ child_pid;
+
+ register long
+ i;
+
+ /*
+ Call application directly rather than from a shell.
+ */
+ child_pid=fork();
+ if (child_pid == (pid_t) -1)
+ status=system(command);
+ else
+ if (child_pid == 0)
+ {
+ status=execvp(arguments[1],arguments+1);
+ _exit(1);
+ }
+ else
+ {
+ int
+ child_status;
+
+ pid_t
+ pid;
+
+ child_status=0;
+ pid=waitpid(child_pid,&child_status,0);
+ if (pid == -1)
+ status=(-1);
+ else
+ {
+ if (WIFEXITED(child_status) != 0)
+ status=WEXITSTATUS(child_status);
+ else
+ if (WIFSIGNALED(child_status))
+ status=(-1);
+ }
+ }
+ for (i=0; i < number_arguments; i++)
+ arguments[i]=DestroyString(arguments[i]);
+ arguments=(char **) RelinquishMagickMemory(arguments);
+ }
+ }
+#endif
+#elif defined(__WINDOWS__)
+ status=NTSystemCommand(command);
+#elif defined(macintosh)
+ status=MACSystemCommand(command);
+#elif defined(vms)
+ status=system(command);
+#else
+# error No suitable system() method.
+#endif
+ if (status < 0)
+ {
+ char
+ *message;
+
+ ExceptionInfo
+ *exception;
+
+ exception=AcquireExceptionInfo();
+ message=GetExceptionMessage(errno);
+ (void) ThrowMagickException(exception,GetMagickModule(),DelegateError,
+ "`%s': %s",command,message);
+ message=DestroyString(message);
+ CatchException(exception);
+ exception=DestroyExceptionInfo(exception);
+ }
+ return(status);
+}
diff --git a/magick/utility.h b/magick/utility.h
new file mode 100644
index 0000000..3830740
--- /dev/null
+++ b/magick/utility.h
@@ -0,0 +1,74 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore utility methods.
+*/
+#ifndef _MAGICKCORE_UTILITY_H
+#define _MAGICKCORE_UTILITY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef enum
+{
+ UndefinedPath,
+ MagickPath,
+ RootPath,
+ HeadPath,
+ TailPath,
+ BasePath,
+ ExtensionPath,
+ SubimagePath,
+ CanonicalPath
+} PathType;
+
+extern MagickExport char
+ *Base64Encode(const unsigned char *,const size_t,size_t *),
+ **GetPathComponents(const char *,unsigned long *),
+ **ListFiles(const char *,const char *,unsigned long *);
+
+extern MagickExport FILE
+ *OpenMagickStream(const char *,const char *);
+
+extern MagickExport int
+ SystemCommand(const MagickBooleanType,const char *);
+
+extern MagickExport MagickBooleanType
+ AcquireUniqueFilename(char *),
+ AcquireUniqueSymbolicLink(const char *,char *),
+ ExpandFilenames(int *,char ***),
+ GetPathAttributes(const char *,void *),
+ GetExecutionPath(char *,const size_t),
+ IsMagickTrue(const char *),
+ IsPathAccessible(const char *);
+
+extern MagickExport unsigned char
+ *Base64Decode(const char *, size_t *);
+
+extern MagickExport unsigned long
+ MultilineCensus(const char *);
+
+extern MagickExport void
+ AppendImageFormat(const char *,char *),
+ ChopPathComponents(char *,const unsigned long),
+ ExpandFilename(char *),
+ GetPathComponent(const char *,PathType,char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/version.c b/magick/version.c
new file mode 100644
index 0000000..7dbeb31
--- /dev/null
+++ b/magick/version.c
@@ -0,0 +1,262 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% V V EEEEE RRRR SSSSS IIIII OOO N N %
+% V V E R R SS I O O NN N %
+% V V EEE RRRR SSS I O O N N N %
+% V V E R R SS I O O N NN %
+% V EEEEE R R SSSSS IIIII OOO N N %
+% %
+% %
+% MagickCore Version and Copyright Methods %
+% %
+% Software Design %
+% John Cristy %
+% September 2002 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+#include "magick/studio.h"
+#include "magick/configure.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/hashmap.h"
+#include "magick/option.h"
+#include "magick/string_.h"
+#include "magick/utility.h"
+#include "magick/version.h"
+
+/*
+ Define declarations.
+*/
+#define MagickURLFilename "index.html"
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k C o p y r i g h t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickCopyright() returns the ImageMagick API copyright as a string.
+%
+% The format of the GetMagickCopyright method is:
+%
+% const char *GetMagickCopyright(void)
+%
+*/
+MagickExport const char *GetMagickCopyright(void)
+{
+ return(MagickCopyright);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k H o m e U R L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickHomeURL() returns the ImageMagick home URL.
+%
+% The format of the GetMagickHomeURL method is:
+%
+% char *GetMagickHomeURL(void)
+%
+*/
+MagickExport char *GetMagickHomeURL(void)
+{
+ char
+ path[MaxTextExtent];
+
+ const char
+ *element;
+
+ ExceptionInfo
+ *exception;
+
+ LinkedListInfo
+ *paths;
+
+ exception=AcquireExceptionInfo();
+ paths=GetConfigurePaths(MagickURLFilename,exception);
+ exception=DestroyExceptionInfo(exception);
+ if (paths == (LinkedListInfo *) NULL)
+ return(ConstantString(MagickHomeURL));
+ element=(const char *) GetNextValueInLinkedList(paths);
+ while (element != (const char *) NULL)
+ {
+ (void) FormatMagickString(path,MaxTextExtent,"%s%s%s",element,
+ DirectorySeparator,MagickURLFilename);
+ if (IsPathAccessible(path) != MagickFalse)
+ return(ConstantString(path));
+ element=(const char *) GetNextValueInLinkedList(paths);
+ }
+ return(ConstantString(MagickHomeURL));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k P a c k a g e N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickPackageName() returns the ImageMagick package name.
+%
+% The format of the GetMagickName method is:
+%
+% const char *GetMagickName(void)
+%
+% No parameters are required.
+%
+*/
+MagickExport const char *GetMagickPackageName(void)
+{
+ return(MagickPackageName);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k Q u a n t u m D e p t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickQuantumDepth() returns the ImageMagick quantum depth.
+%
+% The format of the GetMagickQuantumDepth method is:
+%
+% const char *GetMagickQuantumDepth(unsigned long *depth)
+%
+% A description of each parameter follows:
+%
+% o depth: the quantum depth is returned as a number.
+%
+*/
+MagickExport const char *GetMagickQuantumDepth(unsigned long *depth)
+{
+ if (depth != (unsigned long *) NULL)
+ *depth=(unsigned long) MAGICKCORE_QUANTUM_DEPTH;
+ return(MagickQuantumDepth);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k Q u a n t u m R a n g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickQuantumRange() returns the ImageMagick quantum range.
+%
+% The format of the GetMagickQuantumRange method is:
+%
+% const char *GetMagickQuantumRange(unsigned long *range)
+%
+% A description of each parameter follows:
+%
+% o range: the quantum range is returned as a number.
+%
+*/
+MagickExport const char *GetMagickQuantumRange(unsigned long *range)
+{
+ if (range != (unsigned long *) NULL)
+ *range=(unsigned long) QuantumRange;
+ return(MagickQuantumRange);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k R e l e a s e D a t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickReleaseDate() returns the ImageMagick release date.
+%
+% The format of the GetMagickReleaseDate method is:
+%
+% const char *GetMagickReleaseDate(void)
+%
+% No parameters are required.
+%
+*/
+MagickExport const char *GetMagickReleaseDate(void)
+{
+ return(MagickReleaseDate);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t M a g i c k V e r s i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetMagickVersion() returns the ImageMagick API version as a string and
+% as a number.
+%
+% The format of the GetMagickVersion method is:
+%
+% const char *GetMagickVersion(unsigned long *version)
+%
+% A description of each parameter follows:
+%
+% o version: the ImageMagick version is returned as a number.
+%
+*/
+MagickExport const char *GetMagickVersion(unsigned long *version)
+{
+ if (version != (unsigned long *) NULL)
+ *version=MagickLibVersion;
+ return(MagickVersion);
+}
diff --git a/magick/version.h b/magick/version.h
new file mode 100644
index 0000000..ee69c7a
--- /dev/null
+++ b/magick/version.h
@@ -0,0 +1,84 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore version methods.
+*/
+#ifndef _MAGICKCORE_VERSION_H
+#define _MAGICKCORE_VERSION_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ Define declarations.
+*/
+#define MagickPackageName "ImageMagick"
+#define MagickCopyright "Copyright (C) 1999-2009 ImageMagick Studio LLC"
+#define MagickLibVersion 0x655
+#define MagickLibVersionText "6.5.5"
+#define MagickLibVersionNumber 2,0,0
+#define MagickLibSubversion "-7"
+#define MagickReleaseDate "2009-09-01"
+#define MagickChangeDate "20090831"
+#define MagickAuthoritativeURL "http://www.imagemagick.org"
+#define MagickHomeURL "file:///usr/local/share/doc/ImageMagick-6.5.5/index.html"
+#if (MAGICKCORE_QUANTUM_DEPTH == 8)
+#define MagickQuantumDepth "Q8"
+#define MagickQuantumRange "255"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
+#define MagickQuantumDepth "Q16"
+#define MagickQuantumRange "65535"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
+#define MagickQuantumDepth "Q32"
+#define MagickQuantumRange "4294967295"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 64)
+#define MagickQuantumDepth "Q64"
+#define MagickQuantumRange "18446744073709551615"
+#else
+#define MagickQuantumDepth "Q?"
+#define MagickQuantumRange "?"
+#endif
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+#define MagickHDRISupport ""
+#else
+#define MagickHDRISupport "HDRI "
+#endif
+#if !defined(_OPENMP)
+#define MagickOPENMPSupport ""
+#else
+#define MagickOPENMPSupport "OpenMP "
+#endif
+#define MagickSupport MagickHDRISupport MagickOPENMPSupport
+#define MagickVersion MagickPackageName " " MagickLibVersionText \
+ MagickLibSubversion " " MagickReleaseDate " " MagickQuantumDepth " " \
+ MagickSupport MagickAuthoritativeURL
+
+extern MagickExport char
+ *GetMagickHomeURL(void);
+
+extern MagickExport const char
+ *GetMagickCopyright(void),
+ *GetMagickPackageName(void),
+ *GetMagickQuantumDepth(unsigned long *),
+ *GetMagickQuantumRange(unsigned long *),
+ *GetMagickReleaseDate(void),
+ *GetMagickVersion(unsigned long *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/version.h.in b/magick/version.h.in
new file mode 100644
index 0000000..682bb02
--- /dev/null
+++ b/magick/version.h.in
@@ -0,0 +1,84 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore version methods.
+*/
+#ifndef _MAGICKCORE_VERSION_H
+#define _MAGICKCORE_VERSION_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ Define declarations.
+*/
+#define MagickPackageName "@PACKAGE_NAME@"
+#define MagickCopyright "Copyright (C) 1999-2009 ImageMagick Studio LLC"
+#define MagickLibVersion @PACKAGE_LIB_VERSION@
+#define MagickLibVersionText "@MAGICK_LIB_VERSION_TEXT@"
+#define MagickLibVersionNumber @MAGICK_LIB_VERSION_NUMBER@
+#define MagickLibSubversion "@PACKAGE_LIB_SUBVERSION@"
+#define MagickReleaseDate "@PACKAGE_RELEASE_DATE@"
+#define MagickChangeDate "@PACKAGE_CHANGE_DATE@"
+#define MagickAuthoritativeURL "http://www.imagemagick.org"
+#define MagickHomeURL "file://@DOCUMENTATION_PATH@/index.html"
+#if (MAGICKCORE_QUANTUM_DEPTH == 8)
+#define MagickQuantumDepth "Q8"
+#define MagickQuantumRange "255"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 16)
+#define MagickQuantumDepth "Q16"
+#define MagickQuantumRange "65535"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 32)
+#define MagickQuantumDepth "Q32"
+#define MagickQuantumRange "4294967295"
+#elif (MAGICKCORE_QUANTUM_DEPTH == 64)
+#define MagickQuantumDepth "Q64"
+#define MagickQuantumRange "18446744073709551615"
+#else
+#define MagickQuantumDepth "Q?"
+#define MagickQuantumRange "?"
+#endif
+#if !defined(MAGICKCORE_HDRI_SUPPORT)
+#define MagickHDRISupport ""
+#else
+#define MagickHDRISupport "HDRI "
+#endif
+#if !defined(_OPENMP)
+#define MagickOPENMPSupport ""
+#else
+#define MagickOPENMPSupport "OpenMP "
+#endif
+#define MagickSupport MagickHDRISupport MagickOPENMPSupport
+#define MagickVersion MagickPackageName " " MagickLibVersionText \
+ MagickLibSubversion " " MagickReleaseDate " " MagickQuantumDepth " " \
+ MagickSupport MagickAuthoritativeURL
+
+extern MagickExport char
+ *GetMagickHomeURL(void);
+
+extern MagickExport const char
+ *GetMagickCopyright(void),
+ *GetMagickPackageName(void),
+ *GetMagickQuantumDepth(unsigned long *),
+ *GetMagickQuantumRange(unsigned long *),
+ *GetMagickReleaseDate(void),
+ *GetMagickVersion(unsigned long *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/vms.c b/magick/vms.c
new file mode 100644
index 0000000..299db90
--- /dev/null
+++ b/magick/vms.c
@@ -0,0 +1,271 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% V V M M SSSSS %
+% V V MM MM SS %
+% V V M M M SSS %
+% V V M M SS %
+% V M M SSSSS %
+% %
+% %
+% MagickCore VMS Utility Methods %
+% %
+% Software Design %
+% John Cristy %
+% October 1994 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% The directory methods are strongly based on similar methods written
+% by Rich Salz.
+%
+*/
+
+#if defined(vms)
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/string_.h"
+#include "magick/memory_.h"
+#include "magick/vms.h"
+
+#if !defined(_AXP_) && (!defined(__VMS_VER) || (__VMS_VER < 70000000))
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% c l o s e d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% closedir() closes the named directory stream and frees the DIR structure.
+%
+% The format of the closedir method is:
+%
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+void closedir(DIR *directory)
+{
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(directory != (DIR *) NULL);
+ directory->pattern=DestroyString(directory->pattern);
+ directory=DestroyString(directory);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% o p e n d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% opendir() opens the directory named by filename and associates a directory
+% stream with it.
+%
+% The format of the opendir method is:
+%
+% opendir(entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+DIR *opendir(char *name)
+{
+ DIR
+ *directory;
+
+ /*
+ Allocate memory for handle and the pattern.
+ */
+ directory=(DIR *) AcquireMagickMemory(sizeof(DIR));
+ if (directory == (DIR *) NULL)
+ {
+ errno=ENOMEM;
+ return((DIR *) NULL);
+ }
+ if (strcmp(".",name) == 0)
+ name="";
+ directory->pattern=(char *) AcquireQuantumMemory(strlen(name)+sizeof("*.*")+
+ 1UL,sizeof(*directory->pattern));
+ if (directory->pattern == (char *) NULL)
+ {
+ directory=DestroyString(directory);
+ errno=ENOMEM;
+ return(NULL);
+ }
+ /*
+ Initialize descriptor.
+ */
+ (void) FormatMagickString(directory->pattern,MaxTextExtent,"%s*.*",name);
+ directory->context=0;
+ directory->pat.dsc$a_pointer=directory->pattern;
+ directory->pat.dsc$w_length=strlen(directory->pattern);
+ directory->pat.dsc$b_dtype=DSC$K_DTYPE_T;
+ directory->pat.dsc$b_class=DSC$K_CLASS_S;
+ return(directory);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% r e a d d i r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% readdir() returns a pointer to a structure representing the directory entry
+% at the current position in the directory stream to which entry refers.
+%
+% The format of the readdir
+%
+% readdir(entry)
+%
+% A description of each parameter follows:
+%
+% o entry: Specifies a pointer to a DIR structure.
+%
+%
+*/
+struct dirent *readdir(DIR *directory)
+{
+ char
+ buffer[sizeof(directory->entry.d_name)];
+
+ int
+ status;
+
+ register char
+ *p;
+
+ register int
+ i;
+
+ struct dsc$descriptor_s
+ result;
+
+ /*
+ Initialize the result descriptor.
+ */
+ result.dsc$a_pointer=buffer;
+ result.dsc$w_length=sizeof(buffer)-2;
+ result.dsc$b_dtype=DSC$K_DTYPE_T;
+ result.dsc$b_class=DSC$K_CLASS_S;
+ status=lib$find_file(&directory->pat,&result,&directory->context);
+ if ((status == RMS$_NMF) || (directory->context == 0L))
+ return((struct dirent *) NULL);
+ /*
+ Lowercase all filenames.
+ */
+ buffer[sizeof(buffer)-1]='\0';
+ for (p=buffer; *p; p++)
+ if (isupper((unsigned char) *p))
+ *p=tolower(*p);
+ /*
+ Skip any directory component and just copy the name.
+ */
+ p=buffer;
+ while (isspace((unsigned char) *p) == 0)
+ p++;
+ *p='\0';
+ p=strchr(buffer,']');
+ if (p)
+ (void) CopyMagickString(directory->entry.d_name,p+1,MaxTextExtent);
+ else
+ (void) CopyMagickString(directory->entry.d_name,buffer,MaxTextExtent);
+ directory->entry.d_namlen=strlen(directory->entry.d_name);
+ return(&directory->entry);
+}
+#endif /* !defined(_AXP_) && (!defined(__VMS_VER) || (__VMS_VER < 70000000)) */
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I s M a g i c k C o n f l i c t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% VMSIsMagickConflict() returns true if the image format conflicts with a
+% logical drive (.e.g. SYS$SCRATCH:).
+%
+% Contributed by Forrest Cahoon ([email protected])
+%
+% The format of the VMSIsMagickConflict method is:
+%
+% MagickBooleanType VMSIsMagickConflict(const char *magick)
+%
+% A description of each parameter follows:
+%
+% o magick: Specifies the image format.
+%
+%
+*/
+MagickExport MagickBooleanType VMSIsMagickConflict(const char *magick)
+{
+ ile3
+ item_list[2];
+
+ int
+ device_class,
+ status;
+
+ struct dsc$descriptor_s
+ device;
+
+ assert(magick != (char *) NULL);
+ device.dsc$w_length=strlen(magick);
+ device.dsc$a_pointer=(char *) magick;
+ device.dsc$b_class=DSC$K_CLASS_S;
+ device.dsc$b_dtype=DSC$K_DTYPE_T;
+ item_list[0].ile3$w_length=sizeof(device_class);
+ item_list[0].ile3$w_code=DVI$_DEVCLASS;
+ item_list[0].ile3$ps_bufaddr=&device_class;
+ item_list[0].ile3$ps_retlen_addr=NULL;
+ (void) ResetMagickMemory(&item_list[1],0,sizeof(item_list[1]));
+ status=sys$getdviw(0,0,&device,&item_list,0,0,0,0);
+ if ((status == SS$_NONLOCAL) ||
+ ((status & 0x01) && (device_class & (DC$_DISK | DC$_TAPE))))
+ return(MagickTrue);
+ return(MagickFalse);
+}
+#endif /* defined(vms) */
diff --git a/magick/vms.h b/magick/vms.h
new file mode 100644
index 0000000..4611a9d
--- /dev/null
+++ b/magick/vms.h
@@ -0,0 +1,985 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore VMS compatibility methods.
+*/
+#ifndef _MAGICKCORE_VMS_H
+#define _MAGICKCORE_VMS_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(__DECC) || defined(__DECCXX)
+# include <lib$routines.h>
+# include <starlet.h>
+#endif
+
+#include <errno.h>
+#include <descrip.h>
+#include <rmsdef.h>
+#include <ctype.h>
+#include <dvidef.h>
+#include <dcdef.h>
+#include <ssdef.h>
+
+#define DtSaverGetWindows DTSAVERGETWINDOWS
+#define XAddHosts XADDHOSTS
+#define XAllocClassHint XALLOCCLASSHINT
+#define XAllocColor XALLOCCOLOR
+#define XAllocColorCells XALLOCCOLORCELLS
+#define XAllocIconSize XALLOCICONSIZE
+#define XAllocNamedColor XALLOCNAMEDCOLOR
+#define XAllocSizeHints XALLOCSIZEHINTS
+#define XAllocStandardColormap XALLOCSTANDARDCOLORMAP
+#define XAllocWMHints XALLOCWMHINTS
+#define XAllowEvents XALLOWEVENTS
+#define XAutoRepeatOff XAUTOREPEATOFF
+#define XAutoRepeatOn XAUTOREPEATON
+#define XBaseFontNameListOfFontSet XBASEFONTNAMELISTOFFONTSET
+#define XBell XBELL
+#define XBitmapPad XBITMAPPAD
+#define XBlackPixel XBLACKPIXEL
+#define XBlackPixelOfScreen XBLACKPIXELOFSCREEN
+#define XChangeActivePointerGrab XCHANGEACTIVEPOINTERGRAB
+#define XChangeGC XCHANGEGC
+#define XChangeKeyboardControl XCHANGEKEYBOARDCONTROL
+#define XChangeProperty XCHANGEPROPERTY
+#define XChangeWindowAttributes XCHANGEWINDOWATTRIBUTES
+#define XCheckIfEvent XCHECKIFEVENT
+#define XCheckMaskEvent XCHECKMASKEVENT
+#define XCheckTypedWindowEvent XCHECKTYPEDWINDOWEVENT
+#define XCheckWindowEvent XCHECKWINDOWEVENT
+#define XClearArea XCLEARAREA
+#define XClearWindow XCLEARWINDOW
+#define XClipBox XCLIPBOX
+#define XCloseDisplay XCLOSEDISPLAY
+#define XCloseIM XCLOSEIM
+#define XConfigureWindow XCONFIGUREWINDOW
+#define XConvertSelection XCONVERTSELECTION
+#define XCopyArea XCOPYAREA
+#define XCopyColormapAndFree XCOPYCOLORMAPANDFREE
+#define XCopyGC XCOPYGC
+#define XCopyPlane XCOPYPLANE
+#define XCreateBitmapFromData XCREATEBITMAPFROMDATA
+#define XCreateColormap XCREATECOLORMAP
+#define XCreateFontCursor XCREATEFONTCURSOR
+#define XCreateFontSet XCREATEFONTSET
+#define XCreateGC XCREATEGC
+#define XCreateIC XCREATEIC
+#define XCreateImage XCREATEIMAGE
+#define XCreatePixmap XCREATEPIXMAP
+#define XCreatePixmapCursor XCREATEPIXMAPCURSOR
+#define XCreatePixmapFromBitmapData XCREATEPIXMAPFROMBITMAPDATA
+#define XCreateRegion XCREATEREGION
+#define XCreateSimpleWindow XCREATESIMPLEWINDOW
+#define XCreateWindow XCREATEWINDOW
+#define XDefaultColormap XDEFAULTCOLORMAP
+#define XDefaultColormapOfScreen XDEFAULTCOLORMAPOFSCREEN
+#define XDefaultDepth XDEFAULTDEPTH
+#define XDefaultGC XDEFAULTGC
+#define XDefaultRootWindow XDEFAULTROOTWINDOW
+#define XDefaultScreen XDEFAULTSCREEN
+#define XDefaultScreenOfDisplay XDEFAULTSCREENOFDISPLAY
+#define XDefaultVisual XDEFAULTVISUAL
+#define XDefineCursor XDEFINECURSOR
+#define XDeleteProperty XDELETEPROPERTY
+#define XDestroyIC XDESTROYIC
+#define XDestroyRegion XDESTROYREGION
+#define XDestroySubwindows XDESTROYSUBWINDOWS
+#define XDestroyWindow XDESTROYWINDOW
+#define XDisableAccessControl XDISABLEACCESSCONTROL
+#define XDisplayCells XDISPLAYCELLS
+#define XDisplayHeight XDISPLAYHEIGHT
+#define XDisplayName XDISPLAYNAME
+#define XDisplayOfScreen XDISPLAYOFSCREEN
+#define XDisplayWidth XDISPLAYWIDTH
+#define XDrawArc XDRAWARC
+#define XDrawArcs XDRAWARCS
+#define XDrawImageString XDRAWIMAGESTRING
+#define XDrawImageString16 XDRAWIMAGESTRING16
+#define XDrawLine XDRAWLINE
+#define XDrawLines XDRAWLINES
+#define XDrawPoint XDRAWPOINT
+#define XDrawPoints XDRAWPOINTS
+#define XDrawRectangle XDRAWRECTANGLE
+#define XDrawRectangles XDRAWRECTANGLES
+#define XDrawSegments XDRAWSEGMENTS
+#define XDrawString XDRAWSTRING
+#define XDrawString16 XDRAWSTRING16
+#define XDrawText XDRAWTEXT
+#define XEmptyRegion XEMPTYREGION
+#define XEnableAccessControl XENABLEACCESSCONTROL
+#define XEqualRegion XEQUALREGION
+#define XEventsQueued XEVENTSQUEUED
+#define XExtentsOfFontSet XEXTENTSOFFONTSET
+#define XFetchName XFETCHNAME
+#define XFillArc XFILLARC
+#define XFillArcs XFILLARCS
+#define XFillPolygon XFILLPOLYGON
+#define XFillRectangle XFILLRECTANGLE
+#define XFillRectangles XFILLRECTANGLES
+#define XFilterEvent XFILTEREVENT
+#define XFlush XFLUSH
+#define XFontsOfFontSet XFONTSOFFONTSET
+#define XForceScreenSaver XFORCESCREENSAVER
+#define XFree XFREE
+#define XFreeColormap XFREECOLORMAP
+#define XFreeColors XFREECOLORS
+#define XFreeCursor XFREECURSOR
+#define XFreeDeviceList XFREEDEVICELIST
+#define XFreeDeviceState XFREEDEVICESTATE
+#define XFreeFont XFREEFONT
+#define XFreeFontInfo XFREEFONTINFO
+#define XFreeFontNames XFREEFONTNAMES
+#define XFreeFontSet XFREEFONTSET
+#define XFreeGC XFREEGC
+#define XFreeModifiermap XFREEMODIFIERMAP
+#define XFreePixmap XFREEPIXMAP
+#define XFreeStringList XFREESTRINGLIST
+#define XGetAtomName XGETATOMNAME
+#define XGetCommand XGETCOMMAND
+#define XGetDefault XGETDEFAULT
+#define XGetErrorDatabaseText XGETERRORDATABASETEXT
+#define XGetErrorText XGETERRORTEXT
+#define XGetExtensionVersion XGETEXTENSIONVERSION
+#define XGetFontProperty XGETFONTPROPERTY
+#define XGetGCValues XGETGCVALUES
+#define XGetGeometry XGETGEOMETRY
+#define XGetICValues XGETICVALUES
+#define XGetIMValues XGETIMVALUES
+#define XGetIconName XGETICONNAME
+#define XGetIconSizes XGETICONSIZES
+#define XGetImage XGETIMAGE
+#define XGetKeyboardControl XGETKEYBOARDCONTROL
+#define XGetModifierMapping XGETMODIFIERMAPPING
+#define XGetMotionEvents XGETMOTIONEVENTS
+#define XGetNormalHints XGETNORMALHINTS
+#define XGetRGBColormaps XGETRGBCOLORMAPS
+#define XGetScreenSaver XGETSCREENSAVER
+#define XGetSelectionOwner XGETSELECTIONOWNER
+#define XGetSubImage XGETSUBIMAGE
+#define XGetVisualInfo XGETVISUALINFO
+#define XGetWMColormapWindows XGETWMCOLORMAPWINDOWS
+#define XGetWMHints XGETWMHINTS
+#define XGetWMName XGETWMNAME
+#define XGetWMNormalHints XGETWMNORMALHINTS
+#define XGetWindowAttributes XGETWINDOWATTRIBUTES
+#define XGetWindowProperty XGETWINDOWPROPERTY
+#define XGrabButton XGRABBUTTON
+#define XGrabKeyboard XGRABKEYBOARD
+#define XGrabPointer XGRABPOINTER
+#define XGrabServer XGRABSERVER
+#define XHeightOfScreen XHEIGHTOFSCREEN
+#define XIconifyWindow XICONIFYWINDOW
+#define XIfEvent XIFEVENT
+#define XInstallColormap XINSTALLCOLORMAP
+#define XInternAtom XINTERNATOM
+#define XIntersectRegion XINTERSECTREGION
+#define XKeycodeToKeysym XKEYCODETOKEYSYM
+#define XKeysymToKeycode XKEYSYMTOKEYCODE
+#define XKeysymToString XKEYSYMTOSTRING
+#define XKillClient XKILLCLIENT
+#define XListFonts XLISTFONTS
+#define XListFontsWithInfo XLISTFONTSWITHINFO
+#define XListHosts XLISTHOSTS
+#define XListInputDevices XLISTINPUTDEVICES
+#define XListInstalledColormaps XLISTINSTALLEDCOLORMAPS
+#define XListPixmapFormats XLISTPIXMAPFORMATS
+#define XListProperties XLISTPROPERTIES
+#define XLoadFont XLOADFONT
+#define XLoadQueryFont XLOADQUERYFONT
+#define XLookupColor XLOOKUPCOLOR
+#define XLookupKeysym XLOOKUPKEYSYM
+#define XLookupString XLOOKUPSTRING
+#define XLowerWindow XLOWERWINDOW
+#define XMapRaised XMAPRAISED
+#define XMapSubwindows XMAPSUBWINDOWS
+#define XMapWindow XMAPWINDOW
+#define XMatchVisualInfo XMATCHVISUALINFO
+#define XMaxRequestSize XMAXREQUESTSIZE
+#define XMoveResizeWindow XMOVERESIZEWINDOW
+#define XMoveWindow XMOVEWINDOW
+#define XNextEvent XNEXTEVENT
+#define XOffsetRegion XOFFSETREGION
+#define XOpenDevice XOPENDEVICE
+#define XOpenDisplay XOPENDISPLAY
+#define XOpenIM XOPENIM
+#define XParseColor XPARSECOLOR
+#define XParseGeometry XPARSEGEOMETRY
+#define XPeekEvent XPEEKEVENT
+#define XPeekIfEvent XPEEKIFEVENT
+#define XPending XPENDING
+#define XPointInRegion XPOINTINREGION
+#define XPolygonRegion XPOLYGONREGION
+#define XPutBackEvent XPUTBACKEVENT
+#define XPutImage XPUTIMAGE
+#define XQueryColor XQUERYCOLOR
+#define XQueryColors XQUERYCOLORS
+#define XQueryDeviceState XQUERYDEVICESTATE
+#define XQueryExtension XQUERYEXTENSION
+#define XQueryFont XQUERYFONT
+#define XQueryKeymap XQUERYKEYMAP
+#define XQueryPointer XQUERYPOINTER
+#define XQueryTree XQUERYTREE
+#define XRaiseWindow XRAISEWINDOW
+#define XReadBitmapFile XREADBITMAPFILE
+#define XRecolorCursor XRECOLORCURSOR
+#define XReconfigureWMWindow XRECONFIGUREWMWINDOW
+#define XRectInRegion XRECTINREGION
+#define XRefreshKeyboardMapping XREFRESHKEYBOARDMAPPING
+#define XRemoveHosts XREMOVEHOSTS
+#define XReparentWindow XREPARENTWINDOW
+#define XResetScreenSaver XRESETSCREENSAVER
+#define XResizeWindow XRESIZEWINDOW
+#define XResourceManagerString XRESOURCEMANAGERSTRING
+#define XRestackWindows XRESTACKWINDOWS
+#define XRootWindow XROOTWINDOW
+#define XRootWindowOfScreen XROOTWINDOWOFSCREEN
+#define XScreenNumberOfScreen XSCREENNUMBEROFSCREEN
+#define XScreenOfDisplay XSCREENOFDISPLAY
+#define XSelectAsyncEvent XSELECTASYNCEVENT
+#define XSelectAsyncInput XSELECTASYNCINPUT
+#define XSelectExtensionEvent XSELECTEXTENSIONEVENT
+#define XSelectInput XSELECTINPUT
+#define XSendEvent XSENDEVENT
+#define XServerVendor XSERVERVENDOR
+#define XSetBackground XSETBACKGROUND
+#define XSetClassHint XSETCLASSHINT
+#define XSetClipMask XSETCLIPMASK
+#define XSetClipOrigin XSETCLIPORIGIN
+#define XSetClipRectangles XSETCLIPRECTANGLES
+#define XSetCloseDownMode XSETCLOSEDOWNMODE
+#define XSetCommand XSETCOMMAND
+#define XSetDashes XSETDASHES
+#define XSetErrorHandler XSETERRORHANDLER
+#define XSetFillRule XSETFILLRULE
+#define XSetFillStyle XSETFILLSTYLE
+#define XSetFont XSETFONT
+#define XSetForeground XSETFOREGROUND
+#define XSetFunction XSETFUNCTION
+#define XSetGraphicsExposures XSETGRAPHICSEXPOSURES
+#define XSetICFocus XSETICFOCUS
+#define XSetICValues XSETICVALUES
+#define XSetIOErrorHandler XSETIOERRORHANDLER
+#define XSetIconName XSETICONNAME
+#define XSetInputFocus XSETINPUTFOCUS
+#define XSetLineAttributes XSETLINEATTRIBUTES
+#define XSetLocaleModifiers XSETLOCALEMODIFIERS
+#define XSetNormalHints XSETNORMALHINTS
+#define XSetPlaneMask XSETPLANEMASK
+#define XSetRegion XSETREGION
+#define XSetScreenSaver XSETSCREENSAVER
+#define XSetSelectionOwner XSETSELECTIONOWNER
+#define XSetStandardProperties XSETSTANDARDPROPERTIES
+#define XSetState XSETSTATE
+#define XSetStipple XSETSTIPPLE
+#define XSetSubwindowMode XSETSUBWINDOWMODE
+#define XSetTSOrigin XSETTSORIGIN
+#define XSetTile XSETTILE
+#define XSetTransientForHint XSETTRANSIENTFORHINT
+#define XSetWMColormapWindows XSETWMCOLORMAPWINDOWS
+#define XSetWMHints XSETWMHINTS
+#define XSetWMIconName XSETWMICONNAME
+#define XSetWMName XSETWMNAME
+#define XSetWMNormalHints XSETWMNORMALHINTS
+#define XSetWMProperties XSETWMPROPERTIES
+#define XSetWMProtocols XSETWMPROTOCOLS
+#define XSetWindowBackground XSETWINDOWBACKGROUND
+#define XSetWindowBackgroundPixmap XSETWINDOWBACKGROUNDPIXMAP
+#define XSetWindowColormap XSETWINDOWCOLORMAP
+#define XShapeCombineMask XSHAPECOMBINEMASK
+#define XShapeCombineRectangles XSHAPECOMBINERECTANGLES
+#define XShapeGetRectangles XSHAPEGETRECTANGLES
+#define XShrinkRegion XSHRINKREGION
+#define XStoreBytes XSTOREBYTES
+#define XStoreColor XSTORECOLOR
+#define XStoreColors XSTORECOLORS
+#define XStoreName XSTORENAME
+#define XStringListToTextProperty XSTRINGLISTTOTEXTPROPERTY
+#define XStringToKeysym XSTRINGTOKEYSYM
+#define XSubtractRegion XSUBTRACTREGION
+#define XSupportsLocale XSUPPORTSLOCALE
+#define XSync XSYNC
+#define XSynchronize XSYNCHRONIZE
+#define XTextExtents XTEXTEXTENTS
+#define XTextExtents16 XTEXTEXTENTS16
+#define XTextWidth XTEXTWIDTH
+#define XTextWidth16 XTEXTWIDTH16
+#define XTranslateCoordinates XTRANSLATECOORDINATES
+#define XUndefineCursor XUNDEFINECURSOR
+#define XUngrabButton XUNGRABBUTTON
+#define XUngrabKeyboard XUNGRABKEYBOARD
+#define XUngrabPointer XUNGRABPOINTER
+#define XUngrabServer XUNGRABSERVER
+#define XUninstallColormap XUNINSTALLCOLORMAP
+#define XUnionRectWithRegion XUNIONRECTWITHREGION
+#define XUnionRegion XUNIONREGION
+#define XUnmapWindow XUNMAPWINDOW
+#define XUnsetICFocus XUNSETICFOCUS
+#define XVaCreateNestedList XVACREATENESTEDLIST
+#define XVisualIDFromVisual XVISUALIDFROMVISUAL
+#define XWMGeometry XWMGEOMETRY
+#define XWarpPointer XWARPPOINTER
+#define XWhitePixel XWHITEPIXEL
+#define XWidthOfScreen XWIDTHOFSCREEN
+#define XWindowEvent XWINDOWEVENT
+#define XWithdrawWindow XWITHDRAWWINDOW
+#define XXorRegion XXORREGION
+#define XmActivateProtocol XMACTIVATEPROTOCOL
+#define XmAddProtocolCallback XMADDPROTOCOLCALLBACK
+#define XmAddProtocols XMADDPROTOCOLS
+#define XmChangeColor XMCHANGECOLOR
+#define XmClipboardCopy XMCLIPBOARDCOPY
+#define XmClipboardEndCopy XMCLIPBOARDENDCOPY
+#define XmClipboardInquireLength XMCLIPBOARDINQUIRELENGTH
+#define XmClipboardLock XMCLIPBOARDLOCK
+#define XmClipboardRetrieve XMCLIPBOARDRETRIEVE
+#define XmClipboardStartCopy XMCLIPBOARDSTARTCOPY
+#define XmClipboardUnlock XMCLIPBOARDUNLOCK
+#define XmCreateArrowButton XMCREATEARROWBUTTON
+#define XmCreateArrowButtonGadget XMCREATEARROWBUTTONGADGET
+#define XmCreateBulletinBoardDialog XMCREATEBULLETINBOARDDIALOG
+#define XmCreateCascadeButton XMCREATECASCADEBUTTON
+#define XmCreateCascadeButtonGadget XMCREATECASCADEBUTTONGADGET
+#define XmCreateDialogShell XMCREATEDIALOGSHELL
+#define XmCreateDragIcon XMCREATEDRAGICON
+#define XmCreateDrawingArea XMCREATEDRAWINGAREA
+#define XmCreateDrawnButton XMCREATEDRAWNBUTTON
+#define XmCreateErrorDialog XMCREATEERRORDIALOG
+#define XmCreateFileSelectionBox XMCREATEFILESELECTIONBOX
+#define XmCreateFileSelectionDialog XMCREATEFILESELECTIONDIALOG
+#define XmCreateForm XMCREATEFORM
+#define XmCreateFormDialog XMCREATEFORMDIALOG
+#define XmCreateFrame XMCREATEFRAME
+#define XmCreateInformationDialog XMCREATEINFORMATIONDIALOG
+#define XmCreateLabel XMCREATELABEL
+#define XmCreateLabelGadget XMCREATELABELGADGET
+#define XmCreateMainWindow XMCREATEMAINWINDOW
+#define XmCreateMenuBar XMCREATEMENUBAR
+#define XmCreateMessageBox XMCREATEMESSAGEBOX
+#define XmCreateMessageDialog XMCREATEMESSAGEDIALOG
+#define XmCreateOptionMenu XMCREATEOPTIONMENU
+#define XmCreatePanedWindow XMCREATEPANEDWINDOW
+#define XmCreatePopupMenu XMCREATEPOPUPMENU
+#define XmCreatePromptDialog XMCREATEPROMPTDIALOG
+#define XmCreatePulldownMenu XMCREATEPULLDOWNMENU
+#define XmCreatePushButton XMCREATEPUSHBUTTON
+#define XmCreatePushButtonGadget XMCREATEPUSHBUTTONGADGET
+#define XmCreateQuestionDialog XMCREATEQUESTIONDIALOG
+#define XmCreateRadioBox XMCREATERADIOBOX
+#define XmCreateRowColumn XMCREATEROWCOLUMN
+#define XmCreateScale XMCREATESCALE
+#define XmCreateScrollBar XMCREATESCROLLBAR
+#define XmCreateScrolledList XMCREATESCROLLEDLIST
+#define XmCreateScrolledText XMCREATESCROLLEDTEXT
+#define XmCreateScrolledWindow XMCREATESCROLLEDWINDOW
+#define XmCreateSelectionDialog XMCREATESELECTIONDIALOG
+#define XmCreateSeparator XMCREATESEPARATOR
+#define XmCreateSeparatorGadget XMCREATESEPARATORGADGET
+#define XmCreateTemplateDialog XMCREATETEMPLATEDIALOG
+#define XmCreateText XMCREATETEXT
+#define XmCreateTextField XMCREATETEXTFIELD
+#define XmCreateToggleButton XMCREATETOGGLEBUTTON
+#define XmCreateToggleButtonGadget XMCREATETOGGLEBUTTONGADGET
+#define XmCreateWarningDialog XMCREATEWARNINGDIALOG
+#define XmCvtCTToXmString XMCVTCTTOXMSTRING
+#define XmDestroyPixmap XMDESTROYPIXMAP
+#define XmDragStart XMDRAGSTART
+#define XmDropSiteRegister XMDROPSITEREGISTER
+#define XmDropSiteUnregister XMDROPSITEUNREGISTER
+#define XmDropSiteUpdate XMDROPSITEUPDATE
+#define XmDropTransferStart XMDROPTRANSFERSTART
+#define XmFileSelectionBoxGetChild XMFILESELECTIONBOXGETCHILD
+#define XmFileSelectionDoSearch XMFILESELECTIONDOSEARCH
+#define XmFontListAppendEntry XMFONTLISTAPPENDENTRY
+#define XmFontListCopy XMFONTLISTCOPY
+#define XmFontListCreate XMFONTLISTCREATE
+#define XmFontListEntryCreate XMFONTLISTENTRYCREATE
+#define XmFontListEntryFree XMFONTLISTENTRYFREE
+#define XmFontListEntryGetFont XMFONTLISTENTRYGETFONT
+#define XmFontListEntryGetTag XMFONTLISTENTRYGETTAG
+#define XmFontListEntryLoad XMFONTLISTENTRYLOAD
+#define XmFontListFree XMFONTLISTFREE
+#define XmFontListFreeFontContext XMFONTLISTFREEFONTCONTEXT
+#define XmFontListGetNextFont XMFONTLISTGETNEXTFONT
+#define XmFontListInitFontContext XMFONTLISTINITFONTCONTEXT
+#define XmFontListNextEntry XMFONTLISTNEXTENTRY
+#define XmGetColors XMGETCOLORS
+#define XmGetFocusWidget XMGETFOCUSWIDGET
+#define XmGetMenuCursor XMGETMENUCURSOR
+#define XmGetPixmap XMGETPIXMAP
+#define XmGetPixmapByDepth XMGETPIXMAPBYDEPTH
+#define XmGetTearOffControl XMGETTEAROFFCONTROL
+#define XmGetXmDisplay XMGETXMDISPLAY
+#define XmImMbLookupString XMIMMBLOOKUPSTRING
+#define XmImRegister XMIMREGISTER
+#define XmImSetFocusValues XMIMSETFOCUSVALUES
+#define XmImSetValues XMIMSETVALUES
+#define XmImUnregister XMIMUNREGISTER
+#define XmImUnsetFocus XMIMUNSETFOCUS
+#define XmInstallImage XMINSTALLIMAGE
+#define XmInternAtom XMINTERNATOM
+#define XmIsMotifWMRunning XMISMOTIFWMRUNNING
+#define XmListAddItem XMLISTADDITEM
+#define XmListAddItemUnselected XMLISTADDITEMUNSELECTED
+#define XmListAddItems XMLISTADDITEMS
+#define XmListAddItemsUnselected XMLISTADDITEMSUNSELECTED
+#define XmListDeleteAllItems XMLISTDELETEALLITEMS
+#define XmListDeleteItem XMLISTDELETEITEM
+#define XmListDeleteItemsPos XMLISTDELETEITEMSPOS
+#define XmListDeletePos XMLISTDELETEPOS
+#define XmListDeselectAllItems XMLISTDESELECTALLITEMS
+#define XmListDeselectPos XMLISTDESELECTPOS
+#define XmListGetKbdItemPos XMLISTGETKBDITEMPOS
+#define XmListGetMatchPos XMLISTGETMATCHPOS
+#define XmListGetSelectedPos XMLISTGETSELECTEDPOS
+#define XmListItemExists XMLISTITEMEXISTS
+#define XmListItemPos XMLISTITEMPOS
+#define XmListPosSelected XMLISTPOSSELECTED
+#define XmListReplaceItems XMLISTREPLACEITEMS
+#define XmListReplaceItemsPos XMLISTREPLACEITEMSPOS
+#define XmListSelectItem XMLISTSELECTITEM
+#define XmListSelectPos XMLISTSELECTPOS
+#define XmListSetBottomPos XMLISTSETBOTTOMPOS
+#define XmListSetItem XMLISTSETITEM
+#define XmListSetKbdItemPos XMLISTSETKBDITEMPOS
+#define XmListSetPos XMLISTSETPOS
+#define XmMainWindowSetAreas XMMAINWINDOWSETAREAS
+#define XmMenuPosition XMMENUPOSITION
+#define XmMessageBoxGetChild XMMESSAGEBOXGETCHILD
+#define XmOptionButtonGadget XMOPTIONBUTTONGADGET
+#define XmOptionLabelGadget XMOPTIONLABELGADGET
+#define XmProcessTraversal XMPROCESSTRAVERSAL
+#define XmQmotif XMQMOTIF
+#define XmRemoveProtocolCallback XMREMOVEPROTOCOLCALLBACK
+#define XmRemoveProtocols XMREMOVEPROTOCOLS
+#define XmRemoveTabGroup XMREMOVETABGROUP
+#define XmRepTypeGetId XMREPTYPEGETID
+#define XmRepTypeGetRecord XMREPTYPEGETRECORD
+#define XmRepTypeRegister XMREPTYPEREGISTER
+#define XmRepTypeValidValue XMREPTYPEVALIDVALUE
+#define XmScrollBarSetValues XMSCROLLBARSETVALUES
+#define XmScrolledWindowSetAreas XMSCROLLEDWINDOWSETAREAS
+#define XmSelectionBoxGetChild XMSELECTIONBOXGETCHILD
+#define XmSetColorCalculation XMSETCOLORCALCULATION
+#define XmStringByteCompare XMSTRINGBYTECOMPARE
+#define XmStringCompare XMSTRINGCOMPARE
+#define XmStringConcat XMSTRINGCONCAT
+#define XmStringCopy XMSTRINGCOPY
+#define XmStringCreate XMSTRINGCREATE
+#define XmStringCreateLocalized XMSTRINGCREATELOCALIZED
+#define XmStringCreateLtoR XMSTRINGCREATELTOR
+#define XmStringCreateSimple XMSTRINGCREATESIMPLE
+#define XmStringDraw XMSTRINGDRAW
+#define XmStringDrawUnderline XMSTRINGDRAWUNDERLINE
+#define XmStringExtent XMSTRINGEXTENT
+#define XmStringFree XMSTRINGFREE
+#define XmStringFreeContext XMSTRINGFREECONTEXT
+#define XmStringGetLtoR XMSTRINGGETLTOR
+#define XmStringGetNextComponent XMSTRINGGETNEXTCOMPONENT
+#define XmStringGetNextSegment XMSTRINGGETNEXTSEGMENT
+#define XmStringInitContext XMSTRINGINITCONTEXT
+#define XmStringLength XMSTRINGLENGTH
+#define XmStringLtoRCreate XMSTRINGLTORCREATE
+#define XmStringNConcat XMSTRINGNCONCAT
+#define XmStringSegmentCreate XMSTRINGSEGMENTCREATE
+#define XmStringWidth XMSTRINGWIDTH
+#define XmTextClearSelection XMTEXTCLEARSELECTION
+#define XmTextCopy XMTEXTCOPY
+#define XmTextCut XMTEXTCUT
+#define XmTextFieldClearSelection XMTEXTFIELDCLEARSELECTION
+#define XmTextFieldCopy XMTEXTFIELDCOPY
+#define XmTextFieldCut XMTEXTFIELDCUT
+#define XmTextFieldGetEditable XMTEXTFIELDGETEDITABLE
+#define XmTextFieldGetInsertionPosition XMTEXTFIELDGETINSERTIONPOSITION
+#define XmTextFieldGetLastPosition XMTEXTFIELDGETLASTPOSITION
+#define XmTextFieldGetMaxLength XMTEXTFIELDGETMAXLENGTH
+#define XmTextFieldGetSelection XMTEXTFIELDGETSELECTION
+#define XmTextFieldGetSelectionPosition XMTEXTFIELDGETSELECTIONPOSITION
+#define XmTextFieldGetString XMTEXTFIELDGETSTRING
+#define XmTextFieldInsert XMTEXTFIELDINSERT
+#define XmTextFieldPaste XMTEXTFIELDPASTE
+#define XmTextFieldRemove XMTEXTFIELDREMOVE
+#define XmTextFieldReplace XMTEXTFIELDREPLACE
+#define XmTextFieldSetAddMode XMTEXTFIELDSETADDMODE
+#define XmTextFieldSetHighlight XMTEXTFIELDSETHIGHLIGHT
+#define XmTextFieldSetInsertionPosition XMTEXTFIELDSETINSERTIONPOSITION
+#define XmTextFieldSetMaxLength XMTEXTFIELDSETMAXLENGTH
+#define XmTextFieldSetSelection XMTEXTFIELDSETSELECTION
+#define XmTextFieldSetString XMTEXTFIELDSETSTRING
+#define XmTextFieldShowPosition XMTEXTFIELDSHOWPOSITION
+#define XmTextGetCursorPosition XMTEXTGETCURSORPOSITION
+#define XmTextGetEditable XMTEXTGETEDITABLE
+#define XmTextGetInsertionPosition XMTEXTGETINSERTIONPOSITION
+#define XmTextGetLastPosition XMTEXTGETLASTPOSITION
+#define XmTextGetMaxLength XMTEXTGETMAXLENGTH
+#define XmTextGetSelection XMTEXTGETSELECTION
+#define XmTextGetSelectionPosition XMTEXTGETSELECTIONPOSITION
+#define XmTextGetString XMTEXTGETSTRING
+#define XmTextInsert XMTEXTINSERT
+#define XmTextPaste XMTEXTPASTE
+#define XmTextPosToXY XMTEXTPOSTOXY
+#define XmTextRemove XMTEXTREMOVE
+#define XmTextReplace XMTEXTREPLACE
+#define XmTextSetCursorPosition XMTEXTSETCURSORPOSITION
+#define XmTextSetEditable XMTEXTSETEDITABLE
+#define XmTextSetHighlight XMTEXTSETHIGHLIGHT
+#define XmTextSetInsertionPosition XMTEXTSETINSERTIONPOSITION
+#define XmTextSetSelection XMTEXTSETSELECTION
+#define XmTextSetString XMTEXTSETSTRING
+#define XmTextShowPosition XMTEXTSHOWPOSITION
+#define XmToggleButtonGadgetGetState XMTOGGLEBUTTONGADGETGETSTATE
+#define XmToggleButtonGadgetSetState XMTOGGLEBUTTONGADGETSETSTATE
+#define XmToggleButtonGetState XMTOGGLEBUTTONGETSTATE
+#define XmToggleButtonSetState XMTOGGLEBUTTONSETSTATE
+#define XmUninstallImage XMUNINSTALLIMAGE
+#define XmUpdateDisplay XMUPDATEDISPLAY
+#define XmVaCreateSimpleRadioBox XMVACREATESIMPLERADIOBOX
+#define XmbDrawString XMBDRAWSTRING
+#define XmbLookupString XMBLOOKUPSTRING
+#define XmbResetIC XMBRESETIC
+#define XmbSetWMProperties XMBSETWMPROPERTIES
+#define XmbTextEscapement XMBTEXTESCAPEMENT
+#define XmbTextExtents XMBTEXTEXTENTS
+#define XmbTextListToTextProperty XMBTEXTLISTTOTEXTPROPERTY
+#define XmbTextPropertyToTextList XMBTEXTPROPERTYTOTEXTLIST
+#define XmuClientWindow XMUCLIENTWINDOW
+#define XmuCvtStringToBitmap XMUCVTSTRINGTOBITMAP
+#define XmuLookupStandardColormap XMULOOKUPSTANDARDCOLORMAP
+#define XmuPrintDefaultErrorMessage XMUPRINTDEFAULTERRORMESSAGE
+#define XrmCombineDatabase XRMCOMBINEDATABASE
+#define XrmCombineFileDatabase XRMCOMBINEFILEDATABASE
+#define XrmDestroyDatabase XRMDESTROYDATABASE
+#define XrmGetDatabase XRMGETDATABASE
+#define XrmGetFileDatabase XRMGETFILEDATABASE
+#define XrmGetResource XRMGETRESOURCE
+#define XrmGetStringDatabase XRMGETSTRINGDATABASE
+#define XrmInitialize XRMINITIALIZE
+#define XrmMergeDatabases XRMMERGEDATABASES
+#define XrmParseCommand XRMPARSECOMMAND
+#define XrmPutFileDatabase XRMPUTFILEDATABASE
+#define XrmPutLineResource XRMPUTLINERESOURCE
+#define XrmPutStringResource XRMPUTSTRINGRESOURCE
+#define XrmQPutStringResource XRMQPUTSTRINGRESOURCE
+#define XrmQuarkToString XRMQUARKTOSTRING
+#define XrmStringToBindingQuarkList XRMSTRINGTOBINDINGQUARKLIST
+#define XrmStringToQuark XRMSTRINGTOQUARK
+#define XrmStringToQuark XRMSTRINGTOQUARK
+#define XtAddCallback XTADDCALLBACK
+#define XtAddCallbacks XTADDCALLBACKS
+#define XtAddConverter XTADDCONVERTER
+#define XtAddEventHandler XTADDEVENTHANDLER
+#define XtAddExposureToRegion XTADDEXPOSURETOREGION
+#define XtAddGrab XTADDGRAB
+#define XtAllocateGC XTALLOCATEGC
+#define XtAppAddActions XTAPPADDACTIONS
+#define XtAppAddInput XTAPPADDINPUT
+#define XtAppAddTimeOut XTAPPADDTIMEOUT
+#define XtAppAddWorkProc XTAPPADDWORKPROC
+#define XtAppCreateShell XTAPPCREATESHELL
+#define XtAppInitialize XTAPPINITIALIZE
+#define XtAppMainLoop XTAPPMAINLOOP
+#define XtAppNextEvent XTAPPNEXTEVENT
+#define XtAppPeekEvent XTAPPPEEKEVENT
+#define XtAppPending XTAPPPENDING
+#define XtAppProcessEvent XTAPPPROCESSEVENT
+#define XtAppSetErrorHandler XTAPPSETERRORHANDLER
+#define XtAppSetFallbackResources XTAPPSETFALLBACKRESOURCES
+#define XtAppSetWarningHandler XTAPPSETWARNINGHANDLER
+#define XtAppSetWarningMsgHandler XTAPPSETWARNINGMSGHANDLER
+#define XtAppWarning XTAPPWARNING
+#define XtCallActionProc XTCALLACTIONPROC
+#define XtCallCallbackList XTCALLCALLBACKLIST
+#define XtCallCallbacks XTCALLCALLBACKS
+#define XtCloseDisplay XTCLOSEDISPLAY
+#define XtConfigureWidget XTCONFIGUREWIDGET
+#define XtConvertAndStore XTCONVERTANDSTORE
+#define XtCreateApplicationContext XTCREATEAPPLICATIONCONTEXT
+#define XtCreateManagedWidget XTCREATEMANAGEDWIDGET
+#define XtCreatePopupShell XTCREATEPOPUPSHELL
+#define XtCreateWidget XTCREATEWIDGET
+#define XtDatabase XTDATABASE
+#define XtDestroyApplicationContext XTDESTROYAPPLICATIONCONTEXT
+#define XtDestroyWidget XTDESTROYWIDGET
+#define XtDisownSelection XTDISOWNSELECTION
+#define XtDispatchEvent XTDISPATCHEVENT
+#define XtDisplay XTDISPLAY
+#define XtDisplayOfObject XTDISPLAYOFOBJECT
+#define XtDisplayStringConvWarning XTDISPLAYSTRINGCONVWARNING
+#define XtDisplayToApplicationContext XTDISPLAYTOAPPLICATIONCONTEXT
+#define XtError XTERROR
+#define XtFree XTFREE
+#define XtGetActionKeysym XTGETACTIONKEYSYM
+#define XtGetActionList XTGETACTIONLIST
+#define XtGetApplicationNameAndClass XTGETAPPLICATIONNAMEANDCLASS
+#define XtGetApplicationResources XTGETAPPLICATIONRESOURCES
+#define XtGetGC XTGETGC
+#define XtGetMultiClickTime XTGETMULTICLICKTIME
+#define XtGetSelectionValue XTGETSELECTIONVALUE
+#define XtGetSelectionValues XTGETSELECTIONVALUES
+#define XtGetSubresources XTGETSUBRESOURCES
+#define XtGetValues XTGETVALUES
+#define XtGrabButton XTGRABBUTTON
+#define XtGrabKeyboard XTGRABKEYBOARD
+#define XtGrabPointer XTGRABPOINTER
+#define XtHasCallbacks XTHASCALLBACKS
+#define XtInitialize XTINITIALIZE
+#define XtInitializeWidgetClass XTINITIALIZEWIDGETCLASS
+#define XtInsertEventHandler XTINSERTEVENTHANDLER
+#define XtIsManaged XTISMANAGED
+#define XtIsObject XTISOBJECT
+#define XtIsRealized XTISREALIZED
+#define XtIsSensitive XTISSENSITIVE
+#define XtIsSubclass XTISSUBCLASS
+#define XtLastTimestampProcessed XTLASTTIMESTAMPPROCESSED
+#define XtMainLoop XTMAINLOOP
+#define XtMakeGeometryRequest XTMAKEGEOMETRYREQUEST
+#define XtMakeResizeRequest XTMAKERESIZEREQUEST
+#define XtMalloc XTMALLOC
+#define XtManageChild XTMANAGECHILD
+#define XtManageChildren XTMANAGECHILDREN
+#define XtMergeArgLists XTMERGEARGLISTS
+#define XtMoveWidget XTMOVEWIDGET
+#define XtName XTNAME
+#define XtNameToWidget XTNAMETOWIDGET
+#define XtOpenDisplay XTOPENDISPLAY
+#define XtOverrideTranslations XTOVERRIDETRANSLATIONS
+#define XtOwnSelection XTOWNSELECTION
+#define XtParseTranslationTable XTPARSETRANSLATIONTABLE
+#define XtPopdown XTPOPDOWN
+#define XtPopup XTPOPUP
+#define XtQueryGeometry XTQUERYGEOMETRY
+#define XtRealizeWidget XTREALIZEWIDGET
+#define XtRealloc XTREALLOC
+#define XtRegisterDrawable _XTREGISTERWINDOW
+#define XtReleaseGC XTRELEASEGC
+#define XtRemoveAllCallbacks XTREMOVEALLCALLBACKS
+#define XtRemoveCallback XTREMOVECALLBACK
+#define XtRemoveEventHandler XTREMOVEEVENTHANDLER
+#define XtRemoveGrab XTREMOVEGRAB
+#define XtRemoveInput XTREMOVEINPUT
+#define XtRemoveTimeOut XTREMOVETIMEOUT
+#define XtRemoveWorkProc XTREMOVEWORKPROC
+#define XtResizeWidget XTRESIZEWIDGET
+#define XtResolvePathname XTRESOLVEPATHNAME
+#define XtScreen XTSCREEN
+#define XtSetKeyboardFocus XTSETKEYBOARDFOCUS
+#define XtSetMappedWhenManaged XTSETMAPPEDWHENMANAGED
+#define XtSetSensitive XTSETSENSITIVE
+#define XtSetTypeConverter XTSETTYPECONVERTER
+#define XtSetValues XTSETVALUES
+#define XtShellStrings XTSHELLSTRINGS
+#define XtStrings XTSTRINGS
+#define XtToolkitInitialize XTTOOLKITINITIALIZE
+#define XtTranslateCoords XTTRANSLATECOORDS
+#define XtTranslateKeycode XTTRANSLATEKEYCODE
+#define XtUngrabButton XTUNGRABBUTTON
+#define XtUngrabKeyboard XTUNGRABKEYBOARD
+#define XtUngrabPointer XTUNGRABPOINTER
+#define XtUnmanageChild XTUNMANAGECHILD
+#define XtUnmanageChildren XTUNMANAGECHILDREN
+#define XtUnrealizeWidget XTUNREALIZEWIDGET
+#define XtUnregisterDrawable _XTUNREGISTERWINDOW
+#define XtVaCreateManagedWidget XTVACREATEMANAGEDWIDGET
+#define XtVaCreatePopupShell XTVACREATEPOPUPSHELL
+#define XtVaCreateWidget XTVACREATEWIDGET
+#define XtVaGetValues XTVAGETVALUES
+#define XtVaSetValues XTVASETVALUES
+#define XtWarning XTWARNING
+#define XtWidgetToApplicationContext XTWIDGETTOAPPLICATIONCONTEXT
+#define XtWindow XTWINDOW
+#define XtWindowOfObject XTWINDOWOFOBJECT
+#define XtWindowToWidget XTWINDOWTOWIDGET
+#define XwcDrawString XWCDRAWSTRING
+#define XwcFreeStringList XWCFREESTRINGLIST
+#define XwcTextEscapement XWCTEXTESCAPEMENT
+#define XwcTextExtents XWCTEXTEXTENTS
+#define XwcTextListToTextProperty XWCTEXTLISTTOTEXTPROPERTY
+#define XwcTextPropertyToTextList XWCTEXTPROPERTYTOTEXTLIST
+#define _XRegisterFilterByType _XREGISTERFILTERBYTYPE
+#define _XUnregisterFilter _XUNREGISTERFILTER
+#define _XmBottomShadowColorDefault _XMBOTTOMSHADOWCOLORDEFAULT
+#define _XmClearBorder _XMCLEARBORDER
+#define _XmConfigureObject _XMCONFIGUREOBJECT
+#define _XmDestroyParentCallback _XMDESTROYPARENTCALLBACK
+#define _XmDrawArrow _XMDRAWARROW
+#define _XmDrawShadows _XMDRAWSHADOWS
+#define _XmFontListGetDefaultFont _XMFONTLISTGETDEFAULTFONT
+#define _XmFromHorizontalPixels _XMFROMHORIZONTALPIXELS
+#define _XmFromVerticalPixels _XMFROMVERTICALPIXELS
+#define _XmGetClassExtensionPtr _XMGETCLASSEXTENSIONPTR
+#define _XmGetDefaultFontList _XMGETDEFAULTFONTLIST
+#define _XmGetTextualDragIcon _XMGETTEXTUALDRAGICON
+#define _XmGetWidgetExtData _XMGETWIDGETEXTDATA
+#define _XmGrabKeyboard _XMGRABKEYBOARD
+#define _XmGrabPointer _XMGRABPOINTER
+#define _XmInheritClass _XMINHERITCLASS
+#define _XmInputInGadget _XMINPUTINGADGET
+#define _XmMakeGeometryRequest _XMMAKEGEOMETRYREQUEST
+#define _XmMenuPopDown _XMMENUPOPDOWN
+#define _XmMoveObject _XMMOVEOBJECT
+#define _XmNavigChangeManaged _XMNAVIGCHANGEMANAGED
+#define _XmOSBuildFileList _XMOSBUILDFILELIST
+#define _XmOSFileCompare _XMOSFILECOMPARE
+#define _XmOSFindPatternPart _XMOSFINDPATTERNPART
+#define _XmOSQualifyFileSpec _XMOSQUALIFYFILESPEC
+#define _XmPostPopupMenu _XMPOSTPOPUPMENU
+#define _XmPrimitiveEnter _XMPRIMITIVEENTER
+#define _XmPrimitiveLeave _XMPRIMITIVELEAVE
+#define _XmRedisplayGadgets _XMREDISPLAYGADGETS
+#define _XmShellIsExclusive _XMSHELLISEXCLUSIVE
+#define _XmStringDraw _XMSTRINGDRAW
+#define _XmStringGetTextConcat _XMSTRINGGETTEXTCONCAT
+#define _XmStrings _XMSTRINGS
+#define _XmToHorizontalPixels _XMTOHORIZONTALPIXELS
+#define _XmToVerticalPixels _XMTOVERTICALPIXELS
+#define _XmTopShadowColorDefault _XMTOPSHADOWCOLORDEFAULT
+#define _Xm_fastPtr _XM_FASTPTR
+#define _XtCheckSubclassFlag _XTCHECKSUBCLASSFLAG
+#define _XtInherit _XTINHERIT
+#define _XtInheritTranslations _XTINHERITTRANSLATIONS
+#define applicationShellWidgetClass APPLICATIONSHELLWIDGETCLASS
+#define compositeWidgetClass COMPOSITEWIDGETCLASS
+#define lib$ediv LIB$EDIV
+#define lib$find_file LIB$FIND_FILE
+#define lib$find_file_end LIB$FIND_FILE_END
+#define lib$set_symbol LIB$SET_SYMBOL
+#define lib$sfree1_dd LIB$SFREE1_DD
+#define lib$spawn LIB$SPAWN
+#define lib$subx LIB$SUBX
+#define lib$wait LIB$WAIT
+#define overrideShellWidgetClass OVERRIDESHELLWIDGETCLASS
+#define pthread_attr_create PTHREAD_ATTR_CREATE
+#define pthread_attr_delete PTHREAD_ATTR_DELETE
+#define pthread_attr_destroy PTHREAD_ATTR_DESTROY
+#define pthread_attr_getdetach_np PTHREAD_ATTR_GETDETACH_NP
+#define pthread_attr_getguardsize_np PTHREAD_ATTR_GETGUARDSIZE_NP
+#define pthread_attr_getinheritsched PTHREAD_ATTR_GETINHERITSCHED
+#define pthread_attr_getprio PTHREAD_ATTR_GETPRIO
+#define pthread_attr_getsched PTHREAD_ATTR_GETSCHED
+#define pthread_attr_getschedparam PTHREAD_ATTR_GETSCHEDPARAM
+#define pthread_attr_getschedpolicy PTHREAD_ATTR_GETSCHEDPOLICY
+#define pthread_attr_getstacksize PTHREAD_ATTR_GETSTACKSIZE
+#define pthread_attr_init PTHREAD_ATTR_INIT
+#define pthread_attr_setdetach_np PTHREAD_ATTR_SETDETACH_NP
+#define pthread_attr_setdetachstate PTHREAD_ATTR_SETDETACHSTATE
+#define pthread_attr_setguardsize_np PTHREAD_ATTR_SETGUARDSIZE_NP
+#define pthread_attr_setinheritsched PTHREAD_ATTR_SETINHERITSCHED
+#define pthread_attr_setprio PTHREAD_ATTR_SETPRIO
+#define pthread_attr_setsched PTHREAD_ATTR_SETSCHED
+#define pthread_attr_setschedparam PTHREAD_ATTR_SETSCHEDPARAM
+#define pthread_attr_setschedpolicy PTHREAD_ATTR_SETSCHEDPOLICY
+#define pthread_attr_setstacksize PTHREAD_ATTR_SETSTACKSIZE
+#define pthread_cancel PTHREAD_CANCEL
+#define pthread_cancel_e PTHREAD_CANCEL_E
+#define pthread_cond_broadcast PTHREAD_COND_BROADCAST
+#define pthread_cond_destroy PTHREAD_COND_DESTROY
+#define pthread_cond_init PTHREAD_COND_INIT
+#define pthread_cond_sig_preempt_int_np PTHREAD_COND_SIG_PREEMPT_INT_NP
+#define pthread_cond_signal PTHREAD_COND_SIGNAL
+#define pthread_cond_signal_int_np PTHREAD_COND_SIGNAL_INT_NP
+#define pthread_cond_timedwait PTHREAD_COND_TIMEDWAIT
+#define pthread_cond_wait PTHREAD_COND_WAIT
+#define pthread_condattr_create PTHREAD_CONDATTR_CREATE
+#define pthread_condattr_delete PTHREAD_CONDATTR_DELETE
+#define pthread_condattr_init PTHREAD_CONDATTR_INIT
+#define pthread_create PTHREAD_CREATE
+#define pthread_delay_np PTHREAD_DELAY_NP
+#define pthread_detach PTHREAD_DETACH
+#define pthread_equal PTHREAD_EQUAL
+#define pthread_exc_fetch_fp_np PTHREAD_EXC_FETCH_FP_NP
+#define pthread_exc_handler_np PTHREAD_EXC_HANDLER_NP
+#define pthread_exc_pop_ctx_np PTHREAD_EXC_POP_CTX_NP
+#define pthread_exc_push_ctx_np PTHREAD_EXC_PUSH_CTX_NP
+#define pthread_exc_savecontext_np PTHREAD_EXC_SAVECONTEXT_NP
+#define pthread_exit PTHREAD_EXIT
+#define pthread_get_expiration_np PTHREAD_GET_EXPIRATION_NP
+#define pthread_getprio PTHREAD_GETPRIO
+#define pthread_getschedparam PTHREAD_GETSCHEDPARAM
+#define pthread_getscheduler PTHREAD_GETSCHEDULER
+#define pthread_getspecific PTHREAD_GETSPECIFIC
+#define pthread_getunique_np PTHREAD_GETUNIQUE_NP
+#define pthread_join PTHREAD_JOIN
+#define pthread_join32 PTHREAD_JOIN32
+#define pthread_key_create PTHREAD_KEY_CREATE
+#define pthread_key_delete PTHREAD_KEY_DELETE
+#define pthread_keycreate PTHREAD_KEYCREATE
+#define pthread_kill PTHREAD_KILL
+#define pthread_lock_global_np PTHREAD_LOCK_GLOBAL_NP
+#define pthread_mutex_destroy PTHREAD_MUTEX_DESTROY
+#define pthread_mutex_init PTHREAD_MUTEX_INIT
+#define pthread_mutex_lock PTHREAD_MUTEX_LOCK
+#define pthread_mutex_trylock PTHREAD_MUTEX_TRYLOCK
+#define pthread_mutex_unlock PTHREAD_MUTEX_UNLOCK
+#define pthread_mutexattr_create PTHREAD_MUTEXATTR_CREATE
+#define pthread_mutexattr_delete PTHREAD_MUTEXATTR_DELETE
+#define pthread_mutexattr_destroy PTHREAD_MUTEXATTR_DESTROY
+#define pthread_mutexattr_getkind_np PTHREAD_MUTEXATTR_GETKIND_NP
+#define pthread_mutexattr_init PTHREAD_MUTEXATTR_INIT
+#define pthread_mutexattr_setkind_np PTHREAD_MUTEXATTR_SETKIND_NP
+#define pthread_mutexattr_settype_np PTHREAD_MUTEXATTR_SETTYPE_NP
+#define pthread_once PTHREAD_ONCE
+#define pthread_resume_np PTHREAD_RESUME_NP
+#define pthread_self PTHREAD_SELF
+#define pthread_setasynccancel PTHREAD_SETASYNCCANCEL
+#define pthread_setcancel PTHREAD_SETCANCEL
+#define pthread_setcancelstate PTHREAD_SETCANCELSTATE
+#define pthread_setprio PTHREAD_SETPRIO
+#define pthread_setschedparam PTHREAD_SETSCHEDPARAM
+#define pthread_setscheduler PTHREAD_SETSCHEDULER
+#define pthread_setspecific PTHREAD_SETSPECIFIC
+#define pthread_suspend_np PTHREAD_SUSPEND_NP
+#define pthread_testcancel PTHREAD_TESTCANCEL
+#define pthread_unlock_global_np PTHREAD_UNLOCK_GLOBAL_NP
+#define pthread_yield PTHREAD_YIELD
+#define pthread_yield_np PTHREAD_YIELD_NP
+#define pthread_exc_raise_np PTHREAD_EXC_RAISE_NP
+#define pthread_setcanceltype PTHREAD_SETCANCELTYPE
+#define shellWidgetClass SHELLWIDGETCLASS
+#define sys$assign SYS$ASSIGN
+#define sys$bintim SYS$BINTIM
+#define sys$crembx SYS$CREMBX
+#define sys$dassgn SYS$DASSGN
+#define sys$dclexh SYS$DCLEXH
+#define sys$getdviw SYS$GETDVIW
+#define sys$getsyiw SYS$GETSYIW
+#define sys$gettim SYS$GETTIM
+#define sys$qio SYS$QIO
+#define sys$qiow SYS$QIOW
+#define sys$setef SYS$SETEF
+#define sys$synch SYS$SYNCH
+#define topLevelShellClassRec TOPLEVELSHELLCLASSREC
+#define topLevelShellWidgetClass TOPLEVELSHELLWIDGETCLASS
+#define transientShellWidgetClass TRANSIENTSHELLWIDGETCLASS
+#define vendorShellClassRec VENDORSHELLCLASSREC
+#define vendorShellWidgetClass VENDORSHELLWIDGETCLASS
+#define wmShellWidgetClass WMSHELLWIDGETCLASS
+#define xmArrowButtonWidgetClass XMARROWBUTTONWIDGETCLASS
+#define xmBulletinBoardWidgetClass XMBULLETINBOARDWIDGETCLASS
+#define xmCascadeButtonClassRec XMCASCADEBUTTONCLASSREC
+#define xmCascadeButtonGadgetClass XMCASCADEBUTTONGADGETCLASS
+#define xmCascadeButtonWidgetClass XMCASCADEBUTTONWIDGETCLASS
+#define xmDialogShellWidgetClass XMDIALOGSHELLWIDGETCLASS
+#define xmDrawingAreaWidgetClass XMDRAWINGAREAWIDGETCLASS
+#define xmDrawnButtonWidgetClass XMDRAWNBUTTONWIDGETCLASS
+#define xmFileSelectionBoxWidgetClass XMFILESELECTIONBOXWIDGETCLASS
+#define xmFormWidgetClass XMFORMWIDGETCLASS
+#define xmFrameWidgetClass XMFRAMEWIDGETCLASS
+#define xmGadgetClass XMGADGETCLASS
+#define xmLabelGadgetClass XMLABELGADGETCLASS
+#define xmLabelWidgetClass XMLABELWIDGETCLASS
+#define xmListWidgetClass XMLISTWIDGETCLASS
+#define xmMainWindowWidgetClass XMMAINWINDOWWIDGETCLASS
+#define xmManagerClassRec XMMANAGERCLASSREC
+#define xmManagerWidgetClass XMMANAGERWIDGETCLASS
+#define xmMenuShellWidgetClass XMMENUSHELLWIDGETCLASS
+#define xmMessageBoxWidgetClass XMMESSAGEBOXWIDGETCLASS
+#define xmPrimitiveClassRec XMPRIMITIVECLASSREC
+#define xmPrimitiveWidgetClass XMPRIMITIVEWIDGETCLASS
+#define xmPushButtonClassRec XMPUSHBUTTONCLASSREC
+#define xmPushButtonGadgetClass XMPUSHBUTTONGADGETCLASS
+#define xmPushButtonWidgetClass XMPUSHBUTTONWIDGETCLASS
+#define xmRowColumnWidgetClass XMROWCOLUMNWIDGETCLASS
+#define xmSashWidgetClass XMSASHWIDGETCLASS
+#define xmScaleWidgetClass XMSCALEWIDGETCLASS
+#define xmScrollBarWidgetClass XMSCROLLBARWIDGETCLASS
+#define xmScrolledWindowClassRec XMSCROLLEDWINDOWCLASSREC
+#define xmScrolledWindowWidgetClass XMSCROLLEDWINDOWWIDGETCLASS
+#define xmSeparatorGadgetClass XMSEPARATORGADGETCLASS
+#define xmSeparatorWidgetClass XMSEPARATORWIDGETCLASS
+#define xmTextFieldWidgetClass XMTEXTFIELDWIDGETCLASS
+#define xmTextWidgetClass XMTEXTWIDGETCLASS
+#define xmToggleButtonGadgetClass XMTOGGLEBUTTONGADGETCLASS
+#define xmToggleButtonWidgetClass XMTOGGLEBUTTONWIDGETCLASS
+#define XInitImage XINITIMAGE
+#define _XInitImageFuncPtrs _XINITIMAGEFUNCPTRS
+#define XtParent XTPARENT
+#define XConnectionNumber XCONNECTIONNUMBER
+#define XDrawText16 XDRAWTEXT16
+#define XFindContext XFINDCONTEXT
+#define XGetInputFocus XGETINPUTFOCUS
+#define XSaveContext XSAVECONTEXT
+#define XUniqueContext XUNIQUECONTEXT
+#define XChangePointerControl XCHANGEPOINTERCONTROL
+#pragma __member_alignment __save
+typedef struct _ile3
+{
+#pragma __nomember_alignment
+ unsigned short int
+ ile3$w_length,
+ ile3$w_code;
+
+ void
+ *ile3$ps_bufaddr;
+
+ unsigned short int
+ *ile3$ps_retlen_addr;
+} ile3;
+#pragma __member_alignment __restore
+
+
+#if defined(__VMS_VER) && (__VMS_VER >= 70000000)
+#include <dirent.h>
+#else
+
+/*
+ Typedef declarations.
+*/
+struct dirent
+{
+ char
+ d_name[255];
+
+ int
+ d_namlen;
+};
+
+typedef struct _dirdesc
+{
+ long
+ context;
+
+ char
+ *pattern;
+
+ struct dirent
+ entry;
+
+ struct dsc$descriptor_s
+ pat;
+} DIR;
+
+extern DIR
+ *opendir(char *);
+
+extern struct dirent
+ *readdir(DIR *);
+
+extern void
+ closedir(DIR *);
+#endif
+
+extern MagickExport MagickBooleanType
+ VMSIsMagickConflict(const char *);
+
+extern void
+ XtFree(char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/widget.c b/magick/widget.c
new file mode 100644
index 0000000..d8005f1
--- /dev/null
+++ b/magick/widget.c
@@ -0,0 +1,9652 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% %
+% W W IIIII DDDD GGGG EEEEE TTTTT %
+% W W I D D G E T %
+% W W W I D D G GG EEE T %
+% WW WW I D D G G E T %
+% W W IIIII DDDD GGGG EEEEE T %
+% %
+% %
+% MagickCore X11 User Interface Methods %
+% %
+% Software Design %
+% John Cristy %
+% September 1993 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/image.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/PreRvIcccm.h"
+#include "magick/string_.h"
+#include "magick/token.h"
+#include "magick/utility.h"
+#include "magick/xwindow-private.h"
+#include "magick/widget.h"
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+
+/*
+ Define declarations.
+*/
+#define AreaIsActive(matte_info,position) ( \
+ ((position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
+ (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
+ ? MagickTrue : MagickFalse)
+#define Extent(s) ((int) strlen(s))
+#define MatteIsActive(matte_info,position) ( \
+ ((position.x >= (int) (matte_info.x-matte_info.bevel_width)) && \
+ (position.y >= (int) (matte_info.y-matte_info.bevel_width)) && \
+ (position.x < (int) (matte_info.x+matte_info.width+matte_info.bevel_width)) && \
+ (position.y < (int) (matte_info.y+matte_info.height+matte_info.bevel_width))) \
+ ? MagickTrue : MagickFalse)
+#define MaxTextWidth ((unsigned int) (255*XTextWidth(font_info,"_",1)))
+#define MinTextWidth (26*XTextWidth(font_info,"_",1))
+#define QuantumMargin MagickMax(font_info->max_bounds.width,12)
+#define WidgetTextWidth(font_info,text) \
+ ((unsigned int) XTextWidth(font_info,text,Extent(text)))
+#define WindowIsActive(window_info,position) ( \
+ ((position.x >= 0) && (position.y >= 0) && \
+ (position.x < (int) window_info.width) && \
+ (position.y < (int) window_info.height)) ? MagickTrue : MagickFalse)
+
+/*
+ Enum declarations.
+*/
+typedef enum
+{
+ ControlState = 0x0001,
+ InactiveWidgetState = 0x0004,
+ JumpListState = 0x0008,
+ RedrawActionState = 0x0010,
+ RedrawListState = 0x0020,
+ RedrawWidgetState = 0x0040,
+ UpdateListState = 0x0100
+} WidgetState;
+
+/*
+ Typedef declarations.
+*/
+typedef struct _XWidgetInfo
+{
+ char
+ *cursor,
+ *text,
+ *marker;
+
+ int
+ id;
+
+ unsigned int
+ bevel_width,
+ width,
+ height;
+
+ int
+ x,
+ y,
+ min_y,
+ max_y;
+
+ MagickStatusType
+ raised,
+ active,
+ center,
+ trough,
+ highlight;
+} XWidgetInfo;
+
+/*
+ Variable declarations.
+*/
+static XWidgetInfo
+ monitor_info =
+ {
+ (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
+ MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
+ },
+ submenu_info =
+ {
+ (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
+ MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
+ },
+ *selection_info = (XWidgetInfo *) NULL,
+ toggle_info =
+ {
+ (char *) NULL, (char *) NULL, (char *) NULL, 0, 0, 0, 0, 0, 0, 0, 0,
+ MagickFalse, MagickFalse, MagickFalse, MagickFalse, MagickFalse
+ };
+
+/*
+ Constant declarations.
+*/
+static const int
+ BorderOffset = 4,
+ DoubleClick = 250;
+
+/*
+ Method prototypes.
+*/
+static void
+ XDrawMatte(Display *,const XWindowInfo *,const XWidgetInfo *),
+ XSetBevelColor(Display *,const XWindowInfo *,const MagickStatusType),
+ XSetMatteColor(Display *,const XWindowInfo *,const MagickStatusType),
+ XSetTextColor(Display *,const XWindowInfo *,const MagickStatusType);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y X W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyXWidget() destroys resources associated with the X widget.
+%
+% The format of the DestroyXWidget method is:
+%
+% void DestroyXWidget()
+%
+% A description of each parameter follows:
+%
+*/
+MagickExport void DestroyXWidget(void)
+{
+ if (selection_info != (XWidgetInfo *) NULL)
+ selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w B e v e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawBevel() "sets off" an area with a highlighted upper and left bevel and
+% a shadowed lower and right bevel. The highlighted and shadowed bevels
+% create a 3-D effect.
+%
+% The format of the XDrawBevel function is:
+%
+% XDrawBevel(display,window_info,bevel_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o bevel_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the bevel.
+%
+*/
+static void XDrawBevel(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *bevel_info)
+{
+ int
+ x1,
+ x2,
+ y1,
+ y2;
+
+ unsigned int
+ bevel_width;
+
+ XPoint
+ points[6];
+
+ /*
+ Draw upper and left beveled border.
+ */
+ x1=bevel_info->x;
+ y1=bevel_info->y+bevel_info->height;
+ x2=bevel_info->x+bevel_info->width;
+ y2=bevel_info->y;
+ bevel_width=bevel_info->bevel_width;
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x1;
+ points[1].y=y2;
+ points[2].x=x2;
+ points[2].y=y2;
+ points[3].x=x2+bevel_width;
+ points[3].y=y2-bevel_width;
+ points[4].x=x1-bevel_width;
+ points[4].y=y2-bevel_width;
+ points[5].x=x1-bevel_width;
+ points[5].y=y1+bevel_width;
+ XSetBevelColor(display,window_info,bevel_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,6,Complex,CoordModeOrigin);
+ /*
+ Draw lower and right beveled border.
+ */
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y1;
+ points[2].x=x2;
+ points[2].y=y2;
+ points[3].x=x2+bevel_width;
+ points[3].y=y2-bevel_width;
+ points[4].x=x2+bevel_width;
+ points[4].y=y1+bevel_width;
+ points[5].x=x1-bevel_width;
+ points[5].y=y1+bevel_width;
+ XSetBevelColor(display,window_info,!bevel_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,6,Complex,CoordModeOrigin);
+ (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w B e v e l e d B u t t o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawBeveledButton() draws a button with a highlighted upper and left bevel
+% and a shadowed lower and right bevel. The highlighted and shadowed bevels
+% create a 3-D effect.
+%
+% The format of the XDrawBeveledButton function is:
+%
+% XDrawBeveledButton(display,window_info,button_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o button_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the button.
+%
+*/
+
+static inline int MagickAbsoluteValue(const int x)
+{
+ if (x < 0)
+ return(-x);
+ return(x);
+}
+
+static inline int MagickMax(const int x,const int y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline int MagickMin(const int x,const int y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+static void XDrawBeveledButton(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *button_info)
+{
+ int
+ x,
+ y;
+
+ unsigned int
+ width;
+
+ XFontStruct
+ *font_info;
+
+ XRectangle
+ crop_info;
+
+ /*
+ Draw matte.
+ */
+ XDrawBevel(display,window_info,button_info);
+ XSetMatteColor(display,window_info,button_info->raised);
+ (void) XFillRectangle(display,window_info->id,window_info->widget_context,
+ button_info->x,button_info->y,button_info->width,button_info->height);
+ x=button_info->x-button_info->bevel_width-1;
+ y=button_info->y-button_info->bevel_width-1;
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->trough_color.pixel);
+ if (button_info->raised || (window_info->depth == 1))
+ (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
+ x,y,button_info->width+(button_info->bevel_width << 1)+1,
+ button_info->height+(button_info->bevel_width << 1)+1);
+ if (button_info->text == (char *) NULL)
+ return;
+ /*
+ Set cropping region.
+ */
+ crop_info.width=(unsigned short) button_info->width;
+ crop_info.height=(unsigned short) button_info->height;
+ crop_info.x=button_info->x;
+ crop_info.y=button_info->y;
+ /*
+ Draw text.
+ */
+ font_info=window_info->font_info;
+ width=WidgetTextWidth(font_info,button_info->text);
+ x=button_info->x+(QuantumMargin >> 1);
+ if (button_info->center)
+ x=button_info->x+(button_info->width >> 1)-(width >> 1);
+ y=button_info->y+((button_info->height-
+ (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
+ if ((int) button_info->width == (QuantumMargin >> 1))
+ {
+ /*
+ Option button-- write label to right of button.
+ */
+ XSetTextColor(display,window_info,MagickTrue);
+ x=button_info->x+button_info->width+button_info->bevel_width+
+ (QuantumMargin >> 1);
+ (void) XDrawString(display,window_info->id,window_info->widget_context,
+ x,y,button_info->text,Extent(button_info->text));
+ return;
+ }
+ (void) XSetClipRectangles(display,window_info->widget_context,0,0,&crop_info,
+ 1,Unsorted);
+ XSetTextColor(display,window_info,button_info->raised);
+ (void) XDrawString(display,window_info->id,window_info->widget_context,x,y,
+ button_info->text,Extent(button_info->text));
+ (void) XSetClipMask(display,window_info->widget_context,None);
+ if (button_info->raised == MagickFalse)
+ XDelay(display,SuspendTime << 2);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w B e v e l e d M a t t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawBeveledMatte() draws a matte with a shadowed upper and left bevel and
+% a highlighted lower and right bevel. The highlighted and shadowed bevels
+% create a 3-D effect.
+%
+% The format of the XDrawBeveledMatte function is:
+%
+% XDrawBeveledMatte(display,window_info,matte_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o matte_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the matte.
+%
+*/
+static void XDrawBeveledMatte(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *matte_info)
+{
+ /*
+ Draw matte.
+ */
+ XDrawBevel(display,window_info,matte_info);
+ XDrawMatte(display,window_info,matte_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w M a t t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawMatte() fills a rectangular area with the matte color.
+%
+% The format of the XDrawMatte function is:
+%
+% XDrawMatte(display,window_info,matte_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o matte_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the matte.
+%
+*/
+static void XDrawMatte(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *matte_info)
+{
+ /*
+ Draw matte.
+ */
+ if ((matte_info->trough == MagickFalse) || (window_info->depth == 1))
+ (void) XFillRectangle(display,window_info->id,
+ window_info->highlight_context,matte_info->x,matte_info->y,
+ matte_info->width,matte_info->height);
+ else
+ {
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->trough_color.pixel);
+ (void) XFillRectangle(display,window_info->id,window_info->widget_context,
+ matte_info->x,matte_info->y,matte_info->width,matte_info->height);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w M a t t e T e x t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawMatteText() draws a matte with text. If the text exceeds the extents
+% of the text, a portion of the text relative to the cursor is displayed.
+%
+% The format of the XDrawMatteText function is:
+%
+% XDrawMatteText(display,window_info,text_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o text_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the text.
+%
+*/
+static void XDrawMatteText(Display *display,const XWindowInfo *window_info,
+ XWidgetInfo *text_info)
+{
+ const char
+ *text;
+
+ int
+ n,
+ x,
+ y;
+
+ register int
+ i;
+
+ unsigned int
+ height,
+ width;
+
+ XFontStruct
+ *font_info;
+
+ XRectangle
+ crop_info;
+
+ /*
+ Clear the text area.
+ */
+ XSetMatteColor(display,window_info,MagickFalse);
+ (void) XFillRectangle(display,window_info->id,window_info->widget_context,
+ text_info->x,text_info->y,text_info->width,text_info->height);
+ if (text_info->text == (char *) NULL)
+ return;
+ XSetTextColor(display,window_info,text_info->highlight);
+ font_info=window_info->font_info;
+ x=text_info->x+(QuantumMargin >> 2);
+ y=text_info->y+font_info->ascent+(text_info->height >> 2);
+ width=text_info->width-(QuantumMargin >> 1);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ if (*text_info->text == '\0')
+ {
+ /*
+ No text-- just draw cursor.
+ */
+ (void) XDrawLine(display,window_info->id,window_info->annotate_context,
+ x,y+3,x,y-height+3);
+ return;
+ }
+ /*
+ Set cropping region.
+ */
+ crop_info.width=(unsigned short) text_info->width;
+ crop_info.height=(unsigned short) text_info->height;
+ crop_info.x=text_info->x;
+ crop_info.y=text_info->y;
+ /*
+ Determine beginning of the visible text.
+ */
+ if (text_info->cursor < text_info->marker)
+ text_info->marker=text_info->cursor;
+ else
+ {
+ text=text_info->marker;
+ if (XTextWidth(font_info,(char *) text,(int) (text_info->cursor-text)) >
+ (int) width)
+ {
+ text=text_info->text;
+ for (i=0; i < Extent(text); i++)
+ {
+ n=XTextWidth(font_info,(char *) text+i,(int)
+ (text_info->cursor-text-i));
+ if (n <= (int) width)
+ break;
+ }
+ text_info->marker=(char *) text+i;
+ }
+ }
+ /*
+ Draw text and cursor.
+ */
+ if (text_info->highlight == MagickFalse)
+ {
+ (void) XSetClipRectangles(display,window_info->widget_context,0,0,
+ &crop_info,1,Unsorted);
+ (void) XDrawString(display,window_info->id,window_info->widget_context,
+ x,y,text_info->marker,Extent(text_info->marker));
+ (void) XSetClipMask(display,window_info->widget_context,None);
+ }
+ else
+ {
+ (void) XSetClipRectangles(display,window_info->annotate_context,0,0,
+ &crop_info,1,Unsorted);
+ width=WidgetTextWidth(font_info,text_info->marker);
+ (void) XFillRectangle(display,window_info->id,
+ window_info->annotate_context,x,y-font_info->ascent,width,height);
+ (void) XSetClipMask(display,window_info->annotate_context,None);
+ (void) XSetClipRectangles(display,window_info->highlight_context,0,0,
+ &crop_info,1,Unsorted);
+ (void) XDrawString(display,window_info->id,
+ window_info->highlight_context,x,y,text_info->marker,
+ Extent(text_info->marker));
+ (void) XSetClipMask(display,window_info->highlight_context,None);
+ }
+ x+=XTextWidth(font_info,text_info->marker,(int)
+ (text_info->cursor-text_info->marker));
+ (void) XDrawLine(display,window_info->id,window_info->annotate_context,x,y+3,
+ x,y-height+3);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w T r i a n g l e E a s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawTriangleEast() draws a triangle with a highlighted left bevel and a
+% shadowed right and lower bevel. The highlighted and shadowed bevels create
+% a 3-D effect.
+%
+% The format of the XDrawTriangleEast function is:
+%
+% XDrawTriangleEast(display,window_info,triangle_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the triangle.
+%
+*/
+static void XDrawTriangleEast(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *triangle_info)
+{
+ int
+ x1,
+ x2,
+ x3,
+ y1,
+ y2,
+ y3;
+
+ unsigned int
+ bevel_width;
+
+ XFontStruct
+ *font_info;
+
+ XPoint
+ points[4];
+
+ /*
+ Draw triangle matte.
+ */
+ x1=triangle_info->x;
+ y1=triangle_info->y;
+ x2=triangle_info->x+triangle_info->width;
+ y2=triangle_info->y+(triangle_info->height >> 1);
+ x3=triangle_info->x;
+ y3=triangle_info->y+triangle_info->height;
+ bevel_width=triangle_info->bevel_width;
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x3;
+ points[2].y=y3;
+ XSetMatteColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,3,Complex,CoordModeOrigin);
+ /*
+ Draw bottom bevel.
+ */
+ points[0].x=x2;
+ points[0].y=y2;
+ points[1].x=x3;
+ points[1].y=y3;
+ points[2].x=x3-bevel_width;
+ points[2].y=y3+bevel_width;
+ points[3].x=x2+bevel_width;
+ points[3].y=y2;
+ XSetBevelColor(display,window_info,!triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw Left bevel.
+ */
+ points[0].x=x3;
+ points[0].y=y3;
+ points[1].x=x1;
+ points[1].y=y1;
+ points[2].x=x1-bevel_width+1;
+ points[2].y=y1-bevel_width;
+ points[3].x=x3-bevel_width+1;
+ points[3].y=y3+bevel_width;
+ XSetBevelColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw top bevel.
+ */
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x2+bevel_width;
+ points[2].y=y2;
+ points[3].x=x1-bevel_width;
+ points[3].y=y1-bevel_width;
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
+ if (triangle_info->text == (char *) NULL)
+ return;
+ /*
+ Write label to right of triangle.
+ */
+ font_info=window_info->font_info;
+ XSetTextColor(display,window_info,MagickTrue);
+ x1=triangle_info->x+triangle_info->width+triangle_info->bevel_width+
+ (QuantumMargin >> 1);
+ y1=triangle_info->y+((triangle_info->height-
+ (font_info->ascent+font_info->descent)) >> 1)+font_info->ascent;
+ (void) XDrawString(display,window_info->id,window_info->widget_context,x1,y1,
+ triangle_info->text,Extent(triangle_info->text));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w T r i a n g l e N o r t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawTriangleNorth() draws a triangle with a highlighted left bevel and a
+% shadowed right and lower bevel. The highlighted and shadowed bevels create
+% a 3-D effect.
+%
+% The format of the XDrawTriangleNorth function is:
+%
+% XDrawTriangleNorth(display,window_info,triangle_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the triangle.
+%
+*/
+static void XDrawTriangleNorth(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *triangle_info)
+{
+ int
+ x1,
+ x2,
+ x3,
+ y1,
+ y2,
+ y3;
+
+ unsigned int
+ bevel_width;
+
+ XPoint
+ points[4];
+
+ /*
+ Draw triangle matte.
+ */
+ x1=triangle_info->x;
+ y1=triangle_info->y+triangle_info->height;
+ x2=triangle_info->x+(triangle_info->width >> 1);
+ y2=triangle_info->y;
+ x3=triangle_info->x+triangle_info->width;
+ y3=triangle_info->y+triangle_info->height;
+ bevel_width=triangle_info->bevel_width;
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x3;
+ points[2].y=y3;
+ XSetMatteColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,3,Complex,CoordModeOrigin);
+ /*
+ Draw left bevel.
+ */
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x2;
+ points[2].y=y2-bevel_width-2;
+ points[3].x=x1-bevel_width-1;
+ points[3].y=y1+bevel_width;
+ XSetBevelColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw right bevel.
+ */
+ points[0].x=x2;
+ points[0].y=y2;
+ points[1].x=x3;
+ points[1].y=y3;
+ points[2].x=x3+bevel_width;
+ points[2].y=y3+bevel_width;
+ points[3].x=x2;
+ points[3].y=y2-bevel_width;
+ XSetBevelColor(display,window_info,!triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw lower bevel.
+ */
+ points[0].x=x3;
+ points[0].y=y3;
+ points[1].x=x1;
+ points[1].y=y1;
+ points[2].x=x1-bevel_width;
+ points[2].y=y1+bevel_width;
+ points[3].x=x3+bevel_width;
+ points[3].y=y3+bevel_width;
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w T r i a n g l e S o u t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawTriangleSouth() draws a border with a highlighted left and right bevel
+% and a shadowed lower bevel. The highlighted and shadowed bevels create a
+% 3-D effect.
+%
+% The format of the XDrawTriangleSouth function is:
+%
+% XDrawTriangleSouth(display,window_info,triangle_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o triangle_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the triangle.
+%
+*/
+static void XDrawTriangleSouth(Display *display,const XWindowInfo *window_info,
+ const XWidgetInfo *triangle_info)
+{
+ int
+ x1,
+ x2,
+ x3,
+ y1,
+ y2,
+ y3;
+
+ unsigned int
+ bevel_width;
+
+ XPoint
+ points[4];
+
+ /*
+ Draw triangle matte.
+ */
+ x1=triangle_info->x;
+ y1=triangle_info->y;
+ x2=triangle_info->x+(triangle_info->width >> 1);
+ y2=triangle_info->y+triangle_info->height;
+ x3=triangle_info->x+triangle_info->width;
+ y3=triangle_info->y;
+ bevel_width=triangle_info->bevel_width;
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x3;
+ points[2].y=y3;
+ XSetMatteColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,3,Complex,CoordModeOrigin);
+ /*
+ Draw top bevel.
+ */
+ points[0].x=x3;
+ points[0].y=y3;
+ points[1].x=x1;
+ points[1].y=y1;
+ points[2].x=x1-bevel_width;
+ points[2].y=y1-bevel_width;
+ points[3].x=x3+bevel_width;
+ points[3].y=y3-bevel_width;
+ XSetBevelColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw right bevel.
+ */
+ points[0].x=x2;
+ points[0].y=y2;
+ points[1].x=x3+1;
+ points[1].y=y3-bevel_width;
+ points[2].x=x3+bevel_width;
+ points[2].y=y3-bevel_width;
+ points[3].x=x2;
+ points[3].y=y2+bevel_width;
+ XSetBevelColor(display,window_info,!triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ /*
+ Draw left bevel.
+ */
+ points[0].x=x1;
+ points[0].y=y1;
+ points[1].x=x2;
+ points[1].y=y2;
+ points[2].x=x2;
+ points[2].y=y2+bevel_width;
+ points[3].x=x1-bevel_width;
+ points[3].y=y1-bevel_width;
+ XSetBevelColor(display,window_info,triangle_info->raised);
+ (void) XFillPolygon(display,window_info->id,window_info->widget_context,
+ points,4,Complex,CoordModeOrigin);
+ (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D r a w W i d g e t T e x t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawWidgetText() first clears the widget and draws a text string justifed
+% left (or center) in the x-direction and centered within the y-direction.
+%
+% The format of the XDrawWidgetText function is:
+%
+% XDrawWidgetText(display,window_info,text_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a XWindowText structure.
+%
+% o text_info: Specifies a pointer to XWidgetInfo structure.
+%
+*/
+static void XDrawWidgetText(Display *display,const XWindowInfo *window_info,
+ XWidgetInfo *text_info)
+{
+ GC
+ widget_context;
+
+ int
+ x,
+ y;
+
+ unsigned int
+ height,
+ width;
+
+ XFontStruct
+ *font_info;
+
+ XRectangle
+ crop_info;
+
+ /*
+ Clear the text area.
+ */
+ widget_context=window_info->annotate_context;
+ if (text_info->raised)
+ (void) XClearArea(display,window_info->id,text_info->x,text_info->y,
+ text_info->width,text_info->height,MagickFalse);
+ else
+ {
+ (void) XFillRectangle(display,window_info->id,widget_context,text_info->x,
+ text_info->y,text_info->width,text_info->height);
+ widget_context=window_info->highlight_context;
+ }
+ if (text_info->text == (char *) NULL)
+ return;
+ if (*text_info->text == '\0')
+ return;
+ /*
+ Set cropping region.
+ */
+ font_info=window_info->font_info;
+ crop_info.width=(unsigned short) text_info->width;
+ crop_info.height=(unsigned short) text_info->height;
+ crop_info.x=text_info->x;
+ crop_info.y=text_info->y;
+ /*
+ Draw text.
+ */
+ width=WidgetTextWidth(font_info,text_info->text);
+ x=text_info->x+(QuantumMargin >> 1);
+ if (text_info->center)
+ x=text_info->x+(text_info->width >> 1)-(width >> 1);
+ if (text_info->raised)
+ if (width > (text_info->width-QuantumMargin))
+ x+=(text_info->width-QuantumMargin-width);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ y=text_info->y+((text_info->height-height) >> 1)+font_info->ascent;
+ (void) XSetClipRectangles(display,widget_context,0,0,&crop_info,1,Unsorted);
+ (void) XDrawString(display,window_info->id,widget_context,x,y,text_info->text,
+ Extent(text_info->text));
+ (void) XSetClipMask(display,widget_context,None);
+ if (x < text_info->x)
+ (void) XDrawLine(display,window_info->id,window_info->annotate_context,
+ text_info->x,text_info->y,text_info->x,text_info->y+text_info->height-1);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X E d i t T e x t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XEditText() edits a text string as indicated by the key symbol.
+%
+% The format of the XEditText function is:
+%
+% XEditText(display,text_info,key_symbol,text,state)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o text_info: Specifies a pointer to a XWidgetInfo structure. It
+% contains the extents of the text.
+%
+% o key_symbol: A X11 KeySym that indicates what editing function to
+% perform to the text.
+%
+% o text: A character string to insert into the text.
+%
+% o state: An unsigned long that indicates whether the key symbol is a
+% control character or not.
+%
+*/
+static void XEditText(Display *display,XWidgetInfo *text_info,
+ const KeySym key_symbol,char *text,const unsigned long state)
+{
+ switch ((int) key_symbol)
+ {
+ case XK_BackSpace:
+ case XK_Delete:
+ {
+ if (text_info->highlight)
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *text_info->text='\0';
+ text_info->cursor=text_info->text;
+ text_info->marker=text_info->text;
+ text_info->highlight=MagickFalse;
+ }
+ /*
+ Erase one character.
+ */
+ if (text_info->cursor != text_info->text)
+ {
+ text_info->cursor--;
+ (void) CopyMagickString(text_info->cursor,text_info->cursor+1,
+ MaxTextExtent);
+ text_info->highlight=MagickFalse;
+ break;
+ }
+ }
+ case XK_Left:
+ case XK_KP_Left:
+ {
+ /*
+ Move cursor one position left.
+ */
+ if (text_info->cursor == text_info->text)
+ break;
+ text_info->cursor--;
+ break;
+ }
+ case XK_Right:
+ case XK_KP_Right:
+ {
+ /*
+ Move cursor one position right.
+ */
+ if (text_info->cursor == (text_info->text+Extent(text_info->text)))
+ break;
+ text_info->cursor++;
+ break;
+ }
+ default:
+ {
+ register char
+ *p,
+ *q;
+
+ register int
+ i;
+
+ if (state & ControlState)
+ break;
+ if (*text == '\0')
+ break;
+ if ((Extent(text_info->text)+1) >= (long) MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ if (text_info->highlight)
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *text_info->text='\0';
+ text_info->cursor=text_info->text;
+ text_info->marker=text_info->text;
+ text_info->highlight=MagickFalse;
+ }
+ /*
+ Insert a string into the text.
+ */
+ q=text_info->text+Extent(text_info->text)+strlen(text);
+ for (i=0; i <= Extent(text_info->cursor); i++)
+ {
+ *q=(*(q-Extent(text)));
+ q--;
+ }
+ p=text;
+ for (i=0; i < Extent(text); i++)
+ *text_info->cursor++=(*p++);
+ }
+ break;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X G e t W i d g e t I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetWidgetInfo() initializes the XWidgetInfo structure.
+%
+% The format of the XGetWidgetInfo function is:
+%
+% XGetWidgetInfo(text,widget_info)
+%
+% A description of each parameter follows:
+%
+% o text: A string of characters associated with the widget.
+%
+% o widget_info: Specifies a pointer to a X11 XWidgetInfo structure.
+%
+*/
+static void XGetWidgetInfo(const char *text,XWidgetInfo *widget_info)
+{
+ /*
+ Initialize widget info.
+ */
+ widget_info->id=(~0);
+ widget_info->bevel_width=3;
+ widget_info->width=1;
+ widget_info->height=1;
+ widget_info->x=0;
+ widget_info->y=0;
+ widget_info->min_y=0;
+ widget_info->max_y=0;
+ widget_info->raised=MagickTrue;
+ widget_info->active=MagickFalse;
+ widget_info->center=MagickTrue;
+ widget_info->trough=MagickFalse;
+ widget_info->highlight=MagickFalse;
+ widget_info->text=(char *) text;
+ widget_info->cursor=(char *) text;
+ if (text != (char *) NULL)
+ widget_info->cursor+=Extent(text);
+ widget_info->marker=(char *) text;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X H i g h l i g h t W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XHighlightWidget() draws a highlighted border around a window.
+%
+% The format of the XHighlightWidget function is:
+%
+% XHighlightWidget(display,window_info,x,y)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o x: Specifies an integer representing the rectangle offset in the
+% x-direction.
+%
+% o y: Specifies an integer representing the rectangle offset in the
+% y-direction.
+%
+*/
+static void XHighlightWidget(Display *display,const XWindowInfo *window_info,
+ const int x,const int y)
+{
+ /*
+ Draw the widget highlighting rectangle.
+ */
+ XSetBevelColor(display,window_info,MagickTrue);
+ (void) XDrawRectangle(display,window_info->id,window_info->widget_context,x,y,
+ window_info->width-(x << 1),window_info->height-(y << 1));
+ (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
+ x-1,y-1,window_info->width-(x << 1)+1,window_info->height-(y << 1)+1);
+ XSetBevelColor(display,window_info,MagickFalse);
+ (void) XDrawRectangle(display,window_info->id,window_info->widget_context,
+ x-1,y-1,window_info->width-(x << 1),window_info->height-(y << 1));
+ (void) XSetFillStyle(display,window_info->widget_context,FillSolid);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S c r e e n E v e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XScreenEvent() returns MagickTrue if the any event on the X server queue is
+% associated with the widget window.
+%
+% The format of the XScreenEvent function is:
+%
+% int XScreenEvent(Display *display,XEvent *event,char *data)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o event: Specifies a pointer to a X11 XEvent structure.
+%
+% o data: Specifies a pointer to a XWindows structure.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int XScreenEvent(Display *display,XEvent *event,char *data)
+{
+ XWindows
+ *windows;
+
+ windows=(XWindows *) data;
+ if (event->xany.window == windows->popup.id)
+ {
+ if (event->type == MapNotify)
+ windows->popup.mapped=MagickTrue;
+ if (event->type == UnmapNotify)
+ windows->popup.mapped=MagickFalse;
+ return(MagickTrue);
+ }
+ if (event->xany.window == windows->widget.id)
+ {
+ if (event->type == MapNotify)
+ windows->widget.mapped=MagickTrue;
+ if (event->type == UnmapNotify)
+ windows->widget.mapped=MagickFalse;
+ return(MagickTrue);
+ }
+ switch (event->type)
+ {
+ case ButtonPress:
+ {
+ if ((event->xbutton.button == Button3) &&
+ (event->xbutton.state & Mod1Mask))
+ {
+ /*
+ Convert Alt-Button3 to Button2.
+ */
+ event->xbutton.button=Button2;
+ event->xbutton.state&=(~Mod1Mask);
+ }
+ return(MagickTrue);
+ }
+ case Expose:
+ {
+ if (event->xexpose.window == windows->image.id)
+ {
+ XRefreshWindow(display,&windows->image,event);
+ break;
+ }
+ if (event->xexpose.window == windows->magnify.id)
+ if (event->xexpose.count == 0)
+ if (windows->magnify.mapped)
+ {
+ XMakeMagnifyImage(display,windows);
+ break;
+ }
+ if (event->xexpose.window == windows->command.id)
+ if (event->xexpose.count == 0)
+ {
+ (void) XCommandWidget(display,windows,(const char **) NULL,event);
+ break;
+ }
+ break;
+ }
+ case FocusOut:
+ {
+ /*
+ Set input focus for backdrop window.
+ */
+ if (event->xfocus.window == windows->image.id)
+ (void) XSetInputFocus(display,windows->image.id,RevertToNone,
+ CurrentTime);
+ return(MagickTrue);
+ }
+ case ButtonRelease:
+ case KeyPress:
+ case KeyRelease:
+ case MotionNotify:
+ case SelectionNotify:
+ return(MagickTrue);
+ default:
+ break;
+ }
+ return(MagickFalse);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S e t B e v e l C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetBevelColor() sets the graphic context for drawing a beveled border.
+%
+% The format of the XSetBevelColor function is:
+%
+% XSetBevelColor(display,window_info,raised)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o raised: A value other than zero indicates the color show be a
+% "highlight" color, otherwise the "shadow" color is set.
+%
+*/
+static void XSetBevelColor(Display *display,const XWindowInfo *window_info,
+ const MagickStatusType raised)
+{
+ if (window_info->depth == 1)
+ {
+ Pixmap
+ stipple;
+
+ /*
+ Monochrome window.
+ */
+ (void) XSetBackground(display,window_info->widget_context,
+ XBlackPixel(display,window_info->screen));
+ (void) XSetForeground(display,window_info->widget_context,
+ XWhitePixel(display,window_info->screen));
+ (void) XSetFillStyle(display,window_info->widget_context,
+ FillOpaqueStippled);
+ stipple=window_info->highlight_stipple;
+ if (raised == MagickFalse)
+ stipple=window_info->shadow_stipple;
+ (void) XSetStipple(display,window_info->widget_context,stipple);
+ }
+ else
+ if (raised)
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->highlight_color.pixel);
+ else
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->shadow_color.pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S e t M a t t e C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetMatteColor() sets the graphic context for drawing the matte.
+%
+% The format of the XSetMatteColor function is:
+%
+% XSetMatteColor(display,window_info,raised)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o raised: A value other than zero indicates the matte is active.
+%
+*/
+static void XSetMatteColor(Display *display,const XWindowInfo *window_info,
+ const MagickStatusType raised)
+{
+ if (window_info->depth == 1)
+ {
+ /*
+ Monochrome window.
+ */
+ if (raised)
+ (void) XSetForeground(display,window_info->widget_context,
+ XWhitePixel(display,window_info->screen));
+ else
+ (void) XSetForeground(display,window_info->widget_context,
+ XBlackPixel(display,window_info->screen));
+ }
+ else
+ if (raised)
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->matte_color.pixel);
+ else
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->depth_color.pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X S e t T e x t C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetTextColor() sets the graphic context for drawing text on a matte.
+%
+% The format of the XSetTextColor function is:
+%
+% XSetTextColor(display,window_info,raised)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+% o raised: A value other than zero indicates the color show be a
+% "highlight" color, otherwise the "shadow" color is set.
+%
+*/
+static void XSetTextColor(Display *display,const XWindowInfo *window_info,
+ const MagickStatusType raised)
+{
+ long
+ foreground,
+ matte;
+
+ if (window_info->depth == 1)
+ {
+ /*
+ Monochrome window.
+ */
+ if (raised)
+ (void) XSetForeground(display,window_info->widget_context,
+ XBlackPixel(display,window_info->screen));
+ else
+ (void) XSetForeground(display,window_info->widget_context,
+ XWhitePixel(display,window_info->screen));
+ return;
+ }
+ foreground=(long) XPixelIntensity(&window_info->pixel_info->foreground_color);
+ matte=(long) XPixelIntensity(&window_info->pixel_info->matte_color);
+ if (MagickAbsoluteValue(foreground-matte) > (65535L >> 3))
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->foreground_color.pixel);
+ else
+ (void) XSetForeground(display,window_info->widget_context,
+ window_info->pixel_info->background_color.pixel);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C o l o r B r o w s e r W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XColorBrowserWidget() displays a Color Browser widget with a color query
+% to the user. The user keys a reply and presses the Action or Cancel button
+% to exit. The typed text is returned as the reply function parameter.
+%
+% The format of the XColorBrowserWidget method is:
+%
+% void XColorBrowserWidget(Display *display,XWindows *windows,
+% const char *action,char *reply)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o action: Specifies a pointer to the action of this widget.
+%
+% o reply: the response from the user is returned in this parameter.
+%
+*/
+MagickExport void XColorBrowserWidget(Display *display,XWindows *windows,
+ const char *action,char *reply)
+{
+#define CancelButtonText "Cancel"
+#define ColornameText "Name:"
+#define ColorPatternText "Pattern:"
+#define GrabButtonText "Grab"
+#define ResetButtonText "Reset"
+
+ char
+ **colorlist,
+ primary_selection[MaxTextExtent],
+ reset_pattern[MaxTextExtent],
+ text[MaxTextExtent];
+
+ ExceptionInfo
+ *exception;
+
+ int
+ x,
+ y;
+
+ register int
+ i;
+
+ static char
+ glob_pattern[MaxTextExtent] = "*";
+
+ static MagickStatusType
+ mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
+
+ Status
+ status;
+
+ unsigned int
+ height,
+ text_width,
+ visible_colors,
+ width;
+
+ unsigned long
+ colors,
+ delay,
+ state;
+
+ XColor
+ color;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ action_info,
+ cancel_info,
+ expose_info,
+ grab_info,
+ list_info,
+ mode_info,
+ north_info,
+ reply_info,
+ reset_info,
+ scroll_info,
+ selection_info,
+ slider_info,
+ south_info,
+ text_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Get color list and sort in ascending order.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(action != (char *) NULL);
+ assert(reply != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
+ exception=AcquireExceptionInfo();
+ colorlist=GetColorList(glob_pattern,&colors,exception);
+ if (colorlist == (char **) NULL)
+ {
+ /*
+ Pattern failed, obtain all the colors.
+ */
+ (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
+ colorlist=GetColorList(glob_pattern,&colors,exception);
+ if (colorlist == (char **) NULL)
+ {
+ XNoticeWidget(display,windows,"Unable to obtain colors names:",
+ glob_pattern);
+ (void) XDialogWidget(display,windows,action,"Enter color name:",
+ reply);
+ return;
+ }
+ }
+ /*
+ Determine Color Browser widget attributes.
+ */
+ font_info=windows->widget.font_info;
+ text_width=0;
+ for (i=0; i < (long) colors; i++)
+ if (WidgetTextWidth(font_info,colorlist[i]) > text_width)
+ text_width=WidgetTextWidth(font_info,colorlist[i]);
+ width=WidgetTextWidth(font_info,(char *) action);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ if (WidgetTextWidth(font_info,ResetButtonText) > width)
+ width=WidgetTextWidth(font_info,ResetButtonText);
+ if (WidgetTextWidth(font_info,GrabButtonText) > width)
+ width=WidgetTextWidth(font_info,GrabButtonText);
+ width+=QuantumMargin;
+ if (WidgetTextWidth(font_info,ColorPatternText) > width)
+ width=WidgetTextWidth(font_info,ColorPatternText);
+ if (WidgetTextWidth(font_info,ColornameText) > width)
+ width=WidgetTextWidth(font_info,ColornameText);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Color Browser widget.
+ */
+ windows->widget.width=(unsigned int)
+ (width+MagickMin((int) text_width,(int) MaxTextWidth)+6*QuantumMargin);
+ windows->widget.min_width=(unsigned int)
+ (width+MinTextWidth+4*QuantumMargin);
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int)
+ ((81*height) >> 2)+((13*QuantumMargin) >> 1)+4;
+ windows->widget.min_height=(unsigned int)
+ (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Color Browser widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Browse and Select a Color",
+ MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ mask,&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ XGetWidgetInfo((char *) NULL,&slider_info);
+ XGetWidgetInfo((char *) NULL,&north_info);
+ XGetWidgetInfo((char *) NULL,&south_info);
+ XGetWidgetInfo((char *) NULL,&expose_info);
+ visible_colors=0;
+ delay=SuspendTime << 2;
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ int
+ id;
+
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int)
+ (windows->widget.width-cancel_info.width-QuantumMargin-2);
+ cancel_info.y=(int)
+ (windows->widget.height-cancel_info.height-QuantumMargin);
+ XGetWidgetInfo(action,&action_info);
+ action_info.width=width;
+ action_info.height=(unsigned int) ((3*height) >> 1);
+ action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
+ (action_info.bevel_width << 1));
+ action_info.y=cancel_info.y;
+ XGetWidgetInfo(GrabButtonText,&grab_info);
+ grab_info.width=width;
+ grab_info.height=(unsigned int) ((3*height) >> 1);
+ grab_info.x=QuantumMargin;
+ grab_info.y=((5*QuantumMargin) >> 1)+height;
+ XGetWidgetInfo(ResetButtonText,&reset_info);
+ reset_info.width=width;
+ reset_info.height=(unsigned int) ((3*height) >> 1);
+ reset_info.x=QuantumMargin;
+ reset_info.y=grab_info.y+grab_info.height+QuantumMargin;
+ /*
+ Initialize reply information.
+ */
+ XGetWidgetInfo(reply,&reply_info);
+ reply_info.raised=MagickFalse;
+ reply_info.bevel_width--;
+ reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
+ reply_info.height=height << 1;
+ reply_info.x=(int) (width+(QuantumMargin << 1));
+ reply_info.y=action_info.y-reply_info.height-QuantumMargin;
+ /*
+ Initialize mode information.
+ */
+ XGetWidgetInfo((char *) NULL,&mode_info);
+ mode_info.active=MagickTrue;
+ mode_info.bevel_width=0;
+ mode_info.width=(unsigned int) (action_info.x-(QuantumMargin << 1));
+ mode_info.height=action_info.height;
+ mode_info.x=QuantumMargin;
+ mode_info.y=action_info.y;
+ /*
+ Initialize scroll information.
+ */
+ XGetWidgetInfo((char *) NULL,&scroll_info);
+ scroll_info.bevel_width--;
+ scroll_info.width=height;
+ scroll_info.height=(unsigned int) (reply_info.y-grab_info.y-
+ (QuantumMargin >> 1));
+ scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
+ scroll_info.y=grab_info.y-reply_info.bevel_width;
+ scroll_info.raised=MagickFalse;
+ scroll_info.trough=MagickTrue;
+ north_info=scroll_info;
+ north_info.raised=MagickTrue;
+ north_info.width-=(north_info.bevel_width << 1);
+ north_info.height=north_info.width-1;
+ north_info.x+=north_info.bevel_width;
+ north_info.y+=north_info.bevel_width;
+ south_info=north_info;
+ south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
+ south_info.height;
+ id=slider_info.id;
+ slider_info=north_info;
+ slider_info.id=id;
+ slider_info.width-=2;
+ slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
+ slider_info.bevel_width+2;
+ slider_info.height=scroll_info.height-((slider_info.min_y-
+ scroll_info.y+1) << 1)+4;
+ visible_colors=scroll_info.height/(height+(height >> 3));
+ if (colors > visible_colors)
+ slider_info.height=(unsigned int)
+ ((visible_colors*slider_info.height)/colors);
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.x=scroll_info.x+slider_info.bevel_width+1;
+ slider_info.y=slider_info.min_y;
+ expose_info=scroll_info;
+ expose_info.y=slider_info.y;
+ /*
+ Initialize list information.
+ */
+ XGetWidgetInfo((char *) NULL,&list_info);
+ list_info.raised=MagickFalse;
+ list_info.bevel_width--;
+ list_info.width=(unsigned int)
+ (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
+ list_info.height=scroll_info.height;
+ list_info.x=reply_info.x;
+ list_info.y=scroll_info.y;
+ if (windows->widget.mapped == MagickFalse)
+ state|=JumpListState;
+ /*
+ Initialize text information.
+ */
+ *text='\0';
+ XGetWidgetInfo(text,&text_info);
+ text_info.center=MagickFalse;
+ text_info.width=reply_info.width;
+ text_info.height=height;
+ text_info.x=list_info.x-(QuantumMargin >> 1);
+ text_info.y=QuantumMargin;
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=list_info.width;
+ selection_info.height=(unsigned int) ((9*height) >> 3);
+ selection_info.x=list_info.x;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Color Browser window.
+ */
+ x=QuantumMargin;
+ y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,ColorPatternText,
+ Extent(ColorPatternText));
+ (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawBeveledButton(display,&windows->widget,&grab_info);
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ XDrawBeveledMatte(display,&windows->widget,&list_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ x=QuantumMargin;
+ y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,ColornameText,
+ Extent(ColornameText));
+ XDrawBeveledMatte(display,&windows->widget,&reply_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ selection_info.id=(~0);
+ state|=RedrawActionState;
+ state|=RedrawListState;
+ state&=(~RedrawWidgetState);
+ }
+ if (state & UpdateListState)
+ {
+ char
+ **checklist;
+
+ unsigned long
+ number_colors;
+
+ status=XParseColor(display,windows->widget.map_info->colormap,
+ glob_pattern,&color);
+ if ((status != False) || (strchr(glob_pattern,'-') != (char *) NULL))
+ {
+ /*
+ Reply is a single color name-- exit.
+ */
+ (void) CopyMagickString(reply,glob_pattern,MaxTextExtent);
+ (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ /*
+ Update color list.
+ */
+ checklist=GetColorList(glob_pattern,&number_colors,exception);
+ if (number_colors == 0)
+ {
+ (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
+ (void) XBell(display,0);
+ }
+ else
+ {
+ for (i=0; i < (long) colors; i++)
+ colorlist[i]=DestroyString(colorlist[i]);
+ if (colorlist != (char **) NULL)
+ colorlist=(char **) RelinquishMagickMemory(colorlist);
+ colorlist=checklist;
+ colors=number_colors;
+ }
+ /*
+ Sort color list in ascending order.
+ */
+ slider_info.height=
+ scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
+ if (colors > visible_colors)
+ slider_info.height=(unsigned int)
+ ((visible_colors*slider_info.height)/colors);
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ expose_info.y=slider_info.y;
+ selection_info.id=(~0);
+ list_info.id=(~0);
+ state|=RedrawListState;
+ /*
+ Redraw color name & reply.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~UpdateListState);
+ }
+ if (state & JumpListState)
+ {
+ /*
+ Jump scroll to match user color.
+ */
+ list_info.id=(~0);
+ for (i=0; i < (long) colors; i++)
+ if (LocaleCompare(colorlist[i],reply) >= 0)
+ {
+ list_info.id=LocaleCompare(colorlist[i],reply) == 0 ? i : ~0;
+ break;
+ }
+ if ((i < slider_info.id) ||
+ (i >= (int) (slider_info.id+visible_colors)))
+ slider_info.id=i-(visible_colors >> 1);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ state&=(~JumpListState);
+ }
+ if (state & RedrawListState)
+ {
+ /*
+ Determine slider id and position.
+ */
+ if (slider_info.id >= (int) (colors-visible_colors))
+ slider_info.id=(int) (colors-visible_colors);
+ if ((slider_info.id < 0) || (colors <= visible_colors))
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ if (colors != 0)
+ slider_info.y+=slider_info.id*(slider_info.max_y-
+ slider_info.min_y+1)/colors;
+ if (slider_info.id != selection_info.id)
+ {
+ /*
+ Redraw scroll bar and file names.
+ */
+ selection_info.id=slider_info.id;
+ selection_info.y=list_info.y+(height >> 3)+2;
+ for (i=0; i < (int) visible_colors; i++)
+ {
+ selection_info.raised=(slider_info.id+i) != list_info.id ?
+ MagickTrue : MagickFalse;
+ selection_info.text=(char *) NULL;
+ if ((slider_info.id+i) < (long) colors)
+ selection_info.text=colorlist[slider_info.id+i];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ /*
+ Update slider.
+ */
+ if (slider_info.y > expose_info.y)
+ {
+ expose_info.height=(unsigned int) slider_info.y-expose_info.y;
+ expose_info.y=slider_info.y-expose_info.height-
+ slider_info.bevel_width-1;
+ }
+ else
+ {
+ expose_info.height=(unsigned int) expose_info.y-slider_info.y;
+ expose_info.y=slider_info.y+slider_info.height+
+ slider_info.bevel_width+1;
+ }
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawMatte(display,&windows->widget,&expose_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ expose_info.y=slider_info.y;
+ }
+ state&=(~RedrawListState);
+ }
+ if (state & RedrawActionState)
+ {
+ static char
+ colorname[MaxTextExtent];
+
+ /*
+ Display the selected color in a drawing area.
+ */
+ color=windows->widget.pixel_info->matte_color;
+ (void) XParseColor(display,windows->widget.map_info->colormap,
+ reply_info.text,&windows->widget.pixel_info->matte_color);
+ XBestPixel(display,windows->widget.map_info->colormap,(XColor *) NULL,
+ (unsigned int) windows->widget.visual_info->colormap_size,
+ &windows->widget.pixel_info->matte_color);
+ mode_info.text=colorname;
+ (void) FormatMagickString(mode_info.text,MaxTextExtent,"#%02x%02x%02x",
+ windows->widget.pixel_info->matte_color.red,
+ windows->widget.pixel_info->matte_color.green,
+ windows->widget.pixel_info->matte_color.blue);
+ XDrawBeveledButton(display,&windows->widget,&mode_info);
+ windows->widget.pixel_info->matte_color=color;
+ state&=(~RedrawActionState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (north_info.raised && south_info.raised)
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ else
+ {
+ /*
+ Brief delay before advancing scroll bar.
+ */
+ XDelay(display,delay);
+ delay=SuspendTime;
+ (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
+ if (north_info.raised == MagickFalse)
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ slider_info.id--;
+ state|=RedrawListState;
+ }
+ if (south_info.raised == MagickFalse)
+ if (slider_info.id < (long) colors)
+ {
+ /*
+ Move slider down.
+ */
+ slider_info.id++;
+ state|=RedrawListState;
+ }
+ if (event.type != ButtonRelease)
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(slider_info,event.xbutton))
+ {
+ /*
+ Track slider.
+ */
+ slider_info.active=MagickTrue;
+ break;
+ }
+ if (MatteIsActive(north_info,event.xbutton))
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ north_info.raised=MagickFalse;
+ slider_info.id--;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(south_info,event.xbutton))
+ if (slider_info.id < (long) colors)
+ {
+ /*
+ Move slider down.
+ */
+ south_info.raised=MagickFalse;
+ slider_info.id++;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(scroll_info,event.xbutton))
+ {
+ /*
+ Move slider.
+ */
+ if (event.xbutton.y < slider_info.y)
+ slider_info.id-=(visible_colors-1);
+ else
+ slider_info.id+=(visible_colors-1);
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(list_info,event.xbutton))
+ {
+ int
+ id;
+
+ /*
+ User pressed list matte.
+ */
+ id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
+ selection_info.height;
+ if (id >= (int) colors)
+ break;
+ (void) CopyMagickString(reply_info.text,colorlist[id],
+ MaxTextExtent);
+ reply_info.highlight=MagickFalse;
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=RedrawActionState;
+ if (id == list_info.id)
+ {
+ (void) CopyMagickString(glob_pattern,reply_info.text,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ selection_info.id=(~0);
+ list_info.id=id;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(grab_info,event.xbutton))
+ {
+ /*
+ User pressed Grab button.
+ */
+ grab_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&grab_info);
+ break;
+ }
+ if (MatteIsActive(reset_info,event.xbutton))
+ {
+ /*
+ User pressed Reset button.
+ */
+ reset_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ break;
+ }
+ if (MatteIsActive(mode_info,event.xbutton))
+ {
+ /*
+ User pressed mode button.
+ */
+ (void) CopyMagickString(reply_info.text,mode_info.text,
+ MaxTextExtent);
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ windows->widget.id ? MagickTrue : MagickFalse;
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ /*
+ User pressed action button.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ if (event.xbutton.button != Button2)
+ {
+ static Time
+ click_time;
+
+ /*
+ Move text cursor to position of button press.
+ */
+ x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
+ for (i=1; i <= Extent(reply_info.marker); i++)
+ if (XTextWidth(font_info,reply_info.marker,i) > x)
+ break;
+ reply_info.cursor=reply_info.marker+i-1;
+ if (event.xbutton.time > (click_time+DoubleClick))
+ reply_info.highlight=MagickFalse;
+ else
+ {
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ windows->widget.id ? MagickTrue : MagickFalse;
+ }
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ click_time=event.xbutton.time;
+ break;
+ }
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ windows->widget.id,event.xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (north_info.raised == MagickFalse)
+ {
+ /*
+ User released up button.
+ */
+ delay=SuspendTime << 2;
+ north_info.raised=MagickTrue;
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ }
+ if (south_info.raised == MagickFalse)
+ {
+ /*
+ User released down button.
+ */
+ delay=SuspendTime << 2;
+ south_info.raised=MagickTrue;
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ }
+ if (slider_info.active)
+ {
+ /*
+ Stop tracking slider.
+ */
+ slider_info.active=MagickFalse;
+ break;
+ }
+ if (grab_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(grab_info,event.xbutton))
+ {
+ /*
+ Select a pen color from the X server.
+ */
+ (void) XGetWindowColor(display,windows,reply_info.text);
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=RedrawActionState;
+ }
+ grab_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&grab_info);
+ }
+ if (reset_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(reset_info,event.xbutton))
+ {
+ (void) CopyMagickString(glob_pattern,reset_pattern,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ reset_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ }
+ if (action_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ {
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ if (*reply_info.text == '\0')
+ (void) XBell(display,0);
+ else
+ state|=ExitState;
+ }
+ }
+ action_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (AreaIsActive(scroll_info,event.xkey))
+ {
+ /*
+ Move slider.
+ */
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ slider_info.id=0;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ slider_info.id--;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ slider_info.id++;
+ break;
+ }
+ case XK_Prior:
+ case XK_KP_Prior:
+ {
+ slider_info.id-=visible_colors;
+ break;
+ }
+ case XK_Next:
+ case XK_KP_Next:
+ {
+ slider_info.id+=visible_colors;
+ break;
+ }
+ case XK_End:
+ case XK_KP_End:
+ {
+ slider_info.id=(int) colors;
+ break;
+ }
+ }
+ state|=RedrawListState;
+ break;
+ }
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ /*
+ Read new color or glob patterm.
+ */
+ if (*reply_info.text == '\0')
+ break;
+ (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
+ state|=UpdateListState;
+ break;
+ }
+ if (key_symbol == XK_Control_L)
+ {
+ state|=ControlState;
+ break;
+ }
+ if (state & ControlState)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ reply_info.marker=reply_info.text;
+ reply_info.highlight=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ XEditText(display,&reply_info,key_symbol,command,state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ status=XParseColor(display,windows->widget.map_info->colormap,
+ reply_info.text,&color);
+ if (status != False)
+ state|=RedrawActionState;
+ break;
+ }
+ case KeyRelease:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key release.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (key_symbol == XK_Control_L)
+ state&=(~ControlState);
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MapNotify:
+ {
+ mask&=(~CWX);
+ mask&=(~CWY);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (slider_info.active)
+ {
+ /*
+ Move slider matte.
+ */
+ slider_info.y=event.xmotion.y-
+ ((slider_info.height+slider_info.bevel_width) >> 1)+1;
+ if (slider_info.y < slider_info.min_y)
+ slider_info.y=slider_info.min_y;
+ if (slider_info.y > slider_info.max_y)
+ slider_info.y=slider_info.max_y;
+ slider_info.id=0;
+ if (slider_info.y != slider_info.min_y)
+ slider_info.id=(int) ((colors*(slider_info.y-
+ slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
+ state|=RedrawListState;
+ break;
+ }
+ if (state & InactiveWidgetState)
+ break;
+ if (grab_info.raised == MatteIsActive(grab_info,event.xmotion))
+ {
+ /*
+ Grab button status changed.
+ */
+ grab_info.raised=!grab_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&grab_info);
+ break;
+ }
+ if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
+ {
+ /*
+ Reset button status changed.
+ */
+ reset_info.raised=!reset_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ break;
+ }
+ if (action_info.raised == MatteIsActive(action_info,event.xmotion))
+ {
+ /*
+ Action button status changed.
+ */
+ action_info.raised=action_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ reply_info.highlight=MagickFalse;
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,event.xselection.requestor,
+ event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
+ &format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ if ((Extent(reply_info.text)+length) >= MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ /*
+ Insert primary selection in reply text.
+ */
+ *(data+length)='\0';
+ XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
+ state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ state|=RedrawActionState;
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ if (reply_info.highlight == MagickFalse)
+ break;
+ /*
+ Set primary selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.send_event=MagickTrue;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,
+ NoEventMask,(XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ /*
+ Free color list.
+ */
+ for (i=0; i < (long) colors; i++)
+ colorlist[i]=DestroyString(colorlist[i]);
+ if (colorlist != (char **) NULL)
+ colorlist=(char **) RelinquishMagickMemory(colorlist);
+ exception=DestroyExceptionInfo(exception);
+ if ((*reply == '\0') || (strchr(reply,'-') != (char *) NULL))
+ return;
+ status=XParseColor(display,windows->widget.map_info->colormap,reply,&color);
+ if (status != False)
+ return;
+ XNoticeWidget(display,windows,"Color is unknown to X server:",reply);
+ (void) CopyMagickString(reply,"gray",MaxTextExtent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C o m m a n d W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XCommandWidget() maps a menu and returns the command pointed to by the user
+% when the button is released.
+%
+% The format of the XCommandWidget method is:
+%
+% int XCommandWidget(Display *display,XWindows *windows,
+% const char **selections,XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o selection_number: Specifies the number of the selection that the
+% user choose.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o selections: Specifies a pointer to one or more strings that comprise
+% the choices in the menu.
+%
+% o event: Specifies a pointer to a X11 XEvent structure.
+%
+*/
+MagickExport int XCommandWidget(Display *display,XWindows *windows,
+ const char **selections,XEvent *event)
+{
+#define tile_width 112
+#define tile_height 70
+
+ static const unsigned char
+ tile_bits[]=
+ {
+ 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x38, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x1e, 0xbc, 0x9f, 0x03, 0x00, 0x3e, 0x00, 0xc0,
+ 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x0f, 0x80, 0x3f,
+ 0x00, 0xf0, 0x1f, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc, 0xff, 0x1f,
+ 0xe0, 0x3f, 0x00, 0xfc, 0x1f, 0xf0, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0xfc,
+ 0xff, 0x1f, 0xf0, 0x3f, 0x00, 0xfe, 0x1f, 0xf8, 0x0f, 0x00, 0x00, 0x00,
+ 0x1e, 0xfc, 0xfc, 0x3f, 0xf8, 0x3f, 0x00, 0xff, 0x1e, 0xfc, 0x0f, 0x00,
+ 0x00, 0x00, 0x1e, 0x7c, 0xfc, 0x3e, 0xf8, 0x3c, 0x80, 0x1f, 0x1e, 0x7c,
+ 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c, 0xc0, 0x0f,
+ 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c, 0x7c, 0x3c,
+ 0xc0, 0x07, 0x1e, 0x3e, 0x0f, 0x00, 0x00, 0x00, 0x1e, 0x78, 0x78, 0x3c,
+ 0x7c, 0x7c, 0xc0, 0x0f, 0x1e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x78,
+ 0x78, 0x3c, 0xfc, 0x7c, 0x80, 0x7f, 0x1e, 0x7c, 0x00, 0x00, 0x00, 0x00,
+ 0x1e, 0xf8, 0x78, 0x7c, 0xf8, 0xff, 0x00, 0xff, 0x1f, 0xf8, 0xff, 0x00,
+ 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xfe, 0x1f, 0xf8,
+ 0xff, 0x00, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xf0, 0xff, 0x07, 0xf8,
+ 0x1f, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0xf8, 0x78, 0x7c, 0xc0, 0xef,
+ 0x07, 0xe0, 0x1f, 0xc0, 0xff, 0x01, 0x00, 0x00, 0x1e, 0x70, 0x40, 0x78,
+ 0x00, 0xc7, 0x07, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07,
+ 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x60, 0x00, 0xc0, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xc0, 0x8f, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0x9f, 0x7f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0, 0xdf,
+ 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x78, 0x00,
+ 0xe0, 0xdf, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x0c,
+ 0x78, 0x30, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e,
+ 0x00, 0x0f, 0xf8, 0x70, 0xf0, 0xff, 0x7b, 0x00, 0x00, 0x1f, 0x00, 0xe0,
+ 0x0f, 0x1e, 0x80, 0x0f, 0xf8, 0x78, 0xf0, 0xfd, 0xf9, 0x00, 0xc0, 0x1f,
+ 0x00, 0xf8, 0x0f, 0x00, 0xe0, 0x1f, 0xf8, 0x7c, 0xf0, 0xfc, 0xf9, 0x00,
+ 0xf0, 0x1f, 0x00, 0xfe, 0x0f, 0x00, 0xf0, 0x07, 0xf8, 0x3e, 0xf8, 0xfc,
+ 0xf0, 0x01, 0xf8, 0x1f, 0x00, 0xff, 0x0f, 0x1e, 0xf0, 0x03, 0xf8, 0x3f,
+ 0xf8, 0xf8, 0xf0, 0x01, 0xfc, 0x1f, 0x80, 0x7f, 0x0f, 0x1e, 0xf8, 0x00,
+ 0xf8, 0x1f, 0x78, 0x18, 0xf0, 0x01, 0x7c, 0x1e, 0xc0, 0x0f, 0x0f, 0x1e,
+ 0x7c, 0x00, 0xf0, 0x0f, 0x78, 0x00, 0xf0, 0x01, 0x3e, 0x1e, 0xe0, 0x07,
+ 0x0f, 0x1e, 0x7c, 0x00, 0xf0, 0x07, 0x7c, 0x00, 0xe0, 0x01, 0x3e, 0x1e,
+ 0xe0, 0x03, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x0f, 0x7c, 0x00, 0xe0, 0x03,
+ 0x3e, 0x3e, 0xe0, 0x07, 0x0f, 0x1e, 0x1e, 0x00, 0xf0, 0x1f, 0x3c, 0x00,
+ 0xe0, 0x03, 0x7e, 0x3e, 0xc0, 0x3f, 0x0f, 0x1e, 0x3e, 0x00, 0xf0, 0x1f,
+ 0x3e, 0x00, 0xe0, 0x03, 0xfc, 0x7f, 0x80, 0xff, 0x0f, 0x1e, 0xfc, 0x00,
+ 0xf0, 0x3e, 0x3e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xff, 0x0f, 0x1e,
+ 0xfc, 0x07, 0xf0, 0x7c, 0x1e, 0x00, 0xc0, 0x03, 0xf8, 0xff, 0x03, 0xfc,
+ 0x0f, 0x1e, 0xf8, 0x1f, 0xf0, 0xf8, 0x1e, 0x00, 0xc0, 0x03, 0xe0, 0xf7,
+ 0x03, 0xf0, 0x0f, 0x1e, 0xe0, 0x3f, 0xf0, 0x78, 0x1c, 0x00, 0x80, 0x03,
+ 0x80, 0xe3, 0x03, 0x00, 0x0f, 0x1e, 0xc0, 0x3f, 0xf0, 0x30, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x00, 0x3e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0f, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ int
+ id,
+ y;
+
+ register int
+ i;
+
+ static unsigned int
+ number_selections;
+
+ unsigned int
+ height;
+
+ unsigned long
+ state;
+
+ XFontStruct
+ *font_info;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ font_info=windows->command.font_info;
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ id=(~0);
+ state=DefaultState;
+ if (event == (XEvent *) NULL)
+ {
+ unsigned int
+ width;
+
+ XTextProperty
+ window_name;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine command window attributes.
+ */
+ assert(selections != (const char **) NULL);
+ windows->command.width=0;
+ for (i=0; selections[i] != (char *) NULL; i++)
+ {
+ width=WidgetTextWidth(font_info,(char *) selections[i]);
+ if (width > windows->command.width)
+ windows->command.width=width;
+ }
+ number_selections=(unsigned int) i;
+ windows->command.width+=3*QuantumMargin+10;
+ if ((int) windows->command.width < (tile_width+QuantumMargin+10))
+ windows->command.width=(unsigned int) (tile_width+QuantumMargin+10);
+ windows->command.height=(unsigned int) (number_selections*
+ (((3*height) >> 1)+10)+tile_height+20);
+ windows->command.min_width=windows->command.width;
+ windows->command.min_height=windows->command.height;
+ XConstrainWindowPosition(display,&windows->command);
+ if (windows->command.id != (Window) NULL)
+ {
+ Status
+ status;
+
+ /*
+ Reconfigure command window.
+ */
+ status=XStringListToTextProperty(&windows->command.name,1,
+ &window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->command.id,&window_name);
+ XSetWMIconName(display,windows->command.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->command.width;
+ window_changes.height=(int) windows->command.height;
+ (void) XReconfigureWMWindow(display,windows->command.id,
+ windows->command.screen,(unsigned int) (CWWidth | CWHeight),
+ &window_changes);
+ }
+ /*
+ Allocate selection info memory.
+ */
+ if (selection_info != (XWidgetInfo *) NULL)
+ selection_info=(XWidgetInfo *) RelinquishMagickMemory(selection_info);
+ selection_info=(XWidgetInfo *) AcquireQuantumMemory(number_selections,
+ sizeof(*selection_info));
+ if (selection_info == (XWidgetInfo *) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed","...");
+ return(id);
+ }
+ state|=UpdateConfigurationState | RedrawWidgetState;
+ }
+ /*
+ Wait for next event.
+ */
+ if (event != (XEvent *) NULL)
+ switch (event->type)
+ {
+ case ButtonPress:
+ {
+ for (i=0; i < (int) number_selections; i++)
+ {
+ if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
+ continue;
+ if (i >= (int) windows->command.data)
+ {
+ selection_info[i].raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->command,&selection_info[i]);
+ break;
+ }
+ submenu_info=selection_info[i];
+ submenu_info.active=MagickTrue;
+ toggle_info.y=
+ submenu_info.y+(submenu_info.height >> 1)-(toggle_info.height >> 1);
+ id=i;
+ (void) XCheckWindowEvent(display,windows->widget.id,LeaveWindowMask,
+ event);
+ break;
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ for (i=0; i < (int) number_selections; i++)
+ {
+ if (MatteIsActive(selection_info[i],event->xbutton) == MagickFalse)
+ continue;
+ id=i;
+ if (id >= (int) windows->command.data)
+ {
+ selection_info[id].raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->command,&selection_info[id]);
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, withdraw command widget.
+ */
+ if (event->xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event->xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ (void) XWithdrawWindow(display,windows->command.id,
+ windows->command.screen);
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event->xconfigure.window != windows->command.id)
+ break;
+ if (event->xconfigure.send_event != 0)
+ {
+ windows->command.x=event->xconfigure.x;
+ windows->command.y=event->xconfigure.y;
+ }
+ if ((event->xconfigure.width == (int) windows->command.width) &&
+ (event->xconfigure.height == (int) windows->command.height))
+ break;
+ windows->command.width=(unsigned int)
+ MagickMax(event->xconfigure.width,(int) windows->command.min_width);
+ windows->command.height=(unsigned int)
+ MagickMax(event->xconfigure.height,(int) windows->command.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case Expose:
+ {
+ if (event->xexpose.window != windows->command.id)
+ break;
+ if (event->xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Return the ID of the highlighted menu entry.
+ */
+ for ( ; ; )
+ {
+ for (i=0; i < (int) number_selections; i++)
+ {
+ if (i >= (int) windows->command.data)
+ {
+ if (selection_info[i].raised ==
+ MatteIsActive(selection_info[i],event->xmotion))
+ {
+ /*
+ Button status changed.
+ */
+ selection_info[i].raised=!selection_info[i].raised;
+ XDrawBeveledButton(display,&windows->command,
+ &selection_info[i]);
+ }
+ continue;
+ }
+ if (MatteIsActive(selection_info[i],event->xmotion) == MagickFalse)
+ continue;
+ submenu_info=selection_info[i];
+ submenu_info.active=MagickTrue;
+ toggle_info.raised=MagickTrue;
+ toggle_info.y=submenu_info.y+(submenu_info.height >> 1)-
+ (toggle_info.height >> 1);
+ XDrawTriangleEast(display,&windows->command,&toggle_info);
+ id=i;
+ }
+ XDelay(display,SuspendTime);
+ if (XCheckMaskEvent(display,ButtonMotionMask,event) == MagickFalse)
+ break;
+ while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
+ toggle_info.raised=MagickFalse;
+ if (windows->command.data != 0)
+ XDrawTriangleEast(display,&windows->command,&toggle_info);
+ }
+ break;
+ }
+ case MapNotify:
+ {
+ windows->command.mapped=MagickTrue;
+ break;
+ }
+ case UnmapNotify:
+ {
+ windows->command.mapped=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize button information.
+ */
+ assert(selections != (const char **) NULL);
+ y=tile_height+20;
+ for (i=0; i < (int) number_selections; i++)
+ {
+ XGetWidgetInfo(selections[i],&selection_info[i]);
+ selection_info[i].center=MagickFalse;
+ selection_info[i].bevel_width--;
+ selection_info[i].height=(unsigned int) ((3*height) >> 1);
+ selection_info[i].x=(QuantumMargin >> 1)+4;
+ selection_info[i].width=(unsigned int)
+ (windows->command.width-(selection_info[i].x << 1));
+ selection_info[i].y=y;
+ y+=selection_info[i].height+(selection_info[i].bevel_width << 1)+6;
+ }
+ XGetWidgetInfo((char *) NULL,&toggle_info);
+ toggle_info.bevel_width--;
+ toggle_info.width=(unsigned int)
+ (((5*height) >> 3)-(toggle_info.bevel_width << 1));
+ toggle_info.height=toggle_info.width;
+ toggle_info.x=selection_info[0].x+selection_info[0].width-
+ toggle_info.width-(QuantumMargin >> 1);
+ if (windows->command.mapped)
+ (void) XClearWindow(display,windows->command.id);
+ }
+ if (state & RedrawWidgetState)
+ {
+ Pixmap
+ tile_pixmap;
+
+ /*
+ Draw command buttons.
+ */
+ tile_pixmap=XCreatePixmapFromBitmapData(display,windows->command.id,
+ (char *) tile_bits,tile_width,tile_height,1L,0L,1);
+ if (tile_pixmap != (Pixmap) NULL)
+ {
+ (void) XCopyPlane(display,tile_pixmap,windows->command.id,
+ windows->command.annotate_context,0,0,tile_width,tile_height,
+ (int) ((windows->command.width-tile_width) >> 1),10,1L);
+ (void) XFreePixmap(display,tile_pixmap);
+ }
+ for (i=0; i < (int) number_selections; i++)
+ {
+ XDrawBeveledButton(display,&windows->command,&selection_info[i]);
+ if (i >= (int) windows->command.data)
+ continue;
+ toggle_info.raised=i == id ? MagickTrue : MagickFalse;
+ toggle_info.y=selection_info[i].y+
+ (selection_info[i].height >> 1)-(toggle_info.height >> 1);
+ XDrawTriangleEast(display,&windows->command,&toggle_info);
+ }
+ XHighlightWidget(display,&windows->command,BorderOffset,BorderOffset);
+ }
+ return(id);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C o n f i r m W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XConfirmWidget() displays a Confirm widget with a notice to the user. The
+% function returns -1 if Dismiss is pressed, 0 for Cancel, and 1 for Yes.
+%
+% The format of the XConfirmWidget method is:
+%
+% int XConfirmWidget(Display *display,XWindows *windows,
+% const char *reason,const char *description)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o reason: Specifies the message to display before terminating the
+% program.
+%
+% o description: Specifies any description to the message.
+%
+*/
+MagickExport int XConfirmWidget(Display *display,XWindows *windows,
+ const char *reason,const char *description)
+{
+#define CancelButtonText "Cancel"
+#define DismissButtonText "Dismiss"
+#define YesButtonText "Yes"
+
+ int
+ confirm,
+ x,
+ y;
+
+ Status
+ status;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ cancel_info,
+ dismiss_info,
+ yes_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine Confirm widget attributes.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(reason != (char *) NULL);
+ assert(description != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
+ XCheckRefreshWindows(display,windows);
+ font_info=windows->widget.font_info;
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ if (WidgetTextWidth(font_info,DismissButtonText) > width)
+ width=WidgetTextWidth(font_info,DismissButtonText);
+ if (WidgetTextWidth(font_info,YesButtonText) > width)
+ width=WidgetTextWidth(font_info,YesButtonText);
+ width<<=1;
+ if (description != (char *) NULL)
+ if (WidgetTextWidth(font_info,(char *) description) > width)
+ width=WidgetTextWidth(font_info,(char *) description);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Confirm widget.
+ */
+ windows->widget.width=(unsigned int) (width+9*QuantumMargin);
+ windows->widget.min_width=(unsigned int) (9*QuantumMargin+
+ WidgetTextWidth(font_info,CancelButtonText)+
+ WidgetTextWidth(font_info,DismissButtonText)+
+ WidgetTextWidth(font_info,YesButtonText));
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int) (12*height);
+ windows->widget.min_height=(unsigned int) (7*height);
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Confirm widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Confirm",MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ confirm=0;
+ state=UpdateConfigurationState;
+ XSetCursorState(display,windows,MagickTrue);
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=(unsigned int) QuantumMargin+
+ WidgetTextWidth(font_info,CancelButtonText);
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int) (windows->widget.width-cancel_info.width-
+ QuantumMargin);
+ cancel_info.y=(int) (windows->widget.height-(cancel_info.height << 1));
+ dismiss_info=cancel_info;
+ dismiss_info.text=(char *) DismissButtonText;
+ if (LocaleCompare(description,"Do you want to save it") == 0)
+ dismiss_info.text=(char *) "Don't Save";
+ dismiss_info.width=(unsigned int) QuantumMargin+
+ WidgetTextWidth(font_info,dismiss_info.text);
+ dismiss_info.x=(int)
+ ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
+ yes_info=cancel_info;
+ yes_info.text=(char *) YesButtonText;
+ if (LocaleCompare(description,"Do you want to save it") == 0)
+ yes_info.text=(char *) "Save";
+ yes_info.width=(unsigned int) QuantumMargin+
+ WidgetTextWidth(font_info,yes_info.text);
+ if (yes_info.width < cancel_info.width)
+ yes_info.width=cancel_info.width;
+ yes_info.x=QuantumMargin;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Confirm widget.
+ */
+ width=WidgetTextWidth(font_info,(char *) reason);
+ x=(int) ((windows->widget.width >> 1)-(width >> 1));
+ y=(int) ((windows->widget.height >> 1)-(height << 1));
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
+ if (description != (char *) NULL)
+ {
+ char
+ question[MaxTextExtent];
+
+ (void) CopyMagickString(question,description,MaxTextExtent);
+ (void) ConcatenateMagickString(question,"?",MaxTextExtent);
+ width=WidgetTextWidth(font_info,question);
+ x=(int) ((windows->widget.width >> 1)-(width >> 1));
+ y+=height;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,question,Extent(question));
+ }
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ XDrawBeveledButton(display,&windows->widget,&yes_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~RedrawWidgetState);
+ }
+ /*
+ Wait for next event.
+ */
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed No button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ {
+ /*
+ User pressed Dismiss button.
+ */
+ dismiss_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ if (MatteIsActive(yes_info,event.xbutton))
+ {
+ /*
+ User pressed Yes button.
+ */
+ yes_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&yes_info);
+ break;
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ confirm=0;
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ if (dismiss_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ {
+ confirm=(-1);
+ state|=ExitState;
+ }
+ dismiss_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ }
+ if (yes_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(yes_info,event.xbutton))
+ {
+ confirm=1;
+ state|=ExitState;
+ }
+ yes_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&yes_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ yes_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&yes_info);
+ confirm=1;
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (state & InactiveWidgetState)
+ break;
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
+ {
+ /*
+ Dismiss button status changed.
+ */
+ dismiss_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ if (yes_info.raised == MatteIsActive(yes_info,event.xmotion))
+ {
+ /*
+ Yes button status changed.
+ */
+ yes_info.raised=yes_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&yes_info);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ return(confirm);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D i a l o g W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDialogWidget() displays a Dialog widget with a query to the user. The user
+% keys a reply and presses the Ok or Cancel button to exit. The typed text is
+% returned as the reply function parameter.
+%
+% The format of the XDialogWidget method is:
+%
+% int XDialogWidget(Display *display,XWindows *windows,const char *action,
+% const char *query,char *reply)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o action: Specifies a pointer to the action of this widget.
+%
+% o query: Specifies a pointer to the query to present to the user.
+%
+% o reply: the response from the user is returned in this parameter.
+%
+*/
+MagickExport int XDialogWidget(Display *display,XWindows *windows,
+ const char *action,const char *query,char *reply)
+{
+#define CancelButtonText "Cancel"
+
+ char
+ primary_selection[MaxTextExtent];
+
+ int
+ x;
+
+ register int
+ i;
+
+ static MagickBooleanType
+ raised = MagickFalse;
+
+ Status
+ status;
+
+ unsigned int
+ anomaly,
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ action_info,
+ cancel_info,
+ reply_info,
+ special_info,
+ text_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine Dialog widget attributes.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(action != (char *) NULL);
+ assert(query != (char *) NULL);
+ assert(reply != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
+ XCheckRefreshWindows(display,windows);
+ font_info=windows->widget.font_info;
+ width=WidgetTextWidth(font_info,(char *) action);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ width+=(3*QuantumMargin) >> 1;
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Dialog widget.
+ */
+ windows->widget.width=(unsigned int) MagickMax(2*width,(int) WidgetTextWidth(
+ font_info,(char *) query));
+ if (windows->widget.width < WidgetTextWidth(font_info,reply))
+ windows->widget.width=WidgetTextWidth(font_info,reply);
+ windows->widget.width+=6*QuantumMargin;
+ windows->widget.min_width=(unsigned int)
+ (width+28*XTextWidth(font_info,"#",1)+4*QuantumMargin);
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int) (7*height+(QuantumMargin << 1));
+ windows->widget.min_height=windows->widget.height;
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Dialog widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Dialog",MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ anomaly=(LocaleCompare(action,"Background") == 0) ||
+ (LocaleCompare(action,"New") == 0) ||
+ (LocaleCompare(action,"Quantize") == 0) ||
+ (LocaleCompare(action,"Resize") == 0) ||
+ (LocaleCompare(action,"Save") == 0) ||
+ (LocaleCompare(action,"Shade") == 0);
+ state=UpdateConfigurationState;
+ XSetCursorState(display,windows,MagickTrue);
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int)
+ (windows->widget.width-cancel_info.width-((3*QuantumMargin) >> 1));
+ cancel_info.y=(int)
+ (windows->widget.height-cancel_info.height-((3*QuantumMargin) >> 1));
+ XGetWidgetInfo(action,&action_info);
+ action_info.width=width;
+ action_info.height=(unsigned int) ((3*height) >> 1);
+ action_info.x=cancel_info.x-(cancel_info.width+QuantumMargin+
+ (action_info.bevel_width << 1));
+ action_info.y=cancel_info.y;
+ /*
+ Initialize reply information.
+ */
+ XGetWidgetInfo(reply,&reply_info);
+ reply_info.raised=MagickFalse;
+ reply_info.bevel_width--;
+ reply_info.width=windows->widget.width-(3*QuantumMargin);
+ reply_info.height=height << 1;
+ reply_info.x=(3*QuantumMargin) >> 1;
+ reply_info.y=action_info.y-reply_info.height-QuantumMargin;
+ /*
+ Initialize option information.
+ */
+ XGetWidgetInfo("Dither",&special_info);
+ special_info.raised=raised;
+ special_info.bevel_width--;
+ special_info.width=(unsigned int) QuantumMargin >> 1;
+ special_info.height=(unsigned int) QuantumMargin >> 1;
+ special_info.x=reply_info.x;
+ special_info.y=action_info.y+action_info.height-special_info.height;
+ if (LocaleCompare(action,"Background") == 0)
+ special_info.text=(char *) "Backdrop";
+ if (LocaleCompare(action,"New") == 0)
+ special_info.text=(char *) "Gradation";
+ if (LocaleCompare(action,"Resize") == 0)
+ special_info.text=(char *) "Constrain ratio";
+ if (LocaleCompare(action,"Save") == 0)
+ special_info.text=(char *) "Non-progressive";
+ if (LocaleCompare(action,"Shade") == 0)
+ special_info.text=(char *) "Color shading";
+ /*
+ Initialize text information.
+ */
+ XGetWidgetInfo(query,&text_info);
+ text_info.width=reply_info.width;
+ text_info.height=height;
+ text_info.x=reply_info.x-(QuantumMargin >> 1);
+ text_info.y=QuantumMargin;
+ text_info.center=MagickFalse;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Dialog widget.
+ */
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawBeveledMatte(display,&windows->widget,&reply_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ if (anomaly)
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~RedrawWidgetState);
+ }
+ /*
+ Wait for next event.
+ */
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (anomaly)
+ if (MatteIsActive(special_info,event.xbutton))
+ {
+ /*
+ Option button status changed.
+ */
+ special_info.raised=!special_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ break;
+ }
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ /*
+ User pressed Action button.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ if (event.xbutton.button != Button2)
+ {
+ static Time
+ click_time;
+
+ /*
+ Move text cursor to position of button press.
+ */
+ x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
+ for (i=1; i <= Extent(reply_info.marker); i++)
+ if (XTextWidth(font_info,reply_info.marker,i) > x)
+ break;
+ reply_info.cursor=reply_info.marker+i-1;
+ if (event.xbutton.time > (click_time+DoubleClick))
+ reply_info.highlight=MagickFalse;
+ else
+ {
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ windows->widget.id ? MagickTrue : MagickFalse;
+ }
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ click_time=event.xbutton.time;
+ break;
+ }
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ windows->widget.id,event.xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (action_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(action_info,event.xbutton))
+ state|=ExitState;
+ action_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ state|=ExitState;
+ break;
+ }
+ if (key_symbol == XK_Control_L)
+ {
+ state|=ControlState;
+ break;
+ }
+ if (state & ControlState)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ reply_info.marker=reply_info.text;
+ reply_info.highlight=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ XEditText(display,&reply_info,key_symbol,command,state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ case KeyRelease:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key release.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (key_symbol == XK_Control_L)
+ state&=(~ControlState);
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (state & InactiveWidgetState)
+ break;
+ if (action_info.raised == MatteIsActive(action_info,event.xmotion))
+ {
+ /*
+ Action button status changed.
+ */
+ action_info.raised=action_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ reply_info.highlight=MagickFalse;
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,event.xselection.requestor,
+ event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
+ &format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ if ((Extent(reply_info.text)+length) >= MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ /*
+ Insert primary selection in reply text.
+ */
+ *(data+length)='\0';
+ XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
+ state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ if (reply_info.highlight == MagickFalse)
+ break;
+ /*
+ Set primary selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,0,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ if (anomaly)
+ if (special_info.raised)
+ if (*reply != '\0')
+ raised=MagickTrue;
+ return(raised == MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X F i l e B r o w s e r W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XFileBrowserWidget() displays a File Browser widget with a file query to the
+% user. The user keys a reply and presses the Action or Cancel button to
+% exit. The typed text is returned as the reply function parameter.
+%
+% The format of the XFileBrowserWidget method is:
+%
+% void XFileBrowserWidget(Display *display,XWindows *windows,
+% const char *action,char *reply)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o action: Specifies a pointer to the action of this widget.
+%
+% o reply: the response from the user is returned in this parameter.
+%
+*/
+MagickExport void XFileBrowserWidget(Display *display,XWindows *windows,
+ const char *action,char *reply)
+{
+#define CancelButtonText "Cancel"
+#define DirectoryText "Directory:"
+#define FilenameText "File name:"
+#define GrabButtonText "Grab"
+#define FormatButtonText "Format"
+#define HomeButtonText "Home"
+#define UpButtonText "Up"
+
+ char
+ *cwd,
+ **filelist,
+ home_directory[MaxTextExtent],
+ primary_selection[MaxTextExtent],
+ text[MaxTextExtent],
+ working_path[MaxTextExtent];
+
+ int
+ x,
+ y;
+
+ register int
+ i;
+
+ static char
+ glob_pattern[MaxTextExtent] = "*",
+ format[MaxTextExtent] = "miff";
+
+ static MagickStatusType
+ mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
+
+ Status
+ status;
+
+ unsigned int
+ anomaly,
+ height,
+ text_width,
+ visible_files,
+ width;
+
+ unsigned long
+ delay,
+ files,
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ action_info,
+ cancel_info,
+ expose_info,
+ special_info,
+ list_info,
+ home_info,
+ north_info,
+ reply_info,
+ scroll_info,
+ selection_info,
+ slider_info,
+ south_info,
+ text_info,
+ up_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Read filelist from current directory.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(action != (char *) NULL);
+ assert(reply != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ cwd=getcwd(home_directory,MaxTextExtent);
+ (void) CopyMagickString(working_path,home_directory,MaxTextExtent);
+ filelist=ListFiles(working_path,glob_pattern,&files);
+ if (filelist == (char **) NULL)
+ {
+ /*
+ Directory read failed.
+ */
+ XNoticeWidget(display,windows,"Unable to read directory:",working_path);
+ (void) XDialogWidget(display,windows,action,"Enter filename:",reply);
+ return;
+ }
+ /*
+ Determine File Browser widget attributes.
+ */
+ font_info=windows->widget.font_info;
+ text_width=0;
+ for (i=0; i < (long) files; i++)
+ if (WidgetTextWidth(font_info,filelist[i]) > text_width)
+ text_width=WidgetTextWidth(font_info,filelist[i]);
+ width=WidgetTextWidth(font_info,(char *) action);
+ if (WidgetTextWidth(font_info,GrabButtonText) > width)
+ width=WidgetTextWidth(font_info,GrabButtonText);
+ if (WidgetTextWidth(font_info,FormatButtonText) > width)
+ width=WidgetTextWidth(font_info,FormatButtonText);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ if (WidgetTextWidth(font_info,HomeButtonText) > width)
+ width=WidgetTextWidth(font_info,HomeButtonText);
+ if (WidgetTextWidth(font_info,UpButtonText) > width)
+ width=WidgetTextWidth(font_info,UpButtonText);
+ width+=QuantumMargin;
+ if (WidgetTextWidth(font_info,DirectoryText) > width)
+ width=WidgetTextWidth(font_info,DirectoryText);
+ if (WidgetTextWidth(font_info,FilenameText) > width)
+ width=WidgetTextWidth(font_info,FilenameText);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position File Browser widget.
+ */
+ windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
+ 6*QuantumMargin;
+ windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int)
+ (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
+ windows->widget.min_height=(unsigned int)
+ (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map File Browser widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Browse and Select a File",
+ MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,
+ windows->widget.screen,mask,&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ XGetWidgetInfo((char *) NULL,&slider_info);
+ XGetWidgetInfo((char *) NULL,&north_info);
+ XGetWidgetInfo((char *) NULL,&south_info);
+ XGetWidgetInfo((char *) NULL,&expose_info);
+ visible_files=0;
+ anomaly=(LocaleCompare(action,"Composite") == 0) ||
+ (LocaleCompare(action,"Open") == 0) || (LocaleCompare(action,"Map") == 0);
+ *reply='\0';
+ delay=SuspendTime << 2;
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ int
+ id;
+
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int)
+ (windows->widget.width-cancel_info.width-QuantumMargin-2);
+ cancel_info.y=(int)
+ (windows->widget.height-cancel_info.height-QuantumMargin);
+ XGetWidgetInfo(action,&action_info);
+ action_info.width=width;
+ action_info.height=(unsigned int) ((3*height) >> 1);
+ action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
+ (action_info.bevel_width << 1));
+ action_info.y=cancel_info.y;
+ XGetWidgetInfo(GrabButtonText,&special_info);
+ special_info.width=width;
+ special_info.height=(unsigned int) ((3*height) >> 1);
+ special_info.x=action_info.x-(action_info.width+(QuantumMargin >> 1)+
+ (special_info.bevel_width << 1));
+ special_info.y=action_info.y;
+ if (anomaly == MagickFalse)
+ {
+ register char
+ *p;
+
+ special_info.text=(char *) FormatButtonText;
+ p=reply+Extent(reply)-1;
+ while ((p > (reply+1)) && (*(p-1) != '.'))
+ p--;
+ if ((p > (reply+1)) && (*(p-1) == '.'))
+ (void) CopyMagickString(format,p,MaxTextExtent);
+ }
+ XGetWidgetInfo(UpButtonText,&up_info);
+ up_info.width=width;
+ up_info.height=(unsigned int) ((3*height) >> 1);
+ up_info.x=QuantumMargin;
+ up_info.y=((5*QuantumMargin) >> 1)+height;
+ XGetWidgetInfo(HomeButtonText,&home_info);
+ home_info.width=width;
+ home_info.height=(unsigned int) ((3*height) >> 1);
+ home_info.x=QuantumMargin;
+ home_info.y=up_info.y+up_info.height+QuantumMargin;
+ /*
+ Initialize reply information.
+ */
+ XGetWidgetInfo(reply,&reply_info);
+ reply_info.raised=MagickFalse;
+ reply_info.bevel_width--;
+ reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
+ reply_info.height=height << 1;
+ reply_info.x=(int) (width+(QuantumMargin << 1));
+ reply_info.y=action_info.y-reply_info.height-QuantumMargin;
+ /*
+ Initialize scroll information.
+ */
+ XGetWidgetInfo((char *) NULL,&scroll_info);
+ scroll_info.bevel_width--;
+ scroll_info.width=height;
+ scroll_info.height=(unsigned int)
+ (reply_info.y-up_info.y-(QuantumMargin >> 1));
+ scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
+ scroll_info.y=up_info.y-reply_info.bevel_width;
+ scroll_info.raised=MagickFalse;
+ scroll_info.trough=MagickTrue;
+ north_info=scroll_info;
+ north_info.raised=MagickTrue;
+ north_info.width-=(north_info.bevel_width << 1);
+ north_info.height=north_info.width-1;
+ north_info.x+=north_info.bevel_width;
+ north_info.y+=north_info.bevel_width;
+ south_info=north_info;
+ south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
+ south_info.height;
+ id=slider_info.id;
+ slider_info=north_info;
+ slider_info.id=id;
+ slider_info.width-=2;
+ slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
+ slider_info.bevel_width+2;
+ slider_info.height=scroll_info.height-((slider_info.min_y-
+ scroll_info.y+1) << 1)+4;
+ visible_files=scroll_info.height/(height+(height >> 3));
+ if (files > visible_files)
+ slider_info.height=(unsigned int)
+ ((visible_files*slider_info.height)/files);
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.x=scroll_info.x+slider_info.bevel_width+1;
+ slider_info.y=slider_info.min_y;
+ expose_info=scroll_info;
+ expose_info.y=slider_info.y;
+ /*
+ Initialize list information.
+ */
+ XGetWidgetInfo((char *) NULL,&list_info);
+ list_info.raised=MagickFalse;
+ list_info.bevel_width--;
+ list_info.width=(unsigned int)
+ (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
+ list_info.height=scroll_info.height;
+ list_info.x=reply_info.x;
+ list_info.y=scroll_info.y;
+ if (windows->widget.mapped == MagickFalse)
+ state|=JumpListState;
+ /*
+ Initialize text information.
+ */
+ *text='\0';
+ XGetWidgetInfo(text,&text_info);
+ text_info.center=MagickFalse;
+ text_info.width=reply_info.width;
+ text_info.height=height;
+ text_info.x=list_info.x-(QuantumMargin >> 1);
+ text_info.y=QuantumMargin;
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=list_info.width;
+ selection_info.height=(unsigned int) ((9*height) >> 3);
+ selection_info.x=list_info.x;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw File Browser window.
+ */
+ x=QuantumMargin;
+ y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,DirectoryText,
+ Extent(DirectoryText));
+ (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
+ (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(text_info.text,glob_pattern,
+ MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawBeveledButton(display,&windows->widget,&up_info);
+ XDrawBeveledButton(display,&windows->widget,&home_info);
+ XDrawBeveledMatte(display,&windows->widget,&list_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ x=QuantumMargin;
+ y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,FilenameText,
+ Extent(FilenameText));
+ XDrawBeveledMatte(display,&windows->widget,&reply_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ state&=(~RedrawWidgetState);
+ }
+ if (state & UpdateListState)
+ {
+ char
+ **checklist;
+
+ unsigned long
+ number_files;
+
+ /*
+ Update file list.
+ */
+ checklist=ListFiles(working_path,glob_pattern,&number_files);
+ if (checklist == (char **) NULL)
+ {
+ /*
+ Reply is a filename, exit.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ for (i=0; i < (long) files; i++)
+ filelist[i]=DestroyString(filelist[i]);
+ if (filelist != (char **) NULL)
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ filelist=checklist;
+ files=number_files;
+ /*
+ Update file list.
+ */
+ slider_info.height=
+ scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
+ if (files > visible_files)
+ slider_info.height=(unsigned int)
+ ((visible_files*slider_info.height)/files);
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ expose_info.y=slider_info.y;
+ selection_info.id=(~0);
+ list_info.id=(~0);
+ state|=RedrawListState;
+ /*
+ Redraw directory name & reply.
+ */
+ if (IsGlob(reply_info.text) == MagickFalse)
+ {
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ }
+ (void) CopyMagickString(text_info.text,working_path,MaxTextExtent);
+ (void) ConcatenateMagickString(text_info.text,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(text_info.text,glob_pattern,
+ MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~UpdateListState);
+ }
+ if (state & JumpListState)
+ {
+ /*
+ Jump scroll to match user filename.
+ */
+ list_info.id=(~0);
+ for (i=0; i < (long) files; i++)
+ if (LocaleCompare(filelist[i],reply) >= 0)
+ {
+ list_info.id=LocaleCompare(filelist[i],reply) == 0 ? i : ~0;
+ break;
+ }
+ if ((i < slider_info.id) || (i >= (int) (slider_info.id+visible_files)))
+ slider_info.id=i-(visible_files >> 1);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ state&=(~JumpListState);
+ }
+ if (state & RedrawListState)
+ {
+ /*
+ Determine slider id and position.
+ */
+ if (slider_info.id >= (int) (files-visible_files))
+ slider_info.id=(int) (files-visible_files);
+ if ((slider_info.id < 0) || (files <= visible_files))
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ if (files > 0)
+ slider_info.y+=
+ slider_info.id*(slider_info.max_y-slider_info.min_y+1)/files;
+ if (slider_info.id != selection_info.id)
+ {
+ /*
+ Redraw scroll bar and file names.
+ */
+ selection_info.id=slider_info.id;
+ selection_info.y=list_info.y+(height >> 3)+2;
+ for (i=0; i < (int) visible_files; i++)
+ {
+ selection_info.raised=(slider_info.id+i) != list_info.id ?
+ MagickTrue : MagickFalse;
+ selection_info.text=(char *) NULL;
+ if ((slider_info.id+i) < (long) files)
+ selection_info.text=filelist[slider_info.id+i];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ /*
+ Update slider.
+ */
+ if (slider_info.y > expose_info.y)
+ {
+ expose_info.height=(unsigned int) slider_info.y-expose_info.y;
+ expose_info.y=slider_info.y-expose_info.height-
+ slider_info.bevel_width-1;
+ }
+ else
+ {
+ expose_info.height=(unsigned int) expose_info.y-slider_info.y;
+ expose_info.y=slider_info.y+slider_info.height+
+ slider_info.bevel_width+1;
+ }
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawMatte(display,&windows->widget,&expose_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ expose_info.y=slider_info.y;
+ }
+ state&=(~RedrawListState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (north_info.raised && south_info.raised)
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ else
+ {
+ /*
+ Brief delay before advancing scroll bar.
+ */
+ XDelay(display,delay);
+ delay=SuspendTime;
+ (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
+ if (north_info.raised == MagickFalse)
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ slider_info.id--;
+ state|=RedrawListState;
+ }
+ if (south_info.raised == MagickFalse)
+ if (slider_info.id < (long) files)
+ {
+ /*
+ Move slider down.
+ */
+ slider_info.id++;
+ state|=RedrawListState;
+ }
+ if (event.type != ButtonRelease)
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(slider_info,event.xbutton))
+ {
+ /*
+ Track slider.
+ */
+ slider_info.active=MagickTrue;
+ break;
+ }
+ if (MatteIsActive(north_info,event.xbutton))
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ north_info.raised=MagickFalse;
+ slider_info.id--;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(south_info,event.xbutton))
+ if (slider_info.id < (long) files)
+ {
+ /*
+ Move slider down.
+ */
+ south_info.raised=MagickFalse;
+ slider_info.id++;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(scroll_info,event.xbutton))
+ {
+ /*
+ Move slider.
+ */
+ if (event.xbutton.y < slider_info.y)
+ slider_info.id-=(visible_files-1);
+ else
+ slider_info.id+=(visible_files-1);
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(list_info,event.xbutton))
+ {
+ int
+ id;
+
+ /*
+ User pressed file matte.
+ */
+ id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
+ selection_info.height;
+ if (id >= (long) files)
+ break;
+ (void) CopyMagickString(reply_info.text,filelist[id],MaxTextExtent);
+ reply_info.highlight=MagickFalse;
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ if (id == list_info.id)
+ {
+ register char
+ *p;
+
+
+ p=reply_info.text+strlen(reply_info.text)-1;
+ if (*p == *DirectorySeparator)
+ ChopPathComponents(reply_info.text,1);
+ (void) ConcatenateMagickString(working_path,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(working_path,reply_info.text,
+ MaxTextExtent);
+ *reply='\0';
+ state|=UpdateListState;
+ }
+ selection_info.id=(~0);
+ list_info.id=id;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(up_info,event.xbutton))
+ {
+ /*
+ User pressed Up button.
+ */
+ up_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&up_info);
+ break;
+ }
+ if (MatteIsActive(home_info,event.xbutton))
+ {
+ /*
+ User pressed Home button.
+ */
+ home_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&home_info);
+ break;
+ }
+ if (MatteIsActive(special_info,event.xbutton))
+ {
+ /*
+ User pressed Special button.
+ */
+ special_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ break;
+ }
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ /*
+ User pressed action button.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ if (event.xbutton.button != Button2)
+ {
+ static Time
+ click_time;
+
+ /*
+ Move text cursor to position of button press.
+ */
+ x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
+ for (i=1; i <= Extent(reply_info.marker); i++)
+ if (XTextWidth(font_info,reply_info.marker,i) > x)
+ break;
+ reply_info.cursor=reply_info.marker+i-1;
+ if (event.xbutton.time > (click_time+DoubleClick))
+ reply_info.highlight=MagickFalse;
+ else
+ {
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ windows->widget.id ? MagickTrue : MagickFalse;
+ }
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ click_time=event.xbutton.time;
+ break;
+ }
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ windows->widget.id,event.xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (north_info.raised == MagickFalse)
+ {
+ /*
+ User released up button.
+ */
+ delay=SuspendTime << 2;
+ north_info.raised=MagickTrue;
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ }
+ if (south_info.raised == MagickFalse)
+ {
+ /*
+ User released down button.
+ */
+ delay=SuspendTime << 2;
+ south_info.raised=MagickTrue;
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ }
+ if (slider_info.active)
+ {
+ /*
+ Stop tracking slider.
+ */
+ slider_info.active=MagickFalse;
+ break;
+ }
+ if (up_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(up_info,event.xbutton))
+ {
+ ChopPathComponents(working_path,1);
+ if (*working_path == '\0')
+ (void) CopyMagickString(working_path,DirectorySeparator,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ up_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&up_info);
+ }
+ if (home_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(home_info,event.xbutton))
+ {
+ (void) CopyMagickString(working_path,home_directory,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ home_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&home_info);
+ }
+ if (special_info.raised == MagickFalse)
+ {
+ if (anomaly == MagickFalse)
+ {
+ char
+ **formats;
+
+ ExceptionInfo
+ *exception;
+
+ unsigned long
+ number_formats;
+
+ /*
+ Let user select image format.
+ */
+ exception=AcquireExceptionInfo();
+ formats=GetMagickList("*",&number_formats,exception);
+ exception=DestroyExceptionInfo(exception);
+ (void) XCheckDefineCursor(display,windows->widget.id,
+ windows->widget.busy_cursor);
+ windows->popup.x=windows->widget.x+60;
+ windows->popup.y=windows->widget.y+60;
+ XListBrowserWidget(display,windows,&windows->popup,
+ (const char **) formats,"Select","Select image format type:",
+ format);
+ XSetCursorState(display,windows,MagickTrue);
+ (void) XCheckDefineCursor(display,windows->widget.id,
+ windows->widget.cursor);
+ LocaleLower(format);
+ AppendImageFormat(format,reply_info.text);
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ special_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ for (i=0; i < (int) number_formats; i++)
+ formats[i]=DestroyString(formats[i]);
+ formats=(char **) RelinquishMagickMemory(formats);
+ break;
+ }
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(special_info,event.xbutton))
+ {
+ (void) CopyMagickString(working_path,"x:",MaxTextExtent);
+ state|=ExitState;
+ }
+ special_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ }
+ if (action_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ {
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ if (*reply_info.text == '\0')
+ (void) XBell(display,0);
+ else
+ state|=ExitState;
+ }
+ }
+ action_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ *reply_info.text='\0';
+ *reply='\0';
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (AreaIsActive(scroll_info,event.xkey))
+ {
+ /*
+ Move slider.
+ */
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ slider_info.id=0;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ slider_info.id--;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ slider_info.id++;
+ break;
+ }
+ case XK_Prior:
+ case XK_KP_Prior:
+ {
+ slider_info.id-=visible_files;
+ break;
+ }
+ case XK_Next:
+ case XK_KP_Next:
+ {
+ slider_info.id+=visible_files;
+ break;
+ }
+ case XK_End:
+ case XK_KP_End:
+ {
+ slider_info.id=(int) files;
+ break;
+ }
+ }
+ state|=RedrawListState;
+ break;
+ }
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ /*
+ Read new directory or glob patterm.
+ */
+ if (*reply_info.text == '\0')
+ break;
+ if (IsGlob(reply_info.text))
+ (void) CopyMagickString(glob_pattern,reply_info.text,
+ MaxTextExtent);
+ else
+ {
+ (void) ConcatenateMagickString(working_path,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(working_path,reply_info.text,
+ MaxTextExtent);
+ if (*working_path == '~')
+ ExpandFilename(working_path);
+ *reply='\0';
+ }
+ state|=UpdateListState;
+ break;
+ }
+ if (key_symbol == XK_Control_L)
+ {
+ state|=ControlState;
+ break;
+ }
+ if (state & ControlState)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ reply_info.marker=reply_info.text;
+ reply_info.highlight=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ XEditText(display,&reply_info,key_symbol,command,state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ break;
+ }
+ case KeyRelease:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key release.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (key_symbol == XK_Control_L)
+ state&=(~ControlState);
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MapNotify:
+ {
+ mask&=(~CWX);
+ mask&=(~CWY);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (slider_info.active)
+ {
+ /*
+ Move slider matte.
+ */
+ slider_info.y=event.xmotion.y-
+ ((slider_info.height+slider_info.bevel_width) >> 1)+1;
+ if (slider_info.y < slider_info.min_y)
+ slider_info.y=slider_info.min_y;
+ if (slider_info.y > slider_info.max_y)
+ slider_info.y=slider_info.max_y;
+ slider_info.id=0;
+ if (slider_info.y != slider_info.min_y)
+ slider_info.id=(int) ((files*(slider_info.y-slider_info.min_y+1))/
+ (slider_info.max_y-slider_info.min_y+1));
+ state|=RedrawListState;
+ break;
+ }
+ if (state & InactiveWidgetState)
+ break;
+ if (up_info.raised == MatteIsActive(up_info,event.xmotion))
+ {
+ /*
+ Up button status changed.
+ */
+ up_info.raised=!up_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&up_info);
+ break;
+ }
+ if (home_info.raised == MatteIsActive(home_info,event.xmotion))
+ {
+ /*
+ Home button status changed.
+ */
+ home_info.raised=!home_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&home_info);
+ break;
+ }
+ if (special_info.raised == MatteIsActive(special_info,event.xmotion))
+ {
+ /*
+ Grab button status changed.
+ */
+ special_info.raised=!special_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&special_info);
+ break;
+ }
+ if (action_info.raised == MatteIsActive(action_info,event.xmotion))
+ {
+ /*
+ Action button status changed.
+ */
+ action_info.raised=action_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ reply_info.highlight=MagickFalse;
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,event.xselection.requestor,
+ event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
+ &format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ if ((Extent(reply_info.text)+length) >= MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ /*
+ Insert primary selection in reply text.
+ */
+ *(data+length)='\0';
+ XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
+ state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ state|=RedrawActionState;
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ if (reply_info.highlight == MagickFalse)
+ break;
+ /*
+ Set primary selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,0,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ /*
+ Free file list.
+ */
+ for (i=0; i < (long) files; i++)
+ filelist[i]=DestroyString(filelist[i]);
+ if (filelist != (char **) NULL)
+ filelist=(char **) RelinquishMagickMemory(filelist);
+ if (*reply != '\0')
+ {
+ (void) ConcatenateMagickString(working_path,DirectorySeparator,
+ MaxTextExtent);
+ (void) ConcatenateMagickString(working_path,reply,MaxTextExtent);
+ }
+ (void) CopyMagickString(reply,working_path,MaxTextExtent);
+ if (*reply == '~')
+ ExpandFilename(reply);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X F o n t B r o w s e r W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XFontBrowserWidget() displays a Font Browser widget with a font query to the
+% user. The user keys a reply and presses the Action or Cancel button to
+% exit. The typed text is returned as the reply function parameter.
+%
+% The format of the XFontBrowserWidget method is:
+%
+% void XFontBrowserWidget(Display *display,XWindows *windows,
+% const char *action,char *reply)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o action: Specifies a pointer to the action of this widget.
+%
+% o reply: the response from the user is returned in this parameter.
+%
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static int FontCompare(const void *x,const void *y)
+{
+ register char
+ *p,
+ *q;
+
+ p=(char *) *((char **) x);
+ q=(char *) *((char **) y);
+ while ((*p != '\0') && (*q != '\0') && (*p == *q))
+ {
+ p++;
+ q++;
+ }
+ return(*p-(*q));
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+MagickExport void XFontBrowserWidget(Display *display,XWindows *windows,
+ const char *action,char *reply)
+{
+#define BackButtonText "Back"
+#define CancelButtonText "Cancel"
+#define FontnameText "Name:"
+#define FontPatternText "Pattern:"
+#define ResetButtonText "Reset"
+
+ char
+ back_pattern[MaxTextExtent],
+ **fontlist,
+ **listhead,
+ primary_selection[MaxTextExtent],
+ reset_pattern[MaxTextExtent],
+ text[MaxTextExtent];
+
+ int
+ fonts,
+ x,
+ y;
+
+ register int
+ i;
+
+ static char
+ glob_pattern[MaxTextExtent] = "*";
+
+ static MagickStatusType
+ mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
+
+ Status
+ status;
+
+ unsigned int
+ height,
+ text_width,
+ visible_fonts,
+ width;
+
+ unsigned long
+ delay,
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ action_info,
+ back_info,
+ cancel_info,
+ expose_info,
+ list_info,
+ mode_info,
+ north_info,
+ reply_info,
+ reset_info,
+ scroll_info,
+ selection_info,
+ slider_info,
+ south_info,
+ text_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Get font list and sort in ascending order.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(action != (char *) NULL);
+ assert(reply != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
+ (void) CopyMagickString(reset_pattern,"*",MaxTextExtent);
+ fontlist=XListFonts(display,glob_pattern,32767,&fonts);
+ if (fonts == 0)
+ {
+ /*
+ Pattern failed, obtain all the fonts.
+ */
+ XNoticeWidget(display,windows,"Unable to obtain fonts names:",
+ glob_pattern);
+ (void) CopyMagickString(glob_pattern,"*",MaxTextExtent);
+ fontlist=XListFonts(display,glob_pattern,32767,&fonts);
+ if (fontlist == (char **) NULL)
+ {
+ XNoticeWidget(display,windows,"Unable to obtain fonts names:",
+ glob_pattern);
+ return;
+ }
+ }
+ /*
+ Sort font list in ascending order.
+ */
+ listhead=fontlist;
+ fontlist=(char **) AcquireQuantumMemory((size_t) fonts,sizeof(*fontlist));
+ if (fontlist == (char **) NULL)
+ {
+ XNoticeWidget(display,windows,"MemoryAllocationFailed",
+ "UnableToViewFonts");
+ return;
+ }
+ for (i=0; i < fonts; i++)
+ fontlist[i]=listhead[i];
+ qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
+ /*
+ Determine Font Browser widget attributes.
+ */
+ font_info=windows->widget.font_info;
+ text_width=0;
+ for (i=0; i < fonts; i++)
+ if (WidgetTextWidth(font_info,fontlist[i]) > text_width)
+ text_width=WidgetTextWidth(font_info,fontlist[i]);
+ width=WidgetTextWidth(font_info,(char *) action);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ if (WidgetTextWidth(font_info,ResetButtonText) > width)
+ width=WidgetTextWidth(font_info,ResetButtonText);
+ if (WidgetTextWidth(font_info,BackButtonText) > width)
+ width=WidgetTextWidth(font_info,BackButtonText);
+ width+=QuantumMargin;
+ if (WidgetTextWidth(font_info,FontPatternText) > width)
+ width=WidgetTextWidth(font_info,FontPatternText);
+ if (WidgetTextWidth(font_info,FontnameText) > width)
+ width=WidgetTextWidth(font_info,FontnameText);
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Font Browser widget.
+ */
+ windows->widget.width=width+MagickMin((int) text_width,(int) MaxTextWidth)+
+ 6*QuantumMargin;
+ windows->widget.min_width=width+MinTextWidth+4*QuantumMargin;
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int)
+ (((85*height) >> 2)+((13*QuantumMargin) >> 1)+4);
+ windows->widget.min_height=(unsigned int)
+ (((27*height) >> 1)+((13*QuantumMargin) >> 1)+4);
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Font Browser widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Browse and Select a Font",
+ MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,
+ windows->widget.screen,mask,&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ XGetWidgetInfo((char *) NULL,&slider_info);
+ XGetWidgetInfo((char *) NULL,&north_info);
+ XGetWidgetInfo((char *) NULL,&south_info);
+ XGetWidgetInfo((char *) NULL,&expose_info);
+ visible_fonts=0;
+ delay=SuspendTime << 2;
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ int
+ id;
+
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int)
+ (windows->widget.width-cancel_info.width-QuantumMargin-2);
+ cancel_info.y=(int)
+ (windows->widget.height-cancel_info.height-QuantumMargin);
+ XGetWidgetInfo(action,&action_info);
+ action_info.width=width;
+ action_info.height=(unsigned int) ((3*height) >> 1);
+ action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
+ (action_info.bevel_width << 1));
+ action_info.y=cancel_info.y;
+ XGetWidgetInfo(BackButtonText,&back_info);
+ back_info.width=width;
+ back_info.height=(unsigned int) ((3*height) >> 1);
+ back_info.x=QuantumMargin;
+ back_info.y=((5*QuantumMargin) >> 1)+height;
+ XGetWidgetInfo(ResetButtonText,&reset_info);
+ reset_info.width=width;
+ reset_info.height=(unsigned int) ((3*height) >> 1);
+ reset_info.x=QuantumMargin;
+ reset_info.y=back_info.y+back_info.height+QuantumMargin;
+ /*
+ Initialize reply information.
+ */
+ XGetWidgetInfo(reply,&reply_info);
+ reply_info.raised=MagickFalse;
+ reply_info.bevel_width--;
+ reply_info.width=windows->widget.width-width-((6*QuantumMargin) >> 1);
+ reply_info.height=height << 1;
+ reply_info.x=(int) (width+(QuantumMargin << 1));
+ reply_info.y=action_info.y-(action_info.height << 1)-QuantumMargin;
+ /*
+ Initialize mode information.
+ */
+ XGetWidgetInfo(reply,&mode_info);
+ mode_info.bevel_width=0;
+ mode_info.width=(unsigned int)
+ (action_info.x-reply_info.x-QuantumMargin);
+ mode_info.height=action_info.height << 1;
+ mode_info.x=reply_info.x;
+ mode_info.y=action_info.y-action_info.height+action_info.bevel_width;
+ /*
+ Initialize scroll information.
+ */
+ XGetWidgetInfo((char *) NULL,&scroll_info);
+ scroll_info.bevel_width--;
+ scroll_info.width=height;
+ scroll_info.height=(unsigned int)
+ (reply_info.y-back_info.y-(QuantumMargin >> 1));
+ scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
+ scroll_info.y=back_info.y-reply_info.bevel_width;
+ scroll_info.raised=MagickFalse;
+ scroll_info.trough=MagickTrue;
+ north_info=scroll_info;
+ north_info.raised=MagickTrue;
+ north_info.width-=(north_info.bevel_width << 1);
+ north_info.height=north_info.width-1;
+ north_info.x+=north_info.bevel_width;
+ north_info.y+=north_info.bevel_width;
+ south_info=north_info;
+ south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
+ south_info.height;
+ id=slider_info.id;
+ slider_info=north_info;
+ slider_info.id=id;
+ slider_info.width-=2;
+ slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
+ slider_info.bevel_width+2;
+ slider_info.height=scroll_info.height-((slider_info.min_y-
+ scroll_info.y+1) << 1)+4;
+ visible_fonts=scroll_info.height/(height+(height >> 3));
+ if (fonts > (int) visible_fonts)
+ slider_info.height=(visible_fonts*slider_info.height)/fonts;
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.x=scroll_info.x+slider_info.bevel_width+1;
+ slider_info.y=slider_info.min_y;
+ expose_info=scroll_info;
+ expose_info.y=slider_info.y;
+ /*
+ Initialize list information.
+ */
+ XGetWidgetInfo((char *) NULL,&list_info);
+ list_info.raised=MagickFalse;
+ list_info.bevel_width--;
+ list_info.width=(unsigned int)
+ (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
+ list_info.height=scroll_info.height;
+ list_info.x=reply_info.x;
+ list_info.y=scroll_info.y;
+ if (windows->widget.mapped == MagickFalse)
+ state|=JumpListState;
+ /*
+ Initialize text information.
+ */
+ *text='\0';
+ XGetWidgetInfo(text,&text_info);
+ text_info.center=MagickFalse;
+ text_info.width=reply_info.width;
+ text_info.height=height;
+ text_info.x=list_info.x-(QuantumMargin >> 1);
+ text_info.y=QuantumMargin;
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=list_info.width;
+ selection_info.height=(unsigned int) ((9*height) >> 3);
+ selection_info.x=list_info.x;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Font Browser window.
+ */
+ x=QuantumMargin;
+ y=text_info.y+((text_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,FontPatternText,
+ Extent(FontPatternText));
+ (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawBeveledButton(display,&windows->widget,&back_info);
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ XDrawBeveledMatte(display,&windows->widget,&list_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ x=QuantumMargin;
+ y=reply_info.y+((reply_info.height-height) >> 1)+font_info->ascent;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,FontnameText,
+ Extent(FontnameText));
+ XDrawBeveledMatte(display,&windows->widget,&reply_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ selection_info.id=(~0);
+ state|=RedrawActionState;
+ state|=RedrawListState;
+ state&=(~RedrawWidgetState);
+ }
+ if (state & UpdateListState)
+ {
+ char
+ **checklist;
+
+ int
+ number_fonts;
+
+ /*
+ Update font list.
+ */
+ checklist=XListFonts(display,glob_pattern,32767,&number_fonts);
+ if (checklist == (char **) NULL)
+ {
+ if ((strchr(glob_pattern,'*') == (char *) NULL) &&
+ (strchr(glob_pattern,'?') == (char *) NULL))
+ {
+ /*
+ Might be a scaleable font-- exit.
+ */
+ (void) CopyMagickString(reply,glob_pattern,MaxTextExtent);
+ (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
+ (void) XBell(display,0);
+ }
+ else
+ if (number_fonts == 1)
+ {
+ /*
+ Reply is a single font name-- exit.
+ */
+ (void) CopyMagickString(reply,checklist[0],MaxTextExtent);
+ (void) CopyMagickString(glob_pattern,back_pattern,MaxTextExtent);
+ (void) XFreeFontNames(checklist);
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ else
+ {
+ (void) XFreeFontNames(listhead);
+ fontlist=(char **) RelinquishMagickMemory(fontlist);
+ fontlist=checklist;
+ fonts=number_fonts;
+ }
+ /*
+ Sort font list in ascending order.
+ */
+ listhead=fontlist;
+ fontlist=(char **) AcquireQuantumMemory((size_t) fonts,
+ sizeof(*fontlist));
+ if (fontlist == (char **) NULL)
+ {
+ XNoticeWidget(display,windows,"MemoryAllocationFailed",
+ "UnableToViewFonts");
+ return;
+ }
+ for (i=0; i < fonts; i++)
+ fontlist[i]=listhead[i];
+ qsort((void *) fontlist,(size_t) fonts,sizeof(*fontlist),FontCompare);
+ slider_info.height=
+ scroll_info.height-((slider_info.min_y-scroll_info.y+1) << 1)+1;
+ if (fonts > (int) visible_fonts)
+ slider_info.height=(visible_fonts*slider_info.height)/fonts;
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ expose_info.y=slider_info.y;
+ selection_info.id=(~0);
+ list_info.id=(~0);
+ state|=RedrawListState;
+ /*
+ Redraw font name & reply.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ (void) CopyMagickString(text_info.text,glob_pattern,MaxTextExtent);
+ XDrawWidgetText(display,&windows->widget,&text_info);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~UpdateListState);
+ }
+ if (state & JumpListState)
+ {
+ /*
+ Jump scroll to match user font.
+ */
+ list_info.id=(~0);
+ for (i=0; i < fonts; i++)
+ if (LocaleCompare(fontlist[i],reply) >= 0)
+ {
+ list_info.id=LocaleCompare(fontlist[i],reply) == 0 ? i : ~0;
+ break;
+ }
+ if ((i < slider_info.id) || (i >= (int) (slider_info.id+visible_fonts)))
+ slider_info.id=i-(visible_fonts >> 1);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ state&=(~JumpListState);
+ }
+ if (state & RedrawListState)
+ {
+ /*
+ Determine slider id and position.
+ */
+ if (slider_info.id >= (int) (fonts-visible_fonts))
+ slider_info.id=fonts-visible_fonts;
+ if ((slider_info.id < 0) || (fonts <= (int) visible_fonts))
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ if (fonts > 0)
+ slider_info.y+=
+ slider_info.id*(slider_info.max_y-slider_info.min_y+1)/fonts;
+ if (slider_info.id != selection_info.id)
+ {
+ /*
+ Redraw scroll bar and file names.
+ */
+ selection_info.id=slider_info.id;
+ selection_info.y=list_info.y+(height >> 3)+2;
+ for (i=0; i < (int) visible_fonts; i++)
+ {
+ selection_info.raised=(slider_info.id+i) != list_info.id ?
+ MagickTrue : MagickFalse;
+ selection_info.text=(char *) NULL;
+ if ((slider_info.id+i) < fonts)
+ selection_info.text=fontlist[slider_info.id+i];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ /*
+ Update slider.
+ */
+ if (slider_info.y > expose_info.y)
+ {
+ expose_info.height=(unsigned int) slider_info.y-expose_info.y;
+ expose_info.y=slider_info.y-expose_info.height-
+ slider_info.bevel_width-1;
+ }
+ else
+ {
+ expose_info.height=(unsigned int) expose_info.y-slider_info.y;
+ expose_info.y=slider_info.y+slider_info.height+
+ slider_info.bevel_width+1;
+ }
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawMatte(display,&windows->widget,&expose_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ expose_info.y=slider_info.y;
+ }
+ state&=(~RedrawListState);
+ }
+ if (state & RedrawActionState)
+ {
+ XFontStruct
+ *save_info;
+
+ /*
+ Display the selected font in a drawing area.
+ */
+ save_info=windows->widget.font_info;
+ font_info=XLoadQueryFont(display,reply_info.text);
+ if (font_info != (XFontStruct *) NULL)
+ {
+ windows->widget.font_info=font_info;
+ (void) XSetFont(display,windows->widget.widget_context,
+ font_info->fid);
+ }
+ XDrawBeveledButton(display,&windows->widget,&mode_info);
+ windows->widget.font_info=save_info;
+ if (font_info != (XFontStruct *) NULL)
+ {
+ (void) XSetFont(display,windows->widget.widget_context,
+ windows->widget.font_info->fid);
+ (void) XFreeFont(display,font_info);
+ }
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state&=(~RedrawActionState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (north_info.raised && south_info.raised)
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ else
+ {
+ /*
+ Brief delay before advancing scroll bar.
+ */
+ XDelay(display,delay);
+ delay=SuspendTime;
+ (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
+ if (north_info.raised == MagickFalse)
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ slider_info.id--;
+ state|=RedrawListState;
+ }
+ if (south_info.raised == MagickFalse)
+ if (slider_info.id < fonts)
+ {
+ /*
+ Move slider down.
+ */
+ slider_info.id++;
+ state|=RedrawListState;
+ }
+ if (event.type != ButtonRelease)
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(slider_info,event.xbutton))
+ {
+ /*
+ Track slider.
+ */
+ slider_info.active=MagickTrue;
+ break;
+ }
+ if (MatteIsActive(north_info,event.xbutton))
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ north_info.raised=MagickFalse;
+ slider_info.id--;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(south_info,event.xbutton))
+ if (slider_info.id < fonts)
+ {
+ /*
+ Move slider down.
+ */
+ south_info.raised=MagickFalse;
+ slider_info.id++;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(scroll_info,event.xbutton))
+ {
+ /*
+ Move slider.
+ */
+ if (event.xbutton.y < slider_info.y)
+ slider_info.id-=(visible_fonts-1);
+ else
+ slider_info.id+=(visible_fonts-1);
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(list_info,event.xbutton))
+ {
+ int
+ id;
+
+ /*
+ User pressed list matte.
+ */
+ id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
+ selection_info.height;
+ if (id >= (int) fonts)
+ break;
+ (void) CopyMagickString(reply_info.text,fontlist[id],MaxTextExtent);
+ reply_info.highlight=MagickFalse;
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=RedrawActionState;
+ if (id == list_info.id)
+ {
+ (void) CopyMagickString(glob_pattern,reply_info.text,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ selection_info.id=(~0);
+ list_info.id=id;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(back_info,event.xbutton))
+ {
+ /*
+ User pressed Back button.
+ */
+ back_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&back_info);
+ break;
+ }
+ if (MatteIsActive(reset_info,event.xbutton))
+ {
+ /*
+ User pressed Reset button.
+ */
+ reset_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ break;
+ }
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ /*
+ User pressed action button.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ if (event.xbutton.button != Button2)
+ {
+ static Time
+ click_time;
+
+ /*
+ Move text cursor to position of button press.
+ */
+ x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
+ for (i=1; i <= Extent(reply_info.marker); i++)
+ if (XTextWidth(font_info,reply_info.marker,i) > x)
+ break;
+ reply_info.cursor=reply_info.marker+i-1;
+ if (event.xbutton.time > (click_time+DoubleClick))
+ reply_info.highlight=MagickFalse;
+ else
+ {
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ windows->widget.id ? MagickTrue : MagickFalse;
+ }
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ click_time=event.xbutton.time;
+ break;
+ }
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ windows->widget.id,event.xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (north_info.raised == MagickFalse)
+ {
+ /*
+ User released up button.
+ */
+ delay=SuspendTime << 2;
+ north_info.raised=MagickTrue;
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ }
+ if (south_info.raised == MagickFalse)
+ {
+ /*
+ User released down button.
+ */
+ delay=SuspendTime << 2;
+ south_info.raised=MagickTrue;
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ }
+ if (slider_info.active)
+ {
+ /*
+ Stop tracking slider.
+ */
+ slider_info.active=MagickFalse;
+ break;
+ }
+ if (back_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(back_info,event.xbutton))
+ {
+ (void) CopyMagickString(glob_pattern,back_pattern,
+ MaxTextExtent);
+ state|=UpdateListState;
+ }
+ back_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&back_info);
+ }
+ if (reset_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(reset_info,event.xbutton))
+ {
+ (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
+ (void) CopyMagickString(glob_pattern,reset_pattern,MaxTextExtent);
+ state|=UpdateListState;
+ }
+ reset_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ }
+ if (action_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ {
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ if (*reply_info.text == '\0')
+ (void) XBell(display,0);
+ else
+ state|=ExitState;
+ }
+ }
+ action_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (AreaIsActive(scroll_info,event.xkey))
+ {
+ /*
+ Move slider.
+ */
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ slider_info.id=0;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ slider_info.id--;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ slider_info.id++;
+ break;
+ }
+ case XK_Prior:
+ case XK_KP_Prior:
+ {
+ slider_info.id-=visible_fonts;
+ break;
+ }
+ case XK_Next:
+ case XK_KP_Next:
+ {
+ slider_info.id+=visible_fonts;
+ break;
+ }
+ case XK_End:
+ case XK_KP_End:
+ {
+ slider_info.id=fonts;
+ break;
+ }
+ }
+ state|=RedrawListState;
+ break;
+ }
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ /*
+ Read new font or glob patterm.
+ */
+ if (*reply_info.text == '\0')
+ break;
+ (void) CopyMagickString(back_pattern,glob_pattern,MaxTextExtent);
+ (void) CopyMagickString(glob_pattern,reply_info.text,MaxTextExtent);
+ state|=UpdateListState;
+ break;
+ }
+ if (key_symbol == XK_Control_L)
+ {
+ state|=ControlState;
+ break;
+ }
+ if (state & ControlState)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ reply_info.marker=reply_info.text;
+ reply_info.highlight=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ XEditText(display,&reply_info,key_symbol,command,state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ break;
+ }
+ case KeyRelease:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key release.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (key_symbol == XK_Control_L)
+ state&=(~ControlState);
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MapNotify:
+ {
+ mask&=(~CWX);
+ mask&=(~CWY);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (slider_info.active)
+ {
+ /*
+ Move slider matte.
+ */
+ slider_info.y=event.xmotion.y-
+ ((slider_info.height+slider_info.bevel_width) >> 1)+1;
+ if (slider_info.y < slider_info.min_y)
+ slider_info.y=slider_info.min_y;
+ if (slider_info.y > slider_info.max_y)
+ slider_info.y=slider_info.max_y;
+ slider_info.id=0;
+ if (slider_info.y != slider_info.min_y)
+ slider_info.id=(fonts*(slider_info.y-slider_info.min_y+1))/
+ (slider_info.max_y-slider_info.min_y+1);
+ state|=RedrawListState;
+ break;
+ }
+ if (state & InactiveWidgetState)
+ break;
+ if (back_info.raised == MatteIsActive(back_info,event.xmotion))
+ {
+ /*
+ Back button status changed.
+ */
+ back_info.raised=!back_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&back_info);
+ break;
+ }
+ if (reset_info.raised == MatteIsActive(reset_info,event.xmotion))
+ {
+ /*
+ Reset button status changed.
+ */
+ reset_info.raised=!reset_info.raised;
+ XDrawBeveledButton(display,&windows->widget,&reset_info);
+ break;
+ }
+ if (action_info.raised == MatteIsActive(action_info,event.xmotion))
+ {
+ /*
+ Action button status changed.
+ */
+ action_info.raised=action_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&action_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ reply_info.highlight=MagickFalse;
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,event.xselection.requestor,
+ event.xselection.property,0L,2047L,MagickTrue,XA_STRING,&type,
+ &format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ if ((Extent(reply_info.text)+length) >= MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ /*
+ Insert primary selection in reply text.
+ */
+ *(data+length)='\0';
+ XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
+ state);
+ XDrawMatteText(display,&windows->widget,&reply_info);
+ state|=JumpListState;
+ state|=RedrawActionState;
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ /*
+ Set XA_PRIMARY selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,0,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ /*
+ Free font list.
+ */
+ (void) XFreeFontNames(listhead);
+ fontlist=(char **) RelinquishMagickMemory(fontlist);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X I n f o W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XInfoWidget() displays text in the Info widget. The purpose is to inform
+% the user that what activity is currently being performed (e.g. reading
+% an image, rotating an image, etc.).
+%
+% The format of the XInfoWidget method is:
+%
+% void XInfoWidget(Display *display,XWindows *windows,const char *activity)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o activity: This character string reflects the current activity and is
+% displayed in the Info widget.
+%
+*/
+MagickExport void XInfoWidget(Display *display,XWindows *windows,
+ const char *activity)
+{
+ unsigned int
+ height,
+ margin,
+ width;
+
+ XFontStruct
+ *font_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Map Info widget.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(activity != (char *) NULL);
+ font_info=windows->info.font_info;
+ width=WidgetTextWidth(font_info,(char *) activity)+((3*QuantumMargin) >> 1)+4;
+ height=(unsigned int) (((6*(font_info->ascent+font_info->descent)) >> 2)+4);
+ if ((windows->info.width != width) || (windows->info.height != height))
+ {
+ /*
+ Size Info widget to accommodate the activity text.
+ */
+ windows->info.width=width;
+ windows->info.height=height;
+ window_changes.width=(int) width;
+ window_changes.height=(int) height;
+ (void) XReconfigureWMWindow(display,windows->info.id,windows->info.screen,
+ (unsigned int) (CWWidth | CWHeight),&window_changes);
+ }
+ if (windows->info.mapped == MagickFalse)
+ {
+ (void) XMapRaised(display,windows->info.id);
+ windows->info.mapped=MagickTrue;
+ }
+ /*
+ Initialize Info matte information.
+ */
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ XGetWidgetInfo(activity,&monitor_info);
+ monitor_info.bevel_width--;
+ margin=monitor_info.bevel_width+((windows->info.height-height) >> 1)-2;
+ monitor_info.center=MagickFalse;
+ monitor_info.x=(int) margin;
+ monitor_info.y=(int) margin;
+ monitor_info.width=windows->info.width-(margin << 1);
+ monitor_info.height=windows->info.height-(margin << 1)+1;
+ /*
+ Draw Info widget.
+ */
+ monitor_info.raised=MagickFalse;
+ XDrawBeveledMatte(display,&windows->info,&monitor_info);
+ monitor_info.raised=MagickTrue;
+ XDrawWidgetText(display,&windows->info,&monitor_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X L i s t B r o w s e r W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XListBrowserWidget() displays a List Browser widget with a query to the
+% user. The user keys a reply or select a reply from the list. Finally, the
+% user presses the Action or Cancel button to exit. The typed text is
+% returned as the reply function parameter.
+%
+% The format of the XListBrowserWidget method is:
+%
+% void XListBrowserWidget(Display *display,XWindows *windows,
+% XWindowInfo *window_info,const char **list,const char *action,
+% const char *query,char *reply)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o list: Specifies a pointer to an array of strings. The user can
+% select from these strings as a possible reply value.
+%
+% o action: Specifies a pointer to the action of this widget.
+%
+% o query: Specifies a pointer to the query to present to the user.
+%
+% o reply: the response from the user is returned in this parameter.
+%
+*/
+MagickExport void XListBrowserWidget(Display *display,XWindows *windows,
+ XWindowInfo *window_info,const char **list,const char *action,
+ const char *query,char *reply)
+{
+#define CancelButtonText "Cancel"
+
+ char
+ primary_selection[MaxTextExtent];
+
+ int
+ x;
+
+ register int
+ i;
+
+ static MagickStatusType
+ mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
+
+ Status
+ status;
+
+ unsigned int
+ entries,
+ height,
+ text_width,
+ visible_entries,
+ width;
+
+ unsigned long
+ delay,
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ action_info,
+ cancel_info,
+ expose_info,
+ list_info,
+ north_info,
+ reply_info,
+ scroll_info,
+ selection_info,
+ slider_info,
+ south_info,
+ text_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Count the number of entries in the list.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(window_info != (XWindowInfo *) NULL);
+ assert(list != (const char **) NULL);
+ assert(action != (char *) NULL);
+ assert(query != (char *) NULL);
+ assert(reply != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",action);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ if (list == (const char **) NULL)
+ {
+ XNoticeWidget(display,windows,"No text to browse:",(char *) NULL);
+ return;
+ }
+ for (entries=0; ; entries++)
+ if (list[entries] == (char *) NULL)
+ break;
+ /*
+ Determine Font Browser widget attributes.
+ */
+ font_info=window_info->font_info;
+ text_width=WidgetTextWidth(font_info,(char *) query);
+ for (i=0; i < (int) entries; i++)
+ if (WidgetTextWidth(font_info,(char *) list[i]) > text_width)
+ text_width=WidgetTextWidth(font_info,(char *) list[i]);
+ width=WidgetTextWidth(font_info,(char *) action);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ width+=QuantumMargin;
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position List Browser widget.
+ */
+ window_info->width=(unsigned int) MagickMin((int) text_width,(int)
+ MaxTextWidth)+((9*QuantumMargin) >> 1);
+ window_info->min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
+ if (window_info->width < window_info->min_width)
+ window_info->width=window_info->min_width;
+ window_info->height=(unsigned int)
+ (((81*height) >> 2)+((13*QuantumMargin) >> 1)+4);
+ window_info->min_height=(unsigned int)
+ (((23*height) >> 1)+((13*QuantumMargin) >> 1)+4);
+ if (window_info->height < window_info->min_height)
+ window_info->height=window_info->min_height;
+ XConstrainWindowPosition(display,window_info);
+ /*
+ Map List Browser widget.
+ */
+ (void) CopyMagickString(window_info->name,"Browse",MaxTextExtent);
+ status=XStringListToTextProperty(&window_info->name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,window_info->id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) window_info->width;
+ window_changes.height=(int) window_info->height;
+ window_changes.x=window_info->x;
+ window_changes.y=window_info->y;
+ (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,mask,
+ &window_changes);
+ (void) XMapRaised(display,window_info->id);
+ window_info->mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ XGetWidgetInfo((char *) NULL,&slider_info);
+ XGetWidgetInfo((char *) NULL,&north_info);
+ XGetWidgetInfo((char *) NULL,&south_info);
+ XGetWidgetInfo((char *) NULL,&expose_info);
+ visible_entries=0;
+ delay=SuspendTime << 2;
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ int
+ id;
+
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) ((3*height) >> 1);
+ cancel_info.x=(int)
+ (window_info->width-cancel_info.width-QuantumMargin-2);
+ cancel_info.y=(int)
+ (window_info->height-cancel_info.height-QuantumMargin);
+ XGetWidgetInfo(action,&action_info);
+ action_info.width=width;
+ action_info.height=(unsigned int) ((3*height) >> 1);
+ action_info.x=cancel_info.x-(cancel_info.width+(QuantumMargin >> 1)+
+ (action_info.bevel_width << 1));
+ action_info.y=cancel_info.y;
+ /*
+ Initialize reply information.
+ */
+ XGetWidgetInfo(reply,&reply_info);
+ reply_info.raised=MagickFalse;
+ reply_info.bevel_width--;
+ reply_info.width=window_info->width-((4*QuantumMargin) >> 1);
+ reply_info.height=height << 1;
+ reply_info.x=QuantumMargin;
+ reply_info.y=action_info.y-reply_info.height-QuantumMargin;
+ /*
+ Initialize scroll information.
+ */
+ XGetWidgetInfo((char *) NULL,&scroll_info);
+ scroll_info.bevel_width--;
+ scroll_info.width=height;
+ scroll_info.height=(unsigned int)
+ (reply_info.y-((6*QuantumMargin) >> 1)-height);
+ scroll_info.x=reply_info.x+(reply_info.width-scroll_info.width);
+ scroll_info.y=((5*QuantumMargin) >> 1)+height-reply_info.bevel_width;
+ scroll_info.raised=MagickFalse;
+ scroll_info.trough=MagickTrue;
+ north_info=scroll_info;
+ north_info.raised=MagickTrue;
+ north_info.width-=(north_info.bevel_width << 1);
+ north_info.height=north_info.width-1;
+ north_info.x+=north_info.bevel_width;
+ north_info.y+=north_info.bevel_width;
+ south_info=north_info;
+ south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
+ south_info.height;
+ id=slider_info.id;
+ slider_info=north_info;
+ slider_info.id=id;
+ slider_info.width-=2;
+ slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
+ slider_info.bevel_width+2;
+ slider_info.height=scroll_info.height-((slider_info.min_y-
+ scroll_info.y+1) << 1)+4;
+ visible_entries=scroll_info.height/(height+(height >> 3));
+ if (entries > visible_entries)
+ slider_info.height=(visible_entries*slider_info.height)/entries;
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.x=scroll_info.x+slider_info.bevel_width+1;
+ slider_info.y=slider_info.min_y;
+ expose_info=scroll_info;
+ expose_info.y=slider_info.y;
+ /*
+ Initialize list information.
+ */
+ XGetWidgetInfo((char *) NULL,&list_info);
+ list_info.raised=MagickFalse;
+ list_info.bevel_width--;
+ list_info.width=(unsigned int)
+ (scroll_info.x-reply_info.x-(QuantumMargin >> 1));
+ list_info.height=scroll_info.height;
+ list_info.x=reply_info.x;
+ list_info.y=scroll_info.y;
+ if (window_info->mapped == MagickFalse)
+ for (i=0; i < (int) entries; i++)
+ if (LocaleCompare(list[i],reply) == 0)
+ {
+ list_info.id=i;
+ slider_info.id=i-(visible_entries >> 1);
+ if (slider_info.id < 0)
+ slider_info.id=0;
+ }
+ /*
+ Initialize text information.
+ */
+ XGetWidgetInfo(query,&text_info);
+ text_info.width=reply_info.width;
+ text_info.height=height;
+ text_info.x=list_info.x-(QuantumMargin >> 1);
+ text_info.y=QuantumMargin;
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=list_info.width;
+ selection_info.height=(unsigned int) ((9*height) >> 3);
+ selection_info.x=list_info.x;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw List Browser window.
+ */
+ XDrawWidgetText(display,window_info,&text_info);
+ XDrawBeveledMatte(display,window_info,&list_info);
+ XDrawBeveledMatte(display,window_info,&scroll_info);
+ XDrawTriangleNorth(display,window_info,&north_info);
+ XDrawBeveledButton(display,window_info,&slider_info);
+ XDrawTriangleSouth(display,window_info,&south_info);
+ XDrawBeveledMatte(display,window_info,&reply_info);
+ XDrawMatteText(display,window_info,&reply_info);
+ XDrawBeveledButton(display,window_info,&action_info);
+ XDrawBeveledButton(display,window_info,&cancel_info);
+ XHighlightWidget(display,window_info,BorderOffset,BorderOffset);
+ selection_info.id=(~0);
+ state|=RedrawActionState;
+ state|=RedrawListState;
+ state&=(~RedrawWidgetState);
+ }
+ if (state & RedrawListState)
+ {
+ /*
+ Determine slider id and position.
+ */
+ if (slider_info.id >= (int) (entries-visible_entries))
+ slider_info.id=(int) (entries-visible_entries);
+ if ((slider_info.id < 0) || (entries <= visible_entries))
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ if (entries > 0)
+ slider_info.y+=
+ slider_info.id*(slider_info.max_y-slider_info.min_y+1)/entries;
+ if (slider_info.id != selection_info.id)
+ {
+ /*
+ Redraw scroll bar and file names.
+ */
+ selection_info.id=slider_info.id;
+ selection_info.y=list_info.y+(height >> 3)+2;
+ for (i=0; i < (int) visible_entries; i++)
+ {
+ selection_info.raised=(slider_info.id+i) != list_info.id ?
+ MagickTrue : MagickFalse;
+ selection_info.text=(char *) NULL;
+ if ((slider_info.id+i) < (int) entries)
+ selection_info.text=(char *) list[slider_info.id+i];
+ XDrawWidgetText(display,window_info,&selection_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ /*
+ Update slider.
+ */
+ if (slider_info.y > expose_info.y)
+ {
+ expose_info.height=(unsigned int) slider_info.y-expose_info.y;
+ expose_info.y=slider_info.y-expose_info.height-
+ slider_info.bevel_width-1;
+ }
+ else
+ {
+ expose_info.height=(unsigned int) expose_info.y-slider_info.y;
+ expose_info.y=slider_info.y+slider_info.height+
+ slider_info.bevel_width+1;
+ }
+ XDrawTriangleNorth(display,window_info,&north_info);
+ XDrawMatte(display,window_info,&expose_info);
+ XDrawBeveledButton(display,window_info,&slider_info);
+ XDrawTriangleSouth(display,window_info,&south_info);
+ expose_info.y=slider_info.y;
+ }
+ state&=(~RedrawListState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (north_info.raised && south_info.raised)
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ else
+ {
+ /*
+ Brief delay before advancing scroll bar.
+ */
+ XDelay(display,delay);
+ delay=SuspendTime;
+ (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
+ if (north_info.raised == MagickFalse)
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ slider_info.id--;
+ state|=RedrawListState;
+ }
+ if (south_info.raised == MagickFalse)
+ if (slider_info.id < (int) entries)
+ {
+ /*
+ Move slider down.
+ */
+ slider_info.id++;
+ state|=RedrawListState;
+ }
+ if (event.type != ButtonRelease)
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(slider_info,event.xbutton))
+ {
+ /*
+ Track slider.
+ */
+ slider_info.active=MagickTrue;
+ break;
+ }
+ if (MatteIsActive(north_info,event.xbutton))
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ north_info.raised=MagickFalse;
+ slider_info.id--;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(south_info,event.xbutton))
+ if (slider_info.id < (int) entries)
+ {
+ /*
+ Move slider down.
+ */
+ south_info.raised=MagickFalse;
+ slider_info.id++;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(scroll_info,event.xbutton))
+ {
+ /*
+ Move slider.
+ */
+ if (event.xbutton.y < slider_info.y)
+ slider_info.id-=(visible_entries-1);
+ else
+ slider_info.id+=(visible_entries-1);
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(list_info,event.xbutton))
+ {
+ int
+ id;
+
+ /*
+ User pressed list matte.
+ */
+ id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
+ selection_info.height;
+ if (id >= (int) entries)
+ break;
+ (void) CopyMagickString(reply_info.text,list[id],MaxTextExtent);
+ reply_info.highlight=MagickFalse;
+ reply_info.marker=reply_info.text;
+ reply_info.cursor=reply_info.text+Extent(reply_info.text);
+ XDrawMatteText(display,window_info,&reply_info);
+ selection_info.id=(~0);
+ if (id == list_info.id)
+ {
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,window_info,&action_info);
+ state|=ExitState;
+ }
+ list_info.id=id;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ /*
+ User pressed action button.
+ */
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,window_info,&action_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,window_info,&cancel_info);
+ break;
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ if (event.xbutton.button != Button2)
+ {
+ static Time
+ click_time;
+
+ /*
+ Move text cursor to position of button press.
+ */
+ x=event.xbutton.x-reply_info.x-(QuantumMargin >> 2);
+ for (i=1; i <= Extent(reply_info.marker); i++)
+ if (XTextWidth(font_info,reply_info.marker,i) > x)
+ break;
+ reply_info.cursor=reply_info.marker+i-1;
+ if (event.xbutton.time > (click_time+DoubleClick))
+ reply_info.highlight=MagickFalse;
+ else
+ {
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,reply_info.text,
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,window_info->id,
+ event.xbutton.time);
+ reply_info.highlight=XGetSelectionOwner(display,XA_PRIMARY) ==
+ window_info->id ? MagickTrue : MagickFalse;
+ }
+ XDrawMatteText(display,window_info,&reply_info);
+ click_time=event.xbutton.time;
+ break;
+ }
+ /*
+ Request primary selection.
+ */
+ (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
+ window_info->id,event.xbutton.time);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (window_info->mapped == MagickFalse)
+ break;
+ if (north_info.raised == MagickFalse)
+ {
+ /*
+ User released up button.
+ */
+ delay=SuspendTime << 2;
+ north_info.raised=MagickTrue;
+ XDrawTriangleNorth(display,window_info,&north_info);
+ }
+ if (south_info.raised == MagickFalse)
+ {
+ /*
+ User released down button.
+ */
+ delay=SuspendTime << 2;
+ south_info.raised=MagickTrue;
+ XDrawTriangleSouth(display,window_info,&south_info);
+ }
+ if (slider_info.active)
+ {
+ /*
+ Stop tracking slider.
+ */
+ slider_info.active=MagickFalse;
+ break;
+ }
+ if (action_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == window_info->id)
+ {
+ if (MatteIsActive(action_info,event.xbutton))
+ {
+ if (*reply_info.text == '\0')
+ (void) XBell(display,0);
+ else
+ state|=ExitState;
+ }
+ }
+ action_info.raised=MagickTrue;
+ XDrawBeveledButton(display,window_info,&action_info);
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == window_info->id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ }
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,window_info,&cancel_info);
+ }
+ if (MatteIsActive(reply_info,event.xbutton) == MagickFalse)
+ break;
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == window_info->id)
+ {
+ *reply_info.text='\0';
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != window_info->id)
+ break;
+ if ((event.xconfigure.width == (int) window_info->width) &&
+ (event.xconfigure.height == (int) window_info->height))
+ break;
+ window_info->width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) window_info->min_width);
+ window_info->height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) window_info->min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != window_info->id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != window_info->id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != window_info->id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if (AreaIsActive(scroll_info,event.xkey))
+ {
+ /*
+ Move slider.
+ */
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ slider_info.id=0;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ slider_info.id--;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ slider_info.id++;
+ break;
+ }
+ case XK_Prior:
+ case XK_KP_Prior:
+ {
+ slider_info.id-=visible_entries;
+ break;
+ }
+ case XK_Next:
+ case XK_KP_Next:
+ {
+ slider_info.id+=visible_entries;
+ break;
+ }
+ case XK_End:
+ case XK_KP_End:
+ {
+ slider_info.id=(int) entries;
+ break;
+ }
+ }
+ state|=RedrawListState;
+ break;
+ }
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ /*
+ Read new entry.
+ */
+ if (*reply_info.text == '\0')
+ break;
+ action_info.raised=MagickFalse;
+ XDrawBeveledButton(display,window_info,&action_info);
+ state|=ExitState;
+ break;
+ }
+ if (key_symbol == XK_Control_L)
+ {
+ state|=ControlState;
+ break;
+ }
+ if (state & ControlState)
+ switch ((int) key_symbol)
+ {
+ case XK_u:
+ case XK_U:
+ {
+ /*
+ Erase the entire line of text.
+ */
+ *reply_info.text='\0';
+ reply_info.cursor=reply_info.text;
+ reply_info.marker=reply_info.text;
+ reply_info.highlight=MagickFalse;
+ break;
+ }
+ default:
+ break;
+ }
+ XEditText(display,&reply_info,key_symbol,command,state);
+ XDrawMatteText(display,window_info,&reply_info);
+ break;
+ }
+ case KeyRelease:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key release.
+ */
+ if (event.xkey.window != window_info->id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if (key_symbol == XK_Control_L)
+ state&=(~ControlState);
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != window_info->id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MapNotify:
+ {
+ mask&=(~CWX);
+ mask&=(~CWY);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (slider_info.active)
+ {
+ /*
+ Move slider matte.
+ */
+ slider_info.y=event.xmotion.y-
+ ((slider_info.height+slider_info.bevel_width) >> 1)+1;
+ if (slider_info.y < slider_info.min_y)
+ slider_info.y=slider_info.min_y;
+ if (slider_info.y > slider_info.max_y)
+ slider_info.y=slider_info.max_y;
+ slider_info.id=0;
+ if (slider_info.y != slider_info.min_y)
+ slider_info.id=(int) ((entries*(slider_info.y-
+ slider_info.min_y+1))/(slider_info.max_y-slider_info.min_y+1));
+ state|=RedrawListState;
+ break;
+ }
+ if (state & InactiveWidgetState)
+ break;
+ if (action_info.raised == MatteIsActive(action_info,event.xmotion))
+ {
+ /*
+ Action button status changed.
+ */
+ action_info.raised=action_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,window_info,&action_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=cancel_info.raised == MagickFalse ?
+ MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,window_info,&cancel_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ reply_info.highlight=MagickFalse;
+ XDrawMatteText(display,window_info,&reply_info);
+ break;
+ }
+ case SelectionNotify:
+ {
+ Atom
+ type;
+
+ int
+ format;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ Obtain response from primary selection.
+ */
+ if (event.xselection.property == (Atom) None)
+ break;
+ status=XGetWindowProperty(display,
+ event.xselection.requestor,event.xselection.property,0L,2047L,
+ MagickTrue,XA_STRING,&type,&format,&length,&after,&data);
+ if ((status != Success) || (type != XA_STRING) || (format == 32) ||
+ (length == 0))
+ break;
+ if ((Extent(reply_info.text)+length) >= MaxTextExtent)
+ (void) XBell(display,0);
+ else
+ {
+ /*
+ Insert primary selection in reply text.
+ */
+ *(data+length)='\0';
+ XEditText(display,&reply_info,(KeySym) XK_Insert,(char *) data,
+ state);
+ XDrawMatteText(display,window_info,&reply_info);
+ state|=RedrawActionState;
+ }
+ (void) XFree((void *) data);
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ if (reply_info.highlight == MagickFalse)
+ break;
+ /*
+ Set primary selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.send_event=MagickTrue;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,window_info->id,window_info->screen);
+ XCheckRefreshWindows(display,windows);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M e n u W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMenuWidget() maps a menu and returns the command pointed to by the user
+% when the button is released.
+%
+% The format of the XMenuWidget method is:
+%
+% int XMenuWidget(Display *display,XWindows *windows,const char *title,
+% const char **selections,char *item)
+%
+% A description of each parameter follows:
+%
+% o selection_number: Specifies the number of the selection that the
+% user choose.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o title: Specifies a character string that describes the menu selections.
+%
+% o selections: Specifies a pointer to one or more strings that comprise
+% the choices in the menu.
+%
+% o item: Specifies a character array. The item selected from the menu
+% is returned here.
+%
+*/
+MagickExport int XMenuWidget(Display *display,XWindows *windows,
+ const char *title,const char **selections,char *item)
+{
+ Cursor
+ cursor;
+
+ int
+ id,
+ x,
+ y;
+
+ unsigned int
+ height,
+ number_selections,
+ title_height,
+ top_offset,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XSetWindowAttributes
+ window_attributes;
+
+ XWidgetInfo
+ highlight_info,
+ menu_info,
+ selection_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine Menu widget attributes.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(title != (char *) NULL);
+ assert(selections != (const char **) NULL);
+ assert(item != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
+ font_info=windows->widget.font_info;
+ windows->widget.width=submenu_info.active == 0 ?
+ WidgetTextWidth(font_info,(char *) title) : 0;
+ for (id=0; selections[id] != (char *) NULL; id++)
+ {
+ width=WidgetTextWidth(font_info,(char *) selections[id]);
+ if (width > windows->widget.width)
+ windows->widget.width=width;
+ }
+ number_selections=(unsigned int) id;
+ XGetWidgetInfo((char *) NULL,&menu_info);
+ title_height=(unsigned int) (submenu_info.active == 0 ?
+ (3*(font_info->descent+font_info->ascent) >> 1)+5 : 2);
+ width=WidgetTextWidth(font_info,(char *) title);
+ height=(unsigned int) ((3*(font_info->ascent+font_info->descent)) >> 1);
+ /*
+ Position Menu widget.
+ */
+ windows->widget.width+=QuantumMargin+(menu_info.bevel_width << 1);
+ top_offset=title_height+menu_info.bevel_width-1;
+ windows->widget.height=top_offset+number_selections*height+4;
+ windows->widget.min_width=windows->widget.width;
+ windows->widget.min_height=windows->widget.height;
+ XQueryPosition(display,windows->widget.root,&x,&y);
+ windows->widget.x=x-(QuantumMargin >> 1);
+ if (submenu_info.active != 0)
+ {
+ windows->widget.x=
+ windows->command.x+windows->command.width-QuantumMargin;
+ toggle_info.raised=MagickTrue;
+ XDrawTriangleEast(display,&windows->command,&toggle_info);
+ }
+ windows->widget.y=submenu_info.active == 0 ? y-(long)
+ ((3*title_height) >> 2) : y;
+ if (submenu_info.active != 0)
+ windows->widget.y=windows->command.y+submenu_info.y;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Menu widget.
+ */
+ window_attributes.override_redirect=MagickTrue;
+ (void) XChangeWindowAttributes(display,windows->widget.id,
+ (unsigned long) CWOverrideRedirect,&window_attributes);
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ selection_info.height=height;
+ cursor=XCreateFontCursor(display,XC_right_ptr);
+ (void) XCheckDefineCursor(display,windows->image.id,cursor);
+ (void) XCheckDefineCursor(display,windows->command.id,cursor);
+ (void) XCheckDefineCursor(display,windows->widget.id,cursor);
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&menu_info);
+ menu_info.bevel_width--;
+ menu_info.width=windows->widget.width-((menu_info.bevel_width) << 1);
+ menu_info.height=windows->widget.height-((menu_info.bevel_width) << 1);
+ menu_info.x=(int) menu_info.bevel_width;
+ menu_info.y=(int) menu_info.bevel_width;
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=menu_info.width;
+ selection_info.height=height;
+ selection_info.x=menu_info.x;
+ highlight_info=selection_info;
+ highlight_info.bevel_width--;
+ highlight_info.width-=(highlight_info.bevel_width << 1);
+ highlight_info.height-=(highlight_info.bevel_width << 1);
+ highlight_info.x+=highlight_info.bevel_width;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Menu widget.
+ */
+ if (submenu_info.active == 0)
+ {
+ y=(int) title_height;
+ XSetBevelColor(display,&windows->widget,MagickFalse);
+ (void) XDrawLine(display,windows->widget.id,
+ windows->widget.widget_context,selection_info.x,y-1,
+ (int) selection_info.width,y-1);
+ XSetBevelColor(display,&windows->widget,MagickTrue);
+ (void) XDrawLine(display,windows->widget.id,
+ windows->widget.widget_context,selection_info.x,y,
+ (int) selection_info.width,y);
+ (void) XSetFillStyle(display,windows->widget.widget_context,
+ FillSolid);
+ }
+ /*
+ Draw menu selections.
+ */
+ selection_info.center=MagickTrue;
+ selection_info.y=(int) menu_info.bevel_width;
+ selection_info.text=(char *) title;
+ if (submenu_info.active == 0)
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.y=(int) top_offset;
+ for (id=0; id < (int) number_selections; id++)
+ {
+ selection_info.text=(char *) selections[id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ highlight_info.y=selection_info.y+highlight_info.bevel_width;
+ if (id == selection_info.id)
+ XDrawBevel(display,&windows->widget,&highlight_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ XDrawBevel(display,&windows->widget,&menu_info);
+ state&=(~RedrawWidgetState);
+ }
+ if (number_selections > 2)
+ {
+ /*
+ Redraw Menu line.
+ */
+ y=(int) (top_offset+selection_info.height*(number_selections-1));
+ XSetBevelColor(display,&windows->widget,MagickFalse);
+ (void) XDrawLine(display,windows->widget.id,
+ windows->widget.widget_context,selection_info.x,y-1,
+ (int) selection_info.width,y-1);
+ XSetBevelColor(display,&windows->widget,MagickTrue);
+ (void) XDrawLine(display,windows->widget.id,
+ windows->widget.widget_context,selection_info.x,y,
+ (int) selection_info.width,y);
+ (void) XSetFillStyle(display,windows->widget.widget_context,FillSolid);
+ }
+ /*
+ Wait for next event.
+ */
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (event.xbutton.window != windows->widget.id)
+ {
+ /*
+ exit menu.
+ */
+ if (event.xbutton.window == windows->command.id)
+ (void) XPutBackEvent(display,&event);
+ selection_info.id=(~0);
+ *item='\0';
+ state|=ExitState;
+ break;
+ }
+ state&=(~InactiveWidgetState);
+ id=(event.xbutton.y-top_offset)/(int) selection_info.height;
+ selection_info.id=id;
+ if ((id < 0) || (id >= (int) number_selections))
+ break;
+ /*
+ Highlight this selection.
+ */
+ selection_info.y=(int) (top_offset+id*selection_info.height);
+ selection_info.text=(char *) selections[id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ highlight_info.y=selection_info.y+highlight_info.bevel_width;
+ XDrawBevel(display,&windows->widget,&highlight_info);
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (event.xbutton.window == windows->command.id)
+ if ((state & InactiveWidgetState) == 0)
+ break;
+ /*
+ exit menu.
+ */
+ XSetCursorState(display,windows,MagickFalse);
+ *item='\0';
+ state|=ExitState;
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ if (event.xcrossing.state == 0)
+ break;
+ state&=(~InactiveWidgetState);
+ id=((event.xcrossing.y-top_offset)/(int) selection_info.height);
+ if ((selection_info.id >= 0) &&
+ (selection_info.id < (int) number_selections))
+ {
+ /*
+ Unhighlight last selection.
+ */
+ if (id == selection_info.id)
+ break;
+ selection_info.y=(int)
+ (top_offset+selection_info.id*selection_info.height);
+ selection_info.text=(char *) selections[selection_info.id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ }
+ if ((id < 0) || (id >= (int) number_selections))
+ break;
+ /*
+ Highlight this selection.
+ */
+ selection_info.id=id;
+ selection_info.y=(int)
+ (top_offset+selection_info.id*selection_info.height);
+ selection_info.text=(char *) selections[selection_info.id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ highlight_info.y=selection_info.y+highlight_info.bevel_width;
+ XDrawBevel(display,&windows->widget,&highlight_info);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ id=selection_info.id;
+ if ((id < 0) || (id >= (int) number_selections))
+ break;
+ /*
+ Unhighlight last selection.
+ */
+ selection_info.y=(int) (top_offset+id*selection_info.height);
+ selection_info.id=(~0);
+ selection_info.text=(char *) selections[id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (submenu_info.active != 0)
+ if (event.xmotion.window == windows->command.id)
+ {
+ if ((state & InactiveWidgetState) == 0)
+ {
+ if (MatteIsActive(submenu_info,event.xmotion) == MagickFalse)
+ {
+ selection_info.id=(~0);
+ *item='\0';
+ state|=ExitState;
+ break;
+ }
+ }
+ else
+ if (WindowIsActive(windows->command,event.xmotion))
+ {
+ selection_info.id=(~0);
+ *item='\0';
+ state|=ExitState;
+ break;
+ }
+ }
+ if (event.xmotion.window != windows->widget.id)
+ break;
+ if (state & InactiveWidgetState)
+ break;
+ id=(event.xmotion.y-top_offset)/(int) selection_info.height;
+ if ((selection_info.id >= 0) &&
+ (selection_info.id < (int) number_selections))
+ {
+ /*
+ Unhighlight last selection.
+ */
+ if (id == selection_info.id)
+ break;
+ selection_info.y=(int)
+ (top_offset+selection_info.id*selection_info.height);
+ selection_info.text=(char *) selections[selection_info.id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ }
+ selection_info.id=id;
+ if ((id < 0) || (id >= (int) number_selections))
+ break;
+ /*
+ Highlight this selection.
+ */
+ selection_info.y=(int) (top_offset+id*selection_info.height);
+ selection_info.text=(char *) selections[id];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ highlight_info.y=selection_info.y+highlight_info.bevel_width;
+ XDrawBevel(display,&windows->widget,&highlight_info);
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ (void) XFreeCursor(display,cursor);
+ window_attributes.override_redirect=MagickFalse;
+ (void) XChangeWindowAttributes(display,windows->widget.id,
+ (unsigned long) CWOverrideRedirect,&window_attributes);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ if (submenu_info.active != 0)
+ {
+ submenu_info.active=MagickFalse;
+ toggle_info.raised=MagickFalse;
+ XDrawTriangleEast(display,&windows->command,&toggle_info);
+ }
+ if ((selection_info.id < 0) || (selection_info.id >= (int) number_selections))
+ return(~0);
+ (void) CopyMagickString(item,selections[selection_info.id],MaxTextExtent);
+ return(selection_info.id);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X N o t i c e W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XNoticeWidget() displays a Notice widget with a notice to the user. The
+% function returns when the user presses the "Dismiss" button.
+%
+% The format of the XNoticeWidget method is:
+%
+% void XNoticeWidget(Display *display,XWindows *windows,
+% const char *reason,const char *description)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o reason: Specifies the message to display before terminating the
+% program.
+%
+% o description: Specifies any description to the message.
+%
+*/
+MagickExport void XNoticeWidget(Display *display,XWindows *windows,
+ const char *reason,const char *description)
+{
+#define DismissButtonText "Dismiss"
+#define Timeout 8
+
+ const char
+ *text;
+
+ int
+ x,
+ y;
+
+ Status
+ status;
+
+ time_t
+ timer;
+
+ unsigned int
+ height,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ dismiss_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine Notice widget attributes.
+ */
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(reason != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",reason);
+ XDelay(display,SuspendTime << 3); /* avoid surpise with delay */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ font_info=windows->widget.font_info;
+ width=WidgetTextWidth(font_info,DismissButtonText);
+ text=GetLocaleExceptionMessage(XServerError,reason);
+ if (text != (char *) NULL)
+ if (WidgetTextWidth(font_info,(char *) text) > width)
+ width=WidgetTextWidth(font_info,(char *) text);
+ if (description != (char *) NULL)
+ {
+ text=GetLocaleExceptionMessage(XServerError,description);
+ if (text != (char *) NULL)
+ if (WidgetTextWidth(font_info,(char *) text) > width)
+ width=WidgetTextWidth(font_info,(char *) text);
+ }
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Notice widget.
+ */
+ windows->widget.width=width+4*QuantumMargin;
+ windows->widget.min_width=width+QuantumMargin;
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int) (12*height);
+ windows->widget.min_height=(unsigned int) (7*height);
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Notice widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Notice",MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ (void) XBell(display,0);
+ /*
+ Respond to X events.
+ */
+ timer=time((time_t *) NULL)+Timeout;
+ state=UpdateConfigurationState;
+ do
+ {
+ if (time((time_t *) NULL) > timer)
+ break;
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize Dismiss button information.
+ */
+ XGetWidgetInfo(DismissButtonText,&dismiss_info);
+ dismiss_info.width=(unsigned int) QuantumMargin+
+ WidgetTextWidth(font_info,DismissButtonText);
+ dismiss_info.height=(unsigned int) ((3*height) >> 1);
+ dismiss_info.x=(int)
+ ((windows->widget.width >> 1)-(dismiss_info.width >> 1));
+ dismiss_info.y=(int)
+ (windows->widget.height-(dismiss_info.height << 1));
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Notice widget.
+ */
+ width=WidgetTextWidth(font_info,(char *) reason);
+ x=(int) ((windows->widget.width >> 1)-(width >> 1));
+ y=(int) ((windows->widget.height >> 1)-(height << 1));
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,(char *) reason,Extent(reason));
+ if (description != (char *) NULL)
+ {
+ width=WidgetTextWidth(font_info,(char *) description);
+ x=(int) ((windows->widget.width >> 1)-(width >> 1));
+ y+=height;
+ (void) XDrawString(display,windows->widget.id,
+ windows->widget.annotate_context,x,y,(char *) description,
+ Extent(description));
+ }
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~RedrawWidgetState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (XCheckIfEvent(display,&event,XScreenEvent,(char *) windows) == MagickFalse)
+ {
+ /*
+ Do not block if delay > 0.
+ */
+ XDelay(display,SuspendTime << 2);
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ {
+ /*
+ User pressed Dismiss button.
+ */
+ dismiss_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (dismiss_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ state|=ExitState;
+ dismiss_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ dismiss_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (state & InactiveWidgetState)
+ break;
+ if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
+ {
+ /*
+ Dismiss button status changed.
+ */
+ dismiss_info.raised=
+ dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X P r e f e r e n c e s W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XPreferencesWidget() displays a Preferences widget with program preferences.
+% If the user presses the Apply button, the preferences are stored in a
+% configuration file in the users' home directory.
+%
+% The format of the XPreferencesWidget method is:
+%
+% MagickBooleanType XPreferencesWidget(Display *display,
+% XResourceInfo *resource_info,XWindows *windows)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+*/
+MagickExport MagickBooleanType XPreferencesWidget(Display *display,
+ XResourceInfo *resource_info,XWindows *windows)
+{
+#define ApplyButtonText "Apply"
+#define CacheButtonText "%lu mega-bytes of memory in the undo edit cache "
+#define CancelButtonText "Cancel"
+#define NumberPreferences 8
+
+ static const char
+ *Preferences[] =
+ {
+ "display image centered on a backdrop",
+ "confirm on program exit",
+ "confirm on image edits",
+ "correct image for display gamma",
+ "display warning messages",
+ "apply Floyd/Steinberg error diffusion to image",
+ "use a shared colormap for colormapped X visuals",
+ "display images as an X server pixmap"
+ };
+
+ char
+ cache[MaxTextExtent];
+
+ int
+ x,
+ y;
+
+ register int
+ i;
+
+ Status
+ status;
+
+ unsigned int
+ height,
+ text_width,
+ width;
+
+ unsigned long
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ apply_info,
+ cache_info,
+ cancel_info,
+ preferences_info[NumberPreferences];
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Determine Preferences widget attributes.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(windows != (XWindows *) NULL);
+ XCheckRefreshWindows(display,windows);
+ font_info=windows->widget.font_info;
+ text_width=WidgetTextWidth(font_info,CacheButtonText);
+ for (i=0; i < NumberPreferences; i++)
+ if (WidgetTextWidth(font_info,(char *) Preferences[i]) > text_width)
+ text_width=WidgetTextWidth(font_info,(char *) Preferences[i]);
+ width=WidgetTextWidth(font_info,ApplyButtonText);
+ if (WidgetTextWidth(font_info,CancelButtonText) > width)
+ width=WidgetTextWidth(font_info,CancelButtonText);
+ width+=(unsigned int) QuantumMargin;
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ /*
+ Position Preferences widget.
+ */
+ windows->widget.width=(unsigned int) (MagickMax((int) (width << 1),
+ (int) text_width)+6*QuantumMargin);
+ windows->widget.min_width=(width << 1)+QuantumMargin;
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int)
+ (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
+ windows->widget.min_height=(unsigned int)
+ (7*height+NumberPreferences*(height+(QuantumMargin >> 1)));
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Preferences widget.
+ */
+ (void) CopyMagickString(windows->widget.name,"Preferences",MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,windows->widget.screen,
+ (unsigned int) (CWWidth | CWHeight | CWX | CWY),&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ state=UpdateConfigurationState;
+ XSetCursorState(display,windows,MagickTrue);
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(CancelButtonText,&cancel_info);
+ cancel_info.width=width;
+ cancel_info.height=(unsigned int) (3*height) >> 1;
+ cancel_info.x=(int) windows->widget.width-cancel_info.width-
+ (QuantumMargin << 1);
+ cancel_info.y=(int) windows->widget.height-
+ cancel_info.height-QuantumMargin;
+ XGetWidgetInfo(ApplyButtonText,&apply_info);
+ apply_info.width=width;
+ apply_info.height=(unsigned int) (3*height) >> 1;
+ apply_info.x=QuantumMargin << 1;
+ apply_info.y=cancel_info.y;
+ y=(int) (height << 1);
+ for (i=0; i < NumberPreferences; i++)
+ {
+ XGetWidgetInfo(Preferences[i],&preferences_info[i]);
+ preferences_info[i].bevel_width--;
+ preferences_info[i].width=(unsigned int) QuantumMargin >> 1;
+ preferences_info[i].height=(unsigned int) QuantumMargin >> 1;
+ preferences_info[i].x=QuantumMargin << 1;
+ preferences_info[i].y=y;
+ y+=height+(QuantumMargin >> 1);
+ }
+ preferences_info[0].raised=resource_info->backdrop ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[1].raised=resource_info->confirm_exit ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[2].raised=resource_info->confirm_edit ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[3].raised=resource_info->gamma_correct ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[4].raised=resource_info->display_warnings ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[5].raised=resource_info->quantize_info->dither ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ preferences_info[6].raised=resource_info->colormap !=
+ SharedColormap ? MagickTrue : MagickFalse;
+ preferences_info[7].raised=resource_info->use_pixmap ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ (void) FormatMagickString(cache,MaxTextExtent,CacheButtonText,
+ resource_info->undo_cache);
+ XGetWidgetInfo(cache,&cache_info);
+ cache_info.bevel_width--;
+ cache_info.width=(unsigned int) QuantumMargin >> 1;
+ cache_info.height=(unsigned int) QuantumMargin >> 1;
+ cache_info.x=QuantumMargin << 1;
+ cache_info.y=y;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Preferences widget.
+ */
+ XDrawBeveledButton(display,&windows->widget,&apply_info);
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ for (i=0; i < NumberPreferences; i++)
+ XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
+ XDrawTriangleEast(display,&windows->widget,&cache_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ state&=(~RedrawWidgetState);
+ }
+ /*
+ Wait for next event.
+ */
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(apply_info,event.xbutton))
+ {
+ /*
+ User pressed Apply button.
+ */
+ apply_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&apply_info);
+ break;
+ }
+ if (MatteIsActive(cancel_info,event.xbutton))
+ {
+ /*
+ User pressed Cancel button.
+ */
+ cancel_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ for (i=0; i < NumberPreferences; i++)
+ if (MatteIsActive(preferences_info[i],event.xbutton))
+ {
+ /*
+ User pressed a Preferences button.
+ */
+ preferences_info[i].raised=preferences_info[i].raised ==
+ MagickFalse ? MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&preferences_info[i]);
+ break;
+ }
+ if (MatteIsActive(cache_info,event.xbutton))
+ {
+ /*
+ User pressed Cache button.
+ */
+ x=cache_info.x+cache_info.width+cache_info.bevel_width+
+ (QuantumMargin >> 1);
+ y=cache_info.y+((cache_info.height-height) >> 1);
+ width=WidgetTextWidth(font_info,cache);
+ (void) XClearArea(display,windows->widget.id,x,y,width,height,
+ False);
+ resource_info->undo_cache<<=1;
+ if (resource_info->undo_cache > 256)
+ resource_info->undo_cache=1;
+ (void) FormatMagickString(cache,MaxTextExtent,CacheButtonText,
+ resource_info->undo_cache);
+ cache_info.raised=MagickFalse;
+ XDrawTriangleEast(display,&windows->widget,&cache_info);
+ break;
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (apply_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(apply_info,event.xbutton))
+ state|=ExitState;
+ apply_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&apply_info);
+ apply_info.raised=MagickFalse;
+ }
+ if (cancel_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(cancel_info,event.xbutton))
+ state|=ExitState;
+ cancel_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ }
+ if (cache_info.raised == MagickFalse)
+ {
+ cache_info.raised=MagickTrue;
+ XDrawTriangleEast(display,&windows->widget,&cache_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ (void) XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ apply_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&apply_info);
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (state & InactiveWidgetState)
+ break;
+ if (apply_info.raised == MatteIsActive(apply_info,event.xmotion))
+ {
+ /*
+ Apply button status changed.
+ */
+ apply_info.raised=
+ apply_info.raised == MagickFalse ? MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&apply_info);
+ break;
+ }
+ if (cancel_info.raised == MatteIsActive(cancel_info,event.xmotion))
+ {
+ /*
+ Cancel button status changed.
+ */
+ cancel_info.raised=
+ cancel_info.raised == MagickFalse ? MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&cancel_info);
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+ if (apply_info.raised)
+ return(MagickFalse);
+ /*
+ Save user preferences to the client configuration file.
+ */
+ resource_info->backdrop=
+ preferences_info[0].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->confirm_exit=
+ preferences_info[1].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->confirm_edit=
+ preferences_info[2].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->gamma_correct=
+ preferences_info[3].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->display_warnings=
+ preferences_info[4].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->quantize_info->dither=
+ preferences_info[5].raised == MagickFalse ? MagickTrue : MagickFalse;
+ resource_info->colormap=SharedColormap;
+ if (preferences_info[6].raised)
+ resource_info->colormap=PrivateColormap;
+ resource_info->use_pixmap=
+ preferences_info[7].raised == MagickFalse ? MagickTrue : MagickFalse;
+ XUserPreferences(resource_info);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X P r o g r e s s M o n i t o r W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XProgressMonitorWidget() displays the progress a task is making in
+% completing a task. A span of zero toggles the active status. An inactive
+% state disables the progress monitor.
+%
+% The format of the XProgressMonitorWidget method is:
+%
+% void XProgressMonitorWidget(Display *display,XWindows *windows,
+% const char *task,const MagickOffsetType offset,
+% const MagickSizeType span)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o task: Identifies the task in progress.
+%
+% o offset: Specifies the offset position within the span which represents
+% how much progress has been made in completing a task.
+%
+% o span: Specifies the span relative to completing a task.
+%
+*/
+MagickExport void XProgressMonitorWidget(Display *display,XWindows *windows,
+ const char *task,const MagickOffsetType offset,const MagickSizeType span)
+{
+ unsigned int
+ width;
+
+ XEvent
+ event;
+
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(task != (const char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",task);
+ if (span == 0)
+ return;
+ /*
+ Update image windows if there is a pending expose event.
+ */
+ while (XCheckTypedWindowEvent(display,windows->command.id,Expose,&event))
+ (void) XCommandWidget(display,windows,(const char **) NULL,&event);
+ while (XCheckTypedWindowEvent(display,windows->image.id,Expose,&event))
+ XRefreshWindow(display,&windows->image,&event);
+ while (XCheckTypedWindowEvent(display,windows->info.id,Expose,&event))
+ if (monitor_info.text != (char *) NULL)
+ XInfoWidget(display,windows,monitor_info.text);
+ /*
+ Draw progress monitor bar to represent percent completion of a task.
+ */
+ if ((windows->info.mapped == MagickFalse) || (task != monitor_info.text))
+ XInfoWidget(display,windows,task);
+ width=(unsigned int) (((offset+1)*(windows->info.width-
+ (2*monitor_info.x)))/span);
+ if (width < monitor_info.width)
+ {
+ monitor_info.raised=MagickTrue;
+ XDrawWidgetText(display,&windows->info,&monitor_info);
+ monitor_info.raised=MagickFalse;
+ }
+ monitor_info.width=width;
+ XDrawWidgetText(display,&windows->info,&monitor_info);
+ (void) XFlush(display);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X T e x t V i e w W i d g e t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XTextViewWidget() displays text in a Text View widget.
+%
+% The format of the XTextViewWidget method is:
+%
+% void XTextViewWidget(Display *display,const XResourceInfo *resource_info,
+% XWindows *windows,const MagickBooleanType mono,const char *title,
+% const char **textlist)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window: Specifies a pointer to a XWindows structure.
+%
+% o mono: Use mono-spaced font when displaying text.
+%
+% o title: This character string is displayed at the top of the widget
+% window.
+%
+% o textlist: This string list is displayed within the Text View widget.
+%
+*/
+MagickExport void XTextViewWidget(Display *display,
+ const XResourceInfo *resource_info,XWindows *windows,
+ const MagickBooleanType mono,const char *title,const char **textlist)
+{
+#define DismissButtonText "Dismiss"
+
+ char
+ primary_selection[MaxTextExtent];
+
+ register int
+ i;
+
+ static MagickStatusType
+ mask = (MagickStatusType) (CWWidth | CWHeight | CWX | CWY);
+
+ Status
+ status;
+
+ unsigned int
+ height,
+ lines,
+ text_width,
+ visible_lines,
+ width;
+
+ unsigned long
+ delay,
+ state;
+
+ XEvent
+ event;
+
+ XFontStruct
+ *font_info,
+ *text_info;
+
+ XTextProperty
+ window_name;
+
+ XWidgetInfo
+ dismiss_info,
+ expose_info,
+ list_info,
+ north_info,
+ scroll_info,
+ selection_info,
+ slider_info,
+ south_info;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Convert text string to a text list.
+ */
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(title != (const char *) NULL);
+ assert(textlist != (const char **) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",title);
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ if (textlist == (const char **) NULL)
+ {
+ XNoticeWidget(display,windows,"No text to view:",(char *) NULL);
+ return;
+ }
+ /*
+ Determine Text View widget attributes.
+ */
+ font_info=windows->widget.font_info;
+ text_info=(XFontStruct *) NULL;
+ if (mono != MagickFalse)
+ text_info=XBestFont(display,resource_info,MagickTrue);
+ if (text_info == (XFontStruct *) NULL)
+ text_info=windows->widget.font_info;
+ text_width=0;
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ if (WidgetTextWidth(text_info,(char *) textlist[i]) > text_width)
+ text_width=(unsigned int) XTextWidth(text_info,(char *) textlist[i],
+ MagickMin(Extent(textlist[i]),160));
+ lines=(unsigned int) i;
+ width=WidgetTextWidth(font_info,DismissButtonText);
+ width+=QuantumMargin;
+ height=(unsigned int) (text_info->ascent+text_info->descent);
+ /*
+ Position Text View widget.
+ */
+ windows->widget.width=(unsigned int) (MagickMin((int) text_width,
+ (int) MaxTextWidth)+5*QuantumMargin);
+ windows->widget.min_width=(unsigned int) (MinTextWidth+4*QuantumMargin);
+ if (windows->widget.width < windows->widget.min_width)
+ windows->widget.width=windows->widget.min_width;
+ windows->widget.height=(unsigned int) (MagickMin(MagickMax((int) lines,3),32)*
+ height+((13*height) >> 1)+((9*QuantumMargin) >> 1));
+ windows->widget.min_height=(unsigned int) (3*height+((13*height) >> 1)+((9*
+ QuantumMargin) >> 1));
+ if (windows->widget.height < windows->widget.min_height)
+ windows->widget.height=windows->widget.min_height;
+ XConstrainWindowPosition(display,&windows->widget);
+ /*
+ Map Text View widget.
+ */
+ (void) CopyMagickString(windows->widget.name,title,MaxTextExtent);
+ status=XStringListToTextProperty(&windows->widget.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->widget.id,&window_name);
+ XSetWMIconName(display,windows->widget.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ window_changes.width=(int) windows->widget.width;
+ window_changes.height=(int) windows->widget.height;
+ window_changes.x=windows->widget.x;
+ window_changes.y=windows->widget.y;
+ (void) XReconfigureWMWindow(display,windows->widget.id,
+ windows->widget.screen,(unsigned int) mask,&window_changes);
+ (void) XMapRaised(display,windows->widget.id);
+ windows->widget.mapped=MagickFalse;
+ /*
+ Respond to X events.
+ */
+ XGetWidgetInfo((char *) NULL,&slider_info);
+ XGetWidgetInfo((char *) NULL,&north_info);
+ XGetWidgetInfo((char *) NULL,&south_info);
+ XGetWidgetInfo((char *) NULL,&expose_info);
+ visible_lines=0;
+ delay=SuspendTime << 2;
+ height=(unsigned int) (font_info->ascent+font_info->descent);
+ state=UpdateConfigurationState;
+ do
+ {
+ if (state & UpdateConfigurationState)
+ {
+ int
+ id;
+
+ /*
+ Initialize button information.
+ */
+ XGetWidgetInfo(DismissButtonText,&dismiss_info);
+ dismiss_info.width=width;
+ dismiss_info.height=(unsigned int) ((3*height) >> 1);
+ dismiss_info.x=(int) windows->widget.width-dismiss_info.width-
+ QuantumMargin-2;
+ dismiss_info.y=(int) windows->widget.height-dismiss_info.height-
+ QuantumMargin;
+ /*
+ Initialize scroll information.
+ */
+ XGetWidgetInfo((char *) NULL,&scroll_info);
+ scroll_info.bevel_width--;
+ scroll_info.width=height;
+ scroll_info.height=(unsigned int) (dismiss_info.y-((5*QuantumMargin) >>
+ 1));
+ scroll_info.x=(int) windows->widget.width-QuantumMargin-
+ scroll_info.width;
+ scroll_info.y=(3*QuantumMargin) >> 1;
+ scroll_info.raised=MagickFalse;
+ scroll_info.trough=MagickTrue;
+ north_info=scroll_info;
+ north_info.raised=MagickTrue;
+ north_info.width-=(north_info.bevel_width << 1);
+ north_info.height=north_info.width-1;
+ north_info.x+=north_info.bevel_width;
+ north_info.y+=north_info.bevel_width;
+ south_info=north_info;
+ south_info.y=scroll_info.y+scroll_info.height-scroll_info.bevel_width-
+ south_info.height;
+ id=slider_info.id;
+ slider_info=north_info;
+ slider_info.id=id;
+ slider_info.width-=2;
+ slider_info.min_y=north_info.y+north_info.height+north_info.bevel_width+
+ slider_info.bevel_width+2;
+ slider_info.height=scroll_info.height-((slider_info.min_y-
+ scroll_info.y+1) << 1)+4;
+ visible_lines=scroll_info.height/(text_info->ascent+text_info->descent+
+ ((text_info->ascent+text_info->descent) >> 3));
+ if (lines > visible_lines)
+ slider_info.height=(unsigned int) (visible_lines*slider_info.height)/
+ lines;
+ slider_info.max_y=south_info.y-south_info.bevel_width-
+ slider_info.bevel_width-2;
+ slider_info.x=scroll_info.x+slider_info.bevel_width+1;
+ slider_info.y=slider_info.min_y;
+ expose_info=scroll_info;
+ expose_info.y=slider_info.y;
+ /*
+ Initialize list information.
+ */
+ XGetWidgetInfo((char *) NULL,&list_info);
+ list_info.raised=MagickFalse;
+ list_info.bevel_width--;
+ list_info.width=(unsigned int) scroll_info.x-((3*QuantumMargin) >> 1);
+ list_info.height=scroll_info.height;
+ list_info.x=QuantumMargin;
+ list_info.y=scroll_info.y;
+ /*
+ Initialize selection information.
+ */
+ XGetWidgetInfo((char *) NULL,&selection_info);
+ selection_info.center=MagickFalse;
+ selection_info.width=list_info.width;
+ selection_info.height=(unsigned int)
+ (9*(text_info->ascent+text_info->descent)) >> 3;
+ selection_info.x=list_info.x;
+ state&=(~UpdateConfigurationState);
+ }
+ if (state & RedrawWidgetState)
+ {
+ /*
+ Redraw Text View window.
+ */
+ XDrawBeveledMatte(display,&windows->widget,&list_info);
+ XDrawBeveledMatte(display,&windows->widget,&scroll_info);
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ XHighlightWidget(display,&windows->widget,BorderOffset,BorderOffset);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ state&=(~RedrawWidgetState);
+ }
+ if (state & RedrawListState)
+ {
+ /*
+ Determine slider id and position.
+ */
+ if (slider_info.id >= (int) (lines-visible_lines))
+ slider_info.id=(int) lines-visible_lines;
+ if ((slider_info.id < 0) || (lines <= visible_lines))
+ slider_info.id=0;
+ slider_info.y=slider_info.min_y;
+ if (lines != 0)
+ slider_info.y+=
+ slider_info.id*(slider_info.max_y-slider_info.min_y+1)/lines;
+ if (slider_info.id != selection_info.id)
+ {
+ /*
+ Redraw scroll bar and text.
+ */
+ windows->widget.font_info=text_info;
+ (void) XSetFont(display,windows->widget.annotate_context,
+ text_info->fid);
+ (void) XSetFont(display,windows->widget.highlight_context,
+ text_info->fid);
+ selection_info.id=slider_info.id;
+ selection_info.y=list_info.y+(height >> 3)+2;
+ for (i=0; i < (int) visible_lines; i++)
+ {
+ selection_info.raised=
+ (slider_info.id+i) != list_info.id ? MagickTrue : MagickFalse;
+ selection_info.text=(char *) NULL;
+ if ((slider_info.id+i) < (int) lines)
+ selection_info.text=(char *) textlist[slider_info.id+i];
+ XDrawWidgetText(display,&windows->widget,&selection_info);
+ selection_info.y+=(int) selection_info.height;
+ }
+ windows->widget.font_info=font_info;
+ (void) XSetFont(display,windows->widget.annotate_context,
+ font_info->fid);
+ (void) XSetFont(display,windows->widget.highlight_context,
+ font_info->fid);
+ /*
+ Update slider.
+ */
+ if (slider_info.y > expose_info.y)
+ {
+ expose_info.height=(unsigned int) slider_info.y-expose_info.y;
+ expose_info.y=slider_info.y-expose_info.height-
+ slider_info.bevel_width-1;
+ }
+ else
+ {
+ expose_info.height=(unsigned int) expose_info.y-slider_info.y;
+ expose_info.y=slider_info.y+slider_info.height+
+ slider_info.bevel_width+1;
+ }
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ XDrawMatte(display,&windows->widget,&expose_info);
+ XDrawBeveledButton(display,&windows->widget,&slider_info);
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ expose_info.y=slider_info.y;
+ }
+ state&=(~RedrawListState);
+ }
+ /*
+ Wait for next event.
+ */
+ if (north_info.raised && south_info.raised)
+ (void) XIfEvent(display,&event,XScreenEvent,(char *) windows);
+ else
+ {
+ /*
+ Brief delay before advancing scroll bar.
+ */
+ XDelay(display,delay);
+ delay=SuspendTime;
+ (void) XCheckIfEvent(display,&event,XScreenEvent,(char *) windows);
+ if (north_info.raised == MagickFalse)
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ slider_info.id--;
+ state|=RedrawListState;
+ }
+ if (south_info.raised == MagickFalse)
+ if (slider_info.id < (int) lines)
+ {
+ /*
+ Move slider down.
+ */
+ slider_info.id++;
+ state|=RedrawListState;
+ }
+ if (event.type != ButtonRelease)
+ continue;
+ }
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ if (MatteIsActive(slider_info,event.xbutton))
+ {
+ /*
+ Track slider.
+ */
+ slider_info.active=MagickTrue;
+ break;
+ }
+ if (MatteIsActive(north_info,event.xbutton))
+ if (slider_info.id > 0)
+ {
+ /*
+ Move slider up.
+ */
+ north_info.raised=MagickFalse;
+ slider_info.id--;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(south_info,event.xbutton))
+ if (slider_info.id < (int) lines)
+ {
+ /*
+ Move slider down.
+ */
+ south_info.raised=MagickFalse;
+ slider_info.id++;
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(scroll_info,event.xbutton))
+ {
+ /*
+ Move slider.
+ */
+ if (event.xbutton.y < slider_info.y)
+ slider_info.id-=(visible_lines-1);
+ else
+ slider_info.id+=(visible_lines-1);
+ state|=RedrawListState;
+ break;
+ }
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ {
+ /*
+ User pressed Dismiss button.
+ */
+ dismiss_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ if (MatteIsActive(list_info,event.xbutton))
+ {
+ int
+ id;
+
+ static Time
+ click_time;
+
+ /*
+ User pressed list matte.
+ */
+ id=slider_info.id+(event.xbutton.y-(list_info.y+(height >> 1))+1)/
+ selection_info.height;
+ if (id >= (int) lines)
+ break;
+ if (id != list_info.id)
+ {
+ list_info.id=id;
+ click_time=event.xbutton.time;
+ break;
+ }
+ list_info.id=id;
+ if (event.xbutton.time >= (click_time+DoubleClick))
+ {
+ click_time=event.xbutton.time;
+ break;
+ }
+ click_time=event.xbutton.time;
+ /*
+ Become the XA_PRIMARY selection owner.
+ */
+ (void) CopyMagickString(primary_selection,textlist[list_info.id],
+ MaxTextExtent);
+ (void) XSetSelectionOwner(display,XA_PRIMARY,windows->widget.id,
+ event.xbutton.time);
+ if (XGetSelectionOwner(display,XA_PRIMARY) != windows->widget.id)
+ break;
+ selection_info.id=(~0);
+ list_info.id=id;
+ state|=RedrawListState;
+ break;
+ }
+ break;
+ }
+ case ButtonRelease:
+ {
+ if (windows->widget.mapped == MagickFalse)
+ break;
+ if (north_info.raised == MagickFalse)
+ {
+ /*
+ User released up button.
+ */
+ delay=SuspendTime << 2;
+ north_info.raised=MagickTrue;
+ XDrawTriangleNorth(display,&windows->widget,&north_info);
+ }
+ if (south_info.raised == MagickFalse)
+ {
+ /*
+ User released down button.
+ */
+ delay=SuspendTime << 2;
+ south_info.raised=MagickTrue;
+ XDrawTriangleSouth(display,&windows->widget,&south_info);
+ }
+ if (slider_info.active)
+ {
+ /*
+ Stop tracking slider.
+ */
+ slider_info.active=MagickFalse;
+ break;
+ }
+ if (dismiss_info.raised == MagickFalse)
+ {
+ if (event.xbutton.window == windows->widget.id)
+ if (MatteIsActive(dismiss_info,event.xbutton))
+ state|=ExitState;
+ dismiss_info.raised=MagickTrue;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ }
+ break;
+ }
+ case ClientMessage:
+ {
+ /*
+ If client window delete message, exit.
+ */
+ if (event.xclient.message_type != windows->wm_protocols)
+ break;
+ if (*event.xclient.data.l == (int) windows->wm_take_focus)
+ {
+ (void) XSetInputFocus(display,event.xclient.window,RevertToParent,
+ (Time) event.xclient.data.l[1]);
+ break;
+ }
+ if (*event.xclient.data.l != (int) windows->wm_delete_window)
+ break;
+ if (event.xclient.window == windows->widget.id)
+ {
+ state|=ExitState;
+ break;
+ }
+ break;
+ }
+ case ConfigureNotify:
+ {
+ /*
+ Update widget configuration.
+ */
+ if (event.xconfigure.window != windows->widget.id)
+ break;
+ if ((event.xconfigure.width == (int) windows->widget.width) &&
+ (event.xconfigure.height == (int) windows->widget.height))
+ break;
+ windows->widget.width=(unsigned int)
+ MagickMax(event.xconfigure.width,(int) windows->widget.min_width);
+ windows->widget.height=(unsigned int)
+ MagickMax(event.xconfigure.height,(int) windows->widget.min_height);
+ state|=UpdateConfigurationState;
+ break;
+ }
+ case EnterNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state&=(~InactiveWidgetState);
+ break;
+ }
+ case Expose:
+ {
+ if (event.xexpose.window != windows->widget.id)
+ break;
+ if (event.xexpose.count != 0)
+ break;
+ state|=RedrawWidgetState;
+ break;
+ }
+ case KeyPress:
+ {
+ static char
+ command[MaxTextExtent];
+
+ static int
+ length;
+
+ static KeySym
+ key_symbol;
+
+ /*
+ Respond to a user key press.
+ */
+ if (event.xkey.window != windows->widget.id)
+ break;
+ length=XLookupString((XKeyEvent *) &event.xkey,command,
+ (int) sizeof(command),&key_symbol,(XComposeStatus *) NULL);
+ *(command+length)='\0';
+ if ((key_symbol == XK_Return) || (key_symbol == XK_KP_Enter))
+ {
+ dismiss_info.raised=MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ state|=ExitState;
+ break;
+ }
+ if (AreaIsActive(scroll_info,event.xkey))
+ {
+ /*
+ Move slider.
+ */
+ switch ((int) key_symbol)
+ {
+ case XK_Home:
+ case XK_KP_Home:
+ {
+ slider_info.id=0;
+ break;
+ }
+ case XK_Up:
+ case XK_KP_Up:
+ {
+ slider_info.id--;
+ break;
+ }
+ case XK_Down:
+ case XK_KP_Down:
+ {
+ slider_info.id++;
+ break;
+ }
+ case XK_Prior:
+ case XK_KP_Prior:
+ {
+ slider_info.id-=visible_lines;
+ break;
+ }
+ case XK_Next:
+ case XK_KP_Next:
+ {
+ slider_info.id+=visible_lines;
+ break;
+ }
+ case XK_End:
+ case XK_KP_End:
+ {
+ slider_info.id=(int) lines;
+ break;
+ }
+ }
+ state|=RedrawListState;
+ break;
+ }
+ break;
+ }
+ case KeyRelease:
+ break;
+ case LeaveNotify:
+ {
+ if (event.xcrossing.window != windows->widget.id)
+ break;
+ state|=InactiveWidgetState;
+ break;
+ }
+ case MapNotify:
+ {
+ mask&=(~CWX);
+ mask&=(~CWY);
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ if (slider_info.active)
+ {
+ /*
+ Move slider matte.
+ */
+ slider_info.y=event.xmotion.y-
+ ((slider_info.height+slider_info.bevel_width) >> 1)+1;
+ if (slider_info.y < slider_info.min_y)
+ slider_info.y=slider_info.min_y;
+ if (slider_info.y > slider_info.max_y)
+ slider_info.y=slider_info.max_y;
+ slider_info.id=0;
+ if (slider_info.y != slider_info.min_y)
+ slider_info.id=(int) (lines*(slider_info.y-slider_info.min_y+1))/
+ (slider_info.max_y-slider_info.min_y+1);
+ state|=RedrawListState;
+ break;
+ }
+ if (state & InactiveWidgetState)
+ break;
+ if (dismiss_info.raised == MatteIsActive(dismiss_info,event.xmotion))
+ {
+ /*
+ Dismiss button status changed.
+ */
+ dismiss_info.raised=
+ dismiss_info.raised == MagickFalse ? MagickTrue : MagickFalse;
+ XDrawBeveledButton(display,&windows->widget,&dismiss_info);
+ break;
+ }
+ break;
+ }
+ case SelectionClear:
+ {
+ list_info.id=(~0);
+ selection_info.id=(~0);
+ state|=RedrawListState;
+ break;
+ }
+ case SelectionRequest:
+ {
+ XSelectionEvent
+ notify;
+
+ XSelectionRequestEvent
+ *request;
+
+ if (list_info.id == (~0))
+ break;
+ /*
+ Set primary selection.
+ */
+ request=(&(event.xselectionrequest));
+ (void) XChangeProperty(request->display,request->requestor,
+ request->property,request->target,8,PropModeReplace,
+ (unsigned char *) primary_selection,Extent(primary_selection));
+ notify.type=SelectionNotify;
+ notify.send_event=MagickTrue;
+ notify.display=request->display;
+ notify.requestor=request->requestor;
+ notify.selection=request->selection;
+ notify.target=request->target;
+ notify.time=request->time;
+ if (request->property == None)
+ notify.property=request->target;
+ else
+ notify.property=request->property;
+ (void) XSendEvent(request->display,request->requestor,False,NoEventMask,
+ (XEvent *) ¬ify);
+ }
+ default:
+ break;
+ }
+ } while ((state & ExitState) == 0);
+ if (text_info != windows->widget.font_info)
+ (void) XFreeFont(display,text_info);
+ XSetCursorState(display,windows,MagickFalse);
+ (void) XWithdrawWindow(display,windows->widget.id,windows->widget.screen);
+ XCheckRefreshWindows(display,windows);
+}
+#endif
diff --git a/magick/widget.h b/magick/widget.h
new file mode 100644
index 0000000..9f97b81
--- /dev/null
+++ b/magick/widget.h
@@ -0,0 +1,58 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore X11 widget methods.
+*/
+#ifndef _MAGICKCORE_WIDGET_H
+#define _MAGICKCORE_WIDGET_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+
+#include "magick/xwindow-private.h"
+
+extern MagickExport int
+ XCommandWidget(Display *,XWindows *,const char **,XEvent *),
+ XConfirmWidget(Display *,XWindows *,const char *,const char *),
+ XDialogWidget(Display *,XWindows *,const char *,const char *,char *),
+ XMenuWidget(Display *,XWindows *,const char *,const char **,char *);
+
+extern MagickExport MagickBooleanType
+ XPreferencesWidget(Display *,XResourceInfo *,XWindows *);
+
+extern MagickExport void
+ DestroyXWidget(void),
+ XColorBrowserWidget(Display *,XWindows *,const char *,char *),
+ XFileBrowserWidget(Display *,XWindows *,const char *,char *),
+ XFontBrowserWidget(Display *,XWindows *,const char *,char *),
+ XInfoWidget(Display *,XWindows *,const char *),
+ XListBrowserWidget(Display *,XWindows *,XWindowInfo *,const char **,
+ const char *,const char *,char *),
+ XNoticeWidget(Display *,XWindows *,const char *,const char *),
+ XProgressMonitorWidget(Display *,XWindows *,const char *,
+ const MagickOffsetType,const MagickSizeType),
+ XTextViewWidget(Display *,const XResourceInfo *,XWindows *,
+ const MagickBooleanType,const char *,const char **);
+
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/xml-tree.c b/magick/xml-tree.c
new file mode 100644
index 0000000..433ee08
--- /dev/null
+++ b/magick/xml-tree.c
@@ -0,0 +1,2625 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X X M M L %
+% X X MM MM L %
+% X M M M L %
+% X X M M L %
+% X X M M LLLLL %
+% %
+% TTTTT RRRR EEEEE EEEEE %
+% T R R E E %
+% T RRRR EEE EEE %
+% T R R E E %
+% T R R EEEEE EEEEE %
+% %
+% %
+% XML Tree Methods %
+% %
+% Software Design %
+% John Cristy %
+% December 2004 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% This module implements the standard handy xml-tree methods for storing and
+% retrieving nodes and attributes from an XML string.
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/blob.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/log.h"
+#include "magick/memory_.h"
+#include "magick/semaphore.h"
+#include "magick/string_.h"
+#include "magick/xml-tree.h"
+#include "magick/utility.h"
+
+/*
+ Define declarations.
+*/
+#define NumberPredefinedEntities 10
+#define XMLWhitespace "\t\r\n "
+
+/*
+ Typedef declarations.
+*/
+struct _XMLTreeInfo
+{
+ char
+ *tag,
+ **attributes,
+ *content;
+
+ size_t
+ offset;
+
+ XMLTreeInfo
+ *parent,
+ *next,
+ *sibling,
+ *ordered,
+ *child;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+typedef struct _XMLTreeRoot
+ XMLTreeRoot;
+
+struct _XMLTreeRoot
+{
+ struct _XMLTreeInfo
+ root;
+
+ XMLTreeInfo
+ *node;
+
+ MagickBooleanType
+ standalone;
+
+ char
+ ***processing_instructions,
+ **entities,
+ ***attributes;
+
+ MagickBooleanType
+ debug;
+
+ SemaphoreInfo
+ *semaphore;
+
+ unsigned long
+ signature;
+};
+
+/*
+ Global declarations.
+*/
+static char
+ *sentinel[] = { (char *) NULL };
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d d C h i l d T o X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AddChildToXMLTree() adds a child tag at an offset relative to the start of
+% the parent tag's character content. Return the child tag.
+%
+% The format of the AddChildToXMLTree method is:
+%
+% XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,const char *tag,
+% const size_t offset)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o tag: the tag.
+%
+% o offset: the tag offset.
+%
+*/
+MagickExport XMLTreeInfo *AddChildToXMLTree(XMLTreeInfo *xml_info,
+ const char *tag,const size_t offset)
+{
+ XMLTreeInfo
+ *child;
+
+ if (xml_info == (XMLTreeInfo *) NULL)
+ return((XMLTreeInfo *) NULL);
+ child=(XMLTreeInfo *) AcquireMagickMemory(sizeof(*child));
+ if (child == (XMLTreeInfo *) NULL)
+ return((XMLTreeInfo *) NULL);
+ (void) ResetMagickMemory(child,0,sizeof(*child));
+ child->tag=ConstantString(tag);
+ child->attributes=sentinel;
+ child->content=ConstantString("");
+ child->debug=IsEventLogging();
+ child->signature=MagickSignature;
+ return(InsertTagIntoXMLTree(xml_info,child,offset));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% A d d P a t h T o X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% AddPathToXMLTree() adds a child tag at an offset relative to the start of
+% the parent tag's character content. This method returns the child tag.
+%
+% The format of the AddPathToXMLTree method is:
+%
+% XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,const char *path,
+% const size_t offset)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o path: the path.
+%
+% o offset: the tag offset.
+%
+*/
+MagickExport XMLTreeInfo *AddPathToXMLTree(XMLTreeInfo *xml_info,
+ const char *path,const size_t offset)
+{
+ char
+ **components,
+ subnode[MaxTextExtent],
+ tag[MaxTextExtent];
+
+ long
+ j;
+
+ register long
+ i;
+
+ XMLTreeInfo
+ *child,
+ *node;
+
+ unsigned long
+ number_components;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ node=xml_info;
+ components=GetPathComponents(path,&number_components);
+ if (components == (char **) NULL)
+ return((XMLTreeInfo *) NULL);
+ for (i=0; i < (long) number_components; i++)
+ {
+ GetPathComponent(components[i],SubimagePath,subnode);
+ GetPathComponent(components[i],CanonicalPath,tag);
+ child=GetXMLTreeChild(node,tag);
+ if (child == (XMLTreeInfo *) NULL)
+ child=AddChildToXMLTree(node,tag,offset);
+ node=child;
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ for (j=atol(subnode)-1; j > 0; j--)
+ {
+ node=GetXMLTreeOrdered(node);
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ }
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ components[i]=DestroyString(components[i]);
+ }
+ for ( ; i < (long) number_components; i++)
+ components[i]=DestroyString(components[i]);
+ components=(char **) RelinquishMagickMemory(components);
+ return(node);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% C a n o n i c a l X M L C o n t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% CanonicalXMLContent() converts text to canonical XML content by converting
+% to UTF-8, substituting predefined entities, wrapping as CDATA, or encoding
+% as base-64 as required.
+%
+% The format of the CanonicalXMLContent method is:
+%
+%
+% char *CanonicalXMLContent(const char *content,
+% const MagickBooleanType pedantic)
+%
+% A description of each parameter follows:
+%
+% o content: the content.
+%
+% o pedantic: if true, replace newlines and tabs with their respective
+% entities.
+%
+*/
+
+static unsigned char *ConvertLatin1ToUTF8(const unsigned char *content)
+{
+ register const unsigned char
+ *p;
+
+ register unsigned char
+ *q;
+
+ size_t
+ length;
+
+ unsigned char
+ *utf8;
+
+ unsigned int
+ c;
+
+ length=0;
+ for (p=content; *p != '\0'; p++)
+ length+=(*p & 0x80) != 0 ? 2 : 1;
+ utf8=(unsigned char *) NULL;
+ if (~length >= 1)
+ utf8=(unsigned char *) AcquireQuantumMemory(length+1UL,sizeof(*utf8));
+ if (utf8 == (unsigned char *) NULL)
+ return((unsigned char *) NULL);
+ q=utf8;
+ for (p=content; *p != '\0'; p++)
+ {
+ c=(*p);
+ if ((c & 0x80) == 0)
+ *q++=c;
+ else
+ {
+ *q++=0xc0 | ((c >> 6) & 0x3f);
+ *q++=0x80 | (c & 0x3f);
+ }
+ }
+ *q='\0';
+ return(utf8);
+}
+
+MagickExport char *CanonicalXMLContent(const char *content,
+ const MagickBooleanType pedantic)
+{
+ char
+ *base64,
+ *canonical_content;
+
+ register const unsigned char
+ *p;
+
+ register long
+ i;
+
+ size_t
+ extent,
+ length;
+
+ unsigned char
+ *utf8;
+
+ utf8=ConvertLatin1ToUTF8((const unsigned char *) content);
+ if (utf8 == (unsigned char *) NULL)
+ return((char *) NULL);
+ for (p=utf8; *p != '\0'; p++)
+ if ((*p < 0x20) && (*p != 0x09) && (*p != 0x0a) && (*p != 0x0d))
+ break;
+ if (*p != '\0')
+ {
+ /*
+ String is binary, base64-encode it.
+ */
+ base64=Base64Encode(utf8,strlen((char *) utf8),&length);
+ utf8=(unsigned char *) RelinquishMagickMemory(utf8);
+ if (base64 == (char *) NULL)
+ return((char *) NULL);
+ canonical_content=AcquireString("<base64>");
+ (void) ConcatenateString(&canonical_content,base64);
+ base64=DestroyString(base64);
+ (void) ConcatenateString(&canonical_content,"</base64>");
+ return(canonical_content);
+ }
+ /*
+ Substitute predefined entities.
+ */
+ i=0;
+ canonical_content=AcquireString((char *) NULL);
+ extent=MaxTextExtent;
+ for (p=utf8; *p != '\0'; p++)
+ {
+ if ((i+MaxTextExtent) > (long) extent)
+ {
+ extent+=MaxTextExtent;
+ canonical_content=(char *) ResizeQuantumMemory(canonical_content,extent,
+ sizeof(*canonical_content));
+ if (canonical_content == (char *) NULL)
+ return(canonical_content);
+ }
+ switch (*p)
+ {
+ case '&':
+ {
+ i+=FormatMagickString(canonical_content+i,extent,"&");
+ break;
+ }
+ case '<':
+ {
+ i+=FormatMagickString(canonical_content+i,extent,"<");
+ break;
+ }
+ case '>':
+ {
+ i+=FormatMagickString(canonical_content+i,extent,">");
+ break;
+ }
+ case '"':
+ {
+ i+=FormatMagickString(canonical_content+i,extent,""");
+ break;
+ }
+ case '\n':
+ {
+ if (pedantic == MagickFalse)
+ {
+ canonical_content[i++]=(char) (*p);
+ break;
+ }
+ i+=FormatMagickString(canonical_content+i,extent,"
");
+ break;
+ }
+ case '\t':
+ {
+ if (pedantic == MagickFalse)
+ {
+ canonical_content[i++]=(char) (*p);
+ break;
+ }
+ i+=FormatMagickString(canonical_content+i,extent,"	");
+ break;
+ }
+ case '\r':
+ {
+ i+=FormatMagickString(canonical_content+i,extent,"
");
+ break;
+ }
+ default:
+ {
+ canonical_content[i++]=(char) (*p);
+ break;
+ }
+ }
+ }
+ canonical_content[i]='\0';
+ utf8=(unsigned char *) RelinquishMagickMemory(utf8);
+ return(canonical_content);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyXMLTree() destroys the xml-tree.
+%
+% The format of the DestroyXMLTree method is:
+%
+% XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+
+static char **DestroyXMLTreeAttributes(char **attributes)
+{
+ register long
+ i;
+
+ /*
+ Destroy a tag attribute list.
+ */
+ if ((attributes == (char **) NULL) || (attributes == sentinel))
+ return((char **) NULL);
+ for (i=0; attributes[i] != (char *) NULL; i+=2)
+ {
+ /*
+ Destroy attribute tag and value.
+ */
+ if (attributes[i] != (char *) NULL)
+ attributes[i]=DestroyString(attributes[i]);
+ if (attributes[i+1] != (char *) NULL)
+ attributes[i+1]=DestroyString(attributes[i+1]);
+ }
+ attributes=(char **) RelinquishMagickMemory(attributes);
+ return((char **) NULL);
+}
+
+MagickExport XMLTreeInfo *DestroyXMLTree(XMLTreeInfo *xml_info)
+{
+ char
+ **attributes;
+
+ long
+ j;
+
+ register long
+ i;
+
+ XMLTreeRoot
+ *root;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (xml_info->child != (XMLTreeInfo *) NULL)
+ xml_info->child=DestroyXMLTree(xml_info->child);
+ if (xml_info->ordered != (XMLTreeInfo *) NULL)
+ xml_info->ordered=DestroyXMLTree(xml_info->ordered);
+ if (xml_info->parent == (XMLTreeInfo *) NULL)
+ {
+ /*
+ Free root tag allocations.
+ */
+ root=(XMLTreeRoot *) xml_info;
+ for (i=NumberPredefinedEntities; root->entities[i]; i+=2)
+ root->entities[i+1]=DestroyString(root->entities[i+1]);
+ root->entities=(char **) RelinquishMagickMemory(root->entities);
+ for (i=0; root->attributes[i] != (char **) NULL; i++)
+ {
+ attributes=root->attributes[i];
+ if (attributes[0] != (char *) NULL)
+ attributes[0]=DestroyString(attributes[0]);
+ for (j=1; attributes[j] != (char *) NULL; j+=3)
+ {
+ if (attributes[j] != (char *) NULL)
+ attributes[j]=DestroyString(attributes[j]);
+ if (attributes[j+1] != (char *) NULL)
+ attributes[j+1]=DestroyString(attributes[j+1]);
+ if (attributes[j+2] != (char *) NULL)
+ attributes[j+2]=DestroyString(attributes[j+2]);
+ }
+ attributes=(char **) RelinquishMagickMemory(attributes);
+ }
+ if (root->attributes[0] != (char **) NULL)
+ root->attributes=(char ***) RelinquishMagickMemory(root->attributes);
+ if (root->processing_instructions[0] != (char **) NULL)
+ {
+ for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
+ {
+ for (j=0; root->processing_instructions[i][j] != (char *) NULL; j++)
+ root->processing_instructions[i][j]=DestroyString(
+ root->processing_instructions[i][j]);
+ root->processing_instructions[i][j+1]=DestroyString(
+ root->processing_instructions[i][j+1]);
+ root->processing_instructions[i]=(char **) RelinquishMagickMemory(
+ root->processing_instructions[i]);
+ }
+ root->processing_instructions=(char ***) RelinquishMagickMemory(
+ root->processing_instructions);
+ }
+ }
+ xml_info->attributes=DestroyXMLTreeAttributes(xml_info->attributes);
+ xml_info->content=DestroyString(xml_info->content);
+ xml_info->tag=DestroyString(xml_info->tag);
+ xml_info=(XMLTreeInfo *) RelinquishMagickMemory(xml_info);
+ return((XMLTreeInfo *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t N e x t X M L T r e e T a g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetNextXMLTreeTag() returns the next tag or NULL if not found.
+%
+% The format of the GetNextXMLTreeTag method is:
+%
+% XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport XMLTreeInfo *GetNextXMLTreeTag(XMLTreeInfo *xml_info)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(xml_info->next);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeAttribute() returns the value of the attribute tag with the
+% specified tag if found, otherwise NULL.
+%
+% The format of the GetXMLTreeAttribute method is:
+%
+% const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o tag: the attribute tag.
+%
+*/
+MagickExport const char *GetXMLTreeAttribute(XMLTreeInfo *xml_info,
+ const char *tag)
+{
+ long
+ j;
+
+ register long
+ i;
+
+ XMLTreeRoot
+ *root;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (xml_info->attributes == (char **) NULL)
+ return((const char *) NULL);
+ i=0;
+ while ((xml_info->attributes[i] != (char *) NULL) &&
+ (strcmp(xml_info->attributes[i],tag) != 0))
+ i+=2;
+ if (xml_info->attributes[i] != (char *) NULL)
+ return(xml_info->attributes[i+1]);
+ root=(XMLTreeRoot*) xml_info;
+ while (root->root.parent != (XMLTreeInfo *) NULL)
+ root=(XMLTreeRoot *) root->root.parent;
+ i=0;
+ while ((root->attributes[i] != (char **) NULL) &&
+ (strcmp(root->attributes[i][0],xml_info->tag) != 0))
+ i++;
+ if (root->attributes[i] == (char **) NULL)
+ return((const char *) NULL);
+ j=1;
+ while ((root->attributes[i][j] != (char *) NULL) &&
+ (strcmp(root->attributes[i][j],tag) != 0))
+ j+=3;
+ if (root->attributes[i][j] == (char *) NULL)
+ return((const char *) NULL);
+ return(root->attributes[i][j+1]);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e A t t r i b u t e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeAttributes() injects all attributes associated with the current
+% tag in the specified splay-tree.
+%
+% The format of the GetXMLTreeAttributes method is:
+%
+% MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
+% SplayTreeInfo *attributes)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o attributes: the attribute splay-tree.
+%
+*/
+MagickExport MagickBooleanType GetXMLTreeAttributes(const XMLTreeInfo *xml_info,
+ SplayTreeInfo *attributes)
+{
+ register long
+ i;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((const XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(attributes != (SplayTreeInfo *) NULL);
+ if (xml_info->attributes == (char **) NULL)
+ return(MagickTrue);
+ i=0;
+ while (xml_info->attributes[i] != (char *) NULL)
+ {
+ (void) AddValueToSplayTree(attributes,
+ ConstantString(xml_info->attributes[i]),
+ ConstantString(xml_info->attributes[i+1]));
+ i+=2;
+ }
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e C h i l d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeChild() returns the first child tag with the specified tag if
+% found, otherwise NULL.
+%
+% The format of the GetXMLTreeChild method is:
+%
+% XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport XMLTreeInfo *GetXMLTreeChild(XMLTreeInfo *xml_info,const char *tag)
+{
+ XMLTreeInfo
+ *child;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ child=xml_info->child;
+ if (tag != (const char *) NULL)
+ while ((child != (XMLTreeInfo *) NULL) && (strcmp(child->tag,tag) != 0))
+ child=child->sibling;
+ return(child);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e C o n t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeContent() returns any content associated with specified
+% xml-tree node.
+%
+% The format of the GetXMLTreeContent method is:
+%
+% const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport const char *GetXMLTreeContent(XMLTreeInfo *xml_info)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(xml_info->content);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e O r d e r e d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeOrdered() returns the next ordered node if found, otherwise NULL.
+%
+% The format of the GetXMLTreeOrdered method is:
+%
+% XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport XMLTreeInfo *GetXMLTreeOrdered(XMLTreeInfo *xml_info)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(xml_info->ordered);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e P a t h %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreePath() traverses the XML-tree as defined by the specified path
+% and returns the node if found, otherwise NULL.
+%
+% The format of the GetXMLTreePath method is:
+%
+% XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o path: the path (e.g. property/elapsed-time).
+%
+*/
+MagickExport XMLTreeInfo *GetXMLTreePath(XMLTreeInfo *xml_info,const char *path)
+{
+ char
+ **components,
+ subnode[MaxTextExtent],
+ tag[MaxTextExtent];
+
+ long
+ j;
+
+ register long
+ i;
+
+ XMLTreeInfo
+ *node;
+
+ unsigned long
+ number_components;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ node=xml_info;
+ components=GetPathComponents(path,&number_components);
+ if (components == (char **) NULL)
+ return((XMLTreeInfo *) NULL);
+ for (i=0; i < (long) number_components; i++)
+ {
+ GetPathComponent(components[i],SubimagePath,subnode);
+ GetPathComponent(components[i],CanonicalPath,tag);
+ node=GetXMLTreeChild(node,tag);
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ for (j=atol(subnode)-1; j > 0; j--)
+ {
+ node=GetXMLTreeOrdered(node);
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ }
+ if (node == (XMLTreeInfo *) NULL)
+ break;
+ components[i]=DestroyString(components[i]);
+ }
+ for ( ; i < (long) number_components; i++)
+ components[i]=DestroyString(components[i]);
+ components=(char **) RelinquishMagickMemory(components);
+ return(node);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e P r o c e s s i n g I n s t r u c t i o n s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeProcessingInstructions() returns a null terminated array of
+% processing instructions for the given target.
+%
+% The format of the GetXMLTreeProcessingInstructions method is:
+%
+% const char **GetXMLTreeProcessingInstructions(XMLTreeInfo *xml_info,
+% const char *target)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport const char **GetXMLTreeProcessingInstructions(
+ XMLTreeInfo *xml_info,const char *target)
+{
+ register long
+ i;
+
+ XMLTreeRoot
+ *root;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ root=(XMLTreeRoot *) xml_info;
+ while (root->root.parent != (XMLTreeInfo *) NULL)
+ root=(XMLTreeRoot *) root->root.parent;
+ i=0;
+ while ((root->processing_instructions[i] != (char **) NULL) &&
+ (strcmp(root->processing_instructions[i][0],target) != 0))
+ i++;
+ if (root->processing_instructions[i] == (char **) NULL)
+ return((const char **) sentinel);
+ return((const char **) (root->processing_instructions[i]+1));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e S i b l i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeSibling() returns the node sibling if found, otherwise NULL.
+%
+% The format of the GetXMLTreeSibling method is:
+%
+% XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport XMLTreeInfo *GetXMLTreeSibling(XMLTreeInfo *xml_info)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(xml_info->sibling);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% G e t X M L T r e e T a g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% GetXMLTreeTag() returns the tag associated with specified xml-tree node.
+%
+% The format of the GetXMLTreeTag method is:
+%
+% const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport const char *GetXMLTreeTag(XMLTreeInfo *xml_info)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ return(xml_info->tag);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% I n s e r t I n t o T a g X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% InsertTagIntoXMLTree() inserts a tag at an offset relative to the start of
+% the parent tag's character content. This method returns the child tag.
+%
+% The format of the InsertTagIntoXMLTree method is:
+%
+% XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
+% XMLTreeInfo *child,const size_t offset)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o child: the child tag.
+%
+% o offset: the tag offset.
+%
+*/
+MagickExport XMLTreeInfo *InsertTagIntoXMLTree(XMLTreeInfo *xml_info,
+ XMLTreeInfo *child,const size_t offset)
+{
+ XMLTreeInfo
+ *head,
+ *node,
+ *previous;
+
+ child->ordered=(XMLTreeInfo *) NULL;
+ child->sibling=(XMLTreeInfo *) NULL;
+ child->next=(XMLTreeInfo *) NULL;
+ child->offset=offset;
+ child->parent=xml_info;
+ if (xml_info->child == (XMLTreeInfo *) NULL)
+ {
+ xml_info->child=child;
+ return(child);
+ }
+ head=xml_info->child;
+ if (head->offset > offset)
+ {
+ child->ordered=head;
+ xml_info->child=child;
+ }
+ else
+ {
+ node=head;
+ while ((node->ordered != (XMLTreeInfo *) NULL) &&
+ (node->ordered->offset <= offset))
+ node=node->ordered;
+ child->ordered=node->ordered;
+ node->ordered=child;
+ }
+ previous=(XMLTreeInfo *) NULL;
+ node=head;
+ while ((node != (XMLTreeInfo *) NULL) && (strcmp(node->tag,child->tag) != 0))
+ {
+ previous=node;
+ node=node->sibling;
+ }
+ if ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
+ {
+ while ((node->next != (XMLTreeInfo *) NULL) &&
+ (node->next->offset <= offset))
+ node=node->next;
+ child->next=node->next;
+ node->next=child;
+ }
+ else
+ {
+ if ((previous != (XMLTreeInfo *) NULL) && (node != (XMLTreeInfo *) NULL))
+ previous->sibling=node->sibling;
+ child->next=node;
+ previous=(XMLTreeInfo *) NULL;
+ node=head;
+ while ((node != (XMLTreeInfo *) NULL) && (node->offset <= offset))
+ {
+ previous=node;
+ node=node->sibling;
+ }
+ child->sibling=node;
+ if (previous != (XMLTreeInfo *) NULL)
+ previous->sibling=child;
+ }
+ return(child);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewXMLTree() returns a XMLTreeInfo xml-tree as defined by the specified
+% XML string.
+%
+% The format of the NewXMLTree method is:
+%
+% XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
+%
+% A description of each parameter follows:
+%
+% o xml: The XML string.
+%
+% o exception: return any errors or warnings in this structure.
+%
+*/
+
+static char *ConvertUTF16ToUTF8(const char *content,size_t *length)
+{
+ char
+ *utf8;
+
+ int
+ bits,
+ byte,
+ c,
+ encoding;
+
+ long
+ j;
+
+ register long
+ i;
+
+ size_t
+ extent;
+
+ utf8=(char *) AcquireQuantumMemory(*length,sizeof(*utf8));
+ if (utf8 == (char *) NULL)
+ return((char *) NULL);
+ encoding=(*content == '\xFE') ? 1 : (*content == '\xFF') ? 0 : -1;
+ if (encoding == -1)
+ {
+ /*
+ Already UTF-8.
+ */
+ (void) CopyMagickMemory(utf8,content,*length*sizeof(*utf8));
+ return(utf8);
+ }
+ j=0;
+ extent=(*length);
+ for (i=2; i < (long) (*length-1); i+=2)
+ {
+ c=(encoding != 0) ? ((content[i] & 0xff) << 8) | (content[i+1] & 0xff) :
+ ((content[i+1] & 0xff) << 8) | (content[i] & 0xff);
+ if ((c >= 0xd800) && (c <= 0xdfff) && ((i+=2) < (long) (*length-1)))
+ {
+ byte=(encoding != 0) ? ((content[i] & 0xff) << 8) |
+ (content[i+1] & 0xff) : ((content[i+1] & 0xff) << 8) |
+ (content[i] & 0xff);
+ c=(((c & 0x3ff) << 10) | (byte & 0x3ff))+0x10000;
+ }
+ if ((size_t) (j+MaxTextExtent) > extent)
+ {
+ extent=(size_t) j+MaxTextExtent;
+ utf8=(char *) ResizeQuantumMemory(utf8,extent,sizeof(*utf8));
+ if (utf8 == (char *) NULL)
+ return(utf8);
+ }
+ if (c < 0x80)
+ {
+ utf8[j]=c;
+ j++;
+ continue;
+ }
+ /*
+ Multi-byte UTF-8 sequence.
+ */
+ byte=c;
+ for (bits=0; byte != 0; byte/=2)
+ bits++;
+ bits=(bits-2)/5;
+ utf8[j++]=(0xFF << (7-bits)) | (c >> (6*bits));
+ while (bits != 0)
+ {
+ bits--;
+ utf8[j]=0x80 | ((c >> (6*bits)) & 0x3f);
+ j++;
+ }
+ }
+ *length=(size_t) j;
+ return((char *) ResizeQuantumMemory(utf8,*length,sizeof(*utf8)));
+}
+
+static char *ParseEntities(char *xml,char **entities,int state)
+{
+ char
+ *entity;
+
+ int
+ byte,
+ c;
+
+ register char
+ *p,
+ *q;
+
+ register long
+ i;
+
+ size_t
+ extent,
+ length;
+
+ ssize_t
+ offset;
+
+ /*
+ Normalize line endings.
+ */
+ p=xml;
+ q=xml;
+ for ( ; *xml != '\0'; xml++)
+ while (*xml == '\r')
+ {
+ *(xml++)='\n';
+ if (*xml == '\n')
+ (void) CopyMagickMemory(xml,xml+1,strlen(xml));
+ }
+ for (xml=p; ; )
+ {
+ while ((*xml != '\0') && (*xml != '&') && ((*xml != '%') ||
+ (state != '%')) && (isspace((int) ((unsigned char) *xml) == 0)))
+ xml++;
+ if (*xml == '\0')
+ break;
+ /*
+ States include:
+ '&' for general entity decoding
+ '%' for parameter entity decoding
+ 'c' for CDATA sections
+ ' ' for attributes normalization
+ '*' for non-CDATA attributes normalization
+ */
+ if ((state != 'c') && (strncmp(xml,"&#",2) == 0))
+ {
+ /*
+ Character reference.
+ */
+ if (xml[2] != 'x')
+ c=strtol(xml+2,&entity,10); /* base 10 */
+ else
+ c=strtol(xml+3,&entity,16); /* base 16 */
+ if ((c == 0) || (*entity != ';'))
+ {
+ /*
+ Not a character reference.
+ */
+ xml++;
+ continue;
+ }
+ if (c < 0x80)
+ *(xml++)=c;
+ else
+ {
+ /*
+ Multi-byte UTF-8 sequence.
+ */
+ byte=c;
+ for (i=0; byte != 0; byte/=2)
+ i++;
+ i=(i-2)/5;
+ *xml=(char) ((0xFF << (7-i)) | (c >> (6*i)));
+ xml++;
+ while (i != 0)
+ {
+ i--;
+ *xml=(char) (0x80 | ((c >> (6*i)) & 0x3F));
+ xml++;
+ }
+ }
+ (void) CopyMagickMemory(xml,strchr(xml,';')+1,strlen(strchr(xml,';')));
+ }
+ else
+ if (((*xml == '&') && ((state == '&') || (state == ' ') ||
+ (state == '*'))) || ((state == '%') && (*xml == '%')))
+ {
+ /*
+ Find entity in the list.
+ */
+ i=0;
+ while ((entities[i] != (char *) NULL) &&
+ (strncmp(xml+1,entities[i],strlen(entities[i])) != 0))
+ i+=2;
+ if (entities[i++] == (char *) NULL)
+ xml++;
+ else
+ {
+ /*
+ Found a match.
+ */
+ length=strlen(entities[i]);
+ entity=strchr(xml,';');
+ if ((length-1L) >= (size_t) (entity-xml))
+ {
+ offset=(ssize_t) (xml-p);
+ extent=(size_t) (offset+length+strlen(entity));
+ if (p != q)
+ p=(char *) ResizeQuantumMemory(p,extent,sizeof(*p));
+ else
+ {
+ char
+ *xml;
+
+ xml=(char *) AcquireQuantumMemory(extent,sizeof(*xml));
+ if (xml != (char *) NULL)
+ {
+ (void) CopyMagickString(xml,p,extent*sizeof(*xml));
+ p=xml;
+ }
+ }
+ if (p == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ xml=p+offset;
+ entity=strchr(xml,';');
+ }
+ (void) CopyMagickMemory(xml+length,entity+1,strlen(entity));
+ (void) strncpy(xml,entities[i],length);
+ }
+ }
+ else
+ if (((state == ' ') || (state == '*')) &&
+ (isspace((int) ((unsigned char) *xml) != 0)))
+ *(xml++)=' ';
+ else
+ xml++;
+ }
+ if (state == '*')
+ {
+ /*
+ Normalize spaces for non-CDATA attributes.
+ */
+ for (xml=p; *xml != '\0'; xml++)
+ {
+ i=(long) strspn(xml," ");
+ if (i != 0)
+ (void) CopyMagickMemory(xml,xml+i,strlen(xml+i)+1);
+ while ((*xml != '\0') && (*xml != ' '))
+ xml++;
+ }
+ xml--;
+ if ((xml >= p) && (*xml == ' '))
+ *xml='\0';
+ }
+ return(p == q ? ConstantString(p) : p);
+}
+
+static void ParseCharacterContent(XMLTreeRoot *root,char *xml,
+ const size_t length,const char state)
+{
+ XMLTreeInfo
+ *xml_info;
+
+ xml_info=root->node;
+ if ((xml_info == (XMLTreeInfo *) NULL) || (xml_info->tag == (char *) NULL) ||
+ (length == 0))
+ return;
+ xml[length]='\0';
+ xml=ParseEntities(xml,root->entities,state);
+ if (*xml_info->content != '\0')
+ {
+ (void) ConcatenateString(&xml_info->content,xml);
+ xml=DestroyString(xml);
+ }
+ else
+ {
+ if (xml_info->content != (char *) NULL)
+ xml_info->content=DestroyString(xml_info->content);
+ xml_info->content=xml;
+ }
+}
+
+static XMLTreeInfo *ParseCloseTag(XMLTreeRoot *root,char *tag,
+ char *magick_unused(xml),ExceptionInfo *exception)
+{
+ if ((root->node == (XMLTreeInfo *) NULL) ||
+ (root->node->tag == (char *) NULL) || (strcmp(tag,root->node->tag) != 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","unexpected closing tag </%s>",tag);
+ return(&root->root);
+ }
+ root->node=root->node->parent;
+ return((XMLTreeInfo *) NULL);
+}
+
+static MagickBooleanType ValidateEntities(char *tag,char *xml,char **entities)
+{
+ register long
+ i;
+
+ /*
+ Check for circular entity references.
+ */
+ for ( ; ; xml++)
+ {
+ while ((*xml != '\0') && (*xml != '&'))
+ xml++;
+ if (*xml == '\0')
+ return(MagickTrue);
+ if (strncmp(xml+1,tag,strlen(tag)) == 0)
+ return(MagickFalse);
+ i=0;
+ while ((entities[i] != (char *) NULL) &&
+ (strncmp(entities[i],xml+1,strlen(entities[i]) == 0)))
+ i+=2;
+ if ((entities[i] != (char *) NULL) &&
+ (ValidateEntities(tag,entities[i+1],entities) == 0))
+ return(MagickFalse);
+ }
+ return(MagickTrue);
+}
+
+static void ParseProcessingInstructions(XMLTreeRoot *root,char *xml,
+ size_t length)
+{
+ char
+ *target;
+
+ long
+ j;
+
+ register long
+ i;
+
+ target=xml;
+ xml[length]='\0';
+ xml+=strcspn(xml,XMLWhitespace);
+ if (*xml != '\0')
+ {
+ *xml='\0';
+ xml+=strspn(xml+1,XMLWhitespace)+1;
+ }
+ if (strcmp(target,"xml") == 0)
+ {
+ xml=strstr(xml,"standalone");
+ if ((xml != (char *) NULL) &&
+ (strncmp(xml+strspn(xml+10,XMLWhitespace "='\"")+10,"yes",3) == 0))
+ root->standalone=MagickTrue;
+ return;
+ }
+ if (root->processing_instructions[0] == (char **) NULL)
+ {
+ root->processing_instructions=(char ***) AcquireMagickMemory(sizeof(
+ *root->processing_instructions));
+ if (root->processing_instructions ==(char ***) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ *root->processing_instructions=(char **) NULL;
+ }
+ i=0;
+ while ((root->processing_instructions[i] != (char **) NULL) &&
+ (strcmp(target,root->processing_instructions[i][0]) != 0))
+ i++;
+ if (root->processing_instructions[i] == (char **) NULL)
+ {
+ root->processing_instructions=(char ***) ResizeQuantumMemory(
+ root->processing_instructions,(size_t) (i+2),
+ sizeof(*root->processing_instructions));
+ if (root->processing_instructions == (char ***) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ root->processing_instructions[i]=(char **) AcquireQuantumMemory(3,
+ sizeof(**root->processing_instructions));
+ if (root->processing_instructions[i] == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ root->processing_instructions[i+1]=(char **) NULL;
+ root->processing_instructions[i][0]=ConstantString(target);
+ root->processing_instructions[i][1]=(char *)
+ root->processing_instructions[i+1];
+ root->processing_instructions[i+1]=(char **) NULL;
+ root->processing_instructions[i][2]=ConstantString("");
+ }
+ j=1;
+ while (root->processing_instructions[i][j] != (char *) NULL)
+ j++;
+ root->processing_instructions[i]=(char **) ResizeQuantumMemory(
+ root->processing_instructions[i],(size_t) (j+3),
+ sizeof(**root->processing_instructions));
+ if (root->processing_instructions[i] == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ root->processing_instructions[i][j+2]=(char *) ResizeQuantumMemory(
+ root->processing_instructions[i][j+1],(size_t) (j+1),
+ sizeof(**root->processing_instructions));
+ if (root->processing_instructions[i][j+2] == (char *) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ (void) CopyMagickString(root->processing_instructions[i][j+2]+j-1,
+ root->root.tag != (char *) NULL ? ">" : "<",2);
+ root->processing_instructions[i][j]=ConstantString(xml);
+ root->processing_instructions[i][j+1]=(char *) NULL;
+}
+
+static MagickBooleanType ParseInternalDoctype(XMLTreeRoot *root,char *xml,
+ size_t length,ExceptionInfo *exception)
+{
+ char
+ *c,
+ **entities,
+ *n,
+ **predefined_entitites,
+ q,
+ *t,
+ *v;
+
+ long
+ j;
+
+ register long
+ i;
+
+ n=(char *) NULL;
+ predefined_entitites=(char **) AcquireMagickMemory(sizeof(sentinel));
+ if (predefined_entitites == (char **) NULL)
+ ThrowFatalException(ResourceLimitError,"MemoryAllocationFailed");
+ (void) CopyMagickMemory(predefined_entitites,sentinel,sizeof(sentinel));
+ for (xml[length]='\0'; xml != (char *) NULL; )
+ {
+ while ((*xml != '\0') && (*xml != '<') && (*xml != '%'))
+ xml++;
+ if (*xml == '\0')
+ break;
+ if (strncmp(xml,"<!ENTITY",8) == 0)
+ {
+ /*
+ Parse entity definitions.
+ */
+ xml+=strspn(xml+8,XMLWhitespace)+8;
+ c=xml;
+ n=xml+strspn(xml,XMLWhitespace "%");
+ xml=n+strcspn(n,XMLWhitespace);
+ *xml=';';
+ v=xml+strspn(xml+1,XMLWhitespace)+1;
+ q=(*v);
+ v++;
+ if ((q != '"') && (q != '\''))
+ {
+ /*
+ Skip externals.
+ */
+ xml=strchr(xml,'>');
+ continue;
+ }
+ entities=(*c == '%') ? predefined_entitites : root->entities;
+ for (i=0; entities[i] != (char *) NULL; i++) ;
+ entities=(char **) ResizeQuantumMemory(entities,(size_t) (i+3),
+ sizeof(*entities));
+ if (entities == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
+ if (*c == '%')
+ predefined_entitites=entities;
+ else
+ root->entities=entities;
+ xml++;
+ *xml='\0';
+ xml=strchr(v,q);
+ if (xml != (char *) NULL)
+ {
+ *xml='\0';
+ xml++;
+ }
+ entities[i+1]=ParseEntities(v,predefined_entitites,'%');
+ entities[i+2]=(char *) NULL;
+ if (ValidateEntities(n,entities[i+1],entities) != MagickFalse)
+ entities[i]=n;
+ else
+ {
+ if (entities[i+1] != v)
+ entities[i+1]=DestroyString(entities[i+1]);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","circular entity declaration &%s",n);
+ predefined_entitites=(char **) RelinquishMagickMemory(
+ predefined_entitites);
+ return(MagickFalse);
+ }
+ }
+ else
+ if (strncmp(xml,"<!ATTLIST",9) == 0)
+ {
+ /*
+ Parse default attributes.
+ */
+ t=xml+strspn(xml+9,XMLWhitespace)+9;
+ if (*t == '\0')
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unclosed <!ATTLIST");
+ predefined_entitites=(char **) RelinquishMagickMemory(
+ predefined_entitites);
+ return(MagickFalse);
+ }
+ xml=t+strcspn(t,XMLWhitespace ">");
+ if (*xml == '>')
+ continue;
+ *xml='\0';
+ i=0;
+ while ((root->attributes[i] != (char **) NULL) &&
+ (strcmp(n,root->attributes[i][0]) != 0))
+ i++;
+ while (*(n=(++xml)+strspn(xml,XMLWhitespace)) && (*n != '>'))
+ {
+ xml=n+strcspn(n,XMLWhitespace);
+ if (*xml != '\0')
+ *xml='\0';
+ else
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","malformed <!ATTLIST");
+ predefined_entitites=(char **) RelinquishMagickMemory(
+ predefined_entitites);
+ return(MagickFalse);
+ }
+ xml+=strspn(xml+1,XMLWhitespace)+1;
+ c=(char *) (strncmp(xml,"CDATA",5) != 0 ? "*" : " ");
+ if (strncmp(xml,"NOTATION",8) == 0)
+ xml+=strspn(xml+8,XMLWhitespace)+8;
+ xml=(*xml == '(') ? strchr(xml,')') : xml+
+ strcspn(xml,XMLWhitespace);
+ if (xml == (char *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","malformed <!ATTLIST");
+ predefined_entitites=(char **) RelinquishMagickMemory(
+ predefined_entitites);
+ return(MagickFalse);
+ }
+ xml+=strspn(xml,XMLWhitespace ")");
+ if (strncmp(xml,"#FIXED",6) == 0)
+ xml+=strspn(xml+6,XMLWhitespace)+6;
+ if (*xml == '#')
+ {
+ xml+=strcspn(xml,XMLWhitespace ">")-1;
+ if (*c == ' ')
+ continue;
+ v=(char *) NULL;
+ }
+ else
+ if (((*xml == '"') || (*xml == '\'')) &&
+ ((xml=strchr(v=xml+1,*xml)) != (char *) NULL))
+ *xml='\0';
+ else
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","malformed <!ATTLIST");
+ predefined_entitites=(char **) RelinquishMagickMemory(
+ predefined_entitites);
+ return(MagickFalse);
+ }
+ if (root->attributes[i] == (char **) NULL)
+ {
+ /*
+ New attribute tag.
+ */
+ if (i == 0)
+ root->attributes=(char ***) AcquireQuantumMemory(2,
+ sizeof(*root->attributes));
+ else
+ root->attributes=(char ***) ResizeQuantumMemory(
+ root->attributes,(size_t) (i+2),
+ sizeof(*root->attributes));
+ if (root->attributes == (char ***) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ root->attributes[i]=(char **) AcquireQuantumMemory(2,
+ sizeof(*root->attributes));
+ if (root->attributes[i] == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ root->attributes[i][0]=ConstantString(t);
+ root->attributes[i][1]=(char *) NULL;
+ root->attributes[i+1]=(char **) NULL;
+ }
+ for (j=1; root->attributes[i][j] != (char *) NULL; j+=3) ;
+ root->attributes[i]=(char **) ResizeQuantumMemory(
+ root->attributes[i],(size_t) (j+4),sizeof(*root->attributes));
+ if (root->attributes[i] == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed");
+ root->attributes[i][j+3]=(char *) NULL;
+ root->attributes[i][j+2]=ConstantString(c);
+ root->attributes[i][j+1]=(char *) NULL;
+ if (v != (char *) NULL)
+ root->attributes[i][j+1]=ParseEntities(v,root->entities,*c);
+ root->attributes[i][j]=ConstantString(n);
+ }
+ }
+ else
+ if (strncmp(xml, "<!--", 4) == 0)
+ xml=strstr(xml+4,"-->");
+ else
+ if (strncmp(xml,"<?", 2) == 0)
+ {
+ c=xml+2;
+ xml=strstr(c,"?>");
+ if (xml != (char *) NULL)
+ {
+ ParseProcessingInstructions(root,c,(size_t) (xml-c));
+ xml++;
+ }
+ }
+ else
+ if (*xml == '<')
+ xml=strchr(xml,'>');
+ else
+ if ((*(xml++) == '%') && (root->standalone == MagickFalse))
+ break;
+ }
+ predefined_entitites=(char **) RelinquishMagickMemory(predefined_entitites);
+ return(MagickTrue);
+}
+
+static void ParseOpenTag(XMLTreeRoot *root,char *tag,char **attributes)
+{
+ XMLTreeInfo
+ *xml_info;
+
+ xml_info=root->node;
+ if (xml_info->tag == (char *) NULL)
+ xml_info->tag=ConstantString(tag);
+ else
+ xml_info=AddChildToXMLTree(xml_info,tag,strlen(xml_info->content));
+ xml_info->attributes=attributes;
+ root->node=xml_info;
+}
+
+MagickExport XMLTreeInfo *NewXMLTree(const char *xml,ExceptionInfo *exception)
+{
+ char
+ **attribute,
+ **attributes,
+ *tag,
+ *utf8;
+
+ int
+ c,
+ terminal;
+
+ long
+ j,
+ l;
+
+ register char
+ *p;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ MagickBooleanType
+ status;
+
+ XMLTreeRoot
+ *root;
+
+ /*
+ Convert xml-string to UTF8.
+ */
+ if ((xml == (const char *) NULL) || (strlen(xml) == 0))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","root tag missing");
+ return((XMLTreeInfo *) NULL);
+ }
+ root=(XMLTreeRoot *) NewXMLTreeTag((char *) NULL);
+ length=strlen(xml);
+ utf8=ConvertUTF16ToUTF8(xml,&length);
+ if (utf8 == (char *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","UTF16 to UTF8 failed");
+ return((XMLTreeInfo *) NULL);
+ }
+ terminal=utf8[length-1];
+ utf8[length-1]='\0';
+ p=utf8;
+ while ((*p != '\0') && (*p != '<'))
+ p++;
+ if (*p == '\0')
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","root tag missing");
+ utf8=DestroyString(utf8);
+ return((XMLTreeInfo *) NULL);
+ }
+ attribute=(char **) NULL;
+ for (p++; ; p++)
+ {
+ attributes=(char **) sentinel;
+ tag=p;
+ if ((isalpha((int) ((unsigned char) *p)) !=0) || (*p == '_') ||
+ (*p == ':') || (*p < '\0'))
+ {
+ /*
+ Tag.
+ */
+ if (root->node == (XMLTreeInfo *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","root tag missing");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ p+=strcspn(p,XMLWhitespace "/>");
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ *p++='\0';
+ if ((*p != '\0') && (*p != '/') && (*p != '>'))
+ {
+ /*
+ Find tag in default attributes list.
+ */
+ i=0;
+ while ((root->attributes[i] != (char **) NULL) &&
+ (strcmp(root->attributes[i][0],tag) != 0))
+ i++;
+ attribute=root->attributes[i];
+ }
+ for (l=0; (*p != '\0') && (*p != '/') && (*p != '>'); l+=2)
+ {
+ /*
+ Attribute.
+ */
+ if (l == 0)
+ attributes=(char **) AcquireQuantumMemory(4,sizeof(*attributes));
+ else
+ attributes=(char **) ResizeQuantumMemory(attributes,(size_t) (l+4),
+ sizeof(*attributes));
+ if (attributes == (char **) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ ResourceLimitError,"MemoryAllocationFailed","`%s'","");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ attributes[l+2]=(char *) NULL;
+ attributes[l+1]=(char *) NULL;
+ attributes[l]=p;
+ p+=strcspn(p,XMLWhitespace "=/>");
+ if ((*p != '=') && (isspace((int) ((unsigned char) *p)) == 0))
+ attributes[l]=ConstantString("");
+ else
+ {
+ *p++='\0';
+ p+=strspn(p,XMLWhitespace "=");
+ c=(*p);
+ if ((c == '"') || (c == '\''))
+ {
+ /*
+ Attributes value.
+ */
+ p++;
+ attributes[l+1]=p;
+ while ((*p != '\0') && (*p != c))
+ p++;
+ if (*p != '\0')
+ *p++='\0';
+ else
+ {
+ attributes[l]=ConstantString("");
+ attributes[l+1]=ConstantString("");
+ (void) DestroyXMLTreeAttributes(attributes);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","missing %c",c);
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ j=1;
+ while ((attribute != (char **) NULL) &&
+ (attribute[j] != (char *) NULL) &&
+ (strcmp(attribute[j],attributes[l]) != 0))
+ j+=3;
+ attributes[l+1]=ParseEntities(attributes[l+1],root->entities,
+ (attribute != (char **) NULL) && (attribute[j] !=
+ (char *) NULL) ? *attribute[j+2] : ' ');
+ }
+ attributes[l]=ConstantString(attributes[l]);
+ }
+ while (isspace((int) ((unsigned char) *p)) != 0)
+ p++;
+ }
+ if (*p == '/')
+ {
+ /*
+ Self closing tag.
+ */
+ *p++='\0';
+ if (((*p != '\0') && (*p != '>')) ||
+ ((*p == '\0') && (terminal != '>')))
+ {
+ if (l != 0)
+ (void) DestroyXMLTreeAttributes(attributes);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","missing >");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ ParseOpenTag(root,tag,attributes);
+ (void) ParseCloseTag(root,tag,p,exception);
+ }
+ else
+ {
+ c=(*p);
+ if ((*p == '>') || ((*p == '\0') && (terminal == '>')))
+ {
+ *p='\0';
+ ParseOpenTag(root,tag,attributes);
+ *p=c;
+ }
+ else
+ {
+ if (l != 0)
+ (void) DestroyXMLTreeAttributes(attributes);
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","missing >");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ }
+ }
+ else
+ if (*p == '/')
+ {
+ /*
+ Close tag.
+ */
+ tag=p+1;
+ p+=strcspn(tag,XMLWhitespace ">")+1;
+ c=(*p);
+ if ((c == '\0') && (terminal != '>'))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","missing >");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ *p='\0';
+ if (ParseCloseTag(root,tag,p,exception) != (XMLTreeInfo *) NULL)
+ {
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ *p=c;
+ if (isspace((int) ((unsigned char) *p)) != 0)
+ p+=strspn(p,XMLWhitespace);
+ }
+ else
+ if (strncmp(p,"!--",3) == 0)
+ {
+ /*
+ Comment.
+ */
+ p=strstr(p+3,"--");
+ if ((p == (char *) NULL) || ((*(p+=2) != '>') && (*p != '\0')) ||
+ ((*p == '\0') && (terminal != '>')))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unclosed <!--");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ }
+ else
+ if (strncmp(p,"![CDATA[",8) == 0)
+ {
+ /*
+ Cdata.
+ */
+ p=strstr(p,"]]>");
+ if (p != (char *) NULL)
+ {
+ p+=2;
+ ParseCharacterContent(root,tag+8,(size_t) (p-tag-10),'c');
+ }
+ else
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unclosed <![CDATA[");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ }
+ else
+ if (strncmp(p,"!DOCTYPE",8) == 0)
+ {
+ /*
+ DTD.
+ */
+ for (l=0; (*p != '\0') && (((l == 0) && (*p != '>')) ||
+ ((l != 0) && ((*p != ']') ||
+ (*(p+strspn(p+1,XMLWhitespace)+1) != '>'))));
+ l=(*p == '[') ? 1 : l)
+ p+=strcspn(p+1,"[]>")+1;
+ if ((*p == '\0') && (terminal != '>'))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unclosed <!DOCTYPE");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ if (l != 0)
+ tag=strchr(tag,'[')+1;
+ if (l != 0)
+ {
+ status=ParseInternalDoctype(root,tag,(size_t) (p-tag),
+ exception);
+ if (status == MagickFalse)
+ {
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ p++;
+ }
+ }
+ else
+ if (*p == '?')
+ {
+ /*
+ Processing instructions.
+ */
+ do
+ {
+ p=strchr(p,'?');
+ if (p == (char *) NULL)
+ break;
+ p++;
+ } while ((*p != '\0') && (*p != '>'));
+ if ((p == (char *) NULL) || ((*p == '\0') &&
+ (terminal != '>')))
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unclosed <?");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ ParseProcessingInstructions(root,tag+1,(size_t) (p-tag-2));
+ }
+ else
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),
+ OptionWarning,"ParseError","unexpected <");
+ utf8=DestroyString(utf8);
+ return(&root->root);
+ }
+ if ((p == (char *) NULL) || (*p == '\0'))
+ break;
+ *p++='\0';
+ tag=p;
+ if ((*p != '\0') && (*p != '<'))
+ {
+ /*
+ Tag character content.
+ */
+ while ((*p != '\0') && (*p != '<'))
+ p++;
+ if (*p == '\0')
+ break;
+ ParseCharacterContent(root,tag,(size_t) (p-tag),'&');
+ }
+ else
+ if (*p == '\0')
+ break;
+ }
+ utf8=DestroyString(utf8);
+ if (root->node == (XMLTreeInfo *) NULL)
+ return(&root->root);
+ if (root->node->tag == (char *) NULL)
+ {
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","root tag missing");
+ return(&root->root);
+ }
+ (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
+ "ParseError","unclosed tag: `%s'",root->node->tag);
+ return(&root->root);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% N e w X M L T r e e T a g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% NewXMLTreeTag() returns a new empty xml structure for the xml-tree tag.
+%
+% The format of the NewXMLTreeTag method is:
+%
+% XMLTreeInfo *NewXMLTreeTag(const char *tag)
+%
+% A description of each parameter follows:
+%
+% o tag: the tag.
+%
+*/
+MagickExport XMLTreeInfo *NewXMLTreeTag(const char *tag)
+{
+ static const char
+ *predefined_entities[NumberPredefinedEntities+1] =
+ {
+ "lt;", "<", "gt;", ">", "quot;", """,
+ "apos;", "'", "amp;", "&", (char *) NULL
+ };
+
+ XMLTreeRoot
+ *root;
+
+ root=(XMLTreeRoot *) AcquireMagickMemory(sizeof(*root));
+ if (root == (XMLTreeRoot *) NULL)
+ return((XMLTreeInfo *) NULL);
+ (void) ResetMagickMemory(root,0,sizeof(*root));
+ root->root.tag=(char *) NULL;
+ if (tag != (char *) NULL)
+ root->root.tag=ConstantString(tag);
+ root->node=(&root->root);
+ root->root.content=ConstantString("");
+ root->entities=(char **) AcquireMagickMemory(sizeof(predefined_entities));
+ if (root->entities == (char **) NULL)
+ return((XMLTreeInfo *) NULL);
+ (void) CopyMagickMemory(root->entities,predefined_entities,
+ sizeof(predefined_entities));
+ root->root.attributes=sentinel;
+ root->attributes=(char ***) sentinel;
+ root->processing_instructions=(char ***) sentinel;
+ root->debug=IsEventLogging();
+ root->signature=MagickSignature;
+ return(&root->root);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% P r u n e T a g F r o m X M L T r e e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% PruneTagFromXMLTree() prunes a tag from the xml-tree along with all its
+% subtags.
+%
+% The format of the PruneTagFromXMLTree method is:
+%
+% XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+MagickExport XMLTreeInfo *PruneTagFromXMLTree(XMLTreeInfo *xml_info)
+{
+ XMLTreeInfo
+ *node;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (xml_info->next != (XMLTreeInfo *) NULL)
+ xml_info->next->sibling=xml_info->sibling;
+ if (xml_info->parent != (XMLTreeInfo *) NULL)
+ {
+ node=xml_info->parent->child;
+ if (node == xml_info)
+ xml_info->parent->child=xml_info->ordered;
+ else
+ {
+ while (node->ordered != xml_info)
+ node=node->ordered;
+ node->ordered=node->ordered->ordered;
+ node=xml_info->parent->child;
+ if (strcmp(node->tag,xml_info->tag) != 0)
+ {
+ while (strcmp(node->sibling->tag,xml_info->tag) != 0)
+ node=node->sibling;
+ if (node->sibling != xml_info)
+ node=node->sibling;
+ else
+ node->sibling=(xml_info->next != (XMLTreeInfo *) NULL) ?
+ xml_info->next : node->sibling->sibling;
+ }
+ while ((node->next != (XMLTreeInfo *) NULL) &&
+ (node->next != xml_info))
+ node=node->next;
+ if (node->next != (XMLTreeInfo *) NULL)
+ node->next=node->next->next;
+ }
+ }
+ xml_info->ordered=(XMLTreeInfo *) NULL;
+ xml_info->sibling=(XMLTreeInfo *) NULL;
+ xml_info->next=(XMLTreeInfo *) NULL;
+ return(xml_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t X M L T r e e A t t r i b u t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetXMLTreeAttribute() sets the tag attributes or adds a new attribute if not
+% found. A value of NULL removes the specified attribute.
+%
+% The format of the SetXMLTreeAttribute method is:
+%
+% XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,const char *tag,
+% const char *value)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o tag: The attribute tag.
+%
+% o value: The attribute value.
+%
+*/
+MagickExport XMLTreeInfo *SetXMLTreeAttribute(XMLTreeInfo *xml_info,
+ const char *tag,const char *value)
+{
+ long
+ j;
+
+ register long
+ i;
+
+ size_t
+ length;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ i=0;
+ while ((xml_info->attributes[i] != (char *) NULL) &&
+ (strcmp(xml_info->attributes[i],tag) != 0))
+ i+=2;
+ if (xml_info->attributes[i] == (char *) NULL)
+ {
+ /*
+ Add new attribute tag.
+ */
+ if (value == (const char *) NULL)
+ return(xml_info);
+ if (xml_info->attributes != sentinel)
+ xml_info->attributes=(char **) ResizeQuantumMemory(
+ xml_info->attributes,(size_t) (i+4),sizeof(*xml_info->attributes));
+ else
+ {
+ xml_info->attributes=(char **) AcquireQuantumMemory(4,
+ sizeof(*xml_info->attributes));
+ if (xml_info->attributes != (char **) NULL)
+ xml_info->attributes[1]=ConstantString("");
+ }
+ if (xml_info->attributes == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,
+ "UnableToAcquireString");
+ xml_info->attributes[i]=ConstantString(tag);
+ xml_info->attributes[i+2]=(char *) NULL;
+ length=strlen(xml_info->attributes[i+1]);
+ }
+ /*
+ Add new value to an existing attribute.
+ */
+ for (j=i; xml_info->attributes[j] != (char *) NULL; j+=2) ;
+ if (xml_info->attributes[i+1] != (char *) NULL)
+ xml_info->attributes[i+1]=DestroyString(xml_info->attributes[i+1]);
+ if (value != (const char *) NULL)
+ {
+ xml_info->attributes[i+1]=ConstantString(value);
+ return(xml_info);
+ }
+ if (xml_info->attributes[i] != (char *) NULL)
+ xml_info->attributes[i]=DestroyString(xml_info->attributes[i]);
+ (void) CopyMagickMemory(xml_info->attributes+i,xml_info->attributes+i+2,
+ (size_t) (j-i)*sizeof(*xml_info->attributes));
+ j-=2;
+ xml_info->attributes=(char **) ResizeQuantumMemory(xml_info->attributes,
+ (size_t) (j+2),sizeof(*xml_info->attributes));
+ if (xml_info->attributes == (char **) NULL)
+ ThrowFatalException(ResourceLimitFatalError,"UnableToAcquireString");
+ (void) CopyMagickMemory(xml_info->attributes[j+1]+(i/2),
+ xml_info->attributes[j+1]+(i/2)+1,(size_t) ((j/2)-(i/2))*
+ sizeof(*xml_info->attributes));
+ return(xml_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% S e t X M L T r e e C o n t e n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% SetXMLTreeContent() sets the character content for the given tag and
+% returns the tag.
+%
+% The format of the SetXMLTreeContent method is:
+%
+% XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
+% const char *content)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+% o content: The content.
+%
+*/
+MagickExport XMLTreeInfo *SetXMLTreeContent(XMLTreeInfo *xml_info,
+ const char *content)
+{
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (xml_info->content != (char *) NULL)
+ xml_info->content=DestroyString(xml_info->content);
+ xml_info->content=(char *) ConstantString(content);
+ return(xml_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M L T r e e I n f o T o X M L %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMLTreeInfoToXML() converts an xml-tree to an XML string.
+%
+% The format of the XMLTreeInfoToXML method is:
+%
+% char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
+%
+% A description of each parameter follows:
+%
+% o xml_info: the xml info.
+%
+*/
+
+static char *EncodePredefinedEntities(const char *source,ssize_t offset,
+ char **destination,size_t *length,size_t *extent,MagickBooleanType pedantic)
+{
+ char
+ *canonical_content;
+
+ if (offset < 0)
+ canonical_content=CanonicalXMLContent(source,pedantic);
+ else
+ {
+ char
+ *content;
+
+ content=AcquireString(source);
+ content[offset]='\0';
+ canonical_content=CanonicalXMLContent(content,pedantic);
+ content=DestroyString(content);
+ }
+ if (canonical_content == (char *) NULL)
+ return(*destination);
+ if ((*length+strlen(canonical_content)+MaxTextExtent) > *extent)
+ {
+ *extent=(*length)+strlen(canonical_content)+MaxTextExtent;
+ *destination=(char *) ResizeQuantumMemory(*destination,*extent,
+ sizeof(**destination));
+ if (*destination == (char *) NULL)
+ return(*destination);
+ }
+ *length+=FormatMagickString(*destination+(*length),*extent,"%s",
+ canonical_content);
+ canonical_content=DestroyString(canonical_content);
+ return(*destination);
+}
+
+static char *XMLTreeTagToXML(XMLTreeInfo *xml_info,char **source,size_t *length,
+ size_t *extent,size_t start,char ***attributes)
+{
+ char
+ *content;
+
+ const char
+ *attribute;
+
+ long
+ j;
+
+ register long
+ i;
+
+ size_t
+ offset;
+
+ content=(char *) "";
+ if (xml_info->parent != (XMLTreeInfo *) NULL)
+ content=xml_info->parent->content;
+ offset=0;
+ *source=EncodePredefinedEntities(content+start,(ssize_t) (xml_info->offset-
+ start),source,length,extent,MagickFalse);
+ if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
+ {
+ *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
+ *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(*source));
+ if (*source == (char *) NULL)
+ return(*source);
+ }
+ *length+=FormatMagickString(*source+(*length),*extent,"<%s",xml_info->tag);
+ for (i=0; xml_info->attributes[i]; i+=2)
+ {
+ attribute=GetXMLTreeAttribute(xml_info,xml_info->attributes[i]);
+ if (attribute != xml_info->attributes[i+1])
+ continue;
+ if ((*length+strlen(xml_info->attributes[i])+MaxTextExtent) > *extent)
+ {
+ *extent=(*length)+strlen(xml_info->attributes[i])+MaxTextExtent;
+ *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
+ if (*source == (char *) NULL)
+ return((char *) NULL);
+ }
+ *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
+ xml_info->attributes[i]);
+ (void) EncodePredefinedEntities(xml_info->attributes[i+1],-1,source,length,
+ extent,MagickTrue);
+ *length+=FormatMagickString(*source+(*length),*extent,"\"");
+ }
+ i=0;
+ while ((attributes[i] != (char **) NULL) &&
+ (strcmp(attributes[i][0],xml_info->tag) != 0))
+ i++;
+ j=1;
+ while ((attributes[i] != (char **) NULL) &&
+ (attributes[i][j] != (char *) NULL))
+ {
+ if ((attributes[i][j+1] == (char *) NULL) ||
+ (GetXMLTreeAttribute(xml_info,attributes[i][j]) != attributes[i][j+1]))
+ {
+ j+=3;
+ continue;
+ }
+ if ((*length+strlen(attributes[i][j])+MaxTextExtent) > *extent)
+ {
+ *extent=(*length)+strlen(attributes[i][j])+MaxTextExtent;
+ *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
+ if (*source == (char *) NULL)
+ return((char *) NULL);
+ }
+ *length+=FormatMagickString(*source+(*length),*extent," %s=\"",
+ attributes[i][j]);
+ (void) EncodePredefinedEntities(attributes[i][j+1],-1,source,length,extent,
+ MagickTrue);
+ *length+=FormatMagickString(*source+(*length),*extent,"\"");
+ j+=3;
+ }
+ *length+=FormatMagickString(*source+(*length),*extent,*xml_info->content ?
+ ">" : "/>");
+ if (xml_info->child != (XMLTreeInfo *) NULL)
+ *source=XMLTreeTagToXML(xml_info->child,source,length,extent,0,attributes);
+ else
+ *source=EncodePredefinedEntities(xml_info->content,-1,source,length,extent,
+ MagickFalse);
+ if ((*length+strlen(xml_info->tag)+MaxTextExtent) > *extent)
+ {
+ *extent=(*length)+strlen(xml_info->tag)+MaxTextExtent;
+ *source=(char *) ResizeQuantumMemory(*source,*extent,sizeof(**source));
+ if (*source == (char *) NULL)
+ return((char *) NULL);
+ }
+ if (*xml_info->content != '\0')
+ *length+=FormatMagickString(*source+(*length),*extent,"</%s>",
+ xml_info->tag);
+ while ((content[offset] != '\0') && (offset < xml_info->offset))
+ offset++;
+ if (xml_info->ordered != (XMLTreeInfo *) NULL)
+ content=XMLTreeTagToXML(xml_info->ordered,source,length,extent,offset,
+ attributes);
+ else
+ content=EncodePredefinedEntities(content+offset,-1,source,length,extent,
+ MagickFalse);
+ return(content);
+}
+
+MagickExport char *XMLTreeInfoToXML(XMLTreeInfo *xml_info)
+{
+ char
+ *xml;
+
+ long
+ j,
+ k;
+
+ register char
+ *p,
+ *q;
+
+ register long
+ i;
+
+ size_t
+ extent,
+ length;
+
+ XMLTreeInfo
+ *ordered,
+ *parent;
+
+ XMLTreeRoot
+ *root;
+
+ assert(xml_info != (XMLTreeInfo *) NULL);
+ assert((xml_info->signature == MagickSignature) ||
+ (((XMLTreeRoot *) xml_info)->signature == MagickSignature));
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ if (xml_info->tag == (char *) NULL)
+ return((char *) NULL);
+ xml=AcquireString((char *) NULL);
+ length=0;
+ extent=MaxTextExtent;
+ root=(XMLTreeRoot *) xml_info;
+ while (root->root.parent != (XMLTreeInfo *) NULL)
+ root=(XMLTreeRoot *) root->root.parent;
+ parent=(XMLTreeInfo *) NULL;
+ if (xml_info != (XMLTreeInfo *) NULL)
+ parent=xml_info->parent;
+ if (parent == (XMLTreeInfo *) NULL)
+ for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
+ {
+ /*
+ Pre-root processing instructions.
+ */
+ for (k=2; root->processing_instructions[i][k-1]; k++) ;
+ p=root->processing_instructions[i][1];
+ for (j=1; p != (char *) NULL; j++)
+ {
+ if (root->processing_instructions[i][k][j-1] == '>')
+ {
+ p=root->processing_instructions[i][j];
+ continue;
+ }
+ q=root->processing_instructions[i][0];
+ if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
+ {
+ extent=length+strlen(p)+strlen(q)+MaxTextExtent;
+ xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
+ if (xml == (char *) NULL)
+ return(xml);
+ }
+ length+=FormatMagickString(xml+length,extent,"<?%s%s%s?>\n",q,
+ *p != '\0' ? " " : "",p);
+ p=root->processing_instructions[i][j];
+ }
+ }
+ ordered=(XMLTreeInfo *) NULL;
+ if (xml_info != (XMLTreeInfo *) NULL)
+ ordered=xml_info->ordered;
+ xml_info->parent=(XMLTreeInfo *) NULL;
+ xml_info->ordered=(XMLTreeInfo *) NULL;
+ xml=XMLTreeTagToXML(xml_info,&xml,&length,&extent,0,root->attributes);
+ xml_info->parent=parent;
+ xml_info->ordered=ordered;
+ if (parent == (XMLTreeInfo *) NULL)
+ for (i=0; root->processing_instructions[i] != (char **) NULL; i++)
+ {
+ /*
+ Post-root processing instructions.
+ */
+ for (k=2; root->processing_instructions[i][k-1]; k++) ;
+ p=root->processing_instructions[i][1];
+ for (j=1; p != (char *) NULL; j++)
+ {
+ if (root->processing_instructions[i][k][j-1] == '<')
+ {
+ p=root->processing_instructions[i][j];
+ continue;
+ }
+ q=root->processing_instructions[i][0];
+ if ((length+strlen(p)+strlen(q)+MaxTextExtent) > extent)
+ {
+ extent=length+strlen(p)+strlen(q)+MaxTextExtent;
+ xml=(char *) ResizeQuantumMemory(xml,extent,sizeof(*xml));
+ if (xml == (char *) NULL)
+ return(xml);
+ }
+ length+=FormatMagickString(xml+length,extent,"\n<?%s%s%s?>",q,
+ *p != '\0' ? " " : "",p);
+ p=root->processing_instructions[i][j];
+ }
+ }
+ return((char *) ResizeQuantumMemory(xml,length+1,sizeof(*xml)));
+}
diff --git a/magick/xml-tree.h b/magick/xml-tree.h
new file mode 100644
index 0000000..cf5111a
--- /dev/null
+++ b/magick/xml-tree.h
@@ -0,0 +1,65 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.imagemagick.org/MagicksToolkit/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.
+
+ Magick's toolkit xml-tree methods.
+*/
+#ifndef _MAGICKCORE_XML_TREE_H
+#define _MAGICKCORE_XML_TREE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include <magick/exception.h>
+#include <magick/splay-tree.h>
+
+typedef struct _XMLTreeInfo
+ XMLTreeInfo;
+
+extern MagickExport char
+ *CanonicalXMLContent(const char *,const MagickBooleanType),
+ *XMLTreeInfoToXML(XMLTreeInfo *);
+
+extern MagickExport const char
+ *GetXMLTreeAttribute(XMLTreeInfo *,const char *),
+ *GetXMLTreeContent(XMLTreeInfo *),
+ **GetXMLTreeProcessingInstructions(XMLTreeInfo *,const char *),
+ *GetXMLTreeTag(XMLTreeInfo *);
+
+extern MagickExport MagickBooleanType
+ GetXMLTreeAttributes(const XMLTreeInfo *,SplayTreeInfo *);
+
+extern MagickExport XMLTreeInfo
+ *AddChildToXMLTree(XMLTreeInfo *,const char *,const size_t),
+ *AddPathToXMLTree(XMLTreeInfo *,const char *,const size_t),
+ *DestroyXMLTree(XMLTreeInfo *),
+ *GetNextXMLTreeTag(XMLTreeInfo *),
+ *GetXMLTreeChild(XMLTreeInfo *,const char *),
+ *GetXMLTreeOrdered(XMLTreeInfo *),
+ *GetXMLTreePath(XMLTreeInfo *,const char *),
+ *GetXMLTreeSibling(XMLTreeInfo *),
+ *InsertTagIntoXMLTree(XMLTreeInfo *,XMLTreeInfo *,const size_t),
+ *NewXMLTree(const char *,ExceptionInfo *),
+ *NewXMLTreeTag(const char *),
+ *ParseTagFromXMLTree(XMLTreeInfo *),
+ *PruneTagFromXMLTree(XMLTreeInfo *),
+ *SetXMLTreeAttribute(XMLTreeInfo *,const char *,const char *),
+ *SetXMLTreeContent(XMLTreeInfo *,const char *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/xwdfile.h_vms b/magick/xwdfile.h_vms
new file mode 100644
index 0000000..6f52d7f
--- /dev/null
+++ b/magick/xwdfile.h_vms
@@ -0,0 +1,109 @@
+/* $XConsortium: XWDFile.h,v 1.17 94/04/17 20:10:49 dpw Exp $ */
+/*
+
+Copyright (c) 1985, 1986 X Consortium
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of the X Consortium shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from the X Consortium.
+
+*/
+
+/*
+ * XWDFile.h MIT Project Athena, X Window system window raster
+ * image dumper, dump file format header file.
+ *
+ * Author: Tony Della Fera, DEC
+ * 27-Jun-85
+ *
+ * Modifier: William F. Wyatt, SAO
+ * 18-Nov-86 - version 6 for saving/restoring color maps
+ */
+
+#include <X11/Xmd.h>
+
+#define XWD_FILE_VERSION 7
+#define sz_XWDheader 100
+#define sz_XWDColor 12
+
+typedef CARD32 xwdval; /* for old broken programs */
+
+/* Values in the file are most significant byte first. */
+
+typedef struct _xwd_file_header {
+ /* header_size = SIZEOF(XWDheader) + length of null-terminated
+ * window name. */
+ CARD32 header_size B32;
+
+ CARD32 file_version B32; /* = XWD_FILE_VERSION above */
+ CARD32 pixmap_format B32; /* ZPixmap or XYPixmap */
+ CARD32 pixmap_depth B32; /* Pixmap depth */
+ CARD32 pixmap_width B32; /* Pixmap width */
+ CARD32 pixmap_height B32; /* Pixmap height */
+ CARD32 xoffset B32; /* Bitmap x offset, normally 0 */
+ CARD32 byte_order B32; /* of image data: MSBFirst, LSBFirst */
+
+ /* bitmap_unit applies to bitmaps (depth 1 format XY) only.
+ * It is the number of bits that each scanline is padded to. */
+ CARD32 bitmap_unit B32;
+
+ CARD32 bitmap_bit_order B32; /* bitmaps only: MSBFirst, LSBFirst */
+
+ /* bitmap_pad applies to pixmaps (non-bitmaps) only.
+ * It is the number of bits that each scanline is padded to. */
+ CARD32 bitmap_pad B32;
+
+ CARD32 bits_per_pixel B32; /* Bits per pixel */
+
+ /* bytes_per_line is pixmap_width padded to bitmap_unit (bitmaps)
+ * or bitmap_pad (pixmaps). It is the delta (in bytes) to get
+ * to the same x position on an adjacent row. */
+ CARD32 bytes_per_line B32;
+ CARD32 visual_class B32; /* Class of colormap */
+ CARD32 red_mask B32; /* Z red mask */
+ CARD32 green_mask B32; /* Z green mask */
+ CARD32 blue_mask B32; /* Z blue mask */
+ CARD32 bits_per_rgb B32; /* Log2 of distinct color values */
+ CARD32 colormap_entries B32; /* Number of entries in colormap; not used? */
+ CARD32 ncolors B32; /* Number of XWDColor structures */
+ CARD32 window_width B32; /* Window width */
+ CARD32 window_height B32; /* Window height */
+ CARD32 window_x B32; /* Window upper left X coordinate */
+ CARD32 window_y B32; /* Window upper left Y coordinate */
+ CARD32 window_bdrwidth B32; /* Window border width */
+} XWDFileHeader;
+
+/* Null-terminated window name follows the above structure. */
+
+/* Next comes XWDColor structures, at offset XWDFileHeader.header_size in
+ * the file. XWDFileHeader.ncolors tells how many XWDColor structures
+ * there are.
+ */
+
+typedef struct {
+ CARD32 pixel B32;
+ CARD16 red B16;
+ CARD16 green B16;
+ CARD16 blue B16;
+ CARD8 flags;
+ CARD8 pad;
+} XWDColor;
+
+/* Last comes the image data in the format described by XWDFileHeader. */
diff --git a/magick/xwindow-private.h b/magick/xwindow-private.h
new file mode 100644
index 0000000..c11231e
--- /dev/null
+++ b/magick/xwindow-private.h
@@ -0,0 +1,600 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore X11 window methods.
+*/
+#ifndef _MAGICKCORE_XWINDOW_PRIVATE_H
+#define _MAGICKCORE_XWINDOW_PRIVATE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#if defined(MAGICKCORE_X11_DELEGATE)
+
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "magick/exception.h"
+#include "magick/geometry.h"
+#include "magick/quantize.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+# define klass c_class
+#else
+# define klass class
+#endif
+
+/*
+ Invoke pre-X11R6 ICCCM routines if XlibSpecificationRelease is not 6.
+*/
+#if XlibSpecificationRelease < 6
+#if !defined(PRE_R6_ICCCM)
+#define PRE_R6_ICCCM
+#endif
+#endif
+/*
+ Invoke pre-X11R5 ICCCM routines if XlibSpecificationRelease is not defined.
+*/
+#if !defined(XlibSpecificationRelease)
+#define PRE_R5_ICCCM
+#endif
+/*
+ Invoke pre-X11R4 ICCCM routines if PWinGravity is not defined.
+*/
+#if !defined(PWinGravity)
+#define PRE_R4_ICCCM
+#endif
+
+#define MaxIconSize 96
+#define MaxNumberPens 11
+#define MaxNumberFonts 11
+#define MaxXWindows 12
+#undef index
+
+#define ThrowXWindowException(severity,tag,context) \
+{ \
+ ExceptionInfo \
+ exception; \
+ \
+ GetExceptionInfo(&exception); \
+ (void) ThrowMagickException(&exception,GetMagickModule(),severity, \
+ tag == (const char *) NULL ? "unknown" : tag,"`%s': %s",context, \
+ strerror(errno)); \
+ CatchException(&exception); \
+ (void) DestroyExceptionInfo(&exception); \
+}
+#define ThrowXWindowFatalException(severity,tag,context) \
+{ \
+ ThrowXWindowException(severity,tag,context); \
+ _exit(1); \
+}
+
+typedef enum
+{
+ ForegroundStencil,
+ BackgroundStencil,
+ OpaqueStencil,
+ TransparentStencil
+} AnnotationStencil;
+
+typedef enum
+{
+ UndefinedElement,
+ PointElement,
+ LineElement,
+ RectangleElement,
+ FillRectangleElement,
+ CircleElement,
+ FillCircleElement,
+ EllipseElement,
+ FillEllipseElement,
+ PolygonElement,
+ FillPolygonElement,
+ ColorElement,
+ MatteElement,
+ TextElement,
+ ImageElement
+} ElementType;
+
+typedef enum
+{
+ UndefinedColormap,
+ PrivateColormap,
+ SharedColormap
+} XColormapType;
+
+typedef struct _XDrawInfo
+{
+ int
+ x,
+ y;
+
+ unsigned int
+ width,
+ height;
+
+ double
+ degrees;
+
+ AnnotationStencil
+ stencil;
+
+ ElementType
+ element;
+
+ Pixmap
+ stipple;
+
+ unsigned int
+ line_width;
+
+ XSegment
+ line_info;
+
+ unsigned int
+ number_coordinates;
+
+ RectangleInfo
+ rectangle_info;
+
+ XPoint
+ *coordinate_info;
+
+ char
+ geometry[MaxTextExtent];
+} XDrawInfo;
+
+typedef enum
+{
+ DefaultState = 0x0000,
+ EscapeState = 0x0001,
+ ExitState = 0x0002,
+ FormerImageState = 0x0004,
+ ModifierState = 0x0008,
+ MontageImageState = 0x0010,
+ NextImageState = 0x0020,
+ RetainColorsState = 0x0040,
+ SuspendTime = 50,
+ UpdateConfigurationState = 0x0080,
+ UpdateRegionState = 0x0100
+} XState;
+
+typedef struct _XAnnotateInfo
+{
+ int
+ x,
+ y;
+
+ unsigned int
+ width,
+ height;
+
+ double
+ degrees;
+
+ XFontStruct
+ *font_info;
+
+ char
+ *text;
+
+ AnnotationStencil
+ stencil;
+
+ char
+ geometry[MaxTextExtent];
+
+ struct _XAnnotateInfo
+ *next,
+ *previous;
+} XAnnotateInfo;
+
+typedef struct _XPixelInfo
+{
+ unsigned long
+ colors,
+ *pixels;
+
+ XColor
+ foreground_color,
+ background_color,
+ border_color,
+ matte_color,
+ highlight_color,
+ shadow_color,
+ depth_color,
+ trough_color,
+ box_color,
+ pen_color,
+ pen_colors[MaxNumberPens];
+
+ GC
+ annotate_context,
+ highlight_context,
+ widget_context;
+
+ unsigned short
+ box_index,
+ pen_index;
+} XPixelInfo;
+
+typedef struct _XResourceInfo
+{
+ XrmDatabase
+ resource_database;
+
+ ImageInfo
+ *image_info;
+
+ QuantizeInfo
+ *quantize_info;
+
+ unsigned long
+ colors;
+
+ MagickBooleanType
+ close_server,
+ backdrop;
+
+ char
+ *background_color,
+ *border_color;
+
+ char
+ *client_name;
+
+ XColormapType
+ colormap;
+
+ unsigned int
+ border_width;
+
+ unsigned long
+ delay;
+
+ MagickBooleanType
+ color_recovery,
+ confirm_exit,
+ confirm_edit;
+
+ char
+ *display_gamma;
+
+ char
+ *font,
+ *font_name[MaxNumberFonts],
+ *foreground_color;
+
+ MagickBooleanType
+ display_warnings,
+ gamma_correct;
+
+ char
+ *icon_geometry;
+
+ MagickBooleanType
+ iconic,
+ immutable;
+
+ char
+ *image_geometry;
+
+ char
+ *map_type,
+ *matte_color,
+ *name;
+
+ unsigned int
+ magnify,
+ pause;
+
+ char
+ *pen_colors[MaxNumberPens];
+
+ char
+ *text_font,
+ *title;
+
+ int
+ quantum;
+
+ unsigned int
+ update;
+
+ MagickBooleanType
+ use_pixmap,
+ use_shared_memory;
+
+ unsigned long
+ undo_cache;
+
+ char
+ *visual_type,
+ *window_group,
+ *window_id,
+ *write_filename;
+
+ Image
+ *copy_image;
+
+ int
+ gravity;
+
+ char
+ home_directory[MaxTextExtent];
+} XResourceInfo;
+
+typedef struct _XWindowInfo
+{
+ Window
+ id;
+
+ Window
+ root;
+
+ Visual
+ *visual;
+
+ unsigned int
+ storage_class,
+ depth;
+
+ XVisualInfo
+ *visual_info;
+
+ XStandardColormap
+ *map_info;
+
+ XPixelInfo
+ *pixel_info;
+
+ XFontStruct
+ *font_info;
+
+ GC
+ annotate_context,
+ highlight_context,
+ widget_context;
+
+ Cursor
+ cursor,
+ busy_cursor;
+
+ char
+ *name,
+ *geometry,
+ *icon_name,
+ *icon_geometry,
+ *crop_geometry;
+
+ unsigned long
+ data,
+ flags;
+
+ int
+ x,
+ y;
+
+ unsigned int
+ width,
+ height,
+ min_width,
+ min_height,
+ width_inc,
+ height_inc,
+ border_width;
+
+ MagickBooleanType
+ use_pixmap,
+ immutable,
+ shape,
+ shared_memory;
+
+ int
+ screen;
+
+ XImage
+ *ximage,
+ *matte_image;
+
+ Pixmap
+ highlight_stipple,
+ shadow_stipple,
+ pixmap,
+ *pixmaps,
+ matte_pixmap,
+ *matte_pixmaps;
+
+ XSetWindowAttributes
+ attributes;
+
+ XWindowChanges
+ window_changes;
+
+ void
+ *segment_info;
+
+ unsigned long
+ mask;
+
+ MagickBooleanType
+ orphan,
+ mapped,
+ stasis;
+
+ Image
+ *image;
+
+ MagickBooleanType
+ destroy;
+} XWindowInfo;
+
+typedef struct _XWindows
+{
+ Display
+ *display;
+
+ XStandardColormap
+ *map_info,
+ *icon_map;
+
+ XVisualInfo
+ *visual_info,
+ *icon_visual;
+
+ XPixelInfo
+ *pixel_info,
+ *icon_pixel;
+
+ XFontStruct
+ *font_info;
+
+ XResourceInfo
+ *icon_resources;
+
+ XClassHint
+ *class_hints;
+
+ XWMHints
+ *manager_hints;
+
+ XWindowInfo
+ context,
+ group_leader,
+ backdrop,
+ icon,
+ image,
+ info,
+ magnify,
+ pan,
+ command,
+ widget,
+ popup;
+
+ Atom
+ wm_protocols,
+ wm_delete_window,
+ wm_take_focus,
+ im_protocols,
+ im_remote_command,
+ im_update_widget,
+ im_update_colormap,
+ im_former_image,
+ im_retain_colors,
+ im_next_image,
+ im_exit,
+ dnd_protocols;
+} XWindows;
+
+extern MagickExport char
+ *XGetResourceClass(XrmDatabase,const char *,const char *,char *),
+ *XGetResourceInstance(XrmDatabase,const char *,const char *,const char *),
+ *XGetScreenDensity(Display *);
+
+extern MagickExport Cursor
+ XMakeCursor(Display *,Window,Colormap,char *,char *);
+
+extern MagickExport int
+ XCheckDefineCursor(Display *,Window,Cursor),
+ XError(Display *,XErrorEvent *);
+
+extern MagickExport MagickBooleanType
+ XAnnotateImage(Display *,const XPixelInfo *,XAnnotateInfo *,Image *),
+ XDrawImage(Display *,const XPixelInfo *,XDrawInfo *,Image *),
+ XGetWindowColor(Display *,XWindows *,char *),
+ XMagickProgressMonitor(const char *,const MagickOffsetType,
+ const MagickSizeType,void *),
+ XMakeImage(Display *,const XResourceInfo *,XWindowInfo *,Image *,unsigned int,
+ unsigned int),
+ XQueryColorDatabase(const char *,XColor *),
+ XRemoteCommand(Display *,const char *,const char *);
+
+extern MagickExport void
+ DestroyXResources(void),
+ XBestIconSize(Display *,XWindowInfo *,Image *),
+ XBestPixel(Display *,const Colormap,XColor *,unsigned int,XColor *),
+ XCheckRefreshWindows(Display *,XWindows *),
+ XClientMessage(Display *,const Window,const Atom,const Atom,const Time),
+ XConfigureImageColormap(Display *,XResourceInfo *,XWindows *,Image *),
+ XConstrainWindowPosition(Display *,XWindowInfo *),
+ XDelay(Display *,const unsigned long),
+ XDestroyResourceInfo(XResourceInfo *),
+ XDestroyWindowColors(Display *,Window),
+ XDisplayImageInfo(Display *,const XResourceInfo *,XWindows *,Image *,Image *),
+ XFreeResources(Display *,XVisualInfo *,XStandardColormap *,XPixelInfo *,
+ XFontStruct *,XResourceInfo *,XWindowInfo *),
+ XFreeStandardColormap(Display *,const XVisualInfo *,XStandardColormap *,
+ XPixelInfo *),
+ XHighlightEllipse(Display *,Window,GC,const RectangleInfo *),
+ XHighlightLine(Display *,Window,GC,const XSegment *),
+ XHighlightRectangle(Display *,Window,GC,const RectangleInfo *),
+ XGetAnnotateInfo(XAnnotateInfo *),
+ XGetPixelPacket(Display *,const XVisualInfo *,const XStandardColormap *,
+ const XResourceInfo *,Image *,XPixelInfo *),
+ XGetMapInfo(const XVisualInfo *,const Colormap,XStandardColormap *),
+ XGetResourceInfo(const ImageInfo *,XrmDatabase,const char *,XResourceInfo *),
+ XGetWindowInfo(Display *,XVisualInfo *,XStandardColormap *,XPixelInfo *,
+ XFontStruct *,XResourceInfo *,XWindowInfo *),
+ XMakeMagnifyImage(Display *,XWindows *),
+ XMakeStandardColormap(Display *,XVisualInfo *,XResourceInfo *,Image *,
+ XStandardColormap *,XPixelInfo *),
+ XMakeWindow(Display *,Window,char **,int,XClassHint *,XWMHints *,
+ XWindowInfo *),
+ XQueryPosition(Display *,const Window,int *,int *),
+ XRefreshWindow(Display *,const XWindowInfo *,const XEvent *),
+ XRetainWindowColors(Display *,const Window),
+ XSetCursorState(Display *,XWindows *,const MagickStatusType),
+ XUserPreferences(XResourceInfo *),
+ XWarning(const ExceptionType,const char *,const char *);
+
+extern MagickExport Window
+ XWindowByID(Display *,const Window,const unsigned long),
+ XWindowByName(Display *,const Window,const char *),
+ XWindowByProperty(Display *,const Window,const Atom);
+
+extern MagickExport XFontStruct
+ *XBestFont(Display *,const XResourceInfo *,const MagickBooleanType);
+
+extern MagickExport XrmDatabase
+ XGetResourceDatabase(Display *,const char *);
+
+extern MagickExport XVisualInfo
+ *XBestVisualInfo(Display *,XStandardColormap *,XResourceInfo *);
+
+extern MagickExport XWindows
+ *XInitializeWindows(Display *,XResourceInfo *),
+ *XSetWindows(XWindows *);
+
+static inline MagickRealType XPixelIntensity(const XColor *pixel)
+{
+ MagickRealType
+ intensity;
+
+ intensity=0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue;
+ return(intensity);
+}
+
+#endif
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif
diff --git a/magick/xwindow.c b/magick/xwindow.c
new file mode 100644
index 0000000..b566217
--- /dev/null
+++ b/magick/xwindow.c
@@ -0,0 +1,9637 @@
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X X W W IIIII N N DDDD OOO W W %
+% X X W W I NN N D D O O W W %
+% X W W I N N N D D O O W W %
+% X X W W W I N NN D D O O W W W %
+% X X W W IIIII N N DDDD OOO W W %
+% %
+% %
+% MagickCore X11 Utility Methods %
+% %
+% Software Design %
+% John Cristy %
+% July 1992 %
+% %
+% %
+% Copyright 1999-2009 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 %
+% %
+% http://www.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. %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%
+*/
+
+/*
+ Include declarations.
+*/
+#include "magick/studio.h"
+#include "magick/animate.h"
+#include "magick/artifact.h"
+#include "magick/blob.h"
+#include "magick/cache.h"
+#include "magick/client.h"
+#include "magick/color.h"
+#include "magick/color-private.h"
+#include "magick/composite.h"
+#include "magick/display.h"
+#include "magick/exception.h"
+#include "magick/exception-private.h"
+#include "magick/geometry.h"
+#include "magick/identify.h"
+#include "magick/image.h"
+#include "magick/image-private.h"
+#include "magick/list.h"
+#include "magick/locale_.h"
+#include "magick/log.h"
+#include "magick/magick.h"
+#include "magick/memory_.h"
+#include "magick/monitor.h"
+#include "magick/option.h"
+#include "magick/PreRvIcccm.h"
+#include "magick/quantize.h"
+#include "magick/quantum.h"
+#include "magick/quantum-private.h"
+#include "magick/resource_.h"
+#include "magick/resize.h"
+#include "magick/shear.h"
+#include "magick/statistic.h"
+#include "magick/string_.h"
+#include "magick/transform.h"
+#include "magick/utility.h"
+#include "magick/widget.h"
+#include "magick/xwindow.h"
+#include "magick/xwindow-private.h"
+#include "magick/version.h"
+#if defined(__BEOS__)
+#include <OS.h>
+#endif
+#if defined(MAGICKCORE_X11_DELEGATE)
+#include <X11/Xproto.h>
+#include <X11/Xlocale.h>
+#if defined(MAGICK_HAVE_POLL)
+# include <sys/poll.h>
+#endif
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+#if defined(MAGICKCORE_HAVE_MACHINE_PARAM_H)
+# include <machine/param.h>
+#endif
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#endif
+#if defined(MAGICKCORE_HAVE_SHAPE)
+#include <X11/extensions/shape.h>
+#endif
+
+/*
+ X defines.
+*/
+#define XBlueGamma(color) RoundToQuantum(blue_gamma == 1.0 ? (double) \
+ (color) : ((pow(((double) QuantumScale*(color)),1.0/(double) blue_gamma)* \
+ QuantumRange)))
+#define XGammaPixel(map,color) (unsigned long) (map->base_pixel+ \
+ ((ScaleQuantumToShort(XRedGamma((color)->red))*map->red_max/65535L)* \
+ map->red_mult)+ \
+ ((ScaleQuantumToShort(XGreenGamma((color)->green))*map->green_max/65535L)* \
+ map->green_mult)+ \
+ ((ScaleQuantumToShort(XBlueGamma((color)->blue))*map->blue_max/65535L)* \
+ map->blue_mult))
+#define XGreenGamma(color) RoundToQuantum(green_gamma == 1.0 ? (double) \
+ (color) : ((pow(((double) QuantumScale*(color)),1.0/(double) green_gamma)* \
+ QuantumRange)))
+#define XRedGamma(color) RoundToQuantum(red_gamma == 1.0 ? (double) \
+ (color) : ((pow(((double) QuantumScale*(color)),1.0/(double) red_gamma)* \
+ QuantumRange)))
+#define XStandardPixel(map,color) (unsigned long) (map->base_pixel+ \
+ (((color)->red*map->red_max/65535L)*map->red_mult)+ \
+ (((color)->green*map->green_max/65535L)*map->green_mult)+ \
+ (((color)->blue*map->blue_max/65535L)*map->blue_mult))
+
+#define AccentuateModulate ScaleCharToQuantum(80)
+#define HighlightModulate ScaleCharToQuantum(125)
+#define ShadowModulate ScaleCharToQuantum(135)
+#define DepthModulate ScaleCharToQuantum(185)
+#define TroughModulate ScaleCharToQuantum(110)
+
+#define XLIB_ILLEGAL_ACCESS 1
+#undef ForgetGravity
+#undef NorthWestGravity
+#undef NorthGravity
+#undef NorthEastGravity
+#undef WestGravity
+#undef CenterGravity
+#undef EastGravity
+#undef SouthWestGravity
+#undef SouthGravity
+#undef SouthEastGravity
+#undef StaticGravity
+
+#undef index
+#if defined(hpux9)
+#define XFD_SET int
+#else
+#define XFD_SET fd_set
+#endif
+
+/*
+ Enumeration declarations.
+*/
+typedef enum
+{
+#undef DoRed
+ DoRed = 0x0001,
+#undef DoGreen
+ DoGreen = 0x0002,
+#undef DoBlue
+ DoBlue = 0x0004,
+ DoMatte = 0x0008
+} XColorFlags;
+
+/*
+ Typedef declarations.
+*/
+typedef struct _DiversityPacket
+{
+ Quantum
+ red,
+ green,
+ blue;
+
+ unsigned short
+ index;
+
+ unsigned long
+ count;
+} DiversityPacket;
+
+/*
+ Constant declaractions.
+*/
+static MagickBooleanType
+ xerror_alert = MagickFalse;
+
+/*
+ Method prototypes.
+*/
+static const char
+ *XVisualClassName(const int);
+
+static MagickRealType
+ blue_gamma = 1.0,
+ green_gamma = 1.0,
+ red_gamma = 1.0;
+
+static MagickBooleanType
+ XMakePixmap(Display *,const XResourceInfo *,XWindowInfo *);
+
+static void
+ XMakeImageLSBFirst(const XResourceInfo *,const XWindowInfo *,Image *,
+ XImage *,XImage *),
+ XMakeImageMSBFirst(const XResourceInfo *,const XWindowInfo *,Image *,
+ XImage *,XImage *);
+
+static Window
+ XSelectWindow(Display *,RectangleInfo *);
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% D e s t r o y X R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% DestroyXResources() destroys any X resources.
+%
+% The format of the DestroyXResources method is:
+%
+% void DestroyXResources()
+%
+% A description of each parameter follows:
+%
+*/
+MagickExport void DestroyXResources(void)
+{
+ register int
+ i;
+
+ unsigned int
+ number_windows;
+
+ XWindowInfo
+ *magick_windows[MaxXWindows];
+
+ XWindows
+ *windows;
+
+ DestroyXWidget();
+ windows=XSetWindows((XWindows *) ~0);
+ if ((windows == (XWindows *) NULL) || (windows->display == (Display *) NULL))
+ return;
+ number_windows=0;
+ magick_windows[number_windows++]=(&windows->context);
+ magick_windows[number_windows++]=(&windows->group_leader);
+ magick_windows[number_windows++]=(&windows->backdrop);
+ magick_windows[number_windows++]=(&windows->icon);
+ magick_windows[number_windows++]=(&windows->image);
+ magick_windows[number_windows++]=(&windows->info);
+ magick_windows[number_windows++]=(&windows->magnify);
+ magick_windows[number_windows++]=(&windows->pan);
+ magick_windows[number_windows++]=(&windows->command);
+ magick_windows[number_windows++]=(&windows->widget);
+ magick_windows[number_windows++]=(&windows->popup);
+ magick_windows[number_windows++]=(&windows->context);
+ for (i=0; i < (int) number_windows; i++)
+ {
+ if (magick_windows[i]->mapped != MagickFalse)
+ {
+ (void) XWithdrawWindow(windows->display,magick_windows[i]->id,
+ magick_windows[i]->screen);
+ magick_windows[i]->mapped=MagickFalse;
+ }
+ if (magick_windows[i]->name != (char *) NULL)
+ magick_windows[i]->name=(char *)
+ RelinquishMagickMemory(magick_windows[i]->name);
+ if (magick_windows[i]->icon_name != (char *) NULL)
+ magick_windows[i]->icon_name=(char *)
+ RelinquishMagickMemory(magick_windows[i]->icon_name);
+ if (magick_windows[i]->cursor != (Cursor) NULL)
+ {
+ (void) XFreeCursor(windows->display,magick_windows[i]->cursor);
+ magick_windows[i]->cursor=(Cursor) NULL;
+ }
+ if (magick_windows[i]->busy_cursor != (Cursor) NULL)
+ {
+ (void) XFreeCursor(windows->display,magick_windows[i]->busy_cursor);
+ magick_windows[i]->busy_cursor=(Cursor) NULL;
+ }
+ if (magick_windows[i]->highlight_stipple != (Pixmap) NULL)
+ {
+ (void) XFreePixmap(windows->display,
+ magick_windows[i]->highlight_stipple);
+ magick_windows[i]->highlight_stipple=(Pixmap) NULL;
+ }
+ if (magick_windows[i]->shadow_stipple != (Pixmap) NULL)
+ {
+ (void) XFreePixmap(windows->display,magick_windows[i]->shadow_stipple);
+ magick_windows[i]->shadow_stipple=(Pixmap) NULL;
+ }
+ if (magick_windows[i]->ximage != (XImage *) NULL)
+ {
+ XDestroyImage(magick_windows[i]->ximage);
+ magick_windows[i]->ximage=(XImage *) NULL;
+ }
+ if (magick_windows[i]->pixmap != (Pixmap) NULL)
+ {
+ (void) XFreePixmap(windows->display,magick_windows[i]->pixmap);
+ magick_windows[i]->pixmap=(Pixmap) NULL;
+ }
+ if (magick_windows[i]->id != (Window) NULL)
+ {
+ (void) XDestroyWindow(windows->display,magick_windows[i]->id);
+ magick_windows[i]->id=(Window) NULL;
+ }
+ if (magick_windows[i]->destroy != MagickFalse)
+ {
+ if (magick_windows[i]->image != (Image *) NULL)
+ {
+ magick_windows[i]->image=DestroyImage(magick_windows[i]->image);
+ magick_windows[i]->image=NewImageList();
+ }
+ if (magick_windows[i]->matte_pixmap != (Pixmap) NULL)
+ {
+ (void) XFreePixmap(windows->display,
+ magick_windows[i]->matte_pixmap);
+ magick_windows[i]->matte_pixmap=(Pixmap) NULL;
+ }
+ }
+ if (magick_windows[i]->segment_info != (void *) NULL)
+ {
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ XShmSegmentInfo
+ *segment_info;
+
+ segment_info=(XShmSegmentInfo *) magick_windows[i]->segment_info;
+ if (segment_info != (XShmSegmentInfo *) NULL)
+ if (segment_info[0].shmid >= 0)
+ {
+ if (segment_info[0].shmaddr != NULL)
+ (void) shmdt(segment_info[0].shmaddr);
+ (void) shmctl(segment_info[0].shmid,IPC_RMID,0);
+ segment_info[0].shmaddr=NULL;
+ segment_info[0].shmid=(-1);
+ }
+#endif
+ magick_windows[i]->segment_info=(void *)
+ RelinquishMagickMemory(magick_windows[i]->segment_info);
+ }
+ }
+ windows->icon_resources=(XResourceInfo *)
+ RelinquishMagickMemory(windows->icon_resources);
+ if (windows->icon_pixel != (XPixelInfo *) NULL)
+ {
+ if (windows->icon_pixel->pixels != (unsigned long *) NULL)
+ windows->icon_pixel->pixels=(unsigned long *)
+ RelinquishMagickMemory(windows->icon_pixel->pixels);
+ if (windows->icon_pixel->annotate_context != (GC) NULL)
+ XFreeGC(windows->display,windows->icon_pixel->annotate_context);
+ windows->icon_pixel=(XPixelInfo *)
+ RelinquishMagickMemory(windows->icon_pixel);
+ }
+ if (windows->pixel_info != (XPixelInfo *) NULL)
+ {
+ if (windows->pixel_info->pixels != (unsigned long *) NULL)
+ windows->pixel_info->pixels=(unsigned long *)
+ RelinquishMagickMemory(windows->pixel_info->pixels);
+ if (windows->pixel_info->annotate_context != (GC) NULL)
+ XFreeGC(windows->display,windows->pixel_info->annotate_context);
+ if (windows->pixel_info->widget_context != (GC) NULL)
+ XFreeGC(windows->display,windows->pixel_info->widget_context);
+ if (windows->pixel_info->highlight_context != (GC) NULL)
+ XFreeGC(windows->display,windows->pixel_info->highlight_context);
+ windows->pixel_info=(XPixelInfo *)
+ RelinquishMagickMemory(windows->pixel_info);
+ }
+ if (windows->font_info != (XFontStruct *) NULL)
+ {
+ XFreeFont(windows->display,windows->font_info);
+ windows->font_info=(XFontStruct *) NULL;
+ }
+ if (windows->class_hints != (XClassHint *) NULL)
+ {
+ if (windows->class_hints->res_name != (char *) NULL)
+ XFree(windows->class_hints->res_name);
+ if (windows->class_hints->res_class != (char *) NULL)
+ XFree(windows->class_hints->res_class);
+ XFree(windows->class_hints);
+ windows->class_hints=(XClassHint *) NULL;
+ }
+ if (windows->manager_hints != (XWMHints *) NULL)
+ {
+ XFree(windows->manager_hints);
+ windows->manager_hints=(XWMHints *) NULL;
+ }
+ if (windows->map_info != (XStandardColormap *) NULL)
+ {
+ XFree(windows->map_info);
+ windows->map_info=(XStandardColormap *) NULL;
+ }
+ if (windows->icon_map != (XStandardColormap *) NULL)
+ {
+ XFree(windows->icon_map);
+ windows->icon_map=(XStandardColormap *) NULL;
+ }
+ if (windows->visual_info != (XVisualInfo *) NULL)
+ {
+ XFree(windows->visual_info);
+ windows->visual_info=(XVisualInfo *) NULL;
+ }
+ if (windows->icon_visual != (XVisualInfo *) NULL)
+ {
+ XFree(windows->icon_visual);
+ windows->icon_visual=(XVisualInfo *) NULL;
+ }
+ (void) XSetWindows((XWindows *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X A n n o t a t e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XAnnotateImage() annotates the image with text.
+%
+% The format of the XAnnotateImage method is:
+%
+% MagickBooleanType XAnnotateImage(Display *display,
+% const XPixelInfo *pixel,XAnnotateInfo *annotate_info,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+% o annotate_info: Specifies a pointer to a XAnnotateInfo structure.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType XAnnotateImage(Display *display,
+ const XPixelInfo *pixel,XAnnotateInfo *annotate_info,Image *image)
+{
+ GC
+ annotate_context;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *annotate_image;
+
+ int
+ x,
+ y;
+
+ MagickBooleanType
+ matte;
+
+ Pixmap
+ annotate_pixmap;
+
+ unsigned int
+ depth,
+ height,
+ width;
+
+ Window
+ root_window;
+
+ XGCValues
+ context_values;
+
+ XImage
+ *annotate_ximage;
+
+ /*
+ Initialize annotated image.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(pixel != (XPixelInfo *) NULL);
+ assert(annotate_info != (XAnnotateInfo *) NULL);
+ assert(image != (Image *) NULL);
+ /*
+ Initialize annotated pixmap.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ depth=(unsigned int) XDefaultDepth(display,XDefaultScreen(display));
+ annotate_pixmap=XCreatePixmap(display,root_window,annotate_info->width,
+ annotate_info->height,depth);
+ if (annotate_pixmap == (Pixmap) NULL)
+ return(MagickFalse);
+ /*
+ Initialize graphics info.
+ */
+ context_values.background=0;
+ context_values.foreground=(unsigned long) (~0);
+ context_values.font=annotate_info->font_info->fid;
+ annotate_context=XCreateGC(display,root_window,(unsigned long)
+ GCBackground | GCFont | GCForeground,&context_values);
+ if (annotate_context == (GC) NULL)
+ return(MagickFalse);
+ /*
+ Draw text to pixmap.
+ */
+ (void) XDrawImageString(display,annotate_pixmap,annotate_context,0,
+ (int) annotate_info->font_info->ascent,annotate_info->text,
+ (int) strlen(annotate_info->text));
+ (void) XFreeGC(display,annotate_context);
+ /*
+ Initialize annotated X image.
+ */
+ annotate_ximage=XGetImage(display,annotate_pixmap,0,0,annotate_info->width,
+ annotate_info->height,AllPlanes,ZPixmap);
+ if (annotate_ximage == (XImage *) NULL)
+ return(MagickFalse);
+ (void) XFreePixmap(display,annotate_pixmap);
+ /*
+ Initialize annotated image.
+ */
+ annotate_image=AcquireImage((ImageInfo *) NULL);
+ if (annotate_image == (Image *) NULL)
+ return(MagickFalse);
+ annotate_image->columns=annotate_info->width;
+ annotate_image->rows=annotate_info->height;
+ /*
+ Transfer annotated X image to image.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ (void) XParseGeometry(annotate_info->geometry,&x,&y,&width,&height);
+ (void) GetOneVirtualPixel(image,x,y,&annotate_image->background_color,
+ &image->exception);
+ if (annotate_info->stencil == ForegroundStencil)
+ annotate_image->matte=MagickTrue;
+ exception=(&image->exception);
+ for (y=0; y < (int) annotate_image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(annotate_image,0,y,annotate_image->columns,1,
+ exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) annotate_image->columns; x++)
+ {
+ q->opacity=OpaqueOpacity;
+ if (XGetPixel(annotate_ximage,x,y) == 0)
+ {
+ /*
+ Set this pixel to the background color.
+ */
+ q->red=ScaleShortToQuantum(pixel->box_color.red);
+ q->green=ScaleShortToQuantum(pixel->box_color.green);
+ q->blue=ScaleShortToQuantum(pixel->box_color.blue);
+ if ((annotate_info->stencil == ForegroundStencil) ||
+ (annotate_info->stencil == OpaqueStencil))
+ q->opacity=(Quantum) TransparentOpacity;
+ }
+ else
+ {
+ /*
+ Set this pixel to the pen color.
+ */
+ q->red=ScaleShortToQuantum(pixel->pen_color.red);
+ q->green=ScaleShortToQuantum(pixel->pen_color.green);
+ q->blue=ScaleShortToQuantum(pixel->pen_color.blue);
+ if (annotate_info->stencil == BackgroundStencil)
+ q->opacity=(Quantum) TransparentOpacity;
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(annotate_image,exception) == MagickFalse)
+ break;
+ }
+ XDestroyImage(annotate_ximage);
+ /*
+ Determine annotate geometry.
+ */
+ (void) XParseGeometry(annotate_info->geometry,&x,&y,&width,&height);
+ if ((width != (unsigned int) annotate_image->columns) ||
+ (height != (unsigned int) annotate_image->rows))
+ {
+ char
+ image_geometry[MaxTextExtent];
+
+ /*
+ Scale image.
+ */
+ (void) FormatMagickString(image_geometry,MaxTextExtent,"%ux%u",
+ width,height);
+ (void) TransformImage(&annotate_image,(char *) NULL,image_geometry);
+ }
+ if (annotate_info->degrees != 0.0)
+ {
+ Image
+ *rotate_image;
+
+ int
+ rotations;
+
+ MagickRealType
+ normalized_degrees;
+
+ /*
+ Rotate image.
+ */
+ rotate_image=
+ RotateImage(annotate_image,annotate_info->degrees,&image->exception);
+ if (rotate_image == (Image *) NULL)
+ return(MagickFalse);
+ annotate_image=DestroyImage(annotate_image);
+ annotate_image=rotate_image;
+ /*
+ Annotation is relative to the degree of rotation.
+ */
+ normalized_degrees=annotate_info->degrees;
+ while (normalized_degrees < -45.0)
+ normalized_degrees+=360.0;
+ for (rotations=0; normalized_degrees > 45.0; rotations++)
+ normalized_degrees-=90.0;
+ switch (rotations % 4)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ {
+ /*
+ Rotate 90 degrees.
+ */
+ x-=(int) annotate_image->columns/2;
+ y+=(int) annotate_image->columns/2;
+ break;
+ }
+ case 2:
+ {
+ /*
+ Rotate 180 degrees.
+ */
+ x=x-(int) annotate_image->columns;
+ break;
+ }
+ case 3:
+ {
+ /*
+ Rotate 270 degrees.
+ */
+ x=x-(int) annotate_image->columns/2;
+ y=y-(int) (annotate_image->rows-(annotate_image->columns/2));
+ break;
+ }
+ }
+ }
+ /*
+ Composite text onto the image.
+ */
+ (void) XParseGeometry(annotate_info->geometry,&x,&y,&width,&height);
+ matte=image->matte;
+ (void) CompositeImage(image,annotate_image->matte != MagickFalse ?
+ OverCompositeOp : CopyCompositeOp,annotate_image,x,y);
+ image->matte=matte;
+ annotate_image=DestroyImage(annotate_image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X B e s t F o n t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XBestFont() returns the "best" font. "Best" is defined as a font specified
+% in the X resource database or a font such that the text width displayed
+% with the font does not exceed the specified maximum width.
+%
+% The format of the XBestFont method is:
+%
+% XFontStruct *XBestFont(Display *display,
+% const XResourceInfo *resource_info,const MagickBooleanType text_font)
+%
+% A description of each parameter follows:
+%
+% o font: XBestFont returns a pointer to a XFontStruct structure.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o text_font: True is font should be mono-spaced (typewriter style).
+%
+%
+*/
+
+static char **FontToList(char *font)
+{
+ char
+ **fontlist;
+
+ register char
+ *p,
+ *q;
+
+ register int
+ i;
+
+ unsigned int
+ fonts;
+
+ if (font == (char *) NULL)
+ return((char **) NULL);
+ /*
+ Convert string to an ASCII list.
+ */
+ fonts=1U;
+ for (p=font; *p != '\0'; p++)
+ if ((*p == ':') || (*p == ';') || (*p == ','))
+ fonts++;
+ fontlist=(char **) AcquireQuantumMemory((size_t) fonts+1UL,sizeof(*fontlist));
+ if (fontlist == (char **) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ font);
+ return((char **) NULL);
+ }
+ p=font;
+ for (i=0; i < (int) fonts; i++)
+ {
+ for (q=p; *q != '\0'; q++)
+ if ((*q == ':') || (*q == ';') || (*q == ','))
+ break;
+ fontlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+1UL,
+ sizeof(*fontlist[i]));
+ if (fontlist[i] == (char *) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
+ font);
+ return((char **) NULL);
+ }
+ (void) CopyMagickString(fontlist[i],p,(size_t) (q-p+1));
+ p=q+1;
+ }
+ fontlist[i]=(char *) NULL;
+ return(fontlist);
+}
+
+MagickExport XFontStruct *XBestFont(Display *display,
+ const XResourceInfo *resource_info,const MagickBooleanType text_font)
+{
+ static const char
+ *Fonts[]=
+ {
+ "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-1",
+ "-*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-1",
+ "-*-helvetica-medium-r-normal--12-*-*-*-*-*-iso8859-15",
+ "-*-arial-medium-r-normal--12-*-*-*-*-*-iso8859-15",
+ "-*-helvetica-medium-r-normal--12-*-*-*-*-*-*-*",
+ "-*-arial-medium-r-normal--12-*-*-*-*-*-*-*",
+ "variable",
+ "fixed",
+ (char *) NULL
+ },
+ *TextFonts[]=
+ {
+ "-*-courier-medium-r-normal-*-12-*-*-*-*-*-iso8859-1",
+ "-*-courier-medium-r-normal-*-12-*-*-*-*-*-iso8859-15",
+ "-*-fixed-medium-r-normal-*-12-*-*-*-*-*-*-*",
+ "fixed",
+ (char *) NULL
+ };
+
+ char
+ *font_name;
+
+ register const char
+ **p;
+
+ XFontStruct
+ *font_info;
+
+ font_info=(XFontStruct *) NULL;
+ font_name=resource_info->font;
+ if (text_font != MagickFalse)
+ font_name=resource_info->text_font;
+ if ((font_name != (char *) NULL) && (*font_name != '\0'))
+ {
+ char
+ **fontlist;
+
+ register int
+ i;
+
+ /*
+ Load preferred font specified in the X resource database.
+ */
+ fontlist=FontToList(font_name);
+ if (fontlist != (char **) NULL)
+ {
+ for (i=0; fontlist[i] != (char *) NULL; i++)
+ {
+ if (font_info == (XFontStruct *) NULL)
+ font_info=XLoadQueryFont(display,fontlist[i]);
+ fontlist[i]=DestroyString(fontlist[i]);
+ }
+ fontlist=(char **) RelinquishMagickMemory(fontlist);
+ }
+ if (font_info == (XFontStruct *) NULL)
+ ThrowXWindowFatalException(XServerError,"UnableToLoadFont",font_name);
+ }
+ /*
+ Load fonts from list of fonts until one is found.
+ */
+ p=Fonts;
+ if (text_font != MagickFalse)
+ p=TextFonts;
+ if (XDisplayHeight(display,XDefaultScreen(display)) >= 748)
+ p++;
+ while (*p != (char *) NULL)
+ {
+ if (font_info != (XFontStruct *) NULL)
+ break;
+ font_info=XLoadQueryFont(display,(char *) *p);
+ p++;
+ }
+ return(font_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X B e s t I c o n S i z e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XBestIconSize() returns the "best" icon size. "Best" is defined as an icon
+% size that maintains the aspect ratio of the image. If the window manager
+% has preferred icon sizes, one of the preferred sizes is used.
+%
+% The format of the XBestIconSize method is:
+%
+% void XBestIconSize(Display *display,XWindowInfo *window,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o image: the image.
+%
+*/
+MagickExport void XBestIconSize(Display *display,XWindowInfo *window,
+ Image *image)
+{
+ int
+ i,
+ number_sizes;
+
+ MagickRealType
+ scale_factor;
+
+ unsigned int
+ height,
+ icon_height,
+ icon_width,
+ width;
+
+ Window
+ root_window;
+
+ XIconSize
+ *icon_size,
+ *size_list;
+
+ /*
+ Determine if the window manager has specified preferred icon sizes.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ assert(image != (Image *) NULL);
+ window->width=MaxIconSize;
+ window->height=MaxIconSize;
+ icon_size=(XIconSize *) NULL;
+ number_sizes=0;
+ root_window=XRootWindow(display,window->screen);
+ if (XGetIconSizes(display,root_window,&size_list,&number_sizes) != 0)
+ if ((number_sizes > 0) && (size_list != (XIconSize *) NULL))
+ icon_size=size_list;
+ if (icon_size == (XIconSize *) NULL)
+ {
+ /*
+ Window manager does not restrict icon size.
+ */
+ icon_size=XAllocIconSize();
+ if (icon_size == (XIconSize *) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed",image->filename);
+ return;
+ }
+ icon_size->min_width=1;
+ icon_size->max_width=MaxIconSize;
+ icon_size->min_height=1;
+ icon_size->max_height=MaxIconSize;
+ icon_size->width_inc=1;
+ icon_size->height_inc=1;
+ }
+ /*
+ Determine aspect ratio of image.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ i=0;
+ if (window->crop_geometry)
+ (void) XParseGeometry(window->crop_geometry,&i,&i,&width,&height);
+ /*
+ Look for an icon size that maintains the aspect ratio of image.
+ */
+ scale_factor=(MagickRealType) icon_size->max_width/width;
+ if (scale_factor > ((MagickRealType) icon_size->max_height/height))
+ scale_factor=(MagickRealType) icon_size->max_height/height;
+ icon_width=(unsigned int) icon_size->min_width;
+ while ((int) icon_width < icon_size->max_width)
+ {
+ if (icon_width >= (unsigned int) (scale_factor*width+0.5))
+ break;
+ icon_width+=icon_size->width_inc;
+ }
+ icon_height=(unsigned int) icon_size->min_height;
+ while ((int) icon_height < icon_size->max_height)
+ {
+ if (icon_height >= (unsigned int) (scale_factor*height+0.5))
+ break;
+ icon_height+=icon_size->height_inc;
+ }
+ (void) XFree((void *) icon_size);
+ window->width=icon_width;
+ window->height=icon_height;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X B e s t P i x e l %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XBestPixel() returns a pixel from an array of pixels that is closest to the
+% requested color. If the color array is NULL, the colors are obtained from
+% the X server.
+%
+% The format of the XBestPixel method is:
+%
+% void XBestPixel(Display *display,const Colormap colormap,XColor *colors,
+% unsigned int number_colors,XColor *color)
+%
+% A description of each parameter follows:
+%
+% o pixel: XBestPixel returns the pixel value closest to the requested
+% color.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o colormap: Specifies the ID of the X server colormap.
+%
+% o colors: Specifies an array of XColor structures.
+%
+% o number_colors: Specifies the number of XColor structures in the
+% color definition array.
+%
+% o color: Specifies the desired RGB value to find in the colors array.
+%
+*/
+MagickExport void XBestPixel(Display *display,const Colormap colormap,
+ XColor *colors,unsigned int number_colors,XColor *color)
+{
+ MagickBooleanType
+ query_server;
+
+ MagickPixelPacket
+ pixel;
+
+ MagickRealType
+ min_distance;
+
+ register MagickRealType
+ distance;
+
+ register int
+ i,
+ j;
+
+ Status
+ status;
+
+ /*
+ Find closest representation for the requested RGB color.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(color != (XColor *) NULL);
+ status=XAllocColor(display,colormap,color);
+ if (status != False)
+ return;
+ query_server=colors == (XColor *) NULL ? MagickTrue : MagickFalse;
+ if (query_server != MagickFalse)
+ {
+ /*
+ Read X server colormap.
+ */
+ colors=(XColor *) AcquireQuantumMemory(number_colors,sizeof(*colors));
+ if (colors == (XColor *) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed","...");
+ return;
+ }
+ for (i=0; i < (int) number_colors; i++)
+ colors[i].pixel=(unsigned long) i;
+ if (number_colors > 256)
+ number_colors=256;
+ (void) XQueryColors(display,colormap,colors,(int) number_colors);
+ }
+ min_distance=3.0*((MagickRealType) QuantumRange+1.0)*((MagickRealType)
+ QuantumRange+1.0);
+ j=0;
+ for (i=0; i < (int) number_colors; i++)
+ {
+ pixel.red=colors[i].red-(MagickRealType) color->red;
+ distance=pixel.red*pixel.red;
+ if (distance > min_distance)
+ continue;
+ pixel.green=colors[i].green-(MagickRealType) color->green;
+ distance+=pixel.green*pixel.green;
+ if (distance > min_distance)
+ continue;
+ pixel.blue=colors[i].blue-(MagickRealType) color->blue;
+ distance+=pixel.blue*pixel.blue;
+ if (distance > min_distance)
+ continue;
+ min_distance=distance;
+ color->pixel=colors[i].pixel;
+ j=i;
+ }
+ (void) XAllocColor(display,colormap,&colors[j]);
+ if (query_server != MagickFalse)
+ colors=(XColor *) RelinquishMagickMemory(colors);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X B e s t V i s u a l I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XBestVisualInfo() returns visual information for a visual that is the "best"
+% the server supports. "Best" is defined as:
+%
+% 1. Restrict the visual list to those supported by the default screen.
+%
+% 2. If a visual type is specified, restrict the visual list to those of
+% that type.
+%
+% 3. If a map type is specified, choose the visual that matches the id
+% specified by the Standard Colormap.
+%
+% 4 From the list of visuals, choose one that can display the most
+% simultaneous colors. If more than one visual can display the same
+% number of simultaneous colors, one is chosen based on a rank.
+%
+% The format of the XBestVisualInfo method is:
+%
+% XVisualInfo *XBestVisualInfo(Display *display,
+% XStandardColormap *map_info,XResourceInfo *resource_info)
+%
+% A description of each parameter follows:
+%
+% o visual_info: XBestVisualInfo returns a pointer to a X11 XVisualInfo
+% structure.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o map_info: If map_type is specified, this structure is initialized
+% with info from the Standard Colormap.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+
+static inline int MagickMax(const int x,const int y)
+{
+ if (x > y)
+ return(x);
+ return(y);
+}
+
+static inline unsigned long MagickMin(const unsigned int x,
+ const unsigned int y)
+{
+ if (x < y)
+ return(x);
+ return(y);
+}
+
+MagickExport XVisualInfo *XBestVisualInfo(Display *display,
+ XStandardColormap *map_info,XResourceInfo *resource_info)
+{
+#define MaxStandardColormaps 7
+#define XVisualColormapSize(visual_info) MagickMin((unsigned int) (\
+ (visual_info->klass == TrueColor) || (visual_info->klass == DirectColor) ? \
+ visual_info->red_mask | visual_info->green_mask | visual_info->blue_mask : \
+ (unsigned int) visual_info->colormap_size),1U << visual_info->depth)
+
+ char
+ *map_type,
+ *visual_type;
+
+ long
+ visual_mask;
+
+ register int
+ i;
+
+ static int
+ number_visuals;
+
+ static XVisualInfo
+ visual_template;
+
+ XVisualInfo
+ *visual_info,
+ *visual_list;
+
+ /*
+ Restrict visual search by screen number.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ map_type=resource_info->map_type;
+ visual_type=resource_info->visual_type;
+ visual_mask=VisualScreenMask;
+ visual_template.screen=XDefaultScreen(display);
+ visual_template.depth=XDefaultDepth(display,XDefaultScreen(display));
+ if ((resource_info->immutable != MagickFalse) && (resource_info->colors != 0))
+ if (resource_info->colors <= (1UL << (unsigned long) visual_template.depth))
+ visual_mask|=VisualDepthMask;
+ if (visual_type != (char *) NULL)
+ {
+ /*
+ Restrict visual search by class or visual id.
+ */
+ if (LocaleCompare("staticgray",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=StaticGray;
+ }
+ else
+ if (LocaleCompare("grayscale",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=GrayScale;
+ }
+ else
+ if (LocaleCompare("staticcolor",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=StaticColor;
+ }
+ else
+ if (LocaleCompare("pseudocolor",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=PseudoColor;
+ }
+ else
+ if (LocaleCompare("truecolor",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=TrueColor;
+ }
+ else
+ if (LocaleCompare("directcolor",visual_type) == 0)
+ {
+ visual_mask|=VisualClassMask;
+ visual_template.klass=DirectColor;
+ }
+ else
+ if (LocaleCompare("default",visual_type) == 0)
+ {
+ visual_mask|=VisualIDMask;
+ visual_template.visualid=XVisualIDFromVisual(
+ XDefaultVisual(display,XDefaultScreen(display)));
+ }
+ else
+ if (isdigit((int) ((unsigned char) *visual_type)) != 0)
+ {
+ visual_mask|=VisualIDMask;
+ visual_template.visualid=
+ strtol(visual_type,(char **) NULL,0);
+ }
+ else
+ ThrowXWindowFatalException(XServerError,
+ "UnrecognizedVisualSpecifier",visual_type);
+ }
+ /*
+ Get all visuals that meet our criteria so far.
+ */
+ number_visuals=0;
+ visual_list=XGetVisualInfo(display,visual_mask,&visual_template,
+ &number_visuals);
+ visual_mask=VisualScreenMask | VisualIDMask;
+ if ((number_visuals == 0) || (visual_list == (XVisualInfo *) NULL))
+ {
+ /*
+ Failed to get visual; try using the default visual.
+ */
+ ThrowXWindowFatalException(XServerWarning,"UnableToGetVisual",
+ visual_type);
+ visual_template.visualid=XVisualIDFromVisual(XDefaultVisual(display,
+ XDefaultScreen(display)));
+ visual_list=XGetVisualInfo(display,visual_mask,&visual_template,
+ &number_visuals);
+ if ((number_visuals == 0) || (visual_list == (XVisualInfo *) NULL))
+ return((XVisualInfo *) NULL);
+ ThrowXWindowFatalException(XServerWarning,"UsingDefaultVisual",
+ XVisualClassName(visual_list->klass));
+ }
+ resource_info->color_recovery=MagickFalse;
+ if ((map_info != (XStandardColormap *) NULL) && (map_type != (char *) NULL))
+ {
+ Atom
+ map_property;
+
+ char
+ map_name[MaxTextExtent];
+
+ int
+ j,
+ number_maps;
+
+ Status
+ status;
+
+ Window
+ root_window;
+
+ XStandardColormap
+ *map_list;
+
+ /*
+ Choose a visual associated with a standard colormap.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ status=False;
+ if (LocaleCompare(map_type,"list") != 0)
+ {
+ /*
+ User specified Standard Colormap.
+ */
+ (void) FormatMagickString((char *) map_name,MaxTextExtent,
+ "RGB_%s_MAP",map_type);
+ LocaleUpper(map_name);
+ map_property=XInternAtom(display,(char *) map_name,MagickTrue);
+ if (map_property != (Atom) NULL)
+ status=XGetRGBColormaps(display,root_window,&map_list,&number_maps,
+ map_property);
+ }
+ else
+ {
+ static const char
+ *colormap[MaxStandardColormaps]=
+ {
+ "_HP_RGB_SMOOTH_MAP_LIST",
+ "RGB_BEST_MAP",
+ "RGB_DEFAULT_MAP",
+ "RGB_GRAY_MAP",
+ "RGB_RED_MAP",
+ "RGB_GREEN_MAP",
+ "RGB_BLUE_MAP",
+ };
+
+ /*
+ Choose a standard colormap from a list.
+ */
+ for (i=0; i < MaxStandardColormaps; i++)
+ {
+ map_property=XInternAtom(display,(char *) colormap[i],MagickTrue);
+ if (map_property == (Atom) NULL)
+ continue;
+ status=XGetRGBColormaps(display,root_window,&map_list,&number_maps,
+ map_property);
+ if (status != False)
+ break;
+ }
+ resource_info->color_recovery=i == 0 ? MagickTrue : MagickFalse;
+ }
+ if (status == False)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToGetStandardColormap",
+ map_type);
+ return((XVisualInfo *) NULL);
+ }
+ /*
+ Search all Standard Colormaps and visuals for ids that match.
+ */
+ *map_info=map_list[0];
+#if !defined(PRE_R4_ICCCM)
+ visual_template.visualid=XVisualIDFromVisual(visual_list[0].visual);
+ for (i=0; i < number_maps; i++)
+ for (j=0; j < number_visuals; j++)
+ if (map_list[i].visualid ==
+ XVisualIDFromVisual(visual_list[j].visual))
+ {
+ *map_info=map_list[i];
+ visual_template.visualid=XVisualIDFromVisual(
+ visual_list[j].visual);
+ break;
+ }
+ if (map_info->visualid != visual_template.visualid)
+ {
+ ThrowXWindowFatalException(XServerError,
+ "UnableToMatchVisualToStandardColormap",map_type);
+ return((XVisualInfo *) NULL);
+ }
+#endif
+ if (map_info->colormap == (Colormap) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,
+ "StandardColormapIsNotInitialized",map_type);
+ return((XVisualInfo *) NULL);
+ }
+ (void) XFree((void *) map_list);
+ }
+ else
+ {
+ static const unsigned int
+ rank[]=
+ {
+ StaticGray,
+ GrayScale,
+ StaticColor,
+ DirectColor,
+ TrueColor,
+ PseudoColor
+ };
+
+ XVisualInfo
+ *p;
+
+ /*
+ Pick one visual that displays the most simultaneous colors.
+ */
+ visual_info=visual_list;
+ p=visual_list;
+ for (i=1; i < number_visuals; i++)
+ {
+ p++;
+ if (XVisualColormapSize(p) > XVisualColormapSize(visual_info))
+ visual_info=p;
+ else
+ if (XVisualColormapSize(p) == XVisualColormapSize(visual_info))
+ if (rank[p->klass] > rank[visual_info->klass])
+ visual_info=p;
+ }
+ visual_template.visualid=XVisualIDFromVisual(visual_info->visual);
+ }
+ (void) XFree((void *) visual_list);
+ /*
+ Retrieve only one visual by its screen & id number.
+ */
+ visual_info=XGetVisualInfo(display,visual_mask,&visual_template,
+ &number_visuals);
+ if ((number_visuals == 0) || (visual_info == (XVisualInfo *) NULL))
+ return((XVisualInfo *) NULL);
+ return(visual_info);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C h e c k D e f i n e C u r s o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XCheckDefineCursor() prevents cursor changes on the root window.
+%
+% The format of the XXCheckDefineCursor method is:
+%
+% XCheckDefineCursor(display,window,cursor)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: the window.
+%
+% o cursor: the cursor.
+%
+*/
+MagickExport int XCheckDefineCursor(Display *display,Window window,
+ Cursor cursor)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ if (window == XRootWindow(display,XDefaultScreen(display)))
+ return(0);
+ return(XDefineCursor(display,window,cursor));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C h e c k R e f r e s h W i n d o w s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XCheckRefreshWindows() checks the X server for exposure events for a
+% particular window and updates the areassociated with the exposure event.
+%
+% The format of the XCheckRefreshWindows method is:
+%
+% void XCheckRefreshWindows(Display *display,XWindows *windows)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+*/
+MagickExport void XCheckRefreshWindows(Display *display,XWindows *windows)
+{
+ Window
+ id;
+
+ XEvent
+ event;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ XDelay(display,SuspendTime);
+ id=windows->command.id;
+ while (XCheckTypedWindowEvent(display,id,Expose,&event) != MagickFalse)
+ (void) XCommandWidget(display,windows,(char const **) NULL,&event);
+ id=windows->image.id;
+ while (XCheckTypedWindowEvent(display,id,Expose,&event) != MagickFalse)
+ XRefreshWindow(display,&windows->image,&event);
+ XDelay(display,SuspendTime << 1);
+ id=windows->command.id;
+ while (XCheckTypedWindowEvent(display,id,Expose,&event) != MagickFalse)
+ (void) XCommandWidget(display,windows,(char const **) NULL,&event);
+ id=windows->image.id;
+ while (XCheckTypedWindowEvent(display,id,Expose,&event) != MagickFalse)
+ XRefreshWindow(display,&windows->image,&event);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C l i e n t M e s s a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XClientMessage() sends a reason to a window with XSendEvent. The reason is
+% initialized with a particular protocol type and atom.
+%
+% The format of the XClientMessage function is:
+%
+% XClientMessage(display,window,protocol,reason,timestamp)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window structure.
+%
+% o protocol: Specifies an atom value.
+%
+% o reason: Specifies an atom value which is the reason to send.
+%
+% o timestamp: Specifies a value of type Time.
+%
+*/
+MagickExport void XClientMessage(Display *display,const Window window,
+ const Atom protocol,const Atom reason,const Time timestamp)
+{
+ XClientMessageEvent
+ client_event;
+
+ assert(display != (Display *) NULL);
+ client_event.type=ClientMessage;
+ client_event.window=window;
+ client_event.message_type=protocol;
+ client_event.format=32;
+ client_event.data.l[0]=(long) reason;
+ client_event.data.l[1]=(long) timestamp;
+ (void) XSendEvent(display,window,MagickFalse,NoEventMask,(XEvent *) &client_event);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X C l i e n t W i n d o w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XClientWindow() finds a window, at or below the specified window, which has
+% a WM_STATE property. If such a window is found, it is returned, otherwise
+% the argument window is returned.
+%
+% The format of the XClientWindow function is:
+%
+% client_window=XClientWindow(display,target_window)
+%
+% A description of each parameter follows:
+%
+% o client_window: XClientWindow returns a window, at or below the specified
+% window, which has a WM_STATE property otherwise the argument
+% target_window is returned.
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o target_window: Specifies the window to find a WM_STATE property.
+%
+%
+*/
+static Window XClientWindow(Display *display,Window target_window)
+{
+ Atom
+ state,
+ type;
+
+ int
+ format;
+
+ Status
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ number_items;
+
+ Window
+ client_window;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ state=XInternAtom(display,"WM_STATE",MagickTrue);
+ if (state == (Atom) NULL)
+ return(target_window);
+ type=(Atom) NULL;
+ status=XGetWindowProperty(display,target_window,state,0L,0L,MagickFalse,
+ (Atom) AnyPropertyType,&type,&format,&number_items,&after,&data);
+ if ((status == Success) && (type != (Atom) NULL))
+ return(target_window);
+ client_window=XWindowByProperty(display,target_window,state);
+ if (client_window == (Window) NULL)
+ return(target_window);
+ return(client_window);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C o n f i g u r e I m a g e C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XConfigureImageColormap() creates a new X colormap.
+%
+% The format of the XConfigureImageColormap method is:
+%
+% void XConfigureImageColormap(Display *display,
+% XResourceInfo *resource_info,XWindows *windows,Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o image: the image.
+%
+*/
+MagickExport void XConfigureImageColormap(Display *display,
+ XResourceInfo *resource_info,XWindows *windows,Image *image)
+{
+ Colormap
+ colormap;
+
+ /*
+ Make standard colormap.
+ */
+ XSetCursorState(display,windows,MagickTrue);
+ XCheckRefreshWindows(display,windows);
+ XMakeStandardColormap(display,windows->visual_info,resource_info,image,
+ windows->map_info,windows->pixel_info);
+ colormap=windows->map_info->colormap;
+ (void) XSetWindowColormap(display,windows->image.id,colormap);
+ (void) XSetWindowColormap(display,windows->command.id,colormap);
+ (void) XSetWindowColormap(display,windows->widget.id,colormap);
+ if (windows->magnify.mapped != MagickFalse)
+ (void) XSetWindowColormap(display,windows->magnify.id,colormap);
+ if (windows->pan.mapped != MagickFalse)
+ (void) XSetWindowColormap(display,windows->pan.id,colormap);
+ XSetCursorState(display,windows,MagickFalse);
+ XClientMessage(display,windows->image.id,windows->im_protocols,
+ windows->im_update_colormap,CurrentTime);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X C o n s t r a i n W i n d o w P o s i t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XConstrainWindowPosition() assures a window is positioned within the X
+% server boundaries.
+%
+% The format of the XConstrainWindowPosition method is:
+%
+% void XConstrainWindowPosition(Display *display,XWindowInfo *window_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o window_info: Specifies a pointer to a XWindowInfo structure.
+%
+*/
+MagickExport void XConstrainWindowPosition(Display *display,
+ XWindowInfo *window_info)
+{
+ int
+ limit;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window_info != (XWindowInfo *) NULL);
+ limit=XDisplayWidth(display,window_info->screen)-window_info->width;
+ if (window_info->x < 0)
+ window_info->x=0;
+ else
+ if (window_info->x > (int) limit)
+ window_info->x=(int) limit;
+ limit=XDisplayHeight(display,window_info->screen)-window_info->height;
+ if (window_info->y < 0)
+ window_info->y=0;
+ else
+ if (window_info->y > limit)
+ window_info->y=limit;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D e l a y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDelay() suspends program execution for the number of milliseconds
+% specified.
+%
+% The format of the Delay method is:
+%
+% void XDelay(Display *display,const unsigned long milliseconds)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o milliseconds: Specifies the number of milliseconds to delay before
+% returning.
+%
+*/
+MagickExport void XDelay(Display *display,const unsigned long milliseconds)
+{
+ assert(display != (Display *) NULL);
+ (void) XFlush(display);
+ if (milliseconds == 0)
+ return;
+#if defined(__WINDOWS__)
+ Sleep(milliseconds);
+#elif defined(vms)
+ {
+ float
+ timer;
+
+ timer=milliseconds/1000.0;
+ lib$wait(&timer);
+ }
+#elif defined(MAGICKCORE_HAVE_USLEEP)
+ usleep(1000*milliseconds);
+#elif defined(MAGICKCORE_HAVE_SELECT)
+ {
+ struct timeval
+ timer;
+
+ timer.tv_sec=(long) milliseconds/1000;
+ timer.tv_usec=(long) (milliseconds % 1000)*1000;
+ (void) select(0,(XFD_SET *) NULL,(XFD_SET *) NULL,(XFD_SET *) NULL,&timer);
+ }
+#elif defined(MAGICKCORE_HAVE_POLL)
+ (void) poll((struct pollfd *) NULL,0,(int) milliseconds);
+#elif defined(__BEOS__)
+ snooze(1000*milliseconds);
+#else
+# error "Time delay method not defined."
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D e s t r o y R e s o u r c e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDestroyResourceInfo() frees memory associated with the XResourceInfo
+% structure.
+%
+% The format of the XDestroyResourceInfo method is:
+%
+% void XDestroyResourceInfo(XResourceInfo *resource_info)
+%
+% A description of each parameter follows:
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+MagickExport void XDestroyResourceInfo(XResourceInfo *resource_info)
+{
+ if (resource_info->image_geometry != (char *) NULL)
+ resource_info->image_geometry=(char *)
+ RelinquishMagickMemory(resource_info->image_geometry);
+ if (resource_info->quantize_info != (QuantizeInfo *) NULL)
+ resource_info->quantize_info=DestroyQuantizeInfo(
+ resource_info->quantize_info);
+ if (resource_info->client_name != (char *) NULL)
+ resource_info->client_name=(char *)
+ RelinquishMagickMemory(resource_info->client_name);
+ if (resource_info->name != (char *) NULL)
+ resource_info->name=DestroyString(resource_info->name);
+ (void) ResetMagickMemory(resource_info,0,sizeof(*resource_info));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D e s t r o y W i n d o w C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDestroyWindowColors() frees X11 color resources previously saved on a
+% window by XRetainWindowColors or programs like xsetroot.
+%
+% The format of the XDestroyWindowColors method is:
+%
+% void XDestroyWindowColors(Display *display,Window window)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window structure.
+%
+*/
+MagickExport void XDestroyWindowColors(Display *display,Window window)
+{
+ Atom
+ property,
+ type;
+
+ int
+ format;
+
+ Status
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned long
+ after,
+ length;
+
+ /*
+ If there are previous resources on the root window, destroy them.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ property=XInternAtom(display,"_XSETROOT_ID",MagickFalse);
+ if (property == (Atom) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToCreateProperty",
+ "_XSETROOT_ID");
+ return;
+ }
+ status=XGetWindowProperty(display,window,property,0L,1L,MagickTrue,
+ (Atom) AnyPropertyType,&type,&format,&length,&after,&data);
+ if (status != Success)
+ return;
+ if ((type == XA_PIXMAP) && (format == 32) && (length == 1) && (after == 0))
+ {
+ (void) XKillClient(display,(XID) (*((Pixmap *) data)));
+ (void) XDeleteProperty(display,window,property);
+ }
+ if (type != None)
+ (void) XFree((void *) data);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D i s p l a y I m a g e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDisplayImageInfo() displays information about an X image.
+%
+% The format of the XDisplayImageInfo method is:
+%
+% void XDisplayImageInfo(Display *display,
+% const XResourceInfo *resource_info,XWindows *windows,Image *undo_image,
+% Image *image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o undo_image: the undo image.
+%
+% o image: the image.
+%
+*/
+MagickExport void XDisplayImageInfo(Display *display,
+ const XResourceInfo *resource_info,XWindows *windows,Image *undo_image,
+ Image *image)
+{
+ char
+ filename[MaxTextExtent],
+ *text,
+ **textlist;
+
+ FILE
+ *file;
+
+ int
+ unique_file;
+
+ long
+ bytes;
+
+ register long
+ i;
+
+ unsigned int
+ levels;
+
+ unsigned long
+ number_pixels;
+
+ /*
+ Write info about the X server to a file.
+ */
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(windows != (XWindows *) NULL);
+ assert(image != (Image *) NULL);
+ if (image->debug)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ file=(FILE *) NULL;
+ unique_file=AcquireUniqueFileResource(filename);
+ if (unique_file != -1)
+ file=fdopen(unique_file,"w");
+ if ((unique_file == -1) || (file == (FILE *) NULL))
+ {
+ XNoticeWidget(display,windows,"Unable to display image info",filename);
+ return;
+ }
+ if (resource_info->gamma_correct != MagickFalse)
+ if (resource_info->display_gamma != (char *) NULL)
+ (void) fprintf(file,"Display\n gamma: %s\n\n",
+ resource_info->display_gamma);
+ /*
+ Write info about the X image to a file.
+ */
+ (void) fprintf(file,"X\n visual: %s\n",
+ XVisualClassName((int) windows->image.storage_class));
+ (void) fprintf(file," depth: %d\n",windows->image.ximage->depth);
+ if (windows->visual_info->colormap_size != 0)
+ (void) fprintf(file," colormap size: %d\n",
+ windows->visual_info->colormap_size);
+ if (resource_info->colormap== SharedColormap)
+ (void) fprintf(file," colormap type: Shared\n");
+ else
+ (void) fprintf(file," colormap type: Private\n");
+ (void) fprintf(file," geometry: %dx%d\n",windows->image.ximage->width,
+ windows->image.ximage->height);
+ if (windows->image.crop_geometry != (char *) NULL)
+ (void) fprintf(file," crop geometry: %s\n",windows->image.crop_geometry);
+ if (windows->image.pixmap == (Pixmap) NULL)
+ (void) fprintf(file," type: X Image\n");
+ else
+ (void) fprintf(file," type: Pixmap\n");
+ if (windows->image.shape != MagickFalse)
+ (void) fprintf(file," non-rectangular shape: True\n");
+ else
+ (void) fprintf(file," non-rectangular shape: False\n");
+ if (windows->image.shared_memory != MagickFalse)
+ (void) fprintf(file," shared memory: True\n");
+ else
+ (void) fprintf(file," shared memory: False\n");
+ (void) fprintf(file,"\n");
+ if (resource_info->font != (char *) NULL)
+ (void) fprintf(file,"Font: %s\n\n",resource_info->font);
+ if (resource_info->text_font != (char *) NULL)
+ (void) fprintf(file,"Text font: %s\n\n",resource_info->text_font);
+ /*
+ Write info about the undo cache to a file.
+ */
+ bytes=0;
+ for (levels=0; undo_image != (Image *) NULL; levels++)
+ {
+ number_pixels=undo_image->list->columns*undo_image->list->rows;
+ bytes+=number_pixels*sizeof(PixelPacket);
+ undo_image=GetPreviousImageInList(undo_image);
+ }
+ (void) fprintf(file,"Undo Edit Cache\n levels: %u\n",levels);
+ (void) fprintf(file," bytes: %lumb\n",(unsigned long)
+ (bytes+(1 << 19)) >> 20);
+ (void) fprintf(file," limit: %lumb\n\n",resource_info->undo_cache);
+ /*
+ Write info about the image to a file.
+ */
+ (void) IdentifyImage(image,file,MagickTrue);
+ (void) fclose(file);
+ text=FileToString(filename,~0,&image->exception);
+ (void) RelinquishUniqueFileResource(filename);
+ if (text == (char *) NULL)
+ {
+ XNoticeWidget(display,windows,"MemoryAllocationFailed",
+ "UnableToDisplayImageInfo");
+ return;
+ }
+ textlist=StringToList(text);
+ if (textlist != (char **) NULL)
+ {
+ char
+ title[MaxTextExtent];
+
+ /*
+ Display information about the image in the Text View widget.
+ */
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ (void) FormatMagickString(title,MaxTextExtent,"Image Info: %s",
+ image->filename);
+ XTextViewWidget(display,resource_info,windows,MagickTrue,title,
+ (char const **) textlist);
+ for (i=0; textlist[i] != (char *) NULL; i++)
+ textlist[i]=DestroyString(textlist[i]);
+ textlist=(char **) RelinquishMagickMemory(textlist);
+ }
+ text=DestroyString(text);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X D i t h e r I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDitherImage() dithers the reference image as required by the HP Color
+% Recovery algorithm. The color values are quantized to 3 bits of red and
+% green, and 2 bits of blue (3/3/2) and can be used as indices into a 8-bit X
+% standard colormap.
+%
+% The format of the XDitherImage method is:
+%
+% void XDitherImage(Image *image,XImage *ximage)
+%
+% A description of each parameter follows:
+%
+% o image: the image.
+%
+% o ximage: Specifies a pointer to a XImage structure; returned from
+% XCreateImage.
+%
+%
+*/
+static void XDitherImage(Image *image,XImage *ximage)
+{
+ static const short int
+ dither_red[2][16]=
+ {
+ {-16, 4, -1, 11,-14, 6, -3, 9,-15, 5, -2, 10,-13, 7, -4, 8},
+ { 15, -5, 0,-12, 13, -7, 2,-10, 14, -6, 1,-11, 12, -8, 3, -9}
+ },
+ dither_green[2][16]=
+ {
+ { 11,-15, 7, -3, 8,-14, 4, -2, 10,-16, 6, -4, 9,-13, 5, -1},
+ {-12, 14, -8, 2, -9, 13, -5, 1,-11, 15, -7, 3,-10, 12, -6, 0}
+ },
+ dither_blue[2][16]=
+ {
+ { -3, 9,-13, 7, -1, 11,-15, 5, -4, 8,-14, 6, -2, 10,-16, 4},
+ { 2,-10, 12, -8, 0,-12, 14, -6, 3, -9, 13, -7, 1,-11, 15, -5}
+ };
+
+ PixelPacket
+ color;
+
+ int
+ y;
+
+ long
+ value;
+
+ register char
+ *q;
+
+ register const PixelPacket
+ *p;
+
+ register int
+ i,
+ j,
+ x;
+
+ unsigned int
+ scanline_pad;
+
+ register unsigned long
+ pixel;
+
+ unsigned char
+ *blue_map[2][16],
+ *green_map[2][16],
+ *red_map[2][16];
+
+ /*
+ Allocate and initialize dither maps.
+ */
+ for (i=0; i < 2; i++)
+ for (j=0; j < 16; j++)
+ {
+ red_map[i][j]=(unsigned char *) AcquireQuantumMemory(256UL,
+ sizeof(*red_map));
+ green_map[i][j]=(unsigned char *) AcquireQuantumMemory(256UL,
+ sizeof(*green_map));
+ blue_map[i][j]=(unsigned char *) AcquireQuantumMemory(256UL,
+ sizeof(*blue_map));
+ if ((red_map[i][j] == (unsigned char *) NULL) ||
+ (green_map[i][j] == (unsigned char *) NULL) ||
+ (blue_map[i][j] == (unsigned char *) NULL))
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed",image->filename);
+ return;
+ }
+ }
+ /*
+ Initialize dither tables.
+ */
+ for (i=0; i < 2; i++)
+ for (j=0; j < 16; j++)
+ for (x=0; x < 256; x++)
+ {
+ value=x-16;
+ if (x < 48)
+ value=x/2+8;
+ value+=dither_red[i][j];
+ red_map[i][j][x]=(unsigned char)
+ ((value < 0) ? 0 : (value > 255) ? 255 : value);
+ value=x-16;
+ if (x < 48)
+ value=x/2+8;
+ value+=dither_green[i][j];
+ green_map[i][j][x]=(unsigned char)
+ ((value < 0) ? 0 : (value > 255) ? 255 : value);
+ value=x-32;
+ if (x < 112)
+ value=x/2+24;
+ value+=((unsigned long) dither_blue[i][j] << 1);
+ blue_map[i][j][x]=(unsigned char)
+ ((value < 0) ? 0 : (value > 255) ? 255 : value);
+ }
+ /*
+ Dither image.
+ */
+ scanline_pad=(unsigned int) (ximage->bytes_per_line-
+ ((unsigned long) (ximage->width*ximage->bits_per_pixel) >> 3));
+ i=0;
+ j=0;
+ q=ximage->data;
+ for (y=0; y < (int) image->rows; y++)
+ {
+ p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) image->columns; x++)
+ {
+ color.red=RoundToQuantum((MagickRealType) (red_map[i][j][(int)
+ ScaleQuantumToChar(p->red)] << 8));
+ color.green=RoundToQuantum((MagickRealType) (green_map[i][j][(int)
+ ScaleQuantumToChar(p->green)] << 8));
+ color.blue=RoundToQuantum((MagickRealType) (blue_map[i][j][(int)
+ ScaleQuantumToChar(p->blue)] << 8));
+ pixel=(unsigned long) (((unsigned long) color.red & 0xe0) |
+ (((unsigned long) color.green & 0xe0) >> 3) |
+ (((unsigned long) color.blue & 0xc0) >> 6));
+ *q++=(char) pixel;
+ p++;
+ j++;
+ if (j == 16)
+ j=0;
+ }
+ q+=scanline_pad;
+ i++;
+ if (i == 2)
+ i=0;
+ }
+ /*
+ Free allocated memory.
+ */
+ for (i=0; i < 2; i++)
+ for (j=0; j < 16; j++)
+ {
+ green_map[i][j]=(unsigned char *) RelinquishMagickMemory(green_map[i][j]);
+ blue_map[i][j]=(unsigned char *) RelinquishMagickMemory(blue_map[i][j]);
+ red_map[i][j]=(unsigned char *) RelinquishMagickMemory(red_map[i][j]);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X D r a w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XDrawImage() draws a line on the image.
+%
+% The format of the XDrawImage method is:
+%
+% MagickBooleanType XDrawImage(display,pixel,draw_info,image)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+% o draw_info: Specifies a pointer to a XDrawInfo structure.
+%
+% o image: the image.
+%
+*/
+MagickExport MagickBooleanType XDrawImage(Display *display,
+ const XPixelInfo *pixel,XDrawInfo *draw_info,Image *image)
+{
+ ExceptionInfo
+ *exception;
+
+ GC
+ draw_context;
+
+ Image
+ *draw_image;
+
+ int
+ x,
+ y;
+
+ MagickBooleanType
+ matte;
+
+ Pixmap
+ draw_pixmap;
+
+ unsigned int
+ depth,
+ height,
+ width;
+
+ Window
+ root_window;
+
+ XGCValues
+ context_values;
+
+ XImage
+ *draw_ximage;
+
+ /*
+ Initialize drawd image.
+ */
+ assert(display != (Display *) NULL);
+ assert(pixel != (XPixelInfo *) NULL);
+ assert(draw_info != (XDrawInfo *) NULL);
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ /*
+ Initialize drawd pixmap.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ depth=(unsigned int) XDefaultDepth(display,XDefaultScreen(display));
+ draw_pixmap=XCreatePixmap(display,root_window,draw_info->width,
+ draw_info->height,depth);
+ if (draw_pixmap == (Pixmap) NULL)
+ return(MagickFalse);
+ /*
+ Initialize graphics info.
+ */
+ context_values.background=(unsigned long) (~0);
+ context_values.foreground=0;
+ context_values.line_width=(int) draw_info->line_width;
+ draw_context=XCreateGC(display,root_window,(unsigned long)
+ (GCBackground | GCForeground | GCLineWidth),&context_values);
+ if (draw_context == (GC) NULL)
+ return(MagickFalse);
+ /*
+ Clear pixmap.
+ */
+ (void) XFillRectangle(display,draw_pixmap,draw_context,0,0,draw_info->width,
+ draw_info->height);
+ /*
+ Draw line to pixmap.
+ */
+ (void) XSetBackground(display,draw_context,0);
+ (void) XSetForeground(display,draw_context,(unsigned long) (~0));
+ (void) XSetFillStyle(display,draw_context,FillOpaqueStippled);
+ (void) XSetStipple(display,draw_context,draw_info->stipple);
+ switch (draw_info->element)
+ {
+ case PointElement:
+ default:
+ {
+ (void) XDrawLines(display,draw_pixmap,draw_context,
+ draw_info->coordinate_info,(int) draw_info->number_coordinates,
+ CoordModeOrigin);
+ break;
+ }
+ case LineElement:
+ {
+ (void) XDrawLine(display,draw_pixmap,draw_context,draw_info->line_info.x1,
+ draw_info->line_info.y1,draw_info->line_info.x2,
+ draw_info->line_info.y2);
+ break;
+ }
+ case RectangleElement:
+ {
+ (void) XDrawRectangle(display,draw_pixmap,draw_context,
+ (int) draw_info->rectangle_info.x,(int) draw_info->rectangle_info.y,
+ (unsigned int) draw_info->rectangle_info.width,
+ (unsigned int) draw_info->rectangle_info.height);
+ break;
+ }
+ case FillRectangleElement:
+ {
+ (void) XFillRectangle(display,draw_pixmap,draw_context,
+ (int) draw_info->rectangle_info.x,(int) draw_info->rectangle_info.y,
+ (unsigned int) draw_info->rectangle_info.width,
+ (unsigned int) draw_info->rectangle_info.height);
+ break;
+ }
+ case CircleElement:
+ case EllipseElement:
+ {
+ (void) XDrawArc(display,draw_pixmap,draw_context,
+ (int) draw_info->rectangle_info.x,(int) draw_info->rectangle_info.y,
+ (unsigned int) draw_info->rectangle_info.width,
+ (unsigned int) draw_info->rectangle_info.height,0,360*64);
+ break;
+ }
+ case FillCircleElement:
+ case FillEllipseElement:
+ {
+ (void) XFillArc(display,draw_pixmap,draw_context,
+ (int) draw_info->rectangle_info.x,(int) draw_info->rectangle_info.y,
+ (unsigned int) draw_info->rectangle_info.width,
+ (unsigned int) draw_info->rectangle_info.height,0,360*64);
+ break;
+ }
+ case PolygonElement:
+ {
+ XPoint
+ *coordinate_info;
+
+ coordinate_info=draw_info->coordinate_info;
+ (void) XDrawLines(display,draw_pixmap,draw_context,coordinate_info,
+ (int) draw_info->number_coordinates,CoordModeOrigin);
+ (void) XDrawLine(display,draw_pixmap,draw_context,
+ coordinate_info[draw_info->number_coordinates-1].x,
+ coordinate_info[draw_info->number_coordinates-1].y,
+ coordinate_info[0].x,coordinate_info[0].y);
+ break;
+ }
+ case FillPolygonElement:
+ {
+ (void) XFillPolygon(display,draw_pixmap,draw_context,
+ draw_info->coordinate_info,(int) draw_info->number_coordinates,Complex,
+ CoordModeOrigin);
+ break;
+ }
+ }
+ (void) XFreeGC(display,draw_context);
+ /*
+ Initialize X image.
+ */
+ draw_ximage=XGetImage(display,draw_pixmap,0,0,draw_info->width,
+ draw_info->height,AllPlanes,ZPixmap);
+ if (draw_ximage == (XImage *) NULL)
+ return(MagickFalse);
+ (void) XFreePixmap(display,draw_pixmap);
+ /*
+ Initialize draw image.
+ */
+ draw_image=AcquireImage((ImageInfo *) NULL);
+ if (draw_image == (Image *) NULL)
+ return(MagickFalse);
+ draw_image->columns=draw_info->width;
+ draw_image->rows=draw_info->height;
+ /*
+ Transfer drawn X image to image.
+ */
+ width=(unsigned int) image->columns;
+ height=(unsigned int) image->rows;
+ x=0;
+ y=0;
+ (void) XParseGeometry(draw_info->geometry,&x,&y,&width,&height);
+ (void) GetOneVirtualPixel(image,x,y,&draw_image->background_color,
+ &image->exception);
+ if (SetImageStorageClass(draw_image,DirectClass) == MagickFalse)
+ return(MagickFalse);
+ draw_image->matte=MagickTrue;
+ exception=(&image->exception);
+ for (y=0; y < (int) draw_image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=QueueAuthenticPixels(draw_image,0,y,draw_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) draw_image->columns; x++)
+ {
+ if (XGetPixel(draw_ximage,x,y) == 0)
+ {
+ /*
+ Set this pixel to the background color.
+ */
+ *q=draw_image->background_color;
+ q->opacity=(Quantum) (draw_info->stencil == OpaqueStencil ?
+ TransparentOpacity : OpaqueOpacity);
+ }
+ else
+ {
+ /*
+ Set this pixel to the pen color.
+ */
+ q->red=ScaleShortToQuantum(pixel->pen_color.red);
+ q->green=ScaleShortToQuantum(pixel->pen_color.green);
+ q->blue=ScaleShortToQuantum(pixel->pen_color.blue);
+ q->opacity=(Quantum) (draw_info->stencil == OpaqueStencil ?
+ OpaqueOpacity : TransparentOpacity);
+ }
+ q++;
+ }
+ if (SyncAuthenticPixels(draw_image,exception) == MagickFalse)
+ break;
+ }
+ XDestroyImage(draw_ximage);
+ /*
+ Determine draw geometry.
+ */
+ (void) XParseGeometry(draw_info->geometry,&x,&y,&width,&height);
+ if ((width != (unsigned int) draw_image->columns) ||
+ (height != (unsigned int) draw_image->rows))
+ {
+ char
+ image_geometry[MaxTextExtent];
+
+ /*
+ Scale image.
+ */
+ (void) FormatMagickString(image_geometry,MaxTextExtent,"%ux%u",
+ width,height);
+ (void) TransformImage(&draw_image,(char *) NULL,image_geometry);
+ }
+ if (draw_info->degrees != 0.0)
+ {
+ Image
+ *rotate_image;
+
+ int
+ rotations;
+
+ MagickRealType
+ normalized_degrees;
+
+ /*
+ Rotate image.
+ */
+ rotate_image=RotateImage(draw_image,draw_info->degrees,&image->exception);
+ if (rotate_image == (Image *) NULL)
+ return(MagickFalse);
+ draw_image=DestroyImage(draw_image);
+ draw_image=rotate_image;
+ /*
+ Annotation is relative to the degree of rotation.
+ */
+ normalized_degrees=draw_info->degrees;
+ while (normalized_degrees < -45.0)
+ normalized_degrees+=360.0;
+ for (rotations=0; normalized_degrees > 45.0; rotations++)
+ normalized_degrees-=90.0;
+ switch (rotations % 4)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ {
+ /*
+ Rotate 90 degrees.
+ */
+ x=x-(int) draw_image->columns/2;
+ y=y+(int) draw_image->columns/2;
+ break;
+ }
+ case 2:
+ {
+ /*
+ Rotate 180 degrees.
+ */
+ x=x-(int) draw_image->columns;
+ break;
+ }
+ case 3:
+ {
+ /*
+ Rotate 270 degrees.
+ */
+ x=x-(int) draw_image->columns/2;
+ y=y-(int) (draw_image->rows-(draw_image->columns/2));
+ break;
+ }
+ }
+ }
+ /*
+ Composite text onto the image.
+ */
+ for (y=0; y < (int) draw_image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(draw_image,0,y,draw_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) draw_image->columns; x++)
+ {
+ if (q->opacity != (Quantum) TransparentOpacity)
+ q->opacity=OpaqueOpacity;
+ q++;
+ }
+ if (SyncAuthenticPixels(draw_image,exception) == MagickFalse)
+ break;
+ }
+ (void) XParseGeometry(draw_info->geometry,&x,&y,&width,&height);
+ if (draw_info->stencil == TransparentStencil)
+ (void) CompositeImage(image,CopyOpacityCompositeOp,draw_image,x,y);
+ else
+ {
+ matte=image->matte;
+ (void) CompositeImage(image,OverCompositeOp,draw_image,x,y);
+ image->matte=matte;
+ }
+ draw_image=DestroyImage(draw_image);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X E r r o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XError() ignores BadWindow errors for XQueryTree and XGetWindowAttributes,
+% and ignores BadDrawable errors for XGetGeometry, and ignores BadValue errors
+% for XQueryColor. It returns MagickFalse in those cases. Otherwise it returns
+% True.
+%
+% The format of the XError function is:
+%
+% XError(display,error)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o error: Specifies the error event.
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+MagickExport int XError(Display *display,XErrorEvent *error)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(error != (XErrorEvent *) NULL);
+ xerror_alert=MagickTrue;
+ switch (error->request_code)
+ {
+ case X_GetGeometry:
+ {
+ if ((int) error->error_code == BadDrawable)
+ return(MagickFalse);
+ break;
+ }
+ case X_GetWindowAttributes:
+ case X_QueryTree:
+ {
+ if ((int) error->error_code == BadWindow)
+ return(MagickFalse);
+ break;
+ }
+ case X_QueryColors:
+ {
+ if ((int) error->error_code == BadValue)
+ return(MagickFalse);
+ break;
+ }
+ }
+ return(MagickTrue);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X F r e e R e s o u r c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XFreeResources() frees X11 resources.
+%
+% The format of the XFreeResources method is:
+%
+% void XFreeResources(Display *display,XVisualInfo *visual_info,
+% XStandardColormap *map_info,XPixelInfo *pixel,XFontStruct *font_info,
+% XResourceInfo *resource_info,XWindowInfo *window_info)
+% resource_info,window_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o map_info: If map_type is specified, this structure is initialized
+% with info from the Standard Colormap.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+% o font_info: Specifies a pointer to a XFontStruct structure.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+*/
+MagickExport void XFreeResources(Display *display,XVisualInfo *visual_info,
+ XStandardColormap *map_info,XPixelInfo *pixel,XFontStruct *font_info,
+ XResourceInfo *resource_info,XWindowInfo *window_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ if (window_info != (XWindowInfo *) NULL)
+ {
+ /*
+ Free X image.
+ */
+ if (window_info->ximage != (XImage *) NULL)
+ XDestroyImage(window_info->ximage);
+ if (window_info->id != (Window) NULL)
+ {
+ /*
+ Free destroy window and free cursors.
+ */
+ if (window_info->id != XRootWindow(display,visual_info->screen))
+ (void) XDestroyWindow(display,window_info->id);
+ if (window_info->annotate_context != (GC) NULL)
+ (void) XFreeGC(display,window_info->annotate_context);
+ if (window_info->highlight_context != (GC) NULL)
+ (void) XFreeGC(display,window_info->highlight_context);
+ if (window_info->widget_context != (GC) NULL)
+ (void) XFreeGC(display,window_info->widget_context);
+ if (window_info->cursor != (Cursor) NULL)
+ (void) XFreeCursor(display,window_info->cursor);
+ window_info->cursor=(Cursor) NULL;
+ if (window_info->busy_cursor != (Cursor) NULL)
+ (void) XFreeCursor(display,window_info->busy_cursor);
+ window_info->busy_cursor=(Cursor) NULL;
+ }
+ }
+ /*
+ Free font.
+ */
+ if (font_info != (XFontStruct *) NULL)
+ (void) XFreeFont(display,font_info);
+ if (map_info != (XStandardColormap *) NULL)
+ {
+ /*
+ Free X Standard Colormap.
+ */
+ if (resource_info->map_type == (char *) NULL)
+ (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
+ (void) XFree((void *) map_info);
+ }
+ /*
+ Free X visual info.
+ */
+ if (visual_info != (XVisualInfo *) NULL)
+ (void) XFree((void *) visual_info);
+ if (resource_info->close_server != MagickFalse)
+ (void) XCloseDisplay(display);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X F r e e S t a n d a r d C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XFreeStandardColormap() frees an X11 colormap.
+%
+% The format of the XFreeStandardColormap method is:
+%
+% void XFreeStandardColormap(Display *display,
+% const XVisualInfo *visual_info,XStandardColormap *map_info,
+% XPixelInfo *pixel)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o map_info: If map_type is specified, this structure is initialized
+% with info from the Standard Colormap.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+*/
+MagickExport void XFreeStandardColormap(Display *display,
+ const XVisualInfo *visual_info,XStandardColormap *map_info,XPixelInfo *pixel)
+{
+ /*
+ Free colormap.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(visual_info != (XVisualInfo *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ (void) XFlush(display);
+ if (map_info->colormap != (Colormap) NULL)
+ {
+ if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
+ (void) XFreeColormap(display,map_info->colormap);
+ else
+ if (pixel != (XPixelInfo *) NULL)
+ if ((visual_info->klass != TrueColor) &&
+ (visual_info->klass != DirectColor))
+ (void) XFreeColors(display,map_info->colormap,pixel->pixels,
+ (int) pixel->colors,0);
+ }
+ map_info->colormap=(Colormap) NULL;
+ if (pixel != (XPixelInfo *) NULL)
+ {
+ if (pixel->pixels != (unsigned long *) NULL)
+ pixel->pixels=(unsigned long *) RelinquishMagickMemory(pixel->pixels);
+ pixel->pixels=(unsigned long *) NULL;
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t A n n o t a t e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetAnnotateInfo() initializes the AnnotateInfo structure.
+%
+% The format of the XGetAnnotateInfo method is:
+%
+% void XGetAnnotateInfo(XAnnotateInfo *annotate_info)
+%
+% A description of each parameter follows:
+%
+% o annotate_info: Specifies a pointer to a XAnnotateInfo structure.
+%
+*/
+MagickExport void XGetAnnotateInfo(XAnnotateInfo *annotate_info)
+{
+ /*
+ Initialize annotate structure.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(annotate_info != (XAnnotateInfo *) NULL);
+ annotate_info->x=0;
+ annotate_info->y=0;
+ annotate_info->width=0;
+ annotate_info->height=0;
+ annotate_info->stencil=ForegroundStencil;
+ annotate_info->degrees=0.0;
+ annotate_info->font_info=(XFontStruct *) NULL;
+ annotate_info->text=(char *) NULL;
+ *annotate_info->geometry='\0';
+ annotate_info->previous=(XAnnotateInfo *) NULL;
+ annotate_info->next=(XAnnotateInfo *) NULL;
+ (void) XSupportsLocale();
+ (void) XSetLocaleModifiers("");
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t M a p I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetMapInfo() initializes the XStandardColormap structure.
+%
+% The format of the XStandardColormap method is:
+%
+% void XGetMapInfo(const XVisualInfo *visual_info,const Colormap colormap,
+% XStandardColormap *map_info)
+%
+% A description of each parameter follows:
+%
+% o colormap: Specifies the ID of the X server colormap.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o map_info: Specifies a pointer to a X11 XStandardColormap structure.
+%
+*/
+MagickExport void XGetMapInfo(const XVisualInfo *visual_info,
+ const Colormap colormap,XStandardColormap *map_info)
+{
+ /*
+ Initialize map info.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(visual_info != (XVisualInfo *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ map_info->colormap=colormap;
+ map_info->red_max=visual_info->red_mask;
+ map_info->red_mult=(unsigned long) (map_info->red_max != 0 ? 1 : 0);
+ if (map_info->red_max != 0)
+ while ((map_info->red_max & 0x01) == 0)
+ {
+ map_info->red_max>>=1;
+ map_info->red_mult<<=1;
+ }
+ map_info->green_max=visual_info->green_mask;
+ map_info->green_mult=(unsigned long) (map_info->green_max != 0 ? 1 : 0);
+ if (map_info->green_max != 0)
+ while ((map_info->green_max & 0x01) == 0)
+ {
+ map_info->green_max>>=1;
+ map_info->green_mult<<=1;
+ }
+ map_info->blue_max=visual_info->blue_mask;
+ map_info->blue_mult=(unsigned long) (map_info->blue_max != 0 ? 1 : 0);
+ if (map_info->blue_max != 0)
+ while ((map_info->blue_max & 0x01) == 0)
+ {
+ map_info->blue_max>>=1;
+ map_info->blue_mult<<=1;
+ }
+ map_info->base_pixel=0;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t P i x e l I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetPixelPacket() initializes the PixelPacket structure.
+%
+% The format of the XGetPixelPacket method is:
+%
+% void XGetPixelPacket(Display *display,const XVisualInfo *visual_info,
+% const XStandardColormap *map_info,const XResourceInfo *resource_info,
+% Image *image,XPixelInfo *pixel)
+% pixel)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o map_info: If map_type is specified, this structure is initialized
+% with info from the Standard Colormap.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o image: the image.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+*/
+MagickExport void XGetPixelPacket(Display *display,
+ const XVisualInfo *visual_info,const XStandardColormap *map_info,
+ const XResourceInfo *resource_info,Image *image,XPixelInfo *pixel)
+{
+ static const char
+ *PenColors[MaxNumberPens]=
+ {
+ "#000000000000", /* black */
+ "#00000000ffff", /* blue */
+ "#0000ffffffff", /* cyan */
+ "#0000ffff0000", /* green */
+ "#bdbdbdbdbdbd", /* gray */
+ "#ffff00000000", /* red */
+ "#ffff0000ffff", /* magenta */
+ "#ffffffff0000", /* yellow */
+ "#ffffffffffff", /* white */
+ "#bdbdbdbdbdbd", /* gray */
+ "#bdbdbdbdbdbd" /* gray */
+ };
+
+ Colormap
+ colormap;
+
+ register long
+ i;
+
+ Status
+ status;
+
+ unsigned int
+ packets;
+
+ /*
+ Initialize pixel info.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(visual_info != (XVisualInfo *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(pixel != (XPixelInfo *) NULL);
+ pixel->colors=0;
+ if (image != (Image *) NULL)
+ if (image->storage_class == PseudoClass)
+ pixel->colors=image->colors;
+ packets=(unsigned int)
+ MagickMax((int) pixel->colors,visual_info->colormap_size)+MaxNumberPens;
+ if (pixel->pixels != (unsigned long *) NULL)
+ pixel->pixels=(unsigned long *) RelinquishMagickMemory(pixel->pixels);
+ pixel->pixels=(unsigned long *) AcquireQuantumMemory(packets,
+ sizeof(pixel->pixels));
+ if (pixel->pixels == (unsigned long *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,"UnableToGetPixelInfo",
+ image->filename);
+ /*
+ Set foreground color.
+ */
+ colormap=map_info->colormap;
+ (void) XParseColor(display,colormap,(char *) ForegroundColor,
+ &pixel->foreground_color);
+ status=XParseColor(display,colormap,resource_info->foreground_color,
+ &pixel->foreground_color);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",
+ resource_info->foreground_color);
+ pixel->foreground_color.pixel=
+ XStandardPixel(map_info,&pixel->foreground_color);
+ pixel->foreground_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set background color.
+ */
+ (void) XParseColor(display,colormap,"#d6d6d6d6d6d6",&pixel->background_color);
+ status=XParseColor(display,colormap,resource_info->background_color,
+ &pixel->background_color);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",
+ resource_info->background_color);
+ pixel->background_color.pixel=
+ XStandardPixel(map_info,&pixel->background_color);
+ pixel->background_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set border color.
+ */
+ (void) XParseColor(display,colormap,(char *) BorderColor,
+ &pixel->border_color);
+ status=XParseColor(display,colormap,resource_info->border_color,
+ &pixel->border_color);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",
+ resource_info->border_color);
+ pixel->border_color.pixel=XStandardPixel(map_info,&pixel->border_color);
+ pixel->border_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set matte color.
+ */
+ pixel->matte_color=pixel->background_color;
+ if (resource_info->matte_color != (char *) NULL)
+ {
+ /*
+ Matte color is specified as a X resource or command line argument.
+ */
+ status=XParseColor(display,colormap,resource_info->matte_color,
+ &pixel->matte_color);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",
+ resource_info->matte_color);
+ pixel->matte_color.pixel=XStandardPixel(map_info,&pixel->matte_color);
+ pixel->matte_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ }
+ /*
+ Set highlight color.
+ */
+ pixel->highlight_color.red=(unsigned short) ((
+ pixel->matte_color.red*ScaleQuantumToShort(HighlightModulate))/65535L+
+ (ScaleQuantumToShort((Quantum) (QuantumRange-HighlightModulate))));
+ pixel->highlight_color.green=(unsigned short) ((
+ pixel->matte_color.green*ScaleQuantumToShort(HighlightModulate))/65535L+
+ (ScaleQuantumToShort((Quantum) (QuantumRange-HighlightModulate))));
+ pixel->highlight_color.blue=(unsigned short) ((
+ pixel->matte_color.blue*ScaleQuantumToShort(HighlightModulate))/65535L+
+ (ScaleQuantumToShort((Quantum) (QuantumRange-HighlightModulate))));
+ pixel->highlight_color.pixel=
+ XStandardPixel(map_info,&pixel->highlight_color);
+ pixel->highlight_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set shadow color.
+ */
+ pixel->shadow_color.red=(unsigned short) (((MagickRealType)
+ pixel->matte_color.red*ScaleQuantumToShort(ShadowModulate))/65535L);
+ pixel->shadow_color.green=(unsigned short) (((MagickRealType)
+ pixel->matte_color.green*ScaleQuantumToShort(ShadowModulate))/65535L);
+ pixel->shadow_color.blue=(unsigned short) (((MagickRealType)
+ pixel->matte_color.blue*ScaleQuantumToShort(ShadowModulate))/65535L);
+ pixel->shadow_color.pixel=XStandardPixel(map_info,&pixel->shadow_color);
+ pixel->shadow_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set depth color.
+ */
+ pixel->depth_color.red=(unsigned short) (((MagickRealType)
+ pixel->matte_color.red*ScaleQuantumToShort(DepthModulate))/65535L);
+ pixel->depth_color.green=(unsigned short) (((MagickRealType)
+ pixel->matte_color.green*ScaleQuantumToShort(DepthModulate))/65535L);
+ pixel->depth_color.blue=(unsigned short) (((MagickRealType)
+ pixel->matte_color.blue*ScaleQuantumToShort(DepthModulate))/65535L);
+ pixel->depth_color.pixel=XStandardPixel(map_info,&pixel->depth_color);
+ pixel->depth_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set trough color.
+ */
+ pixel->trough_color.red=(unsigned short) (((MagickRealType)
+ pixel->matte_color.red*ScaleQuantumToShort(TroughModulate))/65535L);
+ pixel->trough_color.green=(unsigned short) (((MagickRealType)
+ pixel->matte_color.green*ScaleQuantumToShort(TroughModulate))/65535L);
+ pixel->trough_color.blue=(unsigned short) (((MagickRealType)
+ pixel->matte_color.blue*ScaleQuantumToShort(TroughModulate))/65535L);
+ pixel->trough_color.pixel=XStandardPixel(map_info,&pixel->trough_color);
+ pixel->trough_color.flags=(char) (DoRed | DoGreen | DoBlue);
+ /*
+ Set pen color.
+ */
+ for (i=0; i < MaxNumberPens; i++)
+ {
+ (void) XParseColor(display,colormap,(char *) PenColors[i],
+ &pixel->pen_colors[i]);
+ status=XParseColor(display,colormap,resource_info->pen_colors[i],
+ &pixel->pen_colors[i]);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",
+ resource_info->pen_colors[i]);
+ pixel->pen_colors[i].pixel=XStandardPixel(map_info,&pixel->pen_colors[i]);
+ pixel->pen_colors[i].flags=(char) (DoRed | DoGreen | DoBlue);
+ }
+ pixel->box_color=pixel->background_color;
+ pixel->pen_color=pixel->foreground_color;
+ pixel->box_index=0;
+ pixel->pen_index=1;
+ if (image != (Image *) NULL)
+ {
+ if ((resource_info->gamma_correct != MagickFalse) &&
+ (image->gamma != 0.0))
+ {
+ GeometryInfo
+ geometry_info;
+
+ MagickStatusType
+ flags;
+
+ /*
+ Initialize map relative to display and image gamma.
+ */
+ flags=ParseGeometry(resource_info->display_gamma,&geometry_info);
+ red_gamma=geometry_info.rho;
+ green_gamma=geometry_info.sigma;
+ if ((flags & SigmaValue) == 0)
+ green_gamma=red_gamma;
+ blue_gamma=geometry_info.xi;
+ if ((flags & XiValue) == 0)
+ blue_gamma=red_gamma;
+ red_gamma*=image->gamma;
+ green_gamma*=image->gamma;
+ blue_gamma*=image->gamma;
+ }
+ if (image->storage_class == PseudoClass)
+ {
+ /*
+ Initialize pixel array for images of type PseudoClass.
+ */
+ for (i=0; i < (long) image->colors; i++)
+ pixel->pixels[i]=
+ XGammaPixel(map_info,image->colormap+i);
+ for (i=0; i < MaxNumberPens; i++)
+ pixel->pixels[image->colors+i]=pixel->pen_colors[i].pixel;
+ pixel->colors+=MaxNumberPens;
+ }
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t R e s o u r c e C l a s s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetResourceClass() queries the X server for the specified resource name or
+% class. If the resource name or class is not defined in the database, the
+% supplied default value is returned.
+%
+% The format of the XGetResourceClass method is:
+%
+% char *XGetResourceClass(XrmDatabase database,const char *client_name,
+% const char *keyword,char *resource_default)
+%
+% A description of each parameter follows:
+%
+% o database: Specifies a resource database; returned from
+% XrmGetStringDatabase.
+%
+% o client_name: Specifies the application name used to retrieve resource
+% info from the X server database.
+%
+% o keyword: Specifies the keyword of the value being retrieved.
+%
+% o resource_default: Specifies the default value to return if the query
+% fails to find the specified keyword/class.
+%
+*/
+MagickExport char *XGetResourceClass(XrmDatabase database,
+ const char *client_name,const char *keyword,char *resource_default)
+{
+ char
+ resource_class[MaxTextExtent],
+ resource_name[MaxTextExtent];
+
+ static char
+ *resource_type;
+
+ Status
+ status;
+
+ XrmValue
+ resource_value;
+
+ if (database == (XrmDatabase) NULL)
+ return(resource_default);
+ *resource_name='\0';
+ *resource_class='\0';
+ if (keyword != (char *) NULL)
+ {
+ int
+ c,
+ k;
+
+ /*
+ Initialize resource keyword and class.
+ */
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.%s",
+ client_name,keyword);
+ c=(int) (*client_name);
+ if ((c >= XK_a) && (c <= XK_z))
+ c-=(XK_a-XK_A);
+ else
+ if ((c >= XK_agrave) && (c <= XK_odiaeresis))
+ c-=(XK_agrave-XK_Agrave);
+ else
+ if ((c >= XK_oslash) && (c <= XK_thorn))
+ c-=(XK_oslash-XK_Ooblique);
+ k=(int) (*keyword);
+ if ((k >= XK_a) && (k <= XK_z))
+ k-=(XK_a-XK_A);
+ else
+ if ((k >= XK_agrave) && (k <= XK_odiaeresis))
+ k-=(XK_agrave-XK_Agrave);
+ else
+ if ((k >= XK_oslash) && (k <= XK_thorn))
+ k-=(XK_oslash-XK_Ooblique);
+ (void) FormatMagickString(resource_class,MaxTextExtent,"%c%s.%c%s",c,
+ client_name+1,k,keyword+1);
+ }
+ status=XrmGetResource(database,resource_name,resource_class,&resource_type,
+ &resource_value);
+ if (status == False)
+ return(resource_default);
+ return(resource_value.addr);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t R e s o u r c e D a t a b a s e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetResourceDatabase() creates a new resource database and initializes it.
+%
+% The format of the XGetResourceDatabase method is:
+%
+% XrmDatabase XGetResourceDatabase(Display *display,
+% const char *client_name)
+%
+% A description of each parameter follows:
+%
+% o database: XGetResourceDatabase() returns the database after it is
+% initialized.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o client_name: Specifies the application name used to retrieve resource
+% info from the X server database.
+%
+*/
+MagickExport XrmDatabase XGetResourceDatabase(Display *display,
+ const char *client_name)
+{
+ char
+ filename[MaxTextExtent];
+
+ int
+ c;
+
+ register const char
+ *p;
+
+ XrmDatabase
+ resource_database,
+ server_database;
+
+ if (display == (Display *) NULL)
+ return((XrmDatabase) NULL);
+ assert(client_name != (char *) NULL);
+ /*
+ Initialize resource database.
+ */
+ XrmInitialize();
+ (void) XGetDefault(display,(char *) client_name,"dummy");
+ resource_database=XrmGetDatabase(display);
+ /*
+ Combine application database.
+ */
+ if (client_name != (char *) NULL)
+ {
+ /*
+ Get basename of client.
+ */
+ p=client_name+(strlen(client_name)-1);
+ while ((p > client_name) && (*p != '/'))
+ p--;
+ if (*p == '/')
+ client_name=p+1;
+ }
+ c=(int) (*client_name);
+ if ((c >= XK_a) && (c <= XK_z))
+ c-=(XK_a-XK_A);
+ else
+ if ((c >= XK_agrave) && (c <= XK_odiaeresis))
+ c-=(XK_agrave-XK_Agrave);
+ else
+ if ((c >= XK_oslash) && (c <= XK_thorn))
+ c-=(XK_oslash-XK_Ooblique);
+#if defined(X11_APPLICATION_PATH)
+ (void) FormatMagickString(filename,MaxTextExtent,"%s%c%s",
+ X11_APPLICATION_PATH,c,client_name+1);
+ (void) XrmCombineFileDatabase(filename,&resource_database,MagickFalse);
+#endif
+ if (XResourceManagerString(display) != (char *) NULL)
+ {
+ /*
+ Combine server database.
+ */
+ server_database=XrmGetStringDatabase(XResourceManagerString(display));
+ XrmCombineDatabase(server_database,&resource_database,MagickFalse);
+ }
+ /*
+ Merge user preferences database.
+ */
+#if defined(X11_PREFERENCES_PATH)
+ (void) FormatMagickString(filename,MaxTextExtent,"%s%src",
+ X11_PREFERENCES_PATH,client_name);
+ ExpandFilename(filename);
+ (void) XrmCombineFileDatabase(filename,&resource_database,MagickFalse);
+#endif
+ return(resource_database);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t R e s o u r c e I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetResourceInfo(image_info,) initializes the ResourceInfo structure.
+%
+% The format of the XGetResourceInfo method is:
+%
+% void XGetResourceInfo(const ImageInfo *image_info,XrmDatabase database,
+% const char *client_name,XResourceInfo *resource_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o database: Specifies a resource database; returned from
+% XrmGetStringDatabase.
+%
+% o client_name: Specifies the application name used to retrieve
+% resource info from the X server database.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+MagickExport void XGetResourceInfo(const ImageInfo *image_info,
+ XrmDatabase database,const char *client_name,XResourceInfo *resource_info)
+{
+ char
+ *cwd,
+ *resource_value;
+
+ /*
+ Initialize resource info fields.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(resource_info != (XResourceInfo *) NULL);
+ (void) ResetMagickMemory(resource_info,0,sizeof(*resource_info));
+ resource_info->resource_database=database;
+ resource_info->image_info=(ImageInfo *) image_info;
+ (void) SetImageInfoProgressMonitor(resource_info->image_info,
+ XMagickProgressMonitor,(void *) NULL);
+ resource_info->quantize_info=CloneQuantizeInfo((QuantizeInfo *) NULL);
+ resource_info->close_server=MagickTrue;
+ resource_info->client_name=AcquireString(client_name);
+ resource_value=XGetResourceClass(database,client_name,"backdrop",
+ (char *) "False");
+ resource_info->backdrop=IsMagickTrue(resource_value);
+ resource_info->background_color=XGetResourceInstance(database,client_name,
+ "background",(char *) "#d6d6d6d6d6d6");
+ resource_info->border_color=XGetResourceInstance(database,client_name,
+ "borderColor",BorderColor);
+ resource_value=XGetResourceClass(database,client_name,"borderWidth",
+ (char *) "2");
+ resource_info->border_width=(unsigned int) atoi(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"colormap",
+ (char *) "shared");
+ resource_info->colormap=UndefinedColormap;
+ if (LocaleCompare("private",resource_value) == 0)
+ resource_info->colormap=PrivateColormap;
+ if (LocaleCompare("shared",resource_value) == 0)
+ resource_info->colormap=SharedColormap;
+ if (resource_info->colormap == UndefinedColormap)
+ ThrowXWindowFatalException(OptionError,"UnrecognizedColormapType",
+ resource_value);
+ resource_value=XGetResourceClass(database,client_name,
+ "colorRecovery",(char *) "False");
+ resource_info->color_recovery=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"confirmExit",
+ (char *) "False");
+ resource_info->confirm_exit=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"confirmEdit",
+ (char *) "False");
+ resource_info->confirm_edit=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"delay",(char *) "1");
+ resource_info->delay=(unsigned int) atoi(resource_value);
+ resource_info->display_gamma=XGetResourceClass(database,client_name,
+ "displayGamma",(char *) "2.2");
+ resource_value=XGetResourceClass(database,client_name,"displayWarnings",
+ (char *) "True");
+ resource_info->display_warnings=IsMagickTrue(resource_value);
+ resource_info->font=XGetResourceClass(database,client_name,"font",
+ (char *) NULL);
+ resource_info->font=XGetResourceClass(database,client_name,"fontList",
+ resource_info->font);
+ resource_info->font_name[0]=XGetResourceClass(database,client_name,"font1",
+ (char *) "fixed");
+ resource_info->font_name[1]=XGetResourceClass(database,client_name,"font2",
+ (char *) "variable");
+ resource_info->font_name[2]=XGetResourceClass(database,client_name,"font3",
+ (char *) "5x8");
+ resource_info->font_name[3]=XGetResourceClass(database,client_name,"font4",
+ (char *) "6x10");
+ resource_info->font_name[4]=XGetResourceClass(database,client_name,"font5",
+ (char *) "7x13bold");
+ resource_info->font_name[5]=XGetResourceClass(database,client_name,"font6",
+ (char *) "8x13bold");
+ resource_info->font_name[6]=XGetResourceClass(database,client_name,"font7",
+ (char *) "9x15bold");
+ resource_info->font_name[7]=XGetResourceClass(database,client_name,"font8",
+ (char *) "10x20");
+ resource_info->font_name[8]=XGetResourceClass(database,client_name,"font9",
+ (char *) "12x24");
+ resource_info->font_name[9]=XGetResourceClass(database,client_name,"font0",
+ (char *) "fixed");
+ resource_info->font_name[10]=XGetResourceClass(database,client_name,"font0",
+ (char *) "fixed");
+ resource_info->foreground_color=XGetResourceInstance(database,client_name,
+ "foreground",ForegroundColor);
+ resource_value=XGetResourceClass(database,client_name,"gammaCorrect",
+ (char *) "True");
+ resource_info->gamma_correct=IsMagickTrue(resource_value);
+ resource_info->image_geometry=ConstantString(XGetResourceClass(database,
+ client_name,"geometry",(char *) NULL));
+ resource_value=XGetResourceClass(database,client_name,"gravity",
+ (char *) "Center");
+ resource_info->gravity=(GravityType) ParseMagickOption(MagickGravityOptions,
+ MagickFalse,resource_value);
+ cwd=getcwd(resource_info->home_directory,MaxTextExtent);
+ resource_info->icon_geometry=XGetResourceClass(database,client_name,
+ "iconGeometry",(char *) NULL);
+ resource_value=XGetResourceClass(database,client_name,"iconic",
+ (char *) "False");
+ resource_info->iconic=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"immutable",
+ LocaleCompare(client_name,"PerlMagick") == 0 ? (char *) "True" :
+ (char *) "False");
+ resource_info->immutable=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"magnify",
+ (char *) "3");
+ resource_info->magnify=(unsigned int) atoi(resource_value);
+ resource_info->map_type=XGetResourceClass(database,client_name,"map",
+ (char *) NULL);
+ resource_info->matte_color=XGetResourceInstance(database,client_name,
+ "mattecolor",(char *) NULL);
+ resource_info->name=ConstantString(XGetResourceClass(database,client_name,
+ "name",(char *) NULL));
+ resource_info->pen_colors[0]=XGetResourceClass(database,client_name,"pen1",
+ (char *) "black");
+ resource_info->pen_colors[1]=XGetResourceClass(database,client_name,"pen2",
+ (char *) "blue");
+ resource_info->pen_colors[2]=XGetResourceClass(database,client_name,"pen3",
+ (char *) "cyan");
+ resource_info->pen_colors[3]=XGetResourceClass(database,client_name,"pen4",
+ (char *) "green");
+ resource_info->pen_colors[4]=XGetResourceClass(database,client_name,"pen5",
+ (char *) "gray");
+ resource_info->pen_colors[5]=XGetResourceClass(database,client_name,"pen6",
+ (char *) "red");
+ resource_info->pen_colors[6]=XGetResourceClass(database,client_name,"pen7",
+ (char *) "magenta");
+ resource_info->pen_colors[7]=XGetResourceClass(database,client_name,"pen8",
+ (char *) "yellow");
+ resource_info->pen_colors[8]=XGetResourceClass(database,client_name,"pen9",
+ (char *) "white");
+ resource_info->pen_colors[9]=XGetResourceClass(database,client_name,"pen0",
+ (char *) "gray");
+ resource_info->pen_colors[10]=XGetResourceClass(database,client_name,"pen0",
+ (char *) "gray");
+ resource_value=XGetResourceClass(database,client_name,"pause",(char *) "0");
+ resource_info->pause=(unsigned int) atoi(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"quantum",(char *) "1");
+ resource_info->quantum=atoi(resource_value);
+ resource_info->text_font=XGetResourceClass(database,client_name,(char *)
+ "font",(char *) "fixed");
+ resource_info->text_font=XGetResourceClass(database,client_name,
+ "textFontList",resource_info->text_font);
+ resource_info->title=XGetResourceClass(database,client_name,"title",
+ (char *) NULL);
+ resource_value=XGetResourceClass(database,client_name,"undoCache",
+ (char *) "16");
+ resource_info->undo_cache=(unsigned int) atol(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"update",
+ (char *) "False");
+ resource_info->update=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"usePixmap",
+ (char *) "True");
+ resource_info->use_pixmap=IsMagickTrue(resource_value);
+ resource_value=XGetResourceClass(database,client_name,"sharedMemory",
+ (char *) "True");
+ resource_info->use_shared_memory=IsMagickTrue(resource_value);
+ resource_info->visual_type=XGetResourceClass(database,client_name,"visual",
+ (char *) NULL);
+ resource_info->window_group=XGetResourceClass(database,client_name,
+ "windowGroup",(char *) NULL);
+ resource_info->window_id=XGetResourceClass(database,client_name,"window",
+ (char *) NULL);
+ resource_info->write_filename=XGetResourceClass(database,client_name,
+ "writeFilename",(char *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t R e s o u r c e I n s t a n c e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetResourceInstance() queries the X server for the specified resource name.
+% If the resource name is not defined in the database, the supplied default
+% value is returned.
+%
+% The format of the XGetResourceInstance method is:
+%
+% char *XGetResourceInstance(XrmDatabase database,const char *client_name,
+% const char *keyword,const char *resource_default)
+%
+% A description of each parameter follows:
+%
+% o database: Specifies a resource database; returned from
+% XrmGetStringDatabase.
+%
+% o client_name: Specifies the application name used to retrieve
+% resource info from the X server database.
+%
+% o keyword: Specifies the keyword of the value being retrieved.
+%
+% o resource_default: Specifies the default value to return if the query
+% fails to find the specified keyword/class.
+%
+*/
+MagickExport char *XGetResourceInstance(XrmDatabase database,
+ const char *client_name,const char *keyword,const char *resource_default)
+{
+ char
+ *resource_type,
+ resource_name[MaxTextExtent];
+
+ Status
+ status;
+
+ XrmValue
+ resource_value;
+
+ if (database == (XrmDatabase) NULL)
+ return((char *) resource_default);
+ *resource_name='\0';
+ if (keyword != (char *) NULL)
+ (void) FormatMagickString(resource_name,MaxTextExtent,"%s.%s",client_name,
+ keyword);
+ status=XrmGetResource(database,resource_name,"ImageMagick",&resource_type,
+ &resource_value);
+ if (status == False)
+ return((char *) resource_default);
+ return(resource_value.addr);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t S c r e e n D e n s i t y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetScreenDensity() returns the density of the X server screen in
+% dots-per-inch.
+%
+% The format of the XGetScreenDensity method is:
+%
+% char *XGetScreenDensity(Display *display)
+%
+% A description of each parameter follows:
+%
+% o density: XGetScreenDensity() returns the density of the X screen in
+% dots-per-inch.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+*/
+MagickExport char *XGetScreenDensity(Display *display)
+{
+ char
+ density[MaxTextExtent];
+
+ double
+ x_density,
+ y_density;
+
+ /*
+ Set density as determined by screen size.
+ */
+ x_density=((((double) DisplayWidth(display,XDefaultScreen(display)))*25.4)/
+ ((double) DisplayWidthMM(display,XDefaultScreen(display))));
+ y_density=((((double) DisplayHeight(display,XDefaultScreen(display)))*25.4)/
+ ((double) DisplayHeightMM(display,XDefaultScreen(display))));
+ (void) FormatMagickString(density,MaxTextExtent,"%gx%g",x_density,y_density);
+ return(GetPageGeometry(density));
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X G e t S u b w i n d o w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetSubwindow() returns the subwindow of a window chosen the user with the
+% pointer and a button press.
+%
+% The format of the XGetSubwindow method is:
+%
+% Window XGetSubwindow(Display *display,Window window,int x,int y)
+%
+% A description of each parameter follows:
+%
+% o subwindow: XGetSubwindow() returns NULL if no subwindow is found
+% otherwise the subwindow is returned.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window.
+%
+% o x: the x coordinate of the pointer relative to the origin of the
+% window.
+%
+% o y: the y coordinate of the pointer relative to the origin of the
+% window.
+%
+%
+*/
+static Window XGetSubwindow(Display *display,Window window,int x,int y)
+{
+ int
+ x_offset,
+ y_offset;
+
+ Status
+ status;
+
+ Window
+ source_window,
+ target_window;
+
+ assert(display != (Display *) NULL);
+ source_window=XRootWindow(display,XDefaultScreen(display));
+ if (window == (Window) NULL)
+ return(source_window);
+ target_window=window;
+ for ( ; ; )
+ {
+ status=XTranslateCoordinates(display,source_window,window,x,y,
+ &x_offset,&y_offset,&target_window);
+ if (status != True)
+ break;
+ if (target_window == (Window) NULL)
+ break;
+ source_window=window;
+ window=target_window;
+ x=x_offset;
+ y=y_offset;
+ }
+ if (target_window == (Window) NULL)
+ target_window=window;
+ return(target_window);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t W i n d o w C o l o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetWindowColor() returns the color of a pixel interactively chosen from the
+% X server.
+%
+% The format of the XGetWindowColor method is:
+%
+% MagickBooleanType XGetWindowColor(Display *display,XWindows *windows,
+% char *name)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o name: the name of the color if found in the X Color Database is
+% returned in this character string.
+%
+*/
+MagickExport MagickBooleanType XGetWindowColor(Display *display,
+ XWindows *windows,char *name)
+{
+ int
+ x,
+ y;
+
+ PixelPacket
+ pixel;
+
+ RectangleInfo
+ crop_info;
+
+ Status
+ status;
+
+ Window
+ child,
+ client_window,
+ root_window,
+ target_window;
+
+ XColor
+ color;
+
+ XImage
+ *ximage;
+
+ XWindowAttributes
+ window_attributes;
+
+ /*
+ Choose a pixel from the X server.
+ */
+ assert(display != (Display *) NULL);
+ assert(name != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
+ *name='\0';
+ target_window=XSelectWindow(display,&crop_info);
+ if (target_window == (Window) NULL)
+ return(MagickFalse);
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ client_window=target_window;
+ if (target_window != root_window)
+ {
+ unsigned int
+ d;
+
+ /*
+ Get client window.
+ */
+ status=XGetGeometry(display,target_window,&root_window,&x,&x,&d,&d,&d,&d);
+ if (status != False)
+ {
+ client_window=XClientWindow(display,target_window);
+ target_window=client_window;
+ }
+ }
+ /*
+ Verify window is viewable.
+ */
+ status=XGetWindowAttributes(display,target_window,&window_attributes);
+ if ((status == False) || (window_attributes.map_state != IsViewable))
+ return(MagickFalse);
+ /*
+ Get window X image.
+ */
+ (void) XTranslateCoordinates(display,root_window,target_window,
+ (int) crop_info.x,(int) crop_info.y,&x,&y,&child);
+ ximage=XGetImage(display,target_window,x,y,1,1,AllPlanes,ZPixmap);
+ if (ximage == (XImage *) NULL)
+ return(MagickFalse);
+ color.pixel=XGetPixel(ximage,0,0);
+ XDestroyImage(ximage);
+ /*
+ Match color against the color database.
+ */
+ (void) XQueryColor(display,window_attributes.colormap,&color);
+ pixel.red=ScaleShortToQuantum(color.red);
+ pixel.green=ScaleShortToQuantum(color.green);
+ pixel.blue=ScaleShortToQuantum(color.blue);
+ pixel.opacity=OpaqueOpacity;
+ (void) QueryColorname(windows->image.image,&pixel,X11Compliance,name,
+ &windows->image.image->exception);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X G e t W i n d o w I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetWindowImage() reads an image from the target X window and returns it.
+% XGetWindowImage() optionally descends the window hierarchy and overlays the
+% target image with each child image in an optimized fashion. Any child
+% window that have the same visual, colormap, and are contained by its parent
+% are exempted.
+%
+% The format of the XGetWindowImage method is:
+%
+% Image *XGetWindowImage(Display *display,const Window window,
+% const unsigned int borders,const unsigned int level)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies the window to obtain the image from.
+%
+% o borders: Specifies whether borders pixels are to be saved with
+% the image.
+%
+% o level: Specifies an unsigned integer representing the level of
+% decent in the window hierarchy. This value must be zero or one on
+% the initial call to XGetWindowImage. A value of zero returns after
+% one call. A value of one causes the function to descend the window
+% hierarchy and overlay the target image with each subwindow image.
+%
+%
+*/
+static Image *XGetWindowImage(Display *display,const Window window,
+ const unsigned int borders,const unsigned int level)
+{
+ typedef struct _ColormapInfo
+ {
+ Colormap
+ colormap;
+
+ XColor
+ *colors;
+
+ struct _ColormapInfo
+ *next;
+ } ColormapInfo;
+
+ typedef struct _WindowInfo
+ {
+ Window
+ window,
+ parent;
+
+ Visual
+ *visual;
+
+ Colormap
+ colormap;
+
+ XSegment
+ bounds;
+
+ RectangleInfo
+ crop_info;
+ } WindowInfo;
+
+ IndexPacket
+ index;
+
+ int
+ display_height,
+ display_width,
+ id,
+ x_offset,
+ y_offset;
+
+ RectangleInfo
+ crop_info;
+
+ register IndexPacket
+ *indexes;
+
+ register int
+ i;
+
+ static ColormapInfo
+ *colormap_info = (ColormapInfo *) NULL;
+
+ static int
+ max_windows = 0,
+ number_windows = 0;
+
+ static WindowInfo
+ *window_info;
+
+ Status
+ status;
+
+ Window
+ child,
+ root_window;
+
+ XWindowAttributes
+ window_attributes;
+
+ /*
+ Verify window is viewable.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ status=XGetWindowAttributes(display,window,&window_attributes);
+ if ((status == False) || (window_attributes.map_state != IsViewable))
+ return((Image *) NULL);
+ /*
+ Cropping rectangle is relative to root window.
+ */
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ (void) XTranslateCoordinates(display,window,root_window,0,0,&x_offset,
+ &y_offset,&child);
+ crop_info.x=(long) x_offset;
+ crop_info.y=(long) y_offset;
+ crop_info.width=(unsigned long) window_attributes.width;
+ crop_info.height=(unsigned long) window_attributes.height;
+ if (borders != MagickFalse)
+ {
+ /*
+ Include border in image.
+ */
+ crop_info.x-=(long) window_attributes.border_width;
+ crop_info.y-=(long) window_attributes.border_width;
+ crop_info.width+=(unsigned long) (window_attributes.border_width << 1);
+ crop_info.height+=(unsigned long) (window_attributes.border_width << 1);
+ }
+ /*
+ Crop to root window.
+ */
+ if (crop_info.x < 0)
+ {
+ crop_info.width+=crop_info.x;
+ crop_info.x=0;
+ }
+ if (crop_info.y < 0)
+ {
+ crop_info.height+=crop_info.y;
+ crop_info.y=0;
+ }
+ display_width=XDisplayWidth(display,XDefaultScreen(display));
+ if ((int) (crop_info.x+crop_info.width) > display_width)
+ crop_info.width=(unsigned long) (display_width-crop_info.x);
+ display_height=XDisplayHeight(display,XDefaultScreen(display));
+ if ((int) (crop_info.y+crop_info.height) > display_height)
+ crop_info.height=(unsigned long) (display_height-crop_info.y);
+ /*
+ Initialize window info attributes.
+ */
+ if (number_windows >= max_windows)
+ {
+ /*
+ Allocate or resize window info buffer.
+ */
+ max_windows+=1024;
+ if (window_info == (WindowInfo *) NULL)
+ window_info=(WindowInfo *) AcquireQuantumMemory((size_t) max_windows,
+ sizeof(*window_info));
+ else
+ window_info=(WindowInfo *) ResizeQuantumMemory(window_info,(size_t)
+ max_windows,sizeof(*window_info));
+ }
+ if (window_info == (WindowInfo *) NULL)
+ {
+ ThrowXWindowFatalException(ResourceLimitError,
+ "MemoryAllocationFailed","...");
+ return((Image *) NULL);
+ }
+ id=number_windows++;
+ window_info[id].window=window;
+ window_info[id].visual=window_attributes.visual;
+ window_info[id].colormap=window_attributes.colormap;
+ window_info[id].bounds.x1=(short) crop_info.x;
+ window_info[id].bounds.y1=(short) crop_info.y;
+ window_info[id].bounds.x2=(short) (crop_info.x+(int) crop_info.width-1);
+ window_info[id].bounds.y2=(short) (crop_info.y+(int) crop_info.height-1);
+ crop_info.x-=x_offset;
+ crop_info.y-=y_offset;
+ window_info[id].crop_info=crop_info;
+ if (level != 0)
+ {
+ unsigned int
+ number_children;
+
+ Window
+ *children;
+
+ /*
+ Descend the window hierarchy.
+ */
+ status=XQueryTree(display,window,&root_window,&window_info[id].parent,
+ &children,&number_children);
+ for (i=0; i < id; i++)
+ if ((window_info[i].window == window_info[id].parent) &&
+ (window_info[i].visual == window_info[id].visual) &&
+ (window_info[i].colormap == window_info[id].colormap))
+ {
+ if ((window_info[id].bounds.x1 <= window_info[i].bounds.x1) ||
+ (window_info[id].bounds.x1 >= window_info[i].bounds.x2) ||
+ (window_info[id].bounds.y1 <= window_info[i].bounds.y1) ||
+ (window_info[id].bounds.y1 >= window_info[i].bounds.y2))
+ {
+ /*
+ Eliminate windows not circumscribed by their parent.
+ */
+ number_windows--;
+ break;
+ }
+ }
+ if ((status == True) && (number_children != 0))
+ {
+ for (i=0; i < (int) number_children; i++)
+ (void) XGetWindowImage(display,children[i],MagickFalse,level+1);
+ (void) XFree((void *) children);
+ }
+ }
+ if (level <= 1)
+ {
+ ColormapInfo
+ *next;
+
+ ExceptionInfo
+ *exception;
+
+ Image
+ *composite_image,
+ *image;
+
+ int
+ y;
+
+ MagickBooleanType
+ import;
+
+ register int
+ j,
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ register unsigned long
+ pixel;
+
+ unsigned int
+ number_colors;
+
+ XColor
+ *colors;
+
+ XImage
+ *ximage;
+
+ /*
+ Get X image for each window in the list.
+ */
+ image=NewImageList();
+ for (id=0; id < number_windows; id++)
+ {
+ /*
+ Does target window intersect top level window?
+ */
+ import=
+ ((window_info[id].bounds.x2 >= window_info[0].bounds.x1) &&
+ (window_info[id].bounds.x1 <= window_info[0].bounds.x2) &&
+ (window_info[id].bounds.y2 >= window_info[0].bounds.y1) &&
+ (window_info[id].bounds.y1 <= window_info[0].bounds.y2)) ?
+ MagickTrue : MagickFalse;
+ /*
+ Is target window contained by another window with the same colormap?
+ */
+ for (j=0; j < id; j++)
+ if ((window_info[id].visual == window_info[j].visual) &&
+ (window_info[id].colormap == window_info[j].colormap))
+ {
+ if ((window_info[id].bounds.x1 <= window_info[j].bounds.x1) ||
+ (window_info[id].bounds.x1 >= window_info[j].bounds.x2) ||
+ (window_info[id].bounds.y1 <= window_info[j].bounds.y1) ||
+ (window_info[id].bounds.y1 >= window_info[j].bounds.y2))
+ import=MagickFalse;
+ }
+ else
+ if ((window_info[id].visual != window_info[j].visual) ||
+ (window_info[id].colormap != window_info[j].colormap))
+ {
+ if ((window_info[id].bounds.x2 > window_info[j].bounds.x1) &&
+ (window_info[id].bounds.x1 < window_info[j].bounds.x2) &&
+ (window_info[id].bounds.y2 > window_info[j].bounds.y1) &&
+ (window_info[id].bounds.y1 < window_info[j].bounds.y2))
+ import=MagickTrue;
+ }
+ if (import == MagickFalse)
+ continue;
+ /*
+ Get X image.
+ */
+ ximage=XGetImage(display,window_info[id].window,(int)
+ window_info[id].crop_info.x,(int) window_info[id].crop_info.y,
+ (unsigned int) window_info[id].crop_info.width,(unsigned int)
+ window_info[id].crop_info.height,AllPlanes,ZPixmap);
+ if (ximage == (XImage *) NULL)
+ continue;
+ /*
+ Initialize window colormap.
+ */
+ number_colors=0;
+ colors=(XColor *) NULL;
+ if (window_info[id].colormap != (Colormap) NULL)
+ {
+ ColormapInfo
+ *p;
+
+ /*
+ Search colormap list for window colormap.
+ */
+ number_colors=(unsigned int) window_info[id].visual->map_entries;
+ for (p=colormap_info; p != (ColormapInfo *) NULL; p=p->next)
+ if (p->colormap == window_info[id].colormap)
+ break;
+ if (p == (ColormapInfo *) NULL)
+ {
+ /*
+ Get the window colormap.
+ */
+ colors=(XColor *) AcquireQuantumMemory(number_colors,
+ sizeof(*colors));
+ if (colors == (XColor *) NULL)
+ {
+ XDestroyImage(ximage);
+ return((Image *) NULL);
+ }
+ if ((window_info[id].visual->klass != DirectColor) &&
+ (window_info[id].visual->klass != TrueColor))
+ for (i=0; i < (int) number_colors; i++)
+ {
+ colors[i].pixel=(unsigned long) i;
+ colors[i].pad='\0';
+ }
+ else
+ {
+ unsigned long
+ blue,
+ blue_bit,
+ green,
+ green_bit,
+ red,
+ red_bit;
+
+ /*
+ DirectColor or TrueColor visual.
+ */
+ red=0;
+ green=0;
+ blue=0;
+ red_bit=window_info[id].visual->red_mask &
+ (~(window_info[id].visual->red_mask)+1);
+ green_bit=window_info[id].visual->green_mask &
+ (~(window_info[id].visual->green_mask)+1);
+ blue_bit=window_info[id].visual->blue_mask &
+ (~(window_info[id].visual->blue_mask)+1);
+ for (i=0; i < (int) number_colors; i++)
+ {
+ colors[i].pixel=red | green | blue;
+ colors[i].pad='\0';
+ red+=red_bit;
+ if (red > window_info[id].visual->red_mask)
+ red=0;
+ green+=green_bit;
+ if (green > window_info[id].visual->green_mask)
+ green=0;
+ blue+=blue_bit;
+ if (blue > window_info[id].visual->blue_mask)
+ blue=0;
+ }
+ }
+ (void) XQueryColors(display,window_info[id].colormap,colors,
+ (int) number_colors);
+ /*
+ Append colormap to colormap list.
+ */
+ p=(ColormapInfo *) AcquireMagickMemory(sizeof(*p));
+ if (p == (ColormapInfo *) NULL)
+ return((Image *) NULL);
+ p->colormap=window_info[id].colormap;
+ p->colors=colors;
+ p->next=colormap_info;
+ colormap_info=p;
+ }
+ colors=p->colors;
+ }
+ /*
+ Allocate image structure.
+ */
+ composite_image=AcquireImage((ImageInfo *) NULL);
+ if (composite_image == (Image *) NULL)
+ {
+ XDestroyImage(ximage);
+ return((Image *) NULL);
+ }
+ /*
+ Convert X image to MIFF format.
+ */
+ if ((window_info[id].visual->klass != TrueColor) &&
+ (window_info[id].visual->klass != DirectColor))
+ composite_image->storage_class=PseudoClass;
+ composite_image->columns=(unsigned long) ximage->width;
+ composite_image->rows=(unsigned long) ximage->height;
+ exception=(&composite_image->exception);
+ switch (composite_image->storage_class)
+ {
+ case DirectClass:
+ default:
+ {
+ register unsigned long
+ color,
+ index;
+
+ unsigned long
+ blue_mask,
+ blue_shift,
+ green_mask,
+ green_shift,
+ red_mask,
+ red_shift;
+
+ /*
+ Determine shift and mask for red, green, and blue.
+ */
+ red_mask=window_info[id].visual->red_mask;
+ red_shift=0;
+ while ((red_mask != 0) && ((red_mask & 0x01) == 0))
+ {
+ red_mask>>=1;
+ red_shift++;
+ }
+ green_mask=window_info[id].visual->green_mask;
+ green_shift=0;
+ while ((green_mask != 0) && ((green_mask & 0x01) == 0))
+ {
+ green_mask>>=1;
+ green_shift++;
+ }
+ blue_mask=window_info[id].visual->blue_mask;
+ blue_shift=0;
+ while ((blue_mask != 0) && ((blue_mask & 0x01) == 0))
+ {
+ blue_mask>>=1;
+ blue_shift++;
+ }
+ /*
+ Convert X image to DirectClass packets.
+ */
+ if ((number_colors != 0) &&
+ (window_info[id].visual->klass == DirectColor))
+ for (y=0; y < (int) composite_image->rows; y++)
+ {
+ q=QueueAuthenticPixels(composite_image,0,y,
+ composite_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) composite_image->columns; x++)
+ {
+ pixel=XGetPixel(ximage,x,y);
+ index=(pixel >> red_shift) & red_mask;
+ q->red=ScaleShortToQuantum(colors[index].red);
+ index=(pixel >> green_shift) & green_mask;
+ q->green=ScaleShortToQuantum(colors[index].green);
+ index=(pixel >> blue_shift) & blue_mask;
+ q->blue=ScaleShortToQuantum(colors[index].blue);
+ q++;
+ }
+ if (SyncAuthenticPixels(composite_image,exception) == MagickFalse)
+ break;
+ }
+ else
+ for (y=0; y < (int) composite_image->rows; y++)
+ {
+ q=QueueAuthenticPixels(composite_image,0,y,
+ composite_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) composite_image->columns; x++)
+ {
+ pixel=XGetPixel(ximage,x,y);
+ color=(pixel >> red_shift) & red_mask;
+ color=(65535UL*color)/red_mask;
+ q->red=ScaleShortToQuantum((unsigned short) color);
+ color=(pixel >> green_shift) & green_mask;
+ color=(65535UL*color)/green_mask;
+ q->green=ScaleShortToQuantum((unsigned short) color);
+ color=(pixel >> blue_shift) & blue_mask;
+ color=(65535UL*color)/blue_mask;
+ q->blue=ScaleShortToQuantum((unsigned short) color);
+ q++;
+ }
+ if (SyncAuthenticPixels(composite_image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ case PseudoClass:
+ {
+ /*
+ Create colormap.
+ */
+ if (AcquireImageColormap(composite_image,number_colors) == MagickFalse)
+ {
+ XDestroyImage(ximage);
+ composite_image=DestroyImage(composite_image);
+ return((Image *) NULL);
+ }
+ for (i=0; i < (int) composite_image->colors; i++)
+ {
+ composite_image->colormap[colors[i].pixel].red=
+ ScaleShortToQuantum(colors[i].red);
+ composite_image->colormap[colors[i].pixel].green=
+ ScaleShortToQuantum(colors[i].green);
+ composite_image->colormap[colors[i].pixel].blue=
+ ScaleShortToQuantum(colors[i].blue);
+ }
+ /*
+ Convert X image to PseudoClass packets.
+ */
+ for (y=0; y < (int) composite_image->rows; y++)
+ {
+ q=QueueAuthenticPixels(composite_image,0,y,composite_image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(composite_image);
+ for (x=0; x < (int) composite_image->columns; x++)
+ {
+ index=(IndexPacket) XGetPixel(ximage,x,y);
+ indexes[x]=index;
+ *q++=composite_image->colormap[(long) index];
+ }
+ if (SyncAuthenticPixels(composite_image,exception) == MagickFalse)
+ break;
+ }
+ break;
+ }
+ }
+ XDestroyImage(ximage);
+ if (image == (Image *) NULL)
+ {
+ image=composite_image;
+ continue;
+ }
+ /*
+ Composite any children in back-to-front order.
+ */
+ (void) XTranslateCoordinates(display,window_info[id].window,window,0,0,
+ &x_offset,&y_offset,&child);
+ x_offset-=(int) crop_info.x;
+ if (x_offset < 0)
+ x_offset=0;
+ y_offset-=(int) crop_info.y;
+ if (y_offset < 0)
+ y_offset=0;
+ (void) CompositeImage(image,CopyCompositeOp,composite_image,x_offset,
+ y_offset);
+ }
+ /*
+ Relinquish resources.
+ */
+ while (colormap_info != (ColormapInfo *) NULL)
+ {
+ next=colormap_info->next;
+ colormap_info->colors=(XColor *)
+ RelinquishMagickMemory(colormap_info->colors);
+ colormap_info=(ColormapInfo *) RelinquishMagickMemory(colormap_info);
+ colormap_info=next;
+ }
+ /*
+ Relinquish resources and restore initial state.
+ */
+ window_info=(WindowInfo *) RelinquishMagickMemory(window_info);
+ max_windows=0;
+ number_windows=0;
+ colormap_info=(ColormapInfo *) NULL;
+ return(image);
+ }
+ return((Image *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t W i n d o w I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetWindowInfo() initializes the XWindowInfo structure.
+%
+% The format of the XGetWindowInfo method is:
+%
+% void XGetWindowInfo(Display *display,XVisualInfo *visual_info,
+% XStandardColormap *map_info,XPixelInfo *pixel,XFontStruct *font_info,
+% XResourceInfo *resource_info,XWindowInfo *window)
+% resource_info,window)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o map_info: If map_type is specified, this structure is initialized
+% with info from the Standard Colormap.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+% o font_info: Specifies a pointer to a XFontStruct structure.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+MagickExport void XGetWindowInfo(Display *display,XVisualInfo *visual_info,
+ XStandardColormap *map_info,XPixelInfo *pixel,XFontStruct *font_info,
+ XResourceInfo *resource_info,XWindowInfo *window)
+{
+ /*
+ Initialize window info.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(visual_info != (XVisualInfo *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ assert(pixel != (XPixelInfo *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ if (window->id != (Window) NULL)
+ {
+ if (window->cursor != (Cursor) NULL)
+ (void) XFreeCursor(display,window->cursor);
+ if (window->busy_cursor != (Cursor) NULL)
+ (void) XFreeCursor(display,window->busy_cursor);
+ if (window->highlight_stipple != (Pixmap) NULL)
+ (void) XFreePixmap(display,window->highlight_stipple);
+ if (window->shadow_stipple != (Pixmap) NULL)
+ (void) XFreePixmap(display,window->shadow_stipple);
+ if (window->name == (char *) NULL)
+ window->name=AcquireString("");
+ if (window->icon_name == (char *) NULL)
+ window->icon_name=AcquireString("");
+ }
+ else
+ {
+ /*
+ Initialize these attributes just once.
+ */
+ window->id=(Window) NULL;
+ if (window->name == (char *) NULL)
+ window->name=AcquireString("");
+ if (window->icon_name == (char *) NULL)
+ window->icon_name=AcquireString("");
+ window->x=XDisplayWidth(display,visual_info->screen) >> 1;
+ window->y=XDisplayWidth(display,visual_info->screen) >> 1;
+ window->ximage=(XImage *) NULL;
+ window->matte_image=(XImage *) NULL;
+ window->pixmap=(Pixmap) NULL;
+ window->matte_pixmap=(Pixmap) NULL;
+ window->mapped=MagickFalse;
+ window->stasis=MagickFalse;
+ window->shared_memory=MagickTrue;
+ window->segment_info=(void *) NULL;
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ {
+ XShmSegmentInfo
+ *segment_info;
+
+ if (window->segment_info == (void *) NULL)
+ window->segment_info=AcquireQuantumMemory(2,sizeof(*segment_info));
+ segment_info=(XShmSegmentInfo *) window->segment_info;
+ segment_info[0].shmid=(-1);
+ segment_info[0].shmaddr=(char *) NULL;
+ segment_info[1].shmid=(-1);
+ segment_info[1].shmaddr=(char *) NULL;
+ }
+#endif
+ }
+ /*
+ Initialize these attributes every time function is called.
+ */
+ window->screen=visual_info->screen;
+ window->root=XRootWindow(display,visual_info->screen);
+ window->visual=visual_info->visual;
+ window->storage_class=(unsigned int) visual_info->klass;
+ window->depth=(unsigned int) visual_info->depth;
+ window->visual_info=visual_info;
+ window->map_info=map_info;
+ window->pixel_info=pixel;
+ window->font_info=font_info;
+ window->cursor=XCreateFontCursor(display,XC_left_ptr);
+ window->busy_cursor=XCreateFontCursor(display,XC_watch);
+ window->geometry=(char *) NULL;
+ window->icon_geometry=(char *) NULL;
+ if (resource_info->icon_geometry != (char *) NULL)
+ (void) CloneString(&window->icon_geometry,resource_info->icon_geometry);
+ window->crop_geometry=(char *) NULL;
+ window->flags=(unsigned long) PSize;
+ window->width=1;
+ window->height=1;
+ window->min_width=1;
+ window->min_height=1;
+ window->width_inc=1;
+ window->height_inc=1;
+ window->border_width=resource_info->border_width;
+ window->annotate_context=pixel->annotate_context;
+ window->highlight_context=pixel->highlight_context;
+ window->widget_context=pixel->widget_context;
+ window->shadow_stipple=(Pixmap) NULL;
+ window->highlight_stipple=(Pixmap) NULL;
+ window->use_pixmap=MagickTrue;
+ window->immutable=MagickFalse;
+ window->shape=MagickFalse;
+ window->data=0;
+ window->mask=(unsigned long) (CWBackingStore | CWBackPixel | CWBackPixmap |
+ CWBitGravity | CWBorderPixel | CWColormap | CWCursor | CWDontPropagate |
+ CWEventMask | CWOverrideRedirect | CWSaveUnder | CWWinGravity);
+ window->attributes.background_pixel=pixel->background_color.pixel;
+ window->attributes.background_pixmap=(Pixmap) NULL;
+ window->attributes.bit_gravity=ForgetGravity;
+ window->attributes.backing_store=WhenMapped;
+ window->attributes.save_under=MagickTrue;
+ window->attributes.border_pixel=pixel->border_color.pixel;
+ window->attributes.colormap=map_info->colormap;
+ window->attributes.cursor=window->cursor;
+ window->attributes.do_not_propagate_mask=NoEventMask;
+ window->attributes.event_mask=NoEventMask;
+ window->attributes.override_redirect=MagickFalse;
+ window->attributes.win_gravity=NorthWestGravity;
+ window->orphan=MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X H i g h l i g h t E l l i p s e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XHighlightEllipse() puts a border on the X server around a region defined by
+% highlight_info.
+%
+% The format of the XHighlightEllipse method is:
+%
+% void XHighlightEllipse(Display *display,Window window,
+% GC annotate_context,const RectangleInfo *highlight_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window structure.
+%
+% o annotate_context: Specifies a pointer to a GC structure.
+%
+% o highlight_info: Specifies a pointer to a RectangleInfo structure. It
+% contains the extents of any highlighting rectangle.
+%
+*/
+MagickExport void XHighlightEllipse(Display *display,Window window,
+ GC annotate_context,const RectangleInfo *highlight_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ assert(annotate_context != (GC) NULL);
+ assert(highlight_info != (RectangleInfo *) NULL);
+ if ((highlight_info->width < 4) || (highlight_info->height < 4))
+ return;
+ (void) XDrawArc(display,window,annotate_context,(int) highlight_info->x,
+ (int) highlight_info->y,(unsigned int) highlight_info->width-1,
+ (unsigned int) highlight_info->height-1,0,360*64);
+ (void) XDrawArc(display,window,annotate_context,(int) highlight_info->x+1,
+ (int) highlight_info->y+1,(unsigned int) highlight_info->width-3,
+ (unsigned int) highlight_info->height-3,0,360*64);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X H i g h l i g h t L i n e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XHighlightLine() puts a border on the X server around a region defined by
+% highlight_info.
+%
+% The format of the XHighlightLine method is:
+%
+% void XHighlightLine(Display *display,Window window,GC annotate_context,
+% const XSegment *highlight_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window structure.
+%
+% o annotate_context: Specifies a pointer to a GC structure.
+%
+% o highlight_info: Specifies a pointer to a RectangleInfo structure. It
+% contains the extents of any highlighting rectangle.
+%
+*/
+MagickExport void XHighlightLine(Display *display,Window window,
+ GC annotate_context,const XSegment *highlight_info)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ assert(annotate_context != (GC) NULL);
+ assert(highlight_info != (XSegment *) NULL);
+ (void) XDrawLine(display,window,annotate_context,highlight_info->x1,
+ highlight_info->y1,highlight_info->x2,highlight_info->y2);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X H i g h l i g h t R e c t a n g l e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XHighlightRectangle() puts a border on the X server around a region defined
+% by highlight_info.
+%
+% The format of the XHighlightRectangle method is:
+%
+% void XHighlightRectangle(Display *display,Window window,
+% GC annotate_context,const RectangleInfo *highlight_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window structure.
+%
+% o annotate_context: Specifies a pointer to a GC structure.
+%
+% o highlight_info: Specifies a pointer to a RectangleInfo structure. It
+% contains the extents of any highlighting rectangle.
+%
+*/
+MagickExport void XHighlightRectangle(Display *display,Window window,
+ GC annotate_context,const RectangleInfo *highlight_info)
+{
+ assert(display != (Display *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(window != (Window) NULL);
+ assert(annotate_context != (GC) NULL);
+ assert(highlight_info != (RectangleInfo *) NULL);
+ if ((highlight_info->width < 4) || (highlight_info->height < 4))
+ return;
+ (void) XDrawRectangle(display,window,annotate_context,(int) highlight_info->x,
+ (int) highlight_info->y,(unsigned int) highlight_info->width-1,
+ (unsigned int) highlight_info->height-1);
+ (void) XDrawRectangle(display,window,annotate_context,(int) highlight_info->x+
+ 1,(int) highlight_info->y+1,(unsigned int) highlight_info->width-3,
+ (unsigned int) highlight_info->height-3);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X I m p o r t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XImportImage() reads an image from an X window.
+%
+% The format of the XImportImage method is:
+%
+% Image *XImportImage(const ImageInfo *image_info,XImportInfo *ximage_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info.
+%
+% o ximage_info: Specifies a pointer to an XImportInfo structure.
+%
+*/
+MagickExport Image *XImportImage(const ImageInfo *image_info,
+ XImportInfo *ximage_info)
+{
+ Colormap
+ *colormaps;
+
+ Display
+ *display;
+
+ Image
+ *image;
+
+ int
+ number_colormaps,
+ number_windows,
+ x;
+
+ RectangleInfo
+ crop_info;
+
+ Status
+ status;
+
+ Window
+ *children,
+ client,
+ prior_target,
+ root,
+ target;
+
+ XTextProperty
+ window_name;
+
+ /*
+ Open X server connection.
+ */
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(ximage_info != (XImportInfo *) NULL);
+ display=XOpenDisplay(image_info->server_name);
+ if (display == (Display *) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToOpenXServer",
+ XDisplayName(image_info->server_name));
+ return((Image *) NULL);
+ }
+ /*
+ Set our forgiving exception handler.
+ */
+ (void) XSetErrorHandler(XError);
+ /*
+ Select target window.
+ */
+ crop_info.x=0;
+ crop_info.y=0;
+ crop_info.width=0;
+ crop_info.height=0;
+ root=XRootWindow(display,XDefaultScreen(display));
+ target=(Window) NULL;
+ if ((image_info->filename != (char *) NULL) &&
+ (*image_info->filename != '\0'))
+ {
+ if (LocaleCompare(image_info->filename,"root") == 0)
+ target=root;
+ else
+ {
+ /*
+ Select window by ID or name.
+ */
+ if (isdigit((unsigned char) *image_info->filename) != 0)
+ target=XWindowByID(display,root,(Window)
+ strtol(image_info->filename,(char **) NULL,0));
+ if (target == (Window) NULL)
+ target=XWindowByName(display,root,image_info->filename);
+ if (target == (Window) NULL)
+ ThrowXWindowFatalException(XServerError,
+ "NoWindowWithSpecifiedIDExists",image_info->filename);
+ }
+ }
+ /*
+ If target window is not defined, interactively select one.
+ */
+ prior_target=target;
+ if (target == (Window) NULL)
+ target=XSelectWindow(display,&crop_info);
+ if (target == (Window) NULL)
+ ThrowXWindowFatalException(XServerError,"UnableToReadXWindowImage",
+ image_info->filename);
+ client=target; /* obsolete */
+ if (target != root)
+ {
+ unsigned int
+ d;
+
+ status=XGetGeometry(display,target,&root,&x,&x,&d,&d,&d,&d);
+ if (status != False)
+ {
+ for ( ; ; )
+ {
+ Window
+ parent;
+
+ /*
+ Find window manager frame.
+ */
+ status=XQueryTree(display,target,&root,&parent,&children,&d);
+ if ((status != False) && (children != (Window *) NULL))
+ (void) XFree((char *) children);
+ if ((status == False) || (parent == (Window) NULL) ||
+ (parent == root))
+ break;
+ target=parent;
+ }
+ /*
+ Get client window.
+ */
+ client=XClientWindow(display,target);
+ if (ximage_info->frame == MagickFalse)
+ target=client;
+ if ((ximage_info->frame == MagickFalse) &&
+ (prior_target != MagickFalse))
+ target=prior_target;
+ XDelay(display,SuspendTime << 4);
+ }
+ }
+ if (ximage_info->screen)
+ {
+ int
+ y;
+
+ Window
+ child;
+
+ XWindowAttributes
+ window_attributes;
+
+ /*
+ Obtain window image directly from screen.
+ */
+ status=XGetWindowAttributes(display,target,&window_attributes);
+ if (status == False)
+ {
+ ThrowXWindowFatalException(XServerError,
+ "UnableToReadXWindowAttributes",image_info->filename);
+ (void) XCloseDisplay(display);
+ return((Image *) NULL);
+ }
+ (void) XTranslateCoordinates(display,target,root,0,0,&x,&y,&child);
+ crop_info.x=x;
+ crop_info.y=y;
+ crop_info.width=(unsigned long) window_attributes.width;
+ crop_info.height=(unsigned long) window_attributes.height;
+ if (ximage_info->borders)
+ {
+ /*
+ Include border in image.
+ */
+ crop_info.x-=window_attributes.border_width;
+ crop_info.y-=window_attributes.border_width;
+ crop_info.width+=window_attributes.border_width << 1;
+ crop_info.height+=window_attributes.border_width << 1;
+ }
+ target=root;
+ }
+ /*
+ If WM_COLORMAP_WINDOWS property is set or multiple colormaps, descend.
+ */
+ number_windows=0;
+ status=XGetWMColormapWindows(display,target,&children,&number_windows);
+ if ((status == True) && (number_windows > 0))
+ {
+ ximage_info->descend=MagickTrue;
+ (void) XFree ((char *) children);
+ }
+ colormaps=XListInstalledColormaps(display,target,&number_colormaps);
+ if (number_colormaps > 0)
+ {
+ if (number_colormaps > 1)
+ ximage_info->descend=MagickTrue;
+ (void) XFree((char *) colormaps);
+ }
+ /*
+ Alert the user not to alter the screen.
+ */
+ if (ximage_info->silent == MagickFalse)
+ (void) XBell(display,0);
+ /*
+ Get image by window id.
+ */
+ (void) XGrabServer(display);
+ image=XGetWindowImage(display,target,ximage_info->borders,
+ ximage_info->descend ? 1U : 0U);
+ (void) XUngrabServer(display);
+ if (image == (Image *) NULL)
+ ThrowXWindowFatalException(XServerError,"UnableToReadXWindowImage",
+ image_info->filename)
+ else
+ {
+ (void) CopyMagickString(image->filename,image_info->filename,
+ MaxTextExtent);
+ if ((crop_info.width != 0) && (crop_info.height != 0))
+ {
+ Image
+ *clone_image,
+ *crop_image;
+
+ /*
+ Crop image as defined by the cropping rectangle.
+ */
+ clone_image=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (clone_image != (Image *) NULL)
+ {
+ crop_image=CropImage(clone_image,&crop_info,&image->exception);
+ if (crop_image != (Image *) NULL)
+ {
+ image=DestroyImage(image);
+ image=crop_image;
+ }
+ }
+ }
+ status=XGetWMName(display,target,&window_name);
+ if (status == True)
+ {
+ if ((image_info->filename != (char *) NULL) &&
+ (*image_info->filename == '\0'))
+ (void) CopyMagickString(image->filename,(char *) window_name.value,
+ (size_t) window_name.nitems+1);
+ (void) XFree((void *) window_name.value);
+ }
+ }
+ if (ximage_info->silent == MagickFalse)
+ {
+ /*
+ Alert the user we're done.
+ */
+ (void) XBell(display,0);
+ (void) XBell(display,0);
+ }
+ (void) XCloseDisplay(display);
+ return(image);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X I n i t i a l i z e W i n d o w s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XInitializeWindows() initializes the XWindows structure.
+%
+% The format of the XInitializeWindows method is:
+%
+% XWindows *XInitializeWindows(Display *display,
+% XResourceInfo *resource_info)
+%
+% A description of each parameter follows:
+%
+% o windows: XInitializeWindows returns a pointer to a XWindows structure.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+MagickExport XWindows *XInitializeWindows(Display *display,
+ XResourceInfo *resource_info)
+{
+ Window
+ root_window;
+
+ XWindows
+ *windows;
+
+ /*
+ Allocate windows structure.
+ */
+ windows=(XWindows *) AcquireMagickMemory(sizeof(*windows));
+ if (windows == (XWindows *) NULL)
+ {
+ ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
+ "...");
+ return((XWindows *) NULL);
+ }
+ (void) ResetMagickMemory(windows,0,sizeof(*windows));
+ windows->pixel_info=(XPixelInfo *) AcquireMagickMemory(
+ sizeof(*windows->pixel_info));
+ windows->icon_pixel=(XPixelInfo *) AcquireMagickMemory(
+ sizeof(*windows->icon_pixel));
+ windows->icon_resources=(XResourceInfo *) AcquireMagickMemory(
+ sizeof(*windows->icon_resources));
+ if ((windows->pixel_info == (XPixelInfo *) NULL) ||
+ (windows->icon_pixel == (XPixelInfo *) NULL) ||
+ (windows->icon_resources == (XResourceInfo *) NULL))
+ {
+ ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
+ "...");
+ return((XWindows *) NULL);
+ }
+ /*
+ Initialize windows structure.
+ */
+ windows->display=display;
+ windows->wm_protocols=XInternAtom(display,"WM_PROTOCOLS",MagickFalse);
+ windows->wm_delete_window=XInternAtom(display,"WM_DELETE_WINDOW",MagickFalse);
+ windows->wm_take_focus=XInternAtom(display,"WM_TAKE_FOCUS",MagickFalse);
+ windows->im_protocols=XInternAtom(display,"IM_PROTOCOLS",MagickFalse);
+ windows->im_remote_command=
+ XInternAtom(display,"IM_REMOTE_COMMAND",MagickFalse);
+ windows->im_update_widget=XInternAtom(display,"IM_UPDATE_WIDGET",MagickFalse);
+ windows->im_update_colormap=
+ XInternAtom(display,"IM_UPDATE_COLORMAP",MagickFalse);
+ windows->im_former_image=XInternAtom(display,"IM_FORMER_IMAGE",MagickFalse);
+ windows->im_next_image=XInternAtom(display,"IM_NEXT_IMAGE",MagickFalse);
+ windows->im_retain_colors=XInternAtom(display,"IM_RETAIN_COLORS",MagickFalse);
+ windows->im_exit=XInternAtom(display,"IM_EXIT",MagickFalse);
+ windows->dnd_protocols=XInternAtom(display,"DndProtocol",MagickFalse);
+#if defined(__WINDOWS__)
+ (void) XSynchronize(display,IsWindows95());
+#endif
+ if (IsEventLogging())
+ {
+ (void) XSynchronize(display,MagickTrue);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Version: %s",
+ GetMagickVersion((unsigned long *) NULL));
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Protocols:");
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " Window Manager: 0x%lx",windows->wm_protocols);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " delete window: 0x%lx",windows->wm_delete_window);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," take focus: 0x%lx",
+ windows->wm_take_focus);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," ImageMagick: 0x%lx",
+ windows->im_protocols);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " remote command: 0x%lx",windows->im_remote_command);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " update widget: 0x%lx",windows->im_update_widget);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " update colormap: 0x%lx",windows->im_update_colormap);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " former image: 0x%lx",windows->im_former_image);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," next image: 0x%lx",
+ windows->im_next_image);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " retain colors: 0x%lx",windows->im_retain_colors);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," exit: 0x%lx",
+ windows->im_exit);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," Drag and Drop: 0x%lx",
+ windows->dnd_protocols);
+ }
+ /*
+ Allocate standard colormap.
+ */
+ windows->map_info=XAllocStandardColormap();
+ windows->icon_map=XAllocStandardColormap();
+ if ((windows->map_info == (XStandardColormap *) NULL) ||
+ (windows->icon_map == (XStandardColormap *) NULL))
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed","...");
+ windows->map_info->colormap=(Colormap) NULL;
+ windows->icon_map->colormap=(Colormap) NULL;
+ windows->pixel_info->pixels=(unsigned long *) NULL;
+ windows->pixel_info->annotate_context=(GC) NULL;
+ windows->pixel_info->highlight_context=(GC) NULL;
+ windows->pixel_info->widget_context=(GC) NULL;
+ windows->font_info=(XFontStruct *) NULL;
+ windows->icon_pixel->annotate_context=(GC) NULL;
+ windows->icon_pixel->pixels=(unsigned long *) NULL;
+ /*
+ Allocate visual.
+ */
+ *windows->icon_resources=(*resource_info);
+ windows->icon_resources->visual_type=(char *) "default";
+ windows->icon_resources->colormap=SharedColormap;
+ windows->visual_info=
+ XBestVisualInfo(display,windows->map_info,resource_info);
+ windows->icon_visual=
+ XBestVisualInfo(display,windows->icon_map,windows->icon_resources);
+ if ((windows->visual_info == (XVisualInfo *) NULL) ||
+ (windows->icon_visual == (XVisualInfo *) NULL))
+ ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
+ resource_info->visual_type);
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Visual:");
+ (void) LogMagickEvent(X11Event,GetMagickModule()," visual id: 0x%lx",
+ windows->visual_info->visualid);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," class: %s",
+ XVisualClassName(windows->visual_info->klass));
+ (void) LogMagickEvent(X11Event,GetMagickModule()," depth: %d planes",
+ windows->visual_info->depth);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " size of colormap: %d entries",windows->visual_info->colormap_size);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue masks: 0x%lx 0x%lx 0x%lx",
+ windows->visual_info->red_mask,windows->visual_info->green_mask,
+ windows->visual_info->blue_mask);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " significant bits in color: %d bits",
+ windows->visual_info->bits_per_rgb);
+ }
+ /*
+ Allocate class and manager hints.
+ */
+ windows->class_hints=XAllocClassHint();
+ windows->manager_hints=XAllocWMHints();
+ if ((windows->class_hints == (XClassHint *) NULL) ||
+ (windows->manager_hints == (XWMHints *) NULL))
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "MemoryAllocationFailed","...");
+ /*
+ Determine group leader if we have one.
+ */
+ root_window=XRootWindow(display,windows->visual_info->screen);
+ windows->group_leader.id=(Window) NULL;
+ if (resource_info->window_group != (char *) NULL)
+ {
+ if (isdigit((unsigned char) *resource_info->window_group) != 0)
+ windows->group_leader.id=XWindowByID(display,root_window,(Window)
+ strtol((char *) resource_info->window_group,(char **) NULL,0));
+ if (windows->group_leader.id == (Window) NULL)
+ windows->group_leader.id=
+ XWindowByName(display,root_window,resource_info->window_group);
+ }
+ return(windows);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e C u r s o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeCursor() creates a crosshairs X11 cursor.
+%
+% The format of the XMakeCursor method is:
+%
+% Cursor XMakeCursor(Display *display,Window window,Colormap colormap,
+% char *background_color,char *foreground_color)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies the ID of the window for which the cursor is
+% assigned.
+%
+% o colormap: Specifies the ID of the colormap from which the background
+% and foreground color will be retrieved.
+%
+% o background_color: Specifies the color to use for the cursor background.
+%
+% o foreground_color: Specifies the color to use for the cursor foreground.
+%
+*/
+MagickExport Cursor XMakeCursor(Display *display,Window window,
+ Colormap colormap,char *background_color,char *foreground_color)
+{
+#define scope_height 17
+#define scope_x_hot 8
+#define scope_y_hot 8
+#define scope_width 17
+
+ static const unsigned char
+ scope_bits[] =
+ {
+ 0x80, 0x03, 0x00, 0x80, 0x02, 0x00, 0x80, 0x02, 0x00, 0x80, 0x02,
+ 0x00, 0x80, 0x02, 0x00, 0x80, 0x02, 0x00, 0x80, 0x02, 0x00, 0x7f,
+ 0xfc, 0x01, 0x01, 0x00, 0x01, 0x7f, 0xfc, 0x01, 0x80, 0x02, 0x00,
+ 0x80, 0x02, 0x00, 0x80, 0x02, 0x00, 0x80, 0x02, 0x00, 0x80, 0x02,
+ 0x00, 0x80, 0x02, 0x00, 0x80, 0x03, 0x00
+ },
+ scope_mask_bits[] =
+ {
+ 0xc0, 0x07, 0x00, 0xc0, 0x07, 0x00, 0xc0, 0x06, 0x00, 0xc0, 0x06,
+ 0x00, 0xc0, 0x06, 0x00, 0xc0, 0x06, 0x00, 0xff, 0xfe, 0x01, 0x7f,
+ 0xfc, 0x01, 0x03, 0x80, 0x01, 0x7f, 0xfc, 0x01, 0xff, 0xfe, 0x01,
+ 0xc0, 0x06, 0x00, 0xc0, 0x06, 0x00, 0xc0, 0x06, 0x00, 0xc0, 0x06,
+ 0x00, 0xc0, 0x07, 0x00, 0xc0, 0x07, 0x00
+ };
+
+ Cursor
+ cursor;
+
+ Pixmap
+ mask,
+ source;
+
+ XColor
+ background,
+ foreground;
+
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ assert(colormap != (Colormap) NULL);
+ assert(background_color != (char *) NULL);
+ assert(foreground_color != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",background_color);
+ source=XCreateBitmapFromData(display,window,(char *) scope_bits,scope_width,
+ scope_height);
+ mask=XCreateBitmapFromData(display,window,(char *) scope_mask_bits,
+ scope_width,scope_height);
+ if ((source == (Pixmap) NULL) || (mask == (Pixmap) NULL))
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToCreatePixmap","...");
+ return((Cursor) NULL);
+ }
+ (void) XParseColor(display,colormap,background_color,&background);
+ (void) XParseColor(display,colormap,foreground_color,&foreground);
+ cursor=XCreatePixmapCursor(display,source,mask,&foreground,&background,
+ scope_x_hot,scope_y_hot);
+ (void) XFreePixmap(display,source);
+ (void) XFreePixmap(display,mask);
+ return(cursor);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeImage() creates an X11 image. If the image size differs from the X11
+% image size, the image is first resized.
+%
+% The format of the XMakeImage method is:
+%
+% MagickBooleanType XMakeImage(Display *display,
+% const XResourceInfo *resource_info,XWindowInfo *window,Image *image,
+% unsigned int width,unsigned int height)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+% o image: the image.
+%
+% o width: Specifies the width in pixels of the rectangular area to
+% display.
+%
+% o height: Specifies the height in pixels of the rectangular area to
+% display.
+%
+*/
+MagickExport MagickBooleanType XMakeImage(Display *display,
+ const XResourceInfo *resource_info,XWindowInfo *window,Image *image,
+ unsigned int width,unsigned int height)
+{
+#define CheckOverflowException(length,width,height) \
+ (((height) != 0) && ((length)/((size_t) height) != ((size_t) width)))
+
+ int
+ depth,
+ format;
+
+ size_t
+ length;
+
+ XImage
+ *matte_image,
+ *ximage;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ assert(width != 0);
+ assert(height != 0);
+ if ((window->width == 0) || (window->height == 0))
+ return(MagickFalse);
+ /*
+ Apply user transforms to the image.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->busy_cursor);
+ (void) XFlush(display);
+ depth=(int) window->depth;
+ if (window->destroy)
+ window->image=DestroyImage(window->image);
+ window->image=image;
+ window->destroy=MagickFalse;
+ if (window->image != (Image *) NULL)
+ {
+ if (window->crop_geometry != (char *) NULL)
+ {
+ Image
+ *crop_image;
+
+ RectangleInfo
+ crop_info;
+
+ /*
+ Crop image.
+ */
+ window->image->page.x=0;
+ window->image->page.y=0;
+ (void) ParsePageGeometry(window->image,window->crop_geometry,
+ &crop_info,&image->exception);
+ crop_image=CropImage(window->image,&crop_info,&image->exception);
+ if (crop_image != (Image *) NULL)
+ {
+ if (window->image != image)
+ window->image=DestroyImage(window->image);
+ window->image=crop_image;
+ window->destroy=MagickTrue;
+ }
+ }
+ if ((width != (unsigned int) window->image->columns) ||
+ (height != (unsigned int) window->image->rows))
+ {
+ Image
+ *resize_image;
+
+ /*
+ Resize image.
+ */
+ resize_image=NewImageList();
+ if (window->pixel_info->colors != 0)
+ resize_image=SampleImage(window->image,width,height,
+ &image->exception);
+ else
+ resize_image=ThumbnailImage(window->image,width,height,
+ &image->exception);
+ if (resize_image != (Image *) NULL)
+ {
+ if (window->image != image)
+ window->image=DestroyImage(window->image);
+ window->image=resize_image;
+ window->destroy=MagickTrue;
+ }
+ }
+ width=(unsigned int) window->image->columns;
+ height=(unsigned int) window->image->rows;
+ }
+ /*
+ Create X image.
+ */
+ ximage=(XImage *) NULL;
+ format=(depth == 1) ? XYBitmap : ZPixmap;
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->shared_memory != MagickFalse)
+ {
+ XShmSegmentInfo
+ *segment_info;
+
+ segment_info=(XShmSegmentInfo *) window->segment_info;
+ segment_info[1].shmid=(-1);
+ segment_info[1].shmaddr=(char *) NULL;
+ ximage=XShmCreateImage(display,window->visual,(unsigned int) depth,format,
+ (char *) NULL,&segment_info[1],width,height);
+ if (ximage == (XImage *) NULL)
+ window->shared_memory=MagickFalse;
+ length=(size_t) ximage->bytes_per_line*ximage->height;
+ if (CheckOverflowException(length,ximage->bytes_per_line,ximage->height))
+ window->shared_memory=MagickFalse;
+ if (window->shared_memory != MagickFalse)
+ segment_info[1].shmid=shmget(IPC_PRIVATE,length,IPC_CREAT | 0777);
+ if (window->shared_memory != MagickFalse)
+ segment_info[1].shmaddr=(char *) shmat(segment_info[1].shmid,0,0);
+ if (segment_info[1].shmid < 0)
+ window->shared_memory=MagickFalse;
+ if (window->shared_memory != MagickFalse)
+ (void) shmctl(segment_info[1].shmid,IPC_RMID,0);
+ else
+ {
+ if (ximage != (XImage *) NULL)
+ XDestroyImage(ximage);
+ ximage=(XImage *) NULL;
+ if (segment_info[1].shmaddr)
+ {
+ (void) shmdt(segment_info[1].shmaddr);
+ segment_info[1].shmaddr=(char *) NULL;
+ }
+ if (segment_info[1].shmid >= 0)
+ {
+ (void) shmctl(segment_info[1].shmid,IPC_RMID,0);
+ segment_info[1].shmid=(-1);
+ }
+ }
+ }
+#endif
+ /*
+ Allocate X image pixel data.
+ */
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->shared_memory)
+ {
+ Status
+ status;
+
+ XShmSegmentInfo
+ *segment_info;
+
+ (void) XSync(display,MagickFalse);
+ xerror_alert=MagickFalse;
+ segment_info=(XShmSegmentInfo *) window->segment_info;
+ ximage->data=segment_info[1].shmaddr;
+ segment_info[1].readOnly=MagickFalse;
+ status=XShmAttach(display,&segment_info[1]);
+ if (status != False)
+ (void) XSync(display,MagickFalse);
+ if ((status == False) || (xerror_alert != MagickFalse))
+ {
+ window->shared_memory=MagickFalse;
+ if (status != False)
+ XShmDetach(display,&segment_info[1]);
+ if (ximage != (XImage *) NULL)
+ {
+ ximage->data=NULL;
+ XDestroyImage(ximage);
+ ximage=(XImage *) NULL;
+ }
+ if (segment_info[1].shmid >= 0)
+ {
+ if (segment_info[1].shmaddr != NULL)
+ (void) shmdt(segment_info[1].shmaddr);
+ (void) shmctl(segment_info[1].shmid,IPC_RMID,0);
+ segment_info[1].shmid=(-1);
+ segment_info[1].shmaddr=(char *) NULL;
+ }
+ }
+ }
+#endif
+ if (window->shared_memory == MagickFalse)
+ ximage=XCreateImage(display,window->visual,(unsigned int) depth,format,0,
+ (char *) NULL,width,height,XBitmapPad(display),0);
+ if (ximage == (XImage *) NULL)
+ {
+ /*
+ Unable to create X image.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->cursor);
+ return(MagickFalse);
+ }
+ length=(size_t) ximage->bytes_per_line*ximage->height;
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"XImage:");
+ (void) LogMagickEvent(X11Event,GetMagickModule()," width, height: %dx%d",
+ ximage->width,ximage->height);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," format: %d",
+ ximage->format);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," byte order: %d",
+ ximage->byte_order);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " bitmap unit, bit order, pad: %d %d %d",ximage->bitmap_unit,
+ ximage->bitmap_bit_order,ximage->bitmap_pad);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," depth: %d",
+ ximage->depth);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," bytes per line: %d",
+ ximage->bytes_per_line);
+ (void) LogMagickEvent(X11Event,GetMagickModule()," bits per pixel: %d",
+ ximage->bits_per_pixel);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue masks: 0x%lx 0x%lx 0x%lx",ximage->red_mask,
+ ximage->green_mask,ximage->blue_mask);
+ }
+ if (window->shared_memory == MagickFalse)
+ {
+ if (ximage->format != XYBitmap)
+ ximage->data=(char *) AcquireQuantumMemory((size_t)
+ ximage->bytes_per_line,(size_t) ximage->height);
+ else
+ ximage->data=(char *) AcquireQuantumMemory((size_t)
+ ximage->bytes_per_line*ximage->depth,(size_t) ximage->height);
+ }
+ if (ximage->data == (char *) NULL)
+ {
+ /*
+ Unable to allocate pixel data.
+ */
+ XDestroyImage(ximage);
+ ximage=(XImage *) NULL;
+ (void) XCheckDefineCursor(display,window->id,window->cursor);
+ return(MagickFalse);
+ }
+ if (window->ximage != (XImage *) NULL)
+ {
+ /*
+ Destroy previous X image.
+ */
+ length=(size_t) window->ximage->bytes_per_line*window->ximage->height;
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->segment_info != (XShmSegmentInfo *) NULL)
+ {
+ XShmSegmentInfo
+ *segment_info;
+
+ segment_info=(XShmSegmentInfo *) window->segment_info;
+ if (segment_info[0].shmid >= 0)
+ {
+ (void) XSync(display,MagickFalse);
+ (void) XShmDetach(display,&segment_info[0]);
+ (void) XSync(display,MagickFalse);
+ if (segment_info[0].shmaddr != (char *) NULL)
+ (void) shmdt(segment_info[0].shmaddr);
+ (void) shmctl(segment_info[0].shmid,IPC_RMID,0);
+ segment_info[0].shmid=(-1);
+ segment_info[0].shmaddr=(char *) NULL;
+ window->ximage->data=(char *) NULL;
+ }
+ }
+#endif
+ if (window->ximage->data != (char *) NULL)
+ free(window->ximage->data);
+ window->ximage->data=(char *) NULL;
+ XDestroyImage(window->ximage);
+ window->ximage=(XImage *) NULL;
+ }
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->segment_info != (XShmSegmentInfo *) NULL)
+ {
+ XShmSegmentInfo
+ *segment_info;
+
+ segment_info=(XShmSegmentInfo *) window->segment_info;
+ segment_info[0]=segment_info[1];
+ }
+#endif
+ window->ximage=ximage;
+ matte_image=(XImage *) NULL;
+ if ((window->shape != MagickFalse) && (window->image != (Image *) NULL))
+ if ((window->image->matte != MagickFalse) &&
+ ((long) width <= XDisplayWidth(display,window->screen)) &&
+ ((long) height <= XDisplayHeight(display,window->screen)))
+ {
+ /*
+ Create matte image.
+ */
+ matte_image=XCreateImage(display,window->visual,1,XYBitmap,0,
+ (char *) NULL,width,height,XBitmapPad(display),0);
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Matte Image:");
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " width, height: %dx%d",matte_image->width,matte_image->height);
+ }
+ if (matte_image != (XImage *) NULL)
+ {
+ /*
+ Allocate matte image pixel data.
+ */
+ matte_image->data=(char *) AcquireQuantumMemory((size_t)
+ matte_image->bytes_per_line*matte_image->depth,
+ (size_t) matte_image->height);
+ if (matte_image->data == (char *) NULL)
+ {
+ XDestroyImage(matte_image);
+ matte_image=(XImage *) NULL;
+ }
+ }
+ }
+ if (window->matte_image != (XImage *) NULL)
+ {
+ /*
+ Free matte image.
+ */
+ if (window->matte_image->data != (char *) NULL)
+ free(window->matte_image->data);
+ window->matte_image->data=(char *) NULL;
+ XDestroyImage(window->matte_image);
+ window->matte_image=(XImage *) NULL;
+ }
+ window->matte_image=matte_image;
+ if (window->matte_pixmap != (Pixmap) NULL)
+ {
+ (void) XFreePixmap(display,window->matte_pixmap);
+ window->matte_pixmap=(Pixmap) NULL;
+#if defined(MAGICKCORE_HAVE_SHAPE)
+ if (window->shape != MagickFalse)
+ XShapeCombineMask(display,window->id,ShapeBounding,0,0,None,ShapeSet);
+#endif
+ }
+ window->stasis=MagickFalse;
+ /*
+ Convert pixels to X image data.
+ */
+ if (window->image != (Image *) NULL)
+ {
+ if ((ximage->byte_order == LSBFirst) || ((ximage->format == XYBitmap) &&
+ (ximage->bitmap_bit_order == LSBFirst)))
+ XMakeImageLSBFirst(resource_info,window,window->image,ximage,
+ matte_image);
+ else
+ XMakeImageMSBFirst(resource_info,window,window->image,ximage,
+ matte_image);
+ }
+ if (window->matte_image != (XImage *) NULL)
+ {
+ /*
+ Create matte pixmap.
+ */
+ window->matte_pixmap=XCreatePixmap(display,window->id,width,height,1);
+ if (window->matte_pixmap != (Pixmap) NULL)
+ {
+ GC
+ graphics_context;
+
+ XGCValues
+ context_values;
+
+ /*
+ Copy matte image to matte pixmap.
+ */
+ context_values.background=1;
+ context_values.foreground=0;
+ graphics_context=XCreateGC(display,window->matte_pixmap,
+ (unsigned long) (GCBackground | GCForeground),&context_values);
+ (void) XPutImage(display,window->matte_pixmap,graphics_context,
+ window->matte_image,0,0,0,0,width,height);
+ (void) XFreeGC(display,graphics_context);
+#if defined(MAGICKCORE_HAVE_SHAPE)
+ if (window->shape != MagickFalse)
+ XShapeCombineMask(display,window->id,ShapeBounding,0,0,
+ window->matte_pixmap,ShapeSet);
+#endif
+ }
+ }
+ (void) XMakePixmap(display,resource_info,window);
+ /*
+ Restore cursor.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->cursor);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a k e I m a g e L S B F i r s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeImageLSBFirst() initializes the pixel data of an X11 Image. The X image
+% pixels are copied in least-significant bit and byte first order. The
+% server's scanline pad is respected. Rather than using one or two general
+% cases, many special cases are found here to help speed up the image
+% conversion.
+%
+% The format of the XMakeImageLSBFirst method is:
+%
+% void XMakeImageLSBFirst(Display *display,XWindows *windows)
+%
+% A description of each parameter follows:
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+% o image: the image.
+%
+% o ximage: Specifies a pointer to a XImage structure; returned from
+% XCreateImage.
+%
+% o matte_image: Specifies a pointer to a XImage structure; returned from
+% XCreateImage.
+%
+*/
+static void XMakeImageLSBFirst(const XResourceInfo *resource_info,
+ const XWindowInfo *window,Image *image,XImage *ximage,XImage *matte_image)
+{
+ Image
+ *canvas;
+
+ int
+ y;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register int
+ x;
+
+ register unsigned char
+ *q;
+
+ unsigned char
+ bit,
+ byte;
+
+ unsigned int
+ scanline_pad;
+
+ unsigned long
+ pixel,
+ *pixels;
+
+ XStandardColormap
+ *map_info;
+
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ canvas=image;
+ if ((image->storage_class == DirectClass) && (image->matte != MagickFalse))
+ {
+ char
+ size[MaxTextExtent];
+
+ Image
+ *pattern;
+
+ ImageInfo
+ *image_info;
+
+ image_info=AcquireImageInfo();
+ (void) CopyMagickString(image_info->filename,
+ resource_info->image_info->texture != (char *) NULL ?
+ resource_info->image_info->texture : "pattern:checkerboard",
+ MaxTextExtent);
+ (void) FormatMagickString(size,MaxTextExtent,"%lux%lu",image->columns,
+ image->rows);
+ image_info->size=ConstantString(size);
+ pattern=ReadImage(image_info,&image->exception);
+ image_info=DestroyImageInfo(image_info);
+ if (pattern != (Image *) NULL)
+ {
+ canvas=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (canvas != (Image *) NULL)
+ (void) CompositeImage(canvas,DstOverCompositeOp,pattern,0,0);
+ pattern=DestroyImage(pattern);
+ }
+ }
+ scanline_pad=(unsigned int) (ximage->bytes_per_line-((ximage->width*
+ ximage->bits_per_pixel) >> 3));
+ map_info=window->map_info;
+ pixels=window->pixel_info->pixels;
+ q=(unsigned char *) ximage->data;
+ x=0;
+ if (ximage->format == XYBitmap)
+ {
+ register unsigned short
+ polarity;
+
+ unsigned char
+ background,
+ foreground;
+
+ /*
+ Convert canvas to big-endian bitmap.
+ */
+ background=(unsigned char)
+ (XPixelIntensity(&window->pixel_info->foreground_color) <
+ XPixelIntensity(&window->pixel_info->background_color) ? 0x80 : 0x00);
+ foreground=(unsigned char)
+ (XPixelIntensity(&window->pixel_info->background_color) <
+ XPixelIntensity(&window->pixel_info->foreground_color) ? 0x80 : 0x00);
+ polarity=(unsigned short) ((PixelIntensityToQuantum(
+ &canvas->colormap[0])) < ((Quantum) QuantumRange/2) ? 1 : 0);
+ if (canvas->colors == 2)
+ polarity=PixelIntensity(&canvas->colormap[0]) <
+ PixelIntensity(&canvas->colormap[1]);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ bit=0;
+ byte=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ byte>>=1;
+ if (indexes[x] == (IndexPacket) polarity)
+ byte|=foreground;
+ else
+ byte|=background;
+ bit++;
+ if (bit == 8)
+ {
+ *q++=byte;
+ bit=0;
+ byte=0;
+ }
+ }
+ if (bit != 0)
+ *q=byte >> (8-bit);
+ q+=scanline_pad;
+ }
+ }
+ else
+ if (window->pixel_info->colors != 0)
+ switch (ximage->bits_per_pixel)
+ {
+ case 2:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 2 bit color-mapped X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ nibble=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]] & 0x0f;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) pixel;
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 2);
+ nibble++;
+ break;
+ }
+ case 2:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 3:
+ {
+ *q|=(unsigned char) (pixel << 6);
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 4 bit color-mapped X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ nibble=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]] & 0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) pixel;
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 6:
+ case 8:
+ {
+ /*
+ Convert to 8 bit color-mapped X canvas.
+ */
+ if (resource_info->color_recovery &&
+ resource_info->quantize_info->dither)
+ {
+ XDitherImage(canvas,ximage);
+ break;
+ }
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]];
+ *q++=(unsigned char) pixel;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ default:
+ {
+ register int
+ k;
+
+ register unsigned int
+ bytes_per_pixel;
+
+ unsigned char
+ channel[sizeof(unsigned long)];
+
+ /*
+ Convert to multi-byte color-mapped X canvas.
+ */
+ bytes_per_pixel=(unsigned int) (ximage->bits_per_pixel >> 3);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]];
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ {
+ channel[k]=(unsigned char) pixel;
+ pixel>>=8;
+ }
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ *q++=channel[k];
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ }
+ else
+ switch (ximage->bits_per_pixel)
+ {
+ case 2:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to contiguous 2 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ nibble=0;
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=XGammaPixel(map_info,p);
+ pixel&=0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) pixel;
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 2);
+ nibble++;
+ break;
+ }
+ case 2:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 3:
+ {
+ *q|=(unsigned char) (pixel << 6);
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to contiguous 4 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ nibble=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=XGammaPixel(map_info,p);
+ pixel&=0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) pixel;
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 6:
+ case 8:
+ {
+ /*
+ Convert to contiguous 8 bit continuous-tone X canvas.
+ */
+ if (resource_info->color_recovery &&
+ resource_info->quantize_info->dither)
+ {
+ XDitherImage(canvas,ximage);
+ break;
+ }
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=XGammaPixel(map_info,p);
+ *q++=(unsigned char) pixel;
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ default:
+ {
+ if ((ximage->bits_per_pixel == 32) && (map_info->red_max == 255) &&
+ (map_info->green_max == 255) && (map_info->blue_max == 255) &&
+ (map_info->red_mult == 65536L) && (map_info->green_mult == 256) &&
+ (map_info->blue_mult == 1))
+ {
+ /*
+ Convert to 32 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if ((red_gamma != 1.0) || (green_gamma != 1.0) ||
+ (blue_gamma != 1.0))
+ {
+ /*
+ Gamma correct canvas.
+ */
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=ScaleQuantumToChar(XBlueGamma(p->blue));
+ *q++=ScaleQuantumToChar(XGreenGamma(p->green));
+ *q++=ScaleQuantumToChar(XRedGamma(p->red));
+ *q++=0;
+ p++;
+ }
+ continue;
+ }
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=ScaleQuantumToChar((Quantum) p->blue);
+ *q++=ScaleQuantumToChar((Quantum) p->green);
+ *q++=ScaleQuantumToChar((Quantum) p->red);
+ *q++=0;
+ p++;
+ }
+ }
+ }
+ else
+ if ((ximage->bits_per_pixel == 32) && (map_info->red_max == 255) &&
+ (map_info->green_max == 255) && (map_info->blue_max == 255) &&
+ (map_info->red_mult == 1) && (map_info->green_mult == 256) &&
+ (map_info->blue_mult == 65536L))
+ {
+ /*
+ Convert to 32 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if ((red_gamma != 1.0) || (green_gamma != 1.0) ||
+ (blue_gamma != 1.0))
+ {
+ /*
+ Gamma correct canvas.
+ */
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=ScaleQuantumToChar(XRedGamma(p->red));
+ *q++=ScaleQuantumToChar(XGreenGamma(p->green));
+ *q++=ScaleQuantumToChar(XBlueGamma(p->blue));
+ *q++=0;
+ p++;
+ }
+ continue;
+ }
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=ScaleQuantumToChar((Quantum) p->red);
+ *q++=ScaleQuantumToChar((Quantum) p->green);
+ *q++=ScaleQuantumToChar((Quantum) p->blue);
+ *q++=0;
+ p++;
+ }
+ }
+ }
+ else
+ {
+ register int
+ k;
+
+ register unsigned int
+ bytes_per_pixel;
+
+ unsigned char
+ channel[sizeof(unsigned long)];
+
+ /*
+ Convert to multi-byte continuous-tone X canvas.
+ */
+ bytes_per_pixel=(unsigned int) (ximage->bits_per_pixel >> 3);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (PixelPacket *) NULL)
+ break;
+ for (x=0; x < (long) canvas->columns; x++)
+ {
+ pixel=XGammaPixel(map_info,p);
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ {
+ channel[k]=(unsigned char) pixel;
+ pixel>>=8;
+ }
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ *q++=channel[k];
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ }
+ break;
+ }
+ }
+ if (matte_image != (XImage *) NULL)
+ {
+ /*
+ Initialize matte canvas.
+ */
+ scanline_pad=(unsigned int) (matte_image->bytes_per_line-
+ ((matte_image->width*matte_image->bits_per_pixel) >> 3));
+ q=(unsigned char *) matte_image->data;
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ bit=0;
+ byte=0;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ byte>>=1;
+ if (p->opacity > (long) (QuantumRange/2))
+ byte|=0x80;
+ bit++;
+ if (bit == 8)
+ {
+ *q++=byte;
+ bit=0;
+ byte=0;
+ }
+ p++;
+ }
+ if (bit != 0)
+ *q=byte >> (8-bit);
+ q+=scanline_pad;
+ }
+ }
+ if (canvas != image)
+ canvas=DestroyImage(canvas);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
++ X M a k e I m a g e M S B F i r s t %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeImageMSBFirst() initializes the pixel data of an X11 Image. The X
+% image pixels are copied in most-significant bit and byte first order. The
+% server's scanline pad is also respected. Rather than using one or two
+% general cases, many special cases are found here to help speed up the image
+% conversion.
+%
+% The format of the XMakeImageMSBFirst method is:
+%
+% XMakeImageMSBFirst(resource_info,window,image,ximage,matte_image)
+%
+% A description of each parameter follows:
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+% o image: the image.
+%
+% o ximage: Specifies a pointer to a XImage structure; returned from
+% XCreateImage.
+%
+% o matte_image: Specifies a pointer to a XImage structure; returned from
+% XCreateImage.
+%
+%
+*/
+static void XMakeImageMSBFirst(const XResourceInfo *resource_info,
+ const XWindowInfo *window,Image *image,XImage *ximage,XImage *matte_image)
+{
+ Image
+ *canvas;
+
+ int
+ y;
+
+ register int
+ x;
+
+ register const IndexPacket
+ *indexes;
+
+ register const PixelPacket
+ *p;
+
+ register unsigned char
+ *q;
+
+ unsigned char
+ bit,
+ byte;
+
+ unsigned int
+ scanline_pad;
+
+ unsigned long
+ pixel,
+ *pixels;
+
+ XStandardColormap
+ *map_info;
+
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ assert(image != (Image *) NULL);
+ if (image->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
+ canvas=image;
+ if ((image->storage_class == DirectClass) && (image->matte != MagickFalse))
+ {
+ char
+ size[MaxTextExtent];
+
+ Image
+ *pattern;
+
+ ImageInfo
+ *image_info;
+
+ image_info=AcquireImageInfo();
+ (void) CopyMagickString(image_info->filename,
+ resource_info->image_info->texture != (char *) NULL ?
+ resource_info->image_info->texture : "pattern:checkerboard",
+ MaxTextExtent);
+ (void) FormatMagickString(size,MaxTextExtent,"%lux%lu",image->columns,
+ image->rows);
+ image_info->size=ConstantString(size);
+ pattern=ReadImage(image_info,&image->exception);
+ image_info=DestroyImageInfo(image_info);
+ if (pattern != (Image *) NULL)
+ {
+ canvas=CloneImage(image,0,0,MagickTrue,&image->exception);
+ if (canvas != (Image *) NULL)
+ (void) CompositeImage(canvas,DstOverCompositeOp,pattern,0,0);
+ pattern=DestroyImage(pattern);
+ }
+ }
+ scanline_pad=(unsigned int) (ximage->bytes_per_line-
+ ((ximage->width*ximage->bits_per_pixel) >> 3));
+ map_info=window->map_info;
+ pixels=window->pixel_info->pixels;
+ q=(unsigned char *) ximage->data;
+ x=0;
+ if (ximage->format == XYBitmap)
+ {
+ register unsigned short
+ polarity;
+
+ unsigned char
+ background,
+ foreground;
+
+ /*
+ Convert canvas to big-endian bitmap.
+ */
+ background=(unsigned char)
+ (XPixelIntensity(&window->pixel_info->foreground_color) <
+ XPixelIntensity(&window->pixel_info->background_color) ? 0x01 : 0x00);
+ foreground=(unsigned char)
+ (XPixelIntensity(&window->pixel_info->background_color) <
+ XPixelIntensity(&window->pixel_info->foreground_color) ? 0x01 : 0x00);
+ polarity=(unsigned short) ((PixelIntensityToQuantum(
+ &canvas->colormap[0])) < ((Quantum) QuantumRange/2) ? 1 : 0);
+ if (canvas->colors == 2)
+ polarity=PixelIntensity(&canvas->colormap[0]) <
+ PixelIntensity(&canvas->colormap[1]);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ bit=0;
+ byte=0;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ byte<<=1;
+ if (indexes[x] == (IndexPacket) polarity)
+ byte|=foreground;
+ else
+ byte|=background;
+ bit++;
+ if (bit == 8)
+ {
+ *q++=byte;
+ bit=0;
+ byte=0;
+ }
+ }
+ if (bit != 0)
+ *q=byte << (8-bit);
+ q+=scanline_pad;
+ }
+ }
+ else
+ if (window->pixel_info->colors != 0)
+ switch (ximage->bits_per_pixel)
+ {
+ case 2:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 2 bit color-mapped X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ nibble=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]] & 0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) (pixel << 6);
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 2:
+ {
+ *q|=(unsigned char) (pixel << 2);
+ nibble++;
+ break;
+ }
+ case 3:
+ {
+ *q|=(unsigned char) pixel;
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 4 bit color-mapped X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ nibble=0;
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]] & 0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) pixel;
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 6:
+ case 8:
+ {
+ /*
+ Convert to 8 bit color-mapped X canvas.
+ */
+ if (resource_info->color_recovery &&
+ resource_info->quantize_info->dither)
+ {
+ XDitherImage(canvas,ximage);
+ break;
+ }
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]];
+ *q++=(unsigned char) pixel;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ default:
+ {
+ register int
+ k;
+
+ register unsigned int
+ bytes_per_pixel;
+
+ unsigned char
+ channel[sizeof(unsigned long)];
+
+ /*
+ Convert to 8 bit color-mapped X canvas.
+ */
+ bytes_per_pixel=(unsigned int) (ximage->bits_per_pixel >> 3);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ indexes=GetVirtualIndexQueue(canvas);
+ for (x=0; x < (int) canvas->columns; x++)
+ {
+ pixel=pixels[(long) indexes[x]];
+ for (k=(int) bytes_per_pixel-1; k >= 0; k--)
+ {
+ channel[k]=(unsigned char) pixel;
+ pixel>>=8;
+ }
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ *q++=channel[k];
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ }
+ else
+ switch (ximage->bits_per_pixel)
+ {
+ case 2:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 4 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ nibble=0;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ pixel=XGammaPixel(map_info,p);
+ pixel&=0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) (pixel << 6);
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 2:
+ {
+ *q|=(unsigned char) (pixel << 2);
+ nibble++;
+ break;
+ }
+ case 3:
+ {
+ *q|=(unsigned char) pixel;
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 4:
+ {
+ register unsigned int
+ nibble;
+
+ /*
+ Convert to 4 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ nibble=0;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ pixel=XGammaPixel(map_info,p);
+ pixel&=0xf;
+ switch (nibble)
+ {
+ case 0:
+ {
+ *q=(unsigned char) (pixel << 4);
+ nibble++;
+ break;
+ }
+ case 1:
+ {
+ *q|=(unsigned char) pixel;
+ q++;
+ nibble=0;
+ break;
+ }
+ }
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ case 6:
+ case 8:
+ {
+ /*
+ Convert to 8 bit continuous-tone X canvas.
+ */
+ if (resource_info->color_recovery &&
+ resource_info->quantize_info->dither)
+ {
+ XDitherImage(canvas,ximage);
+ break;
+ }
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ pixel=XGammaPixel(map_info,p);
+ *q++=(unsigned char) pixel;
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ break;
+ }
+ default:
+ {
+ if ((ximage->bits_per_pixel == 32) && (map_info->red_max == 255) &&
+ (map_info->green_max == 255) && (map_info->blue_max == 255) &&
+ (map_info->red_mult == 65536L) && (map_info->green_mult == 256) &&
+ (map_info->blue_mult == 1))
+ {
+ /*
+ Convert to 32 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if ((red_gamma != 1.0) || (green_gamma != 1.0) ||
+ (blue_gamma != 1.0))
+ {
+ /*
+ Gamma correct canvas.
+ */
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=0;
+ *q++=ScaleQuantumToChar(XRedGamma(p->red));
+ *q++=ScaleQuantumToChar(XGreenGamma(p->green));
+ *q++=ScaleQuantumToChar(XBlueGamma(p->blue));
+ p++;
+ }
+ continue;
+ }
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=0;
+ *q++=ScaleQuantumToChar((Quantum) p->red);
+ *q++=ScaleQuantumToChar((Quantum) p->green);
+ *q++=ScaleQuantumToChar((Quantum) p->blue);
+ p++;
+ }
+ }
+ }
+ else
+ if ((ximage->bits_per_pixel == 32) && (map_info->red_max == 255) &&
+ (map_info->green_max == 255) && (map_info->blue_max == 255) &&
+ (map_info->red_mult == 1) && (map_info->green_mult == 256) &&
+ (map_info->blue_mult == 65536L))
+ {
+ /*
+ Convert to 32 bit continuous-tone X canvas.
+ */
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ if ((red_gamma != 1.0) || (green_gamma != 1.0) ||
+ (blue_gamma != 1.0))
+ {
+ /*
+ Gamma correct canvas.
+ */
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=0;
+ *q++=ScaleQuantumToChar(XBlueGamma(p->blue));
+ *q++=ScaleQuantumToChar(XGreenGamma(p->green));
+ *q++=ScaleQuantumToChar(XRedGamma(p->red));
+ p++;
+ }
+ continue;
+ }
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ *q++=0;
+ *q++=ScaleQuantumToChar((Quantum) p->blue);
+ *q++=ScaleQuantumToChar((Quantum) p->green);
+ *q++=ScaleQuantumToChar((Quantum) p->red);
+ p++;
+ }
+ }
+ }
+ else
+ {
+ register int
+ k;
+
+ register unsigned int
+ bytes_per_pixel;
+
+ unsigned char
+ channel[sizeof(unsigned long)];
+
+ /*
+ Convert to multi-byte continuous-tone X canvas.
+ */
+ bytes_per_pixel=(unsigned int) (ximage->bits_per_pixel >> 3);
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,
+ &canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ pixel=XGammaPixel(map_info,p);
+ for (k=(int) bytes_per_pixel-1; k >= 0; k--)
+ {
+ channel[k]=(unsigned char) pixel;
+ pixel>>=8;
+ }
+ for (k=0; k < (int) bytes_per_pixel; k++)
+ *q++=channel[k];
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ }
+ break;
+ }
+ }
+ if (matte_image != (XImage *) NULL)
+ {
+ /*
+ Initialize matte canvas.
+ */
+ scanline_pad=(unsigned int) (matte_image->bytes_per_line-
+ ((matte_image->width*matte_image->bits_per_pixel) >> 3));
+ q=(unsigned char *) matte_image->data;
+ for (y=0; y < (int) canvas->rows; y++)
+ {
+ p=GetVirtualPixels(canvas,0,y,canvas->columns,1,&canvas->exception);
+ if (p == (const PixelPacket *) NULL)
+ break;
+ bit=0;
+ byte=0;
+ for (x=(int) canvas->columns-1; x >= 0; x--)
+ {
+ byte<<=1;
+ if (p->opacity > (long) (QuantumRange/2))
+ byte|=0x01;
+ bit++;
+ if (bit == 8)
+ {
+ *q++=byte;
+ bit=0;
+ byte=0;
+ }
+ p++;
+ }
+ if (bit != 0)
+ *q=byte << (8-bit);
+ q+=scanline_pad;
+ }
+ }
+ if (canvas != image)
+ canvas=DestroyImage(canvas);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e M a g n i f y I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeMagnifyImage() magnifies a region of an X image and displays it.
+%
+% The format of the XMakeMagnifyImage method is:
+%
+% void XMakeMagnifyImage(display,windows)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+*/
+MagickExport void XMakeMagnifyImage(Display *display,XWindows *windows)
+{
+ char
+ tuple[MaxTextExtent];
+
+ int
+ y;
+
+ long
+ n;
+
+ MagickPixelPacket
+ pixel;
+
+ register int
+ x;
+
+ register long
+ i;
+
+ register unsigned char
+ *p,
+ *q;
+
+ static unsigned int
+ previous_magnify = 0;
+
+ static XWindowInfo
+ magnify_window;
+
+ unsigned int
+ height,
+ j,
+ k,
+ l,
+ magnify,
+ scanline_pad,
+ width;
+
+ XImage
+ *ximage;
+
+ /*
+ Check boundary conditions.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ magnify=1;
+ for (n=1; n < (long) windows->magnify.data; n++)
+ magnify<<=1;
+ while ((magnify*windows->image.ximage->width) < windows->magnify.width)
+ magnify<<=1;
+ while ((magnify*windows->image.ximage->height) < windows->magnify.height)
+ magnify<<=1;
+ while (magnify > windows->magnify.width)
+ magnify>>=1;
+ while (magnify > windows->magnify.height)
+ magnify>>=1;
+ if (magnify != previous_magnify)
+ {
+ Status
+ status;
+
+ XTextProperty
+ window_name;
+
+ /*
+ New magnify factor: update magnify window name.
+ */
+ i=0;
+ while ((1 << i) <= (int) magnify)
+ i++;
+ (void) FormatMagickString(windows->magnify.name,MaxTextExtent,
+ "Magnify %luX",i);
+ status=XStringListToTextProperty(&windows->magnify.name,1,&window_name);
+ if (status != False)
+ {
+ XSetWMName(display,windows->magnify.id,&window_name);
+ XSetWMIconName(display,windows->magnify.id,&window_name);
+ (void) XFree((void *) window_name.value);
+ }
+ }
+ previous_magnify=magnify;
+ ximage=windows->image.ximage;
+ width=(unsigned int) windows->magnify.ximage->width;
+ height=(unsigned int) windows->magnify.ximage->height;
+ if ((windows->magnify.x < 0) ||
+ (windows->magnify.x >= windows->image.ximage->width))
+ windows->magnify.x=windows->image.ximage->width >> 1;
+ x=windows->magnify.x-((width/magnify) >> 1);
+ if (x < 0)
+ x=0;
+ else
+ if (x > (int) (ximage->width-(width/magnify)))
+ x=ximage->width-width/magnify;
+ if ((windows->magnify.y < 0) ||
+ (windows->magnify.y >= windows->image.ximage->height))
+ windows->magnify.y=windows->image.ximage->height >> 1;
+ y=windows->magnify.y-((height/magnify) >> 1);
+ if (y < 0)
+ y=0;
+ else
+ if (y > (int) (ximage->height-(height/magnify)))
+ y=ximage->height-height/magnify;
+ q=(unsigned char *) windows->magnify.ximage->data;
+ scanline_pad=(unsigned int) (windows->magnify.ximage->bytes_per_line-
+ ((width*windows->magnify.ximage->bits_per_pixel) >> 3));
+ if (ximage->bits_per_pixel < 8)
+ {
+ register unsigned char
+ background,
+ byte,
+ foreground,
+ p_bit,
+ q_bit;
+
+ register unsigned int
+ plane;
+
+ XPixelInfo
+ *pixel_info;
+
+ pixel_info=windows->magnify.pixel_info;
+ switch (ximage->bitmap_bit_order)
+ {
+ case LSBFirst:
+ {
+ /*
+ Magnify little-endian bitmap.
+ */
+ background=0x00;
+ foreground=0x80;
+ if (ximage->format == XYBitmap)
+ {
+ background=(unsigned char)
+ (XPixelIntensity(&pixel_info->foreground_color) <
+ XPixelIntensity(&pixel_info->background_color) ? 0x80 : 0x00);
+ foreground=(unsigned char)
+ (XPixelIntensity(&pixel_info->background_color) <
+ XPixelIntensity(&pixel_info->foreground_color) ? 0x80 : 0x00);
+ if (windows->magnify.depth > 1)
+ Swap(background,foreground);
+ }
+ for (i=0; i < (long) height; i+=magnify)
+ {
+ /*
+ Propogate pixel magnify rows.
+ */
+ for (j=0; j < magnify; j++)
+ {
+ p=(unsigned char *) ximage->data+y*ximage->bytes_per_line+
+ ((x*ximage->bits_per_pixel) >> 3);
+ p_bit=(unsigned char) (x*ximage->bits_per_pixel) & 0x07;
+ q_bit=0;
+ byte=0;
+ for (k=0; k < width; k+=magnify)
+ {
+ /*
+ Propogate pixel magnify columns.
+ */
+ for (l=0; l < magnify; l++)
+ {
+ /*
+ Propogate each bit plane.
+ */
+ for (plane=0; (int) plane < ximage->bits_per_pixel; plane++)
+ {
+ byte>>=1;
+ if (*p & (0x01 << (p_bit+plane)))
+ byte|=foreground;
+ else
+ byte|=background;
+ q_bit++;
+ if (q_bit == 8)
+ {
+ *q++=byte;
+ q_bit=0;
+ byte=0;
+ }
+ }
+ }
+ p_bit+=ximage->bits_per_pixel;
+ if (p_bit == 8)
+ {
+ p++;
+ p_bit=0;
+ }
+ if (q_bit != 0)
+ *q=byte >> (8-q_bit);
+ q+=scanline_pad;
+ }
+ }
+ y++;
+ }
+ break;
+ }
+ case MSBFirst:
+ default:
+ {
+ /*
+ Magnify big-endian bitmap.
+ */
+ background=0x00;
+ foreground=0x01;
+ if (ximage->format == XYBitmap)
+ {
+ background=(unsigned char)
+ (XPixelIntensity(&pixel_info->foreground_color) <
+ XPixelIntensity(&pixel_info->background_color) ? 0x01 : 0x00);
+ foreground=(unsigned char)
+ (XPixelIntensity(&pixel_info->background_color) <
+ XPixelIntensity(&pixel_info->foreground_color) ? 0x01 : 0x00);
+ if (windows->magnify.depth > 1)
+ Swap(background,foreground);
+ }
+ for (i=0; i < (long) height; i+=magnify)
+ {
+ /*
+ Propogate pixel magnify rows.
+ */
+ for (j=0; j < magnify; j++)
+ {
+ p=(unsigned char *) ximage->data+y*ximage->bytes_per_line+
+ ((x*ximage->bits_per_pixel) >> 3);
+ p_bit=(unsigned char) (x*ximage->bits_per_pixel) & 0x07;
+ q_bit=0;
+ byte=0;
+ for (k=0; k < width; k+=magnify)
+ {
+ /*
+ Propogate pixel magnify columns.
+ */
+ for (l=0; l < magnify; l++)
+ {
+ /*
+ Propogate each bit plane.
+ */
+ for (plane=0; (int) plane < ximage->bits_per_pixel; plane++)
+ {
+ byte<<=1;
+ if (*p & (0x80 >> (p_bit+plane)))
+ byte|=foreground;
+ else
+ byte|=background;
+ q_bit++;
+ if (q_bit == 8)
+ {
+ *q++=byte;
+ q_bit=0;
+ byte=0;
+ }
+ }
+ }
+ p_bit+=ximage->bits_per_pixel;
+ if (p_bit == 8)
+ {
+ p++;
+ p_bit=0;
+ }
+ if (q_bit != 0)
+ *q=byte << (8-q_bit);
+ q+=scanline_pad;
+ }
+ }
+ y++;
+ }
+ break;
+ }
+ }
+ }
+ else
+ switch (ximage->bits_per_pixel)
+ {
+ case 6:
+ case 8:
+ {
+ /*
+ Magnify 8 bit X image.
+ */
+ for (i=0; i < (long) height; i+=magnify)
+ {
+ /*
+ Propogate pixel magnify rows.
+ */
+ for (j=0; j < magnify; j++)
+ {
+ p=(unsigned char *) ximage->data+y*ximage->bytes_per_line+
+ ((x*ximage->bits_per_pixel) >> 3);
+ for (k=0; k < width; k+=magnify)
+ {
+ /*
+ Propogate pixel magnify columns.
+ */
+ for (l=0; l < magnify; l++)
+ *q++=(*p);
+ p++;
+ }
+ q+=scanline_pad;
+ }
+ y++;
+ }
+ break;
+ }
+ default:
+ {
+ register unsigned int
+ bytes_per_pixel,
+ m;
+
+ /*
+ Magnify multi-byte X image.
+ */
+ bytes_per_pixel=(unsigned int) ximage->bits_per_pixel >> 3;
+ for (i=0; i < (long) height; i+=magnify)
+ {
+ /*
+ Propogate pixel magnify rows.
+ */
+ for (j=0; j < magnify; j++)
+ {
+ p=(unsigned char *) ximage->data+y*ximage->bytes_per_line+
+ ((x*ximage->bits_per_pixel) >> 3);
+ for (k=0; k < width; k+=magnify)
+ {
+ /*
+ Propogate pixel magnify columns.
+ */
+ for (l=0; l < magnify; l++)
+ for (m=0; m < bytes_per_pixel; m++)
+ *q++=(*(p+m));
+ p+=bytes_per_pixel;
+ }
+ q+=scanline_pad;
+ }
+ y++;
+ }
+ break;
+ }
+ }
+ /*
+ Copy X image to magnify pixmap.
+ */
+ x=windows->magnify.x-((width/magnify) >> 1);
+ if (x < 0)
+ x=(int) ((width >> 1)-windows->magnify.x*magnify);
+ else
+ if (x > (int) (ximage->width-(width/magnify)))
+ x=(int) ((ximage->width-windows->magnify.x)*magnify-(width >> 1));
+ else
+ x=0;
+ y=windows->magnify.y-((height/magnify) >> 1);
+ if (y < 0)
+ y=(int) ((height >> 1)-windows->magnify.y*magnify);
+ else
+ if (y > (int) (ximage->height-(height/magnify)))
+ y=(int) ((ximage->height-windows->magnify.y)*magnify-(height >> 1));
+ else
+ y=0;
+ if ((x != 0) || (y != 0))
+ (void) XFillRectangle(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,0,0,width,height);
+ (void) XPutImage(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,windows->magnify.ximage,0,0,x,y,width-x,
+ height-y);
+ if ((magnify > 1) && ((magnify <= (width >> 1)) &&
+ (magnify <= (height >> 1))))
+ {
+ RectangleInfo
+ highlight_info;
+
+ /*
+ Highlight center pixel.
+ */
+ highlight_info.x=(long) windows->magnify.width >> 1;
+ highlight_info.y=(long) windows->magnify.height >> 1;
+ highlight_info.width=magnify;
+ highlight_info.height=magnify;
+ (void) XDrawRectangle(display,windows->magnify.pixmap,
+ windows->magnify.highlight_context,(int) highlight_info.x,
+ (int) highlight_info.y,(unsigned int) highlight_info.width-1,
+ (unsigned int) highlight_info.height-1);
+ if (magnify > 2)
+ (void) XDrawRectangle(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,(int) highlight_info.x+1,
+ (int) highlight_info.y+1,(unsigned int) highlight_info.width-3,
+ (unsigned int) highlight_info.height-3);
+ }
+ /*
+ Show center pixel color.
+ */
+ (void) GetOneVirtualMagickPixel(windows->image.image,windows->magnify.x,
+ windows->magnify.y,&pixel,&windows->image.image->exception);
+ (void) FormatMagickString(tuple,MaxTextExtent,"%d,%d: ",
+ windows->magnify.x,windows->magnify.y);
+ (void) ConcatenateMagickString(tuple,"(",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
+ if (pixel.colorspace == CMYKColorspace)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
+ }
+ if (pixel.matte != MagickFalse)
+ {
+ (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
+ ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
+ }
+ (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
+ height=(unsigned int) windows->magnify.font_info->ascent+
+ windows->magnify.font_info->descent;
+ x=windows->magnify.font_info->max_bounds.width >> 1;
+ y=windows->magnify.font_info->ascent+(height >> 2);
+ (void) XDrawImageString(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,x,y,tuple,(int) strlen(tuple));
+ GetColorTuple(&pixel,MagickTrue,tuple);
+ y+=height;
+ (void) XDrawImageString(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,x,y,tuple,(int) strlen(tuple));
+ (void) QueryMagickColorname(windows->image.image,&pixel,SVGCompliance,tuple,
+ &windows->image.image->exception);
+ y+=height;
+ (void) XDrawImageString(display,windows->magnify.pixmap,
+ windows->magnify.annotate_context,x,y,tuple,(int) strlen(tuple));
+ /*
+ Refresh magnify window.
+ */
+ magnify_window=windows->magnify;
+ magnify_window.x=0;
+ magnify_window.y=0;
+ XRefreshWindow(display,&magnify_window,(XEvent *) NULL);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e P i x m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakePixmap() creates an X11 pixmap.
+%
+% The format of the XMakePixmap method is:
+%
+% void XMakeStandardColormap(Display *display,XVisualInfo *visual_info,
+% XResourceInfo *resource_info,Image *image,XStandardColormap *map_info,
+% XPixelInfo *pixel)
+%
+% A description of each parameter follows:
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+%
+*/
+static MagickBooleanType XMakePixmap(Display *display,
+ const XResourceInfo *resource_info,XWindowInfo *window)
+{
+ unsigned int
+ height,
+ width;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ if (window->pixmap != (Pixmap) NULL)
+ {
+ /*
+ Destroy previous X pixmap.
+ */
+ (void) XFreePixmap(display,window->pixmap);
+ window->pixmap=(Pixmap) NULL;
+ }
+ if (window->use_pixmap == MagickFalse)
+ return(MagickFalse);
+ if (window->ximage == (XImage *) NULL)
+ return(MagickFalse);
+ /*
+ Display busy cursor.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->busy_cursor);
+ (void) XFlush(display);
+ /*
+ Create pixmap.
+ */
+ width=(unsigned int) window->ximage->width;
+ height=(unsigned int) window->ximage->height;
+ window->pixmap=XCreatePixmap(display,window->id,width,height,window->depth);
+ if (window->pixmap == (Pixmap) NULL)
+ {
+ /*
+ Unable to allocate pixmap.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->cursor);
+ return(MagickFalse);
+ }
+ /*
+ Copy X image to pixmap.
+ */
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->shared_memory)
+ (void) XShmPutImage(display,window->pixmap,window->annotate_context,
+ window->ximage,0,0,0,0,width,height,MagickTrue);
+#endif
+ if (window->shared_memory == MagickFalse)
+ (void) XPutImage(display,window->pixmap,window->annotate_context,
+ window->ximage,0,0,0,0,width,height);
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Pixmap:");
+ (void) LogMagickEvent(X11Event,GetMagickModule()," width, height: %ux%u",
+ width,height);
+ }
+ /*
+ Restore cursor.
+ */
+ (void) XCheckDefineCursor(display,window->id,window->cursor);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e S t a n d a r d C o l o r m a p %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeStandardColormap() creates an X11 Standard Colormap.
+%
+% The format of the XMakeStandardColormap method is:
+%
+% XMakeStandardColormap(display,visual_info,resource_info,image,
+% map_info,pixel)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o visual_info: Specifies a pointer to a X11 XVisualInfo structure;
+% returned from XGetVisualInfo.
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+% o image: the image.
+%
+% o map_info: If a Standard Colormap type is specified, this structure is
+% initialized with info from the Standard Colormap.
+%
+% o pixel: Specifies a pointer to a XPixelInfo structure.
+%
+%
+*/
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+static inline MagickRealType DiversityPixelIntensity(
+ const DiversityPacket *pixel)
+{
+ MagickRealType
+ intensity;
+
+ intensity=0.299*pixel->red+0.587*pixel->green+0.114*pixel->blue;
+ return(intensity);
+}
+
+static int IntensityCompare(const void *x,const void *y)
+{
+ DiversityPacket
+ *color_1,
+ *color_2;
+
+ int
+ diversity;
+
+ color_1=(DiversityPacket *) x;
+ color_2=(DiversityPacket *) y;
+ diversity=(int) (DiversityPixelIntensity(color_2)-
+ DiversityPixelIntensity(color_1));
+ return(diversity);
+}
+
+static int PopularityCompare(const void *x,const void *y)
+{
+ DiversityPacket
+ *color_1,
+ *color_2;
+
+ color_1=(DiversityPacket *) x;
+ color_2=(DiversityPacket *) y;
+ return((int) color_2->count-(int) color_1->count);
+}
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+static inline Quantum ScaleXToQuantum(const unsigned long x,
+ const unsigned long scale)
+{
+ return((Quantum) (((MagickRealType) QuantumRange*x)/scale+0.5));
+}
+
+MagickExport void XMakeStandardColormap(Display *display,
+ XVisualInfo *visual_info,XResourceInfo *resource_info,Image *image,
+ XStandardColormap *map_info,XPixelInfo *pixel)
+{
+ Colormap
+ colormap;
+
+ ExceptionInfo
+ *exception;
+
+ register IndexPacket
+ *indexes;
+
+ register long
+ i;
+
+ Status
+ status;
+
+ unsigned long
+ number_colors,
+ retain_colors;
+
+ unsigned short
+ gray_value;
+
+ XColor
+ color,
+ *colors,
+ *p;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(visual_info != (XVisualInfo *) NULL);
+ assert(map_info != (XStandardColormap *) NULL);
+ assert(resource_info != (XResourceInfo *) NULL);
+ assert(pixel != (XPixelInfo *) NULL);
+ exception=(&image->exception);
+ if (resource_info->map_type != (char *) NULL)
+ {
+ /*
+ Standard Colormap is already defined (i.e. xstdcmap).
+ */
+ XGetPixelPacket(display,visual_info,map_info,resource_info,image,
+ pixel);
+ number_colors=(unsigned int) (map_info->base_pixel+
+ (map_info->red_max+1)*(map_info->green_max+1)*(map_info->blue_max+1));
+ if ((map_info->red_max*map_info->green_max*map_info->blue_max) != 0)
+ if ((image->matte == MagickFalse) &&
+ (resource_info->color_recovery == MagickFalse) &&
+ resource_info->quantize_info->dither &&
+ (number_colors < MaxColormapSize))
+ {
+ Image
+ *affinity_image;
+
+ register PixelPacket
+ *__restrict q;
+
+ /*
+ Improve image appearance with error diffusion.
+ */
+ affinity_image=AcquireImage((ImageInfo *) NULL);
+ if (affinity_image == (Image *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToDitherImage",image->filename);
+ affinity_image->columns=number_colors;
+ affinity_image->rows=1;
+ /*
+ Initialize colormap image.
+ */
+ q=QueueAuthenticPixels(affinity_image,0,0,affinity_image->columns,
+ 1,exception);
+ if (q != (PixelPacket *) NULL)
+ {
+ for (i=0; i < (long) number_colors; i++)
+ {
+ q->red=(Quantum) 0;
+ if (map_info->red_max != 0)
+ q->red=ScaleXToQuantum((unsigned long) (i/
+ map_info->red_mult),map_info->red_max);
+ q->green=(Quantum) 0;
+ if (map_info->green_max != 0)
+ q->green=ScaleXToQuantum((unsigned long) ((i/
+ map_info->green_mult) % (map_info->green_max+1)),
+ map_info->green_max);
+ q->blue=(Quantum) 0;
+ if (map_info->blue_max != 0)
+ q->blue=ScaleXToQuantum((unsigned long) (i %
+ map_info->green_mult),map_info->blue_max);
+ q->opacity=(Quantum) TransparentOpacity;
+ q++;
+ }
+ (void) SyncAuthenticPixels(affinity_image,exception);
+ (void) RemapImage(resource_info->quantize_info,image,
+ affinity_image);
+ }
+ XGetPixelPacket(display,visual_info,map_info,resource_info,image,
+ pixel);
+ (void) SetImageStorageClass(image,DirectClass);
+ affinity_image=DestroyImage(affinity_image);
+ }
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ "Standard Colormap:");
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " colormap id: 0x%lx",map_info->colormap);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue max: %lu %lu %lu",map_info->red_max,
+ map_info->green_max,map_info->blue_max);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue mult: %lu %lu %lu",map_info->red_mult,
+ map_info->green_mult,map_info->blue_mult);
+ }
+ return;
+ }
+ if ((visual_info->klass != DirectColor) &&
+ (visual_info->klass != TrueColor))
+ if ((image->storage_class == DirectClass) ||
+ ((int) image->colors > visual_info->colormap_size))
+ {
+ QuantizeInfo
+ quantize_info;
+
+ /*
+ Image has more colors than the visual supports.
+ */
+ quantize_info=(*resource_info->quantize_info);
+ quantize_info.number_colors=(unsigned long) visual_info->colormap_size;
+ (void) QuantizeImage(&quantize_info,image);
+ }
+ /*
+ Free previous and create new colormap.
+ */
+ (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
+ colormap=XDefaultColormap(display,visual_info->screen);
+ if (visual_info->visual != XDefaultVisual(display,visual_info->screen))
+ colormap=XCreateColormap(display,XRootWindow(display,visual_info->screen),
+ visual_info->visual,visual_info->klass == DirectColor ?
+ AllocAll : AllocNone);
+ if (colormap == (Colormap) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,"UnableToCreateColormap",
+ image->filename);
+ /*
+ Initialize the map and pixel info structures.
+ */
+ XGetMapInfo(visual_info,colormap,map_info);
+ XGetPixelPacket(display,visual_info,map_info,resource_info,image,pixel);
+ /*
+ Allocating colors in server colormap is based on visual class.
+ */
+ switch (visual_info->klass)
+ {
+ case StaticGray:
+ case StaticColor:
+ {
+ /*
+ Define Standard Colormap for StaticGray or StaticColor visual.
+ */
+ number_colors=image->colors;
+ colors=(XColor *) AcquireQuantumMemory((size_t)
+ visual_info->colormap_size,sizeof(*colors));
+ if (colors == (XColor *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ p=colors;
+ color.flags=(char) (DoRed | DoGreen | DoBlue);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ color.red=ScaleQuantumToShort(XRedGamma(image->colormap[i].red));
+ color.green=ScaleQuantumToShort(XGreenGamma(image->colormap[i].green));
+ color.blue=ScaleQuantumToShort(XBlueGamma(image->colormap[i].blue));
+ if (visual_info->klass != StaticColor)
+ {
+ gray_value=(unsigned short) XPixelIntensity(&color);
+ color.red=gray_value;
+ color.green=gray_value;
+ color.blue=gray_value;
+ }
+ status=XAllocColor(display,colormap,&color);
+ if (status == False)
+ {
+ colormap=XCopyColormapAndFree(display,colormap);
+ (void) XAllocColor(display,colormap,&color);
+ }
+ pixel->pixels[i]=color.pixel;
+ *p++=color;
+ }
+ break;
+ }
+ case GrayScale:
+ case PseudoColor:
+ {
+ unsigned int
+ colormap_type;
+
+ /*
+ Define Standard Colormap for GrayScale or PseudoColor visual.
+ */
+ number_colors=image->colors;
+ colors=(XColor *) AcquireQuantumMemory((size_t)
+ visual_info->colormap_size,sizeof(*colors));
+ if (colors == (XColor *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ /*
+ Preallocate our GUI colors.
+ */
+ (void) XAllocColor(display,colormap,&pixel->foreground_color);
+ (void) XAllocColor(display,colormap,&pixel->background_color);
+ (void) XAllocColor(display,colormap,&pixel->border_color);
+ (void) XAllocColor(display,colormap,&pixel->matte_color);
+ (void) XAllocColor(display,colormap,&pixel->highlight_color);
+ (void) XAllocColor(display,colormap,&pixel->shadow_color);
+ (void) XAllocColor(display,colormap,&pixel->depth_color);
+ (void) XAllocColor(display,colormap,&pixel->trough_color);
+ for (i=0; i < MaxNumberPens; i++)
+ (void) XAllocColor(display,colormap,&pixel->pen_colors[i]);
+ /*
+ Determine if image colors will "fit" into X server colormap.
+ */
+ colormap_type=resource_info->colormap;
+ status=XAllocColorCells(display,colormap,MagickFalse,(unsigned long *)
+ NULL,0,pixel->pixels,(unsigned int) image->colors);
+ if (status != False)
+ colormap_type=PrivateColormap;
+ if (colormap_type == SharedColormap)
+ {
+ DiversityPacket
+ *diversity;
+
+ int
+ y;
+
+ register int
+ x;
+
+ unsigned short
+ index;
+
+ XColor
+ *server_colors;
+
+ /*
+ Define Standard colormap for shared GrayScale or PseudoColor visual.
+ */
+ diversity=(DiversityPacket *) AcquireQuantumMemory(image->colors,
+ sizeof(*diversity));
+ if (diversity == (DiversityPacket *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ diversity[i].red=image->colormap[i].red;
+ diversity[i].green=image->colormap[i].green;
+ diversity[i].blue=image->colormap[i].blue;
+ diversity[i].index=(unsigned short) i;
+ diversity[i].count=0;
+ }
+ for (y=0; y < (int) image->rows; y++)
+ {
+ register long
+ x;
+
+ register PixelPacket
+ *__restrict q;
+
+ q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+ if (q == (PixelPacket *) NULL)
+ break;
+ indexes=GetAuthenticIndexQueue(image);
+ for (x=(long) image->columns-1; x >= 0; x--)
+ diversity[(long) indexes[x]].count++;
+ }
+ /*
+ Sort colors by decreasing intensity.
+ */
+ qsort((void *) diversity,image->colors,sizeof(*diversity),
+ IntensityCompare);
+ for (i=0; i < (long) image->colors; )
+ {
+ diversity[i].count<<=4; /* increase this colors popularity */
+ i+=MagickMax((long) (image->colors >> 4),2);
+ }
+ diversity[image->colors-1].count<<=4;
+ qsort((void *) diversity,image->colors,sizeof(*diversity),
+ PopularityCompare);
+ /*
+ Allocate colors.
+ */
+ p=colors;
+ color.flags=(char) (DoRed | DoGreen | DoBlue);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ index=diversity[i].index;
+ color.red=
+ ScaleQuantumToShort(XRedGamma(image->colormap[index].red));
+ color.green=
+ ScaleQuantumToShort(XGreenGamma(image->colormap[index].green));
+ color.blue=
+ ScaleQuantumToShort(XBlueGamma(image->colormap[index].blue));
+ if (visual_info->klass != PseudoColor)
+ {
+ gray_value=(unsigned short) XPixelIntensity(&color);
+ color.red=gray_value;
+ color.green=gray_value;
+ color.blue=gray_value;
+ }
+ status=XAllocColor(display,colormap,&color);
+ if (status == False)
+ break;
+ pixel->pixels[index]=color.pixel;
+ *p++=color;
+ }
+ /*
+ Read X server colormap.
+ */
+ server_colors=(XColor *) AcquireQuantumMemory((size_t)
+ visual_info->colormap_size,sizeof(*server_colors));
+ if (server_colors == (XColor *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ for (x=visual_info->colormap_size-1; x >= 0; x--)
+ server_colors[x].pixel=(unsigned long) x;
+ (void) XQueryColors(display,colormap,server_colors,
+ (int) MagickMin((unsigned int) visual_info->colormap_size,256));
+ /*
+ Select remaining colors from X server colormap.
+ */
+ for (; i < (long) image->colors; i++)
+ {
+ index=diversity[i].index;
+ color.red=
+ ScaleQuantumToShort(XRedGamma(image->colormap[index].red));
+ color.green=
+ ScaleQuantumToShort(XGreenGamma(image->colormap[index].green));
+ color.blue=
+ ScaleQuantumToShort(XBlueGamma(image->colormap[index].blue));
+ if (visual_info->klass != PseudoColor)
+ {
+ gray_value=(unsigned short) XPixelIntensity(&color);
+ color.red=gray_value;
+ color.green=gray_value;
+ color.blue=gray_value;
+ }
+ XBestPixel(display,colormap,server_colors,(unsigned int)
+ visual_info->colormap_size,&color);
+ pixel->pixels[index]=color.pixel;
+ *p++=color;
+ }
+ if ((int) image->colors < visual_info->colormap_size)
+ {
+ /*
+ Fill up colors array-- more choices for pen colors.
+ */
+ retain_colors=MagickMin((unsigned int)
+ (visual_info->colormap_size-image->colors),256);
+ for (i=0; i < (long) retain_colors; i++)
+ *p++=server_colors[i];
+ number_colors+=retain_colors;
+ }
+ server_colors=(XColor *) RelinquishMagickMemory(server_colors);
+ diversity=(DiversityPacket *) RelinquishMagickMemory(diversity);
+ break;
+ }
+ /*
+ Define Standard colormap for private GrayScale or PseudoColor visual.
+ */
+ if (status == False)
+ {
+ /*
+ Not enough colormap entries in the colormap-- Create a new colormap.
+ */
+ colormap=XCreateColormap(display,
+ XRootWindow(display,visual_info->screen),visual_info->visual,
+ AllocNone);
+ if (colormap == (Colormap) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ map_info->colormap=colormap;
+ if ((int) image->colors < visual_info->colormap_size)
+ {
+ /*
+ Retain colors from the default colormap to help lessens the
+ effects of colormap flashing.
+ */
+ retain_colors=MagickMin((unsigned int)
+ (visual_info->colormap_size-image->colors),256);
+ p=colors+image->colors;
+ for (i=0; i < (long) retain_colors; i++)
+ {
+ p->pixel=(unsigned long) i;
+ p++;
+ }
+ (void) XQueryColors(display,
+ XDefaultColormap(display,visual_info->screen),
+ colors+image->colors,(int) retain_colors);
+ /*
+ Transfer colors from default to private colormap.
+ */
+ (void) XAllocColorCells(display,colormap,MagickFalse,
+ (unsigned long *) NULL,0,pixel->pixels,(unsigned int)
+ retain_colors);
+ p=colors+image->colors;
+ for (i=0; i < (long) retain_colors; i++)
+ {
+ p->pixel=pixel->pixels[i];
+ p++;
+ }
+ (void) XStoreColors(display,colormap,colors+image->colors,
+ (int) retain_colors);
+ number_colors+=retain_colors;
+ }
+ (void) XAllocColorCells(display,colormap,MagickFalse,
+ (unsigned long *) NULL,0,pixel->pixels,(unsigned int)
+ image->colors);
+ }
+ /*
+ Store the image colormap.
+ */
+ p=colors;
+ color.flags=(char) (DoRed | DoGreen | DoBlue);
+ for (i=0; i < (long) image->colors; i++)
+ {
+ color.red=ScaleQuantumToShort(XRedGamma(image->colormap[i].red));
+ color.green=ScaleQuantumToShort(XGreenGamma(image->colormap[i].green));
+ color.blue=ScaleQuantumToShort(XBlueGamma(image->colormap[i].blue));
+ if (visual_info->klass != PseudoColor)
+ {
+ gray_value=(unsigned short) XPixelIntensity(&color);
+ color.red=gray_value;
+ color.green=gray_value;
+ color.blue=gray_value;
+ }
+ color.pixel=pixel->pixels[i];
+ *p++=color;
+ }
+ (void) XStoreColors(display,colormap,colors,(int) image->colors);
+ break;
+ }
+ case TrueColor:
+ case DirectColor:
+ default:
+ {
+ MagickBooleanType
+ linear_colormap;
+
+ /*
+ Define Standard Colormap for TrueColor or DirectColor visual.
+ */
+ number_colors=(unsigned int) ((map_info->red_max*map_info->red_mult)+
+ (map_info->green_max*map_info->green_mult)+
+ (map_info->blue_max*map_info->blue_mult)+1);
+ linear_colormap=(number_colors > 4096) ||
+ (((int) (map_info->red_max+1) == visual_info->colormap_size) &&
+ ((int) (map_info->green_max+1) == visual_info->colormap_size) &&
+ ((int) (map_info->blue_max+1) == visual_info->colormap_size)) ?
+ MagickTrue : MagickFalse;
+ if (linear_colormap != MagickFalse)
+ number_colors=(unsigned long) visual_info->colormap_size;
+ /*
+ Allocate color array.
+ */
+ colors=(XColor *) AcquireQuantumMemory(number_colors,sizeof(*colors));
+ if (colors == (XColor *) NULL)
+ ThrowXWindowFatalException(ResourceLimitFatalError,
+ "UnableToCreateColormap",image->filename);
+ /*
+ Initialize linear color ramp.
+ */
+ p=colors;
+ color.flags=(char) (DoRed | DoGreen | DoBlue);
+ if (linear_colormap != MagickFalse)
+ for (i=0; i < (long) number_colors; i++)
+ {
+ color.blue=(unsigned short) 0;
+ if (map_info->blue_max != 0)
+ color.blue=(unsigned short) ((unsigned long)
+ ((65535L*(i % map_info->green_mult))/map_info->blue_max));
+ color.green=color.blue;
+ color.red=color.blue;
+ color.pixel=XStandardPixel(map_info,&color);
+ *p++=color;
+ }
+ else
+ for (i=0; i < (long) number_colors; i++)
+ {
+ color.red=(unsigned short) 0;
+ if (map_info->red_max != 0)
+ color.red=(unsigned short) ((unsigned long)
+ ((65535L*(i/map_info->red_mult))/map_info->red_max));
+ color.green=(unsigned int) 0;
+ if (map_info->green_max != 0)
+ color.green=(unsigned short) ((unsigned long)
+ ((65535L*((i/map_info->green_mult) % (map_info->green_max+1)))/
+ map_info->green_max));
+ color.blue=(unsigned short) 0;
+ if (map_info->blue_max != 0)
+ color.blue=(unsigned short) ((unsigned long)
+ ((65535L*(i % map_info->green_mult))/map_info->blue_max));
+ color.pixel=XStandardPixel(map_info,&color);
+ *p++=color;
+ }
+ if ((visual_info->klass == DirectColor) &&
+ (colormap != XDefaultColormap(display,visual_info->screen)))
+ (void) XStoreColors(display,colormap,colors,(int) number_colors);
+ else
+ for (i=0; i < (long) number_colors; i++)
+ (void) XAllocColor(display,colormap,&colors[i]);
+ break;
+ }
+ }
+ if ((visual_info->klass != DirectColor) &&
+ (visual_info->klass != TrueColor))
+ {
+ /*
+ Set foreground, background, border, etc. pixels.
+ */
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->foreground_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->background_color);
+ if (pixel->background_color.pixel == pixel->foreground_color.pixel)
+ {
+ /*
+ Foreground and background colors must differ.
+ */
+ pixel->background_color.red=(~pixel->foreground_color.red);
+ pixel->background_color.green=
+ (~pixel->foreground_color.green);
+ pixel->background_color.blue=
+ (~pixel->foreground_color.blue);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->background_color);
+ }
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->border_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->matte_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->highlight_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->shadow_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->depth_color);
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->trough_color);
+ for (i=0; i < MaxNumberPens; i++)
+ {
+ XBestPixel(display,colormap,colors,(unsigned int) number_colors,
+ &pixel->pen_colors[i]);
+ pixel->pixels[image->colors+i]=pixel->pen_colors[i].pixel;
+ }
+ pixel->colors=image->colors+MaxNumberPens;
+ }
+ colors=(XColor *) RelinquishMagickMemory(colors);
+ if (IsEventLogging())
+ {
+ (void) LogMagickEvent(X11Event,GetMagickModule(),"Standard Colormap:");
+ (void) LogMagickEvent(X11Event,GetMagickModule()," colormap id: 0x%lx",
+ map_info->colormap);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue max: %lu %lu %lu",map_info->red_max,
+ map_info->green_max,map_info->blue_max);
+ (void) LogMagickEvent(X11Event,GetMagickModule(),
+ " red, green, blue mult: %lu %lu %lu",map_info->red_mult,
+ map_info->green_mult,map_info->blue_mult);
+ }
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a k e W i n d o w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMakeWindow() creates an X11 window.
+%
+% The format of the XMakeWindow method is:
+%
+% void XMakeWindow(Display *display,Window parent,char **argv,int argc,
+% XClassHint *class_hint,XWMHints *manager_hints,
+% XWindowInfo *window_info)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o parent: Specifies the parent window_info.
+%
+% o argv: Specifies the application's argument list.
+%
+% o argc: Specifies the number of arguments.
+%
+% o class_hint: Specifies a pointer to a X11 XClassHint structure.
+%
+% o manager_hints: Specifies a pointer to a X11 XWMHints structure.
+%
+% o window_info: Specifies a pointer to a X11 XWindowInfo structure.
+%
+*/
+MagickExport void XMakeWindow(Display *display,Window parent,char **argv,
+ int argc,XClassHint *class_hint,XWMHints *manager_hints,
+ XWindowInfo *window_info)
+{
+#define MinWindowSize 64
+
+ Atom
+ atom_list[2];
+
+ int
+ gravity;
+
+ static XTextProperty
+ icon_name,
+ window_name;
+
+ Status
+ status;
+
+ XSizeHints
+ *size_hints;
+
+ /*
+ Set window info hints.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window_info != (XWindowInfo *) NULL);
+ size_hints=XAllocSizeHints();
+ if (size_hints == (XSizeHints *) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToMakeXWindow",argv[0]);
+ size_hints->flags=(long) window_info->flags;
+ size_hints->x=window_info->x;
+ size_hints->y=window_info->y;
+ size_hints->width=(int) window_info->width;
+ size_hints->height=(int) window_info->height;
+ if (window_info->immutable != MagickFalse)
+ {
+ /*
+ Window size cannot be changed.
+ */
+ size_hints->min_width=size_hints->width;
+ size_hints->min_height=size_hints->height;
+ size_hints->max_width=size_hints->width;
+ size_hints->max_height=size_hints->height;
+ size_hints->flags|=PMinSize;
+ size_hints->flags|=PMaxSize;
+ }
+ else
+ {
+ /*
+ Window size can be changed.
+ */
+ size_hints->min_width=(int) window_info->min_width;
+ size_hints->min_height=(int) window_info->min_height;
+ size_hints->flags|=PResizeInc;
+ size_hints->width_inc=(int) window_info->width_inc;
+ size_hints->height_inc=(int) window_info->height_inc;
+#if !defined(PRE_R4_ICCCM)
+ size_hints->flags|=PBaseSize;
+ size_hints->base_width=size_hints->width_inc;
+ size_hints->base_height=size_hints->height_inc;
+#endif
+ }
+ gravity=NorthWestGravity;
+ if (window_info->geometry != (char *) NULL)
+ {
+ char
+ default_geometry[MaxTextExtent],
+ geometry[MaxTextExtent];
+
+ int
+ flags;
+
+ register char
+ *p;
+
+ /*
+ User specified geometry.
+ */
+ (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
+ size_hints->width,size_hints->height);
+ (void) CopyMagickString(geometry,window_info->geometry,MaxTextExtent);
+ p=geometry;
+ while (strlen(p) != 0)
+ {
+ if ((isspace((int) ((unsigned char) *p)) == 0) && (*p != '%'))
+ p++;
+ else
+ (void) CopyMagickString(p,p+1,MaxTextExtent);
+ }
+ flags=XWMGeometry(display,window_info->screen,geometry,default_geometry,
+ window_info->border_width,size_hints,&size_hints->x,&size_hints->y,
+ &size_hints->width,&size_hints->height,&gravity);
+ if ((flags & WidthValue) && (flags & HeightValue))
+ size_hints->flags|=USSize;
+ if ((flags & XValue) && (flags & YValue))
+ {
+ size_hints->flags|=USPosition;
+ window_info->x=size_hints->x;
+ window_info->y=size_hints->y;
+ }
+ }
+#if !defined(PRE_R4_ICCCM)
+ size_hints->win_gravity=gravity;
+ size_hints->flags|=PWinGravity;
+#endif
+ if (window_info->id == (Window) NULL)
+ window_info->id=XCreateWindow(display,parent,window_info->x,window_info->y,
+ (unsigned int) size_hints->width,(unsigned int) size_hints->height,
+ window_info->border_width,(int) window_info->depth,InputOutput,
+ window_info->visual,window_info->mask,&window_info->attributes);
+ else
+ {
+ MagickStatusType
+ mask;
+
+ XEvent
+ sans_event;
+
+ XWindowChanges
+ window_changes;
+
+ /*
+ Window already exists; change relevant attributes.
+ */
+ (void) XChangeWindowAttributes(display,window_info->id,window_info->mask,
+ &window_info->attributes);
+ mask=ConfigureNotify;
+ while (XCheckTypedWindowEvent(display,window_info->id,(int) mask,&sans_event)) ;
+ window_changes.x=window_info->x;
+ window_changes.y=window_info->y;
+ window_changes.width=(int) window_info->width;
+ window_changes.height=(int) window_info->height;
+ mask=(MagickStatusType) (CWWidth | CWHeight);
+ if (window_info->flags & USPosition)
+ mask|=CWX | CWY;
+ (void) XReconfigureWMWindow(display,window_info->id,window_info->screen,
+ mask,&window_changes);
+ }
+ if (window_info->id == (Window) NULL)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
+ window_info->name);
+ status=XStringListToTextProperty(&window_info->name,1,&window_name);
+ if (status == False)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateTextProperty",
+ window_info->name);
+ status=XStringListToTextProperty(&window_info->icon_name,1,&icon_name);
+ if (status == False)
+ ThrowXWindowFatalException(XServerFatalError,"UnableToCreateTextProperty",
+ window_info->icon_name);
+ if (window_info->icon_geometry != (char *) NULL)
+ {
+ int
+ flags,
+ height,
+ width;
+
+ /*
+ User specified icon geometry.
+ */
+ size_hints->flags|=USPosition;
+ flags=XWMGeometry(display,window_info->screen,window_info->icon_geometry,
+ (char *) NULL,0,size_hints,&manager_hints->icon_x,
+ &manager_hints->icon_y,&width,&height,&gravity);
+ if ((flags & XValue) && (flags & YValue))
+ manager_hints->flags|=IconPositionHint;
+ }
+ XSetWMProperties(display,window_info->id,&window_name,&icon_name,argv,argc,
+ size_hints,manager_hints,class_hint);
+ if (window_name.value != (void *) NULL)
+ {
+ (void) XFree((void *) window_name.value);
+ window_name.value=(unsigned char *) NULL;
+ window_name.nitems=0;
+ }
+ if (icon_name.value != (void *) NULL)
+ {
+ (void) XFree((void *) icon_name.value);
+ icon_name.value=(unsigned char *) NULL;
+ icon_name.nitems=0;
+ }
+ atom_list[0]=XInternAtom(display,"WM_DELETE_WINDOW",MagickFalse);
+ atom_list[1]=XInternAtom(display,"WM_TAKE_FOCUS",MagickFalse);
+ (void) XSetWMProtocols(display,window_info->id,atom_list,2);
+ (void) XFree((void *) size_hints);
+ if (window_info->shape != MagickFalse)
+ {
+#if defined(MAGICKCORE_HAVE_SHAPE)
+ int
+ error_base,
+ event_base;
+
+ /*
+ Can we apply a non-rectangular shaping mask?
+ */
+ error_base=0;
+ event_base=0;
+ if (XShapeQueryExtension(display,&error_base,&event_base) == 0)
+ window_info->shape=MagickFalse;
+#else
+ window_info->shape=MagickFalse;
+#endif
+ }
+ if (window_info->shared_memory)
+ {
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ /*
+ Can we use shared memory with this window?
+ */
+ if (XShmQueryExtension(display) == 0)
+ window_info->shared_memory=MagickFalse;
+#else
+ window_info->shared_memory=MagickFalse;
+#endif
+ }
+ window_info->image=NewImageList();
+ window_info->destroy=MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X M a g i c k P r o g r e s s M o n i t o r %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XMagickProgressMonitor() displays the progress a task is making in
+% completing a task.
+%
+% The format of the XMagickProgressMonitor method is:
+%
+% void XMagickProgressMonitor(const char *task,
+% const MagickOffsetType quantum,const MagickSizeType span,
+% void *client_data)
+%
+% A description of each parameter follows:
+%
+% o task: Identifies the task in progress.
+%
+% o quantum: Specifies the quantum position within the span which represents
+% how much progress has been made in completing a task.
+%
+% o span: Specifies the span relative to completing a task.
+%
+% o client_data: Pointer to any client data.
+%
+*/
+
+static const char *GetLocaleMonitorMessage(const char *text)
+{
+ char
+ message[MaxTextExtent],
+ tag[MaxTextExtent];
+
+ const char
+ *locale_message;
+
+ register char
+ *p;
+
+ (void) CopyMagickMemory(tag,text,MaxTextExtent);
+ p=strrchr(tag,'/');
+ if (p != (char *) NULL)
+ *p='\0';
+ (void) FormatMagickString(message,MaxTextExtent,"Monitor/%s",tag);
+ locale_message=GetLocaleMessage(message);
+ if (locale_message == message)
+ return(text);
+ return(locale_message);
+}
+
+MagickExport MagickBooleanType XMagickProgressMonitor(const char *tag,
+ const MagickOffsetType quantum,const MagickSizeType span,
+ void *magick_unused(client_data))
+{
+ XWindows
+ *windows;
+
+ windows=XSetWindows((XWindows *) ~0);
+ if (windows == (XWindows *) NULL)
+ return(MagickTrue);
+ if (windows->info.mapped != MagickFalse)
+ XProgressMonitorWidget(windows->display,windows,
+ GetLocaleMonitorMessage(tag),quantum,span);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X Q u e r y C o l o r D a t a b a s e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XQueryColorDatabase() looks up a RGB values for a color given in the target
+% string.
+%
+% The format of the XQueryColorDatabase method is:
+%
+% MagickBooleanType XQueryColorDatabase(const char *target,XColor *color)
+%
+% A description of each parameter follows:
+%
+% o target: Specifies the color to lookup in the X color database.
+%
+% o color: A pointer to an PixelPacket structure. The RGB value of the target
+% color is returned as this value.
+%
+*/
+MagickExport MagickBooleanType XQueryColorDatabase(const char *target,
+ XColor *color)
+{
+ Colormap
+ colormap;
+
+ static Display
+ *display = (Display *) NULL;
+
+ Status
+ status;
+
+ XColor
+ xcolor;
+
+ /*
+ Initialize color return value.
+ */
+ assert(color != (XColor *) NULL);
+ color->red=0;
+ color->green=0;
+ color->blue=0;
+ color->flags=(char) (DoRed | DoGreen | DoBlue);
+ if ((target == (char *) NULL) || (*target == '\0'))
+ target="#ffffffffffff";
+ /*
+ Let the X server define the color for us.
+ */
+ if (display == (Display *) NULL)
+ display=XOpenDisplay((char *) NULL);
+ if (display == (Display *) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",target);
+ return(MagickFalse);
+ }
+ colormap=XDefaultColormap(display,XDefaultScreen(display));
+ status=XParseColor(display,colormap,(char *) target,&xcolor);
+ if (status == False)
+ ThrowXWindowFatalException(XServerError,"ColorIsNotKnownToServer",target)
+ else
+ {
+ color->red=xcolor.red;
+ color->green=xcolor.green;
+ color->blue=xcolor.blue;
+ color->flags=xcolor.flags;
+ }
+ return(status != False ? MagickTrue : MagickFalse);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X Q u e r y P o s i t i o n %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XQueryPosition() gets the pointer coordinates relative to a window.
+%
+% The format of the XQueryPosition method is:
+%
+% void XQueryPosition(Display *display,const Window window,int *x,int *y)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a Window.
+%
+% o x: Return the x coordinate of the pointer relative to the origin of the
+% window.
+%
+% o y: Return the y coordinate of the pointer relative to the origin of the
+% window.
+%
+*/
+MagickExport void XQueryPosition(Display *display,const Window window,int *x,int *y)
+{
+ int
+ x_root,
+ y_root;
+
+ unsigned int
+ mask;
+
+ Window
+ root_window;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ assert(x != (int *) NULL);
+ assert(y != (int *) NULL);
+ (void) XQueryPointer(display,window,&root_window,&root_window,&x_root,&y_root,
+ x,y,&mask);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X R e f r e s h W i n d o w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XRefreshWindow() refreshes an image in a X window.
+%
+% The format of the XRefreshWindow method is:
+%
+% void XRefreshWindow(Display *display,const XWindowInfo *window,
+% const XEvent *event)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+% o event: Specifies a pointer to a XEvent structure. If it is NULL,
+% the entire image is refreshed.
+%
+*/
+MagickExport void XRefreshWindow(Display *display,const XWindowInfo *window,
+ const XEvent *event)
+{
+ int
+ x,
+ y;
+
+ unsigned int
+ height,
+ width;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (XWindowInfo *) NULL);
+ if (window->ximage == (XImage *) NULL)
+ return;
+ if (event != (XEvent *) NULL)
+ {
+ /*
+ Determine geometry from expose event.
+ */
+ x=event->xexpose.x;
+ y=event->xexpose.y;
+ width=(unsigned int) event->xexpose.width;
+ height=(unsigned int) event->xexpose.height;
+ }
+ else
+ {
+ XEvent
+ sans_event;
+
+ /*
+ Refresh entire window; discard outstanding expose events.
+ */
+ x=0;
+ y=0;
+ width=window->width;
+ height=window->height;
+ while (XCheckTypedWindowEvent(display,window->id,Expose,&sans_event)) ;
+ }
+ /*
+ Check boundary conditions.
+ */
+ if ((window->ximage->width-(x+window->x)) < (int) width)
+ width=(unsigned int) (window->ximage->width-(x+window->x));
+ if ((window->ximage->height-(y+window->y)) < (int) height)
+ height=(unsigned int) (window->ximage->height-(y+window->y));
+ /*
+ Refresh image.
+ */
+ if (window->matte_pixmap != (Pixmap) NULL)
+ {
+#if defined(MAGICKCORE_HAVE_SHAPE)
+ if (window->shape != MagickFalse)
+ XShapeCombineMask(display,window->id,ShapeBounding,0,0,
+ window->matte_pixmap,ShapeSet);
+#endif
+ (void) XSetClipMask(display,window->annotate_context,
+ window->matte_pixmap);
+ }
+ if (window->pixmap != (Pixmap) NULL)
+ {
+ if (window->depth > 1)
+ (void) XCopyArea(display,window->pixmap,window->id,
+ window->annotate_context,x+window->x,y+window->y,width,height,x,y);
+ else
+ (void) XCopyPlane(display,window->pixmap,window->id,
+ window->highlight_context,x+window->x,y+window->y,width,height,x,y,
+ 1L);
+ }
+ else
+ {
+#if defined(MAGICKCORE_HAVE_SHARED_MEMORY)
+ if (window->shared_memory)
+ (void) XShmPutImage(display,window->id,window->annotate_context,
+ window->ximage,x+window->x,y+window->y,x,y,width,height,MagickTrue);
+#endif
+ if (window->shared_memory == MagickFalse)
+ (void) XPutImage(display,window->id,window->annotate_context,
+ window->ximage,x+window->x,y+window->y,x,y,width,height);
+ }
+ if (window->matte_pixmap != (Pixmap) NULL)
+ (void) XSetClipMask(display,window->annotate_context,None);
+ (void) XFlush(display);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X R e m o t e C o m m a n d %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XRemoteCommand() forces a remote display(1) to display the specified
+% image filename.
+%
+% The format of the XRemoteCommand method is:
+%
+% MagickBooleanType XRemoteCommand(Display *display,const char *window,
+% const char *filename)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies the name or id of an X window.
+%
+% o filename: the name of the image filename to display.
+%
+*/
+MagickExport MagickBooleanType XRemoteCommand(Display *display,
+ const char *window,const char *filename)
+{
+ Atom
+ remote_atom;
+
+ Window
+ remote_window,
+ root_window;
+
+ assert(filename != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
+ if (display == (Display *) NULL)
+ display=XOpenDisplay((char *) NULL);
+ if (display == (Display *) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToOpenXServer",filename);
+ return(MagickFalse);
+ }
+ remote_atom=XInternAtom(display,"IM_PROTOCOLS",MagickFalse);
+ remote_window=(Window) NULL;
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ if (window != (char *) NULL)
+ {
+ /*
+ Search window hierarchy and identify any clients by name or ID.
+ */
+ if (isdigit((unsigned char) *window) != 0)
+ remote_window=XWindowByID(display,root_window,(Window)
+ strtol((char *) window,(char **) NULL,0));
+ if (remote_window == (Window) NULL)
+ remote_window=XWindowByName(display,root_window,window);
+ }
+ if (remote_window == (Window) NULL)
+ remote_window=XWindowByProperty(display,root_window,remote_atom);
+ if (remote_window == (Window) NULL)
+ {
+ ThrowXWindowException(XServerError,"UnableToConnectToRemoteDisplay",
+ filename);
+ return(MagickFalse);
+ }
+ /*
+ Send remote command.
+ */
+ remote_atom=XInternAtom(display,"IM_REMOTE_COMMAND",MagickFalse);
+ (void) XChangeProperty(display,remote_window,remote_atom,XA_STRING,8,
+ PropModeReplace,(unsigned char *) filename,(int) strlen(filename));
+ (void) XSync(display,MagickFalse);
+ return(MagickTrue);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X R e t a i n W i n d o w C o l o r s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XRetainWindowColors() sets X11 color resources on a window. This preserves
+% the colors associated with an image displayed on the window.
+%
+% The format of the XRetainWindowColors method is:
+%
+% void XRetainWindowColors(Display *display,const Window window)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o window: Specifies a pointer to a XWindowInfo structure.
+%
+*/
+MagickExport void XRetainWindowColors(Display *display,const Window window)
+{
+ Atom
+ property;
+
+ Pixmap
+ pixmap;
+
+ /*
+ Put property on the window.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ property=XInternAtom(display,"_XSETROOT_ID",MagickFalse);
+ if (property == (Atom) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToCreateProperty",
+ "_XSETROOT_ID");
+ return;
+ }
+ pixmap=XCreatePixmap(display,window,1,1,1);
+ if (pixmap == (Pixmap) NULL)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToCreateBitmap","");
+ return;
+ }
+ (void) XChangeProperty(display,window,property,XA_PIXMAP,32,PropModeReplace,
+ (unsigned char *) &pixmap,1);
+ (void) XSetCloseDownMode(display,RetainPermanent);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X S e l e c t W i n d o w %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSelectWindow() allows a user to select a window using the mouse. If the
+% mouse moves, a cropping rectangle is drawn and the extents of the rectangle
+% is returned in the crop_info structure.
+%
+% The format of the XSelectWindow function is:
+%
+% target_window=XSelectWindow(display,crop_info)
+%
+% A description of each parameter follows:
+%
+% o window: XSelectWindow returns the window id.
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o crop_info: Specifies a pointer to a RectangleInfo structure. It
+% contains the extents of any cropping rectangle.
+%
+%
+*/
+static Window XSelectWindow(Display *display,RectangleInfo *crop_info)
+{
+#define MinimumCropArea (unsigned int) 9
+
+ Cursor
+ target_cursor;
+
+ GC
+ annotate_context;
+
+ int
+ presses,
+ x_offset,
+ y_offset;
+
+ Status
+ status;
+
+ Window
+ root_window,
+ target_window;
+
+ XEvent
+ event;
+
+ XGCValues
+ context_values;
+
+ /*
+ Initialize graphic context.
+ */
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(crop_info != (RectangleInfo *) NULL);
+ root_window=XRootWindow(display,XDefaultScreen(display));
+ context_values.background=XBlackPixel(display,XDefaultScreen(display));
+ context_values.foreground=XWhitePixel(display,XDefaultScreen(display));
+ context_values.function=GXinvert;
+ context_values.plane_mask=
+ context_values.background ^ context_values.foreground;
+ context_values.subwindow_mode=IncludeInferiors;
+ annotate_context=XCreateGC(display,root_window,(unsigned long) (GCBackground |
+ GCForeground | GCFunction | GCSubwindowMode),&context_values);
+ if (annotate_context == (GC) NULL)
+ return(MagickFalse);
+ /*
+ Grab the pointer using target cursor.
+ */
+ target_cursor=XMakeCursor(display,root_window,XDefaultColormap(display,
+ XDefaultScreen(display)),(char * ) "white",(char * ) "black");
+ status=XGrabPointer(display,root_window,MagickFalse,(unsigned int)
+ (ButtonPressMask | ButtonReleaseMask | ButtonMotionMask),GrabModeSync,
+ GrabModeAsync,root_window,target_cursor,CurrentTime);
+ if (status != GrabSuccess)
+ {
+ ThrowXWindowFatalException(XServerError,"UnableToGrabMouse","");
+ return((Window) NULL);
+ }
+ /*
+ Select a window.
+ */
+ crop_info->width=0;
+ crop_info->height=0;
+ presses=0;
+ target_window=(Window) NULL;
+ x_offset=0;
+ y_offset=0;
+ do
+ {
+ if ((crop_info->width*crop_info->height) >= MinimumCropArea)
+ (void) XDrawRectangle(display,root_window,annotate_context,
+ (int) crop_info->x,(int) crop_info->y,(unsigned int) crop_info->width-1,
+ (unsigned int) crop_info->height-1);
+ /*
+ Allow another event.
+ */
+ (void) XAllowEvents(display,SyncPointer,CurrentTime);
+ (void) XWindowEvent(display,root_window,ButtonPressMask |
+ ButtonReleaseMask | ButtonMotionMask,&event);
+ if ((crop_info->width*crop_info->height) >= MinimumCropArea)
+ (void) XDrawRectangle(display,root_window,annotate_context,
+ (int) crop_info->x,(int) crop_info->y,(unsigned int) crop_info->width-1,
+ (unsigned int) crop_info->height-1);
+ switch (event.type)
+ {
+ case ButtonPress:
+ {
+ target_window=XGetSubwindow(display,event.xbutton.subwindow,
+ event.xbutton.x,event.xbutton.y);
+ if (target_window == (Window) NULL)
+ target_window=root_window;
+ x_offset=event.xbutton.x_root;
+ y_offset=event.xbutton.y_root;
+ crop_info->x=x_offset;
+ crop_info->y=y_offset;
+ crop_info->width=0;
+ crop_info->height=0;
+ presses++;
+ break;
+ }
+ case ButtonRelease:
+ {
+ presses--;
+ break;
+ }
+ case MotionNotify:
+ {
+ /*
+ Discard pending button motion events.
+ */
+ while (XCheckMaskEvent(display,ButtonMotionMask,&event)) ;
+ crop_info->x=event.xmotion.x;
+ crop_info->y=event.xmotion.y;
+ /*
+ Check boundary conditions.
+ */
+ if ((int) crop_info->x < x_offset)
+ crop_info->width=(unsigned int) (x_offset-crop_info->x);
+ else
+ {
+ crop_info->width=(unsigned int) (crop_info->x-x_offset);
+ crop_info->x=x_offset;
+ }
+ if ((int) crop_info->y < y_offset)
+ crop_info->height=(unsigned int) (y_offset-crop_info->y);
+ else
+ {
+ crop_info->height=(unsigned int) (crop_info->y-y_offset);
+ crop_info->y=y_offset;
+ }
+ }
+ default:
+ break;
+ }
+ } while ((target_window == (Window) NULL) || (presses > 0));
+ (void) XUngrabPointer(display,CurrentTime);
+ (void) XFreeCursor(display,target_cursor);
+ (void) XFreeGC(display,annotate_context);
+ if ((crop_info->width*crop_info->height) < MinimumCropArea)
+ {
+ crop_info->width=0;
+ crop_info->height=0;
+ }
+ if ((crop_info->width != 0) && (crop_info->height != 0))
+ target_window=root_window;
+ return(target_window);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X S e t C u r s o r S t a t e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetCursorState() sets the cursor state to busy, otherwise the cursor are
+% reset to their default.
+%
+% The format of the XXSetCursorState method is:
+%
+% XSetCursorState(display,windows,const MagickStatusType state)
+%
+% A description of each parameter follows:
+%
+% o display: Specifies a connection to an X server; returned from
+% XOpenDisplay.
+%
+% o windows: Specifies a pointer to a XWindows structure.
+%
+% o state: An unsigned integer greater than 0 sets the cursor state
+% to busy, otherwise the cursor are reset to their default.
+%
+*/
+MagickExport void XSetCursorState(Display *display,XWindows *windows,
+ const MagickStatusType state)
+{
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(windows != (XWindows *) NULL);
+ if (state)
+ {
+ (void) XCheckDefineCursor(display,windows->image.id,
+ windows->image.busy_cursor);
+ (void) XCheckDefineCursor(display,windows->pan.id,
+ windows->pan.busy_cursor);
+ (void) XCheckDefineCursor(display,windows->magnify.id,
+ windows->magnify.busy_cursor);
+ (void) XCheckDefineCursor(display,windows->command.id,
+ windows->command.busy_cursor);
+ }
+ else
+ {
+ (void) XCheckDefineCursor(display,windows->image.id,
+ windows->image.cursor);
+ (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
+ (void) XCheckDefineCursor(display,windows->magnify.id,
+ windows->magnify.cursor);
+ (void) XCheckDefineCursor(display,windows->command.id,
+ windows->command.cursor);
+ (void) XCheckDefineCursor(display,windows->command.id,
+ windows->widget.cursor);
+ (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
+ }
+ windows->info.mapped=MagickFalse;
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X S e t W i n d o w s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XSetWindows() sets the X windows structure if the windows info is specified.
+% Otherwise the current windows structure is returned.
+%
+% The format of the XSetWindows method is:
+%
+% XWindows *XSetWindows(XWindows *windows_info)
+%
+% A description of each parameter follows:
+%
+% o windows_info: Initialize the Windows structure with this information.
+%
+*/
+MagickExport XWindows *XSetWindows(XWindows *windows_info)
+{
+ static XWindows
+ *windows = (XWindows *) NULL;
+
+ if (windows_info != (XWindows *) ~0)
+ {
+ windows=(XWindows *) RelinquishMagickMemory(windows);
+ windows=windows_info;
+ }
+ return(windows);
+}
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X U s e r P r e f e r e n c e s %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XUserPreferences() saves the preferences in a configuration file in the
+% users' home directory.
+%
+% The format of the XUserPreferences method is:
+%
+% void XUserPreferences(XResourceInfo *resource_info)
+%
+% A description of each parameter follows:
+%
+% o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
+%
+*/
+MagickExport void XUserPreferences(XResourceInfo *resource_info)
+{
+#if defined(X11_PREFERENCES_PATH)
+ char
+ cache[MaxTextExtent],
+ filename[MaxTextExtent],
+ specifier[MaxTextExtent];
+
+ const char
+ *value;
+
+ XrmDatabase
+ preferences_database;
+
+ /*
+ Save user preferences to the client configuration file.
+ */
+ assert(resource_info != (XResourceInfo *) NULL);
+ preferences_database=XrmGetStringDatabase("");
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.backdrop",
+ GetClientName());
+ value=resource_info->backdrop ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.colormap",
+ GetClientName());
+ value=resource_info->colormap == SharedColormap ? "Shared" : "Private";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.confirmExit",
+ GetClientName());
+ value=resource_info->confirm_exit ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.confirmEdit",
+ GetClientName());
+ value=resource_info->confirm_edit ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.displayWarnings",
+ GetClientName());
+ value=resource_info->display_warnings ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.dither",
+ GetClientName());
+ value=resource_info->quantize_info->dither ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.gammaCorrect",
+ GetClientName());
+ value=resource_info->gamma_correct ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.undoCache",
+ GetClientName());
+ (void) FormatMagickString(cache,MaxTextExtent,"%lu",
+ resource_info->undo_cache);
+ XrmPutStringResource(&preferences_database,specifier,cache);
+ (void) FormatMagickString(specifier,MaxTextExtent,"%s.usePixmap",
+ GetClientName());
+ value=resource_info->use_pixmap ? "True" : "False";
+ XrmPutStringResource(&preferences_database,specifier,(char *) value);
+ (void) FormatMagickString(filename,MaxTextExtent,"%s%src",
+ X11_PREFERENCES_PATH,GetClientName());
+ ExpandFilename(filename);
+ XrmPutFileDatabase(preferences_database,filename);
+#endif
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X V i s u a l C l a s s N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XVisualClassName() returns the visual class name as a character string.
+%
+% The format of the XVisualClassName method is:
+%
+% char *XVisualClassName(const int visual_class)
+%
+% A description of each parameter follows:
+%
+% o visual_type: XVisualClassName returns the visual class as a character
+% string.
+%
+% o class: Specifies the visual class.
+%
+%
+*/
+static const char *XVisualClassName(const int visual_class)
+{
+ switch (visual_class)
+ {
+ case StaticGray: return("StaticGray");
+ case GrayScale: return("GrayScale");
+ case StaticColor: return("StaticColor");
+ case PseudoColor: return("PseudoColor");
+ case TrueColor: return("TrueColor");
+ case DirectColor: return("DirectColor");
+ }
+ return("unknown visual class");
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X W a r n i n g %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XWarning() displays a warning reason in a Notice widget.
+%
+% The format of the XWarning method is:
+%
+% void XWarning(const unsigned int warning,const char *reason,
+% const char *description)
+%
+% A description of each parameter follows:
+%
+% o warning: Specifies the numeric warning category.
+%
+% o reason: Specifies the reason to display before terminating the
+% program.
+%
+% o description: Specifies any description to the reason.
+%
+*/
+MagickExport void XWarning(const ExceptionType magick_unused(warning),
+ const char *reason,const char *description)
+{
+ char
+ text[MaxTextExtent];
+
+ XWindows
+ *windows;
+
+ if (reason == (char *) NULL)
+ return;
+ (void) CopyMagickString(text,reason,MaxTextExtent);
+ (void) ConcatenateMagickString(text,":",MaxTextExtent);
+ windows=XSetWindows((XWindows *) ~0);
+ XNoticeWidget(windows->display,windows,text,(char *) description);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X W i n d o w B y I D %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XWindowByID() locates a child window with a given ID. If not window with
+% the given name is found, 0 is returned. Only the window specified and its
+% subwindows are searched.
+%
+% The format of the XWindowByID function is:
+%
+% child=XWindowByID(display,window,id)
+%
+% A description of each parameter follows:
+%
+% o child: XWindowByID returns the window with the specified
+% id. If no windows are found, XWindowByID returns 0.
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o id: Specifies the id of the window to locate.
+%
+*/
+MagickExport Window XWindowByID(Display *display,const Window root_window,
+ const unsigned long id)
+{
+ RectangleInfo
+ rectangle_info;
+
+ register int
+ i;
+
+ Status
+ status;
+
+ unsigned int
+ number_children;
+
+ Window
+ child,
+ *children,
+ window;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(root_window != (Window) NULL);
+ if (id == 0)
+ return(XSelectWindow(display,&rectangle_info));
+ if (root_window == id)
+ return(id);
+ status=XQueryTree(display,root_window,&child,&child,&children,
+ &number_children);
+ if (status == False)
+ return((Window) NULL);
+ window=(Window) NULL;
+ for (i=0; i < (int) number_children; i++)
+ {
+ /*
+ Search each child and their children.
+ */
+ window=XWindowByID(display,children[i],id);
+ if (window != (Window) NULL)
+ break;
+ }
+ if (children != (Window *) NULL)
+ (void) XFree((void *) children);
+ return(window);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X W i n d o w B y N a m e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XWindowByName() locates a window with a given name on a display. If no
+% window with the given name is found, 0 is returned. If more than one window
+% has the given name, the first one is returned. Only root and its children
+% are searched.
+%
+% The format of the XWindowByName function is:
+%
+% window=XWindowByName(display,root_window,name)
+%
+% A description of each parameter follows:
+%
+% o window: XWindowByName returns the window id.
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o root_window: Specifies the id of the root window.
+%
+% o name: Specifies the name of the window to locate.
+%
+*/
+MagickExport Window XWindowByName(Display *display,const Window root_window,
+ const char *name)
+{
+ register int
+ i;
+
+ Status
+ status;
+
+ unsigned int
+ number_children;
+
+ Window
+ *children,
+ child,
+ window;
+
+ XTextProperty
+ window_name;
+
+ assert(display != (Display *) NULL);
+ assert(root_window != (Window) NULL);
+ assert(name != (char *) NULL);
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name);
+ if (XGetWMName(display,root_window,&window_name) != 0)
+ if (LocaleCompare((char *) window_name.value,name) == 0)
+ return(root_window);
+ status=XQueryTree(display,root_window,&child,&child,&children,
+ &number_children);
+ if (status == False)
+ return((Window) NULL);
+ window=(Window) NULL;
+ for (i=0; i < (int) number_children; i++)
+ {
+ /*
+ Search each child and their children.
+ */
+ window=XWindowByName(display,children[i],name);
+ if (window != (Window) NULL)
+ break;
+ }
+ if (children != (Window *) NULL)
+ (void) XFree((void *) children);
+ return(window);
+}
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X W i n d o w B y P r o p e r y %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XWindowByProperty() locates a child window with a given property. If not
+% window with the given name is found, 0 is returned. If more than one window
+% has the given property, the first one is returned. Only the window
+% specified and its subwindows are searched.
+%
+% The format of the XWindowByProperty function is:
+%
+% child=XWindowByProperty(display,window,property)
+%
+% A description of each parameter follows:
+%
+% o child: XWindowByProperty returns the window id with the specified
+% property. If no windows are found, XWindowByProperty returns 0.
+%
+% o display: Specifies a pointer to the Display structure; returned from
+% XOpenDisplay.
+%
+% o property: Specifies the property of the window to locate.
+%
+*/
+MagickExport Window XWindowByProperty(Display *display,const Window window,
+ const Atom property)
+{
+ Atom
+ type;
+
+ int
+ format;
+
+ Status
+ status;
+
+ unsigned char
+ *data;
+
+ unsigned int
+ i,
+ number_children;
+
+ unsigned long
+ after,
+ number_items;
+
+ Window
+ child,
+ *children,
+ parent,
+ root;
+
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
+ assert(display != (Display *) NULL);
+ assert(window != (Window) NULL);
+ assert(property != (Atom) NULL);
+ status=XQueryTree(display,window,&root,&parent,&children,&number_children);
+ if (status == False)
+ return((Window) NULL);
+ type=(Atom) NULL;
+ child=(Window) NULL;
+ for (i=0; (i < number_children) && (child == (Window) NULL); i++)
+ {
+ status=XGetWindowProperty(display,children[i],property,0L,0L,MagickFalse,
+ (Atom) AnyPropertyType,&type,&format,&number_items,&after,&data);
+ if (data != NULL)
+ (void) XFree((void *) data);
+ if ((status == Success) && (type != (Atom) NULL))
+ child=children[i];
+ }
+ for (i=0; (i < number_children) && (child == (Window) NULL); i++)
+ child=XWindowByProperty(display,children[i],property);
+ if (children != (Window *) NULL)
+ (void) XFree((void *) children);
+ return(child);
+}
+#else
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X I m p o r t I m a g e %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XImportImage() reads an image from an X window.
+%
+% The format of the XImportImage method is:
+%
+% Image *XImportImage(const ImageInfo *image_info,XImportInfo *ximage_info)
+%
+% A description of each parameter follows:
+%
+% o image_info: the image info..
+%
+% o ximage_info: Specifies a pointer to an XImportInfo structure.
+%
+*/
+MagickExport Image *XImportImage(const ImageInfo *image_info,
+ XImportInfo *ximage_info)
+{
+ assert(image_info != (const ImageInfo *) NULL);
+ assert(image_info->signature == MagickSignature);
+ if (image_info->debug != MagickFalse)
+ (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+ image_info->filename);
+ assert(ximage_info != (XImportInfo *) NULL);
+ return((Image *) NULL);
+}
+#endif
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% %
+% %
+% %
+% X G e t I m p o r t I n f o %
+% %
+% %
+% %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% XGetImportInfo() initializes the XImportInfo structure.
+%
+% The format of the XGetImportInfo method is:
+%
+% void XGetImportInfo(XImportInfo *ximage_info)
+%
+% A description of each parameter follows:
+%
+% o ximage_info: Specifies a pointer to an ImageInfo structure.
+%
+*/
+MagickExport void XGetImportInfo(XImportInfo *ximage_info)
+{
+ assert(ximage_info != (XImportInfo *) NULL);
+ ximage_info->frame=MagickFalse;
+ ximage_info->borders=MagickFalse;
+ ximage_info->screen=MagickFalse;
+ ximage_info->descend=MagickTrue;
+ ximage_info->silent=MagickFalse;
+}
diff --git a/magick/xwindow.h b/magick/xwindow.h
new file mode 100644
index 0000000..ec8ba63
--- /dev/null
+++ b/magick/xwindow.h
@@ -0,0 +1,45 @@
+/*
+ Copyright 1999-2009 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.
+ obtain a copy of the License at
+
+ http://www.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.
+
+ MagickCore X11 window methods.
+*/
+#ifndef _MAGICKCORE_XWINDOW_H
+#define _MAGICKCORE_XWINDOW_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+typedef struct _XImportInfo
+{
+ MagickBooleanType
+ frame,
+ borders,
+ screen,
+ descend,
+ silent;
+} XImportInfo;
+
+extern MagickExport Image
+ *XImportImage(const ImageInfo *,XImportInfo *);
+
+extern MagickExport void
+ XGetImportInfo(XImportInfo *);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif