home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / ImageMagick-4.0.6.tar.gz / ImageMagick-4.0.6.tar / ImageMagick-4.0.6 / PerlMagick / Magick.xs < prev    next >
Text File  |  1998-05-12  |  90KB  |  3,677 lines

  1. /* Released Feb. 17, 1997  by Kyle Shorter (magick@sympatico.org)    */
  2. /* Maintained by John Cristy (cristy@sympatico.org)                     */
  3. /* Public Domain                            */
  4.  
  5. #ifdef __cplusplus
  6. extern "C" {
  7. #endif
  8. #include "EXTERN.h"
  9. #include "XSUB.h"
  10. #include "perl.h"
  11. #include "magick.h"
  12. #include <setjmp.h>
  13. #ifdef __cplusplus
  14. }
  15. #endif
  16.  
  17. #define NUMBEROF(a) (sizeof a / sizeof *a)
  18. #define ENDOF(a)    (&a[NUMBEROF(a)])
  19. #define NULV        ((void *)NULL)
  20.  
  21. char *IM_packname = "Image::Magick";
  22. char *client_name = "PerlMagick";
  23. int IM_do_warn = 0;        /* if != 0: error messages call Perl warn */
  24.  
  25. static char *complain = "Reference is not my type";
  26.  
  27. #define warning(e, s1, s2) { SetErrorStatus(e); warninghandler(s1, s2); }
  28.  
  29. static double
  30. constant(char *name, int arg)
  31. {
  32.     errno = 0;
  33.     switch (*name)
  34.     {
  35.     case 'P':
  36.     if (strEQ(name, "PluginWarning"))
  37.         return PluginWarning;
  38.     break;
  39.  
  40.     case 'O':
  41.     if (strEQ(name, "OptionError"))
  42.         return OptionError;
  43.     if (strEQ(name, "OptionWarning"))
  44.         return OptionWarning;
  45.     break;
  46.  
  47.     case 'X':
  48.     if (strEQ(name, "XServerError"))
  49.         return XServerError;
  50.     if (strEQ(name, "XServerWarning"))
  51.         return XServerWarning;
  52.     break;
  53.  
  54.     case 'R':
  55.     if (strEQ(name, "ResourceLimitError"))
  56.         return ResourceLimitError;
  57.     break;
  58.  
  59.     case 'S':
  60.     if (strEQ(name, "Success"))
  61.         return 0;
  62.     break;
  63.  
  64.     case 'M':
  65.     if (strEQ(name, "MissingPluginWarning"))
  66.         return MissingPluginWarning;
  67.     break;
  68.  
  69.     case 'C':
  70.     if (strEQ(name, "CorruptImageWarning"))
  71.         return CorruptImageWarning;
  72.     break;
  73.  
  74.     case 'F':
  75.     if (strEQ(name, "FileOpenWarning"))
  76.         return FileOpenWarning;
  77.     break;
  78.     }
  79.  
  80.     errno = EINVAL;
  81.     return 0;
  82. }
  83.  
  84. /*  The data type for the Image::Magick package.  In reality, it can either
  85.  *  be an Image* (if its type is scalar) or AV* (if its type is array).
  86.  */
  87. typedef void *Image__Magick;
  88.  
  89. /*  The data type for remembering options set by the user, which basically
  90.  *  correspond to ImageMagick's command line options.  Each AV* type of
  91.  *  Image__Magick has a special variable created for it (see getinfo) that
  92.  *  holds one.
  93.  */
  94. struct info {
  95.     ImageInfo info;
  96.     QuantizeInfo quant;
  97. };
  98.  
  99. /* the different types of arguments that can be passed as arguments from Perl */
  100. union alist {
  101.     int t_int;
  102.     double t_dbl;
  103.     char *t_str;
  104.     Image *t_img;
  105. };
  106.  
  107. /*  The p_XXX arrays match the ImageMagick enums.  The positions must match
  108.  *  between both.  The names are the minimum length to match, e.g., in p_noises
  109.  *  AddNoise("laplacian")  and  AddNoise("LaplacianNoise") both work.
  110.  */
  111. static char *p_noises[] = {
  112.     "Uniform", "Gaussian", "Multiplicative", "Impulse", "Laplacian", "Poisson",
  113. 0 };
  114.  
  115. static char *p_alignments[] = {
  116.     "Undefined", "Left", "Center", "Right", 0 };
  117.  
  118. static char *p_boolean[] = {
  119.     "False", "True", 0 };
  120.  
  121. static char *p_compressions[] = {
  122.     "Undefined", "None", "JPEG", "LZW", "Runlength", "Zip", 0 };
  123.  
  124. static char *p_filters[] = {
  125.     "Box", "Mitchell", "Triangle", 0 };
  126.  
  127. static char *p_gravities[] = {
  128.     "Forget", "NorthWest", "North", "NorthEast", "West",
  129.     "Center", "East", "SouthWest", "South", "SouthEast", "Static", 0 };
  130.  
  131. static char *p_interlaces[] = {
  132.     "Undefined", "None", "Line", "Plane", "Partition", 0 };
  133.  
  134. static char *p_layers[] = {
  135.     "Undefined", "Red", "Green", "Blue", "Matte", 0 };
  136.  
  137. static char *p_methods[] = {
  138.     "Point", "Replace", "Floodfill", "Reset", 0 };
  139.  
  140. static char *p_modes[] = {
  141.     "Frame", "Unframe", "Concatenate", 0 };
  142.  
  143. static char *p_previews[] = {
  144.     "Rotate", "Shear", "Roll", "Hue", "Saturation", "Brightness", "Gamma",
  145.     "Spiff", "Dull", "Grayscale", "Quantize", "Despeckle", "ReduceNoise",
  146.     "AddNoise", "Sharpen", "Blur", "EdgeDetect", "Spread", "Solarize", "Shade",
  147.     "Raise", "Segment", "Swirl", "Implode", "OilPaint", "Charcoal", 0 };
  148.  
  149. static char *p_primitives[] = {
  150.     "Undefined", "Point", "Line", "Rectangle", "FillRectangle", "Circle",
  151.     "FillCircle", "Polygon", "FillPolygon", "Color", "Matte", "Text", "Image",
  152. 0 };
  153.  
  154. static char *p_units[] = {
  155.     "Undefined", "pixels / inch", "pixels / centimeter", 0 };
  156.  
  157. static char *p_composites[] = {
  158.     "Undefined", "Over", "In", "Out", "Atop", "Xor", "Plus", "Minus", "Add",
  159.     "Subtract", "Difference", "Bumpmap", "Replace", "ReplaceRed",
  160.     "ReplaceGreen", "ReplaceBlue", "ReplaceMatte", "Blend", "Displace", 0 };
  161.  
  162. static char *p_colorspaces[] = {
  163.     "Undefined", "RGB", "Gray", "Transparent", "OHTA", "XYZ", "YCbCr", "YCC",
  164.     "YIQ", "YPbPr", "YUV", "CMYK", 0 };
  165.  
  166. static char *p_classes[] = {
  167.     "Undefined", "DirectClass", "PseudoClass", 0 };
  168.  
  169. /* types for struct routines.args.type */
  170. #define P_STR    (char **)0    /* arg is a string */
  171. #define P_INT    (char **)1    /* arg in an integer */
  172. #define P_DBL    (char **)2    /* arg is a double */
  173. #define P_IMG    (char **)3    /* arg is an image reference */
  174.  
  175. /* anything else is a pointer to an array of strings whose position in */
  176. /* the array is returned as an integer, i.e., an enum.  */
  177.  
  178. #define ARGMAX    12
  179.  
  180. /*  The list of ImageMagick routines and their parameters currently supported
  181.  *  by this interface (except for Read, Write and new).  I did it this way
  182.  *  to avoid a lot of duplicated code, and to use the neato ALIASes that
  183.  *  XS provides.  Parameter names are matched up to the name's length so
  184.  *  that e.g. the parameter name "comment" matches "commentstuff".
  185.  */
  186. static struct routines {
  187.     char *name;        /* the routine name */
  188.     struct args {
  189.         char *varname;    /* the parameter's name */
  190.         char **type;    /* the parameter's type, P_XXX or p_xxx */
  191.     } args[ARGMAX];
  192. } routines[] = {
  193.     {    "Comment", { {"comment", P_STR} } },
  194.     {    "Label",   { {"label", P_STR} } },
  195.     {    "AddNoise", { {"noise", p_noises} } },
  196.     {    "Colorize", { {"color", P_STR}, {"pen", P_STR} } },
  197.     {    "Border", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  198.                     {"color", P_STR} } },
  199.     {    "Blur", { {"factor", P_DBL} } },
  200.     {    "Chop", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  201.                     {"x", P_INT}, {"y", P_INT} } },
  202.     {    "Crop", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  203.                     {"x", P_INT}, {"y", P_INT} } },
  204.     {    "Despeckle", },
  205.     {    "Edge", { {"factor", P_DBL} } },
  206.     {    "Emboss", },
  207.     {    "Enhance", },
  208.     {    "Flip", },
  209.     {    "Flop", },
  210.     {    "Frame", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  211.          {"inner", P_INT}, {"outer", P_INT}, {"color", P_STR} } },
  212.     {    "Implode", { {"factor", P_DBL} } },
  213.     {    "Magnify", },
  214.     {    "MedianFilter", },
  215.     {    "Minify", },
  216.     {    "OilPaint", { {"radius", P_INT} } },
  217.     {    "ReduceNoise", },
  218.     {    "Roll", { {"geom", P_STR}, {"x", P_INT}, {"y", P_INT} } },
  219.     {    "Rotate", { {"degree", P_DBL}, {"crop", p_boolean},
  220.           {"sharpen", p_boolean} } },
  221.     {    "Sample", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT} } },
  222.     {    "Scale", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT} } },
  223.     {    "Shade", { {"geom", P_STR}, {"azimuth", P_DBL},
  224.            {"elevat", P_DBL}, {"color", p_boolean} } },
  225.     {    "Sharpen", { {"factor", P_DBL} } },
  226.     {    "Shear", { {"geom", P_STR}, {"x", P_DBL}, {"y", P_DBL},
  227.            {"crop", p_boolean} } },
  228.     {    "Spread", { {"amount", P_INT} } },
  229.     {    "Swirl", { {"degree", P_DBL} } },
  230.     {    "Zoom", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  231.           {"filter", p_filters} } },
  232.     {    "IsGrayImage", },
  233.     {    "Annotate", { {"server", P_STR}, {"font", P_STR}, {"point", P_INT},
  234.             {"density", P_STR}, {"box", P_STR}, {"pen", P_STR},
  235.             {"geom", P_STR}, {"text", P_STR}, {"x", P_INT},
  236.             {"y", P_INT}, {"align", p_alignments} } },
  237.     {    "ColorFloodfill", },
  238.     {    "Composite", { {"compos", p_composites}, {"image", P_IMG},
  239.             {"geom", P_STR}, {"x", P_INT}, {"y", P_INT},
  240.             {"grav", p_gravities} } },
  241.     {    "Contrast", { {"sharp", p_boolean} } },
  242.     {    "CycleColormap", { {"amount", P_INT} } },
  243.     {    "Draw", { {"prim", p_primitives}, {"points", P_STR},
  244.           {"meth", p_methods}, {"pen", P_STR}, {"linew", P_INT},
  245.           {"server", P_STR}, } },
  246.     {    "Equalize", },
  247.     {    "Gamma", { {"gamma", P_STR}, {"red", P_DBL}, {"green", P_DBL},
  248.             {"blue", P_DBL} } },
  249.     {    "Map", { {"image", P_IMG}, {"dither", p_boolean} } },
  250.     {    "MatteFloodfill", },
  251.     {    "Modulate", { {"factor", P_STR}, {"bright", P_DBL}, {"satur", P_DBL},
  252.             {"hue", P_DBL} } },
  253.     {    "Negate", { {"gray", p_boolean} } },
  254.     {    "Normalize", },
  255.     {    "NumberColors", },
  256.     {    "Opaque", { {"color", P_STR}, {"pen", P_STR} } },
  257.     {    "Quantize", { {"colors", P_INT}, {"tree", P_INT},
  258.                 {"colorsp", p_colorspaces}, {"dither", p_boolean} } },
  259.     {    "Raise", { {"geom", P_STR}, {"width", P_INT}, {"height", P_INT},
  260.             {"x", P_INT}, {"y", P_INT}, {"raise", p_boolean} } },
  261.     {    "Segment", { {"colorsp", p_colorspaces}, {"verbose", p_boolean},
  262.             {"clust", P_DBL}, {"smooth", P_DBL} } },
  263.     {    "Signature", },
  264.     {    "Solarize", { {"factor", P_DBL} } },
  265.     {    "Sync", },
  266.     {    "Texture", { {"file", P_STR} } },
  267.     {    "Transform", { {"crop", P_STR}, {"geom", P_STR} } },
  268.     {    "Transparent", { {"color", P_STR} } },
  269.     {   "Threshold", { {"threshold", P_DBL} } },
  270.     {   "Charcoal", { {"factor", P_STR} } },
  271.     {    "Trim", },
  272.     {    "Wave", { {"geom", P_STR}, {"ampli", P_DBL}, {"wave", P_DBL} } },
  273.     {    "Layer", { {"layer", p_layers} } },
  274. };
  275.  
  276. static SV *im_er_mes;        /* Perl variable for storing messages */
  277. static jmp_buf *im_er_jmp;    /* long jump return for FATAL errors */
  278.  
  279. /* something like a Perl-like ignore case string compare; 
  280.  * the strings are equal if the second string runs out first.
  281.  */
  282. static int
  283. strEQcase(const char *a, const char *b)
  284. {
  285.     char c;
  286.     int i;
  287.  
  288.     for (i=0 ; (c = *b); a++, b++, i++)
  289.     if ((isUPPER(c) ? toLOWER(c) : c) != (isUPPER(*a) ? toLOWER(*a) : *a))
  290.         return 0;
  291.     return i;
  292. }
  293.  
  294. /* look through a list of strings matching it to str and return */
  295. /* its index in the list, or -1 for not found */
  296. static int
  297. LookupStr(char **list, const char *str)
  298. {
  299.     char **p;
  300.     int offset = (-1);
  301.     int longest = 0;
  302.  
  303.     for (p = list; *p; p++)
  304.     if (strEQcase(str, *p) > longest)
  305.         {
  306.             offset = p - list;
  307.             longest=strEQcase(str, *p);
  308.         }
  309.  
  310.     return offset;
  311. }
  312.  
  313. /*  Makes a duplicate of the given string */
  314.  
  315. static char *
  316. copy_string(char *src)
  317. {
  318.     char *dest;
  319.  
  320.     if (!src)
  321.     return((char *) NULL);
  322.     dest = (char *) safemalloc(Extent(src) + 1);
  323.     strcpy(dest, src);
  324.     return(dest);
  325. }
  326.  
  327.  
  328. /*  Makes a duplicate of the given info, or if info is NULL, a new one. */
  329. static struct info *
  330. copy_info(struct info *info)
  331. {
  332.     struct info *newinfo = (struct info *) safemalloc(sizeof (struct info));
  333.  
  334.     if (!info)
  335.     {
  336.     (void) SetClientName(client_name);
  337.     GetImageInfo(&newinfo->info);
  338.     GetQuantizeInfo(&newinfo->quant);
  339.     return newinfo;
  340.     }
  341.  
  342.     *newinfo = *info;
  343.     newinfo->info.filename =
  344.       strcpy((char *) safemalloc(MaxTextExtent), info->info.filename);
  345.     if (info->info.server_name)
  346.     newinfo->info.server_name = copy_string(info->info.server_name);
  347.     if (info->info.font)
  348.     newinfo->info.font = copy_string(info->info.font);
  349.     if (info->info.pen)
  350.     newinfo->info.pen = copy_string(info->info.pen);
  351.     if (info->info.box)
  352.     newinfo->info.box = copy_string(info->info.box);
  353.     if (info->info.size)
  354.     newinfo->info.size = copy_string(info->info.size);
  355.     if (info->info.tile)
  356.     newinfo->info.tile = copy_string(info->info.tile);
  357.     if (info->info.density)
  358.     newinfo->info.density = copy_string(info->info.density);
  359.     if (info->info.page)
  360.     newinfo->info.page = copy_string(info->info.page);
  361.     if (info->info.dispose)
  362.     newinfo->info.dispose = copy_string(info->info.dispose);
  363.     if (info->info.delay)
  364.     newinfo->info.delay = copy_string(info->info.delay);
  365.     if (info->info.iterations)
  366.     newinfo->info.iterations = copy_string(info->info.iterations);
  367.     if (info->info.texture)
  368.     newinfo->info.texture = copy_string(info->info.texture);
  369.     if (info->info.undercolor)
  370.     newinfo->info.undercolor = copy_string(info->info.undercolor);
  371.  
  372.     return newinfo;
  373. }
  374.  
  375. /*  Get rid of a previously created info structure. */
  376. static void
  377. destroy_info(struct info *info)
  378. {
  379.     if (info->info.filename)
  380.     safefree(info->info.filename);
  381.     if (info->info.server_name)
  382.     safefree(info->info.server_name);
  383.     if (info->info.font)
  384.     safefree(info->info.font);
  385.     if (info->info.pen)
  386.     safefree(info->info.pen);
  387.     if (info->info.box)
  388.     safefree(info->info.box);
  389.     if (info->info.size)
  390.     safefree(info->info.size);
  391.     if (info->info.tile)
  392.     safefree(info->info.tile);
  393.     if (info->info.density)
  394.     safefree(info->info.density);
  395.     if (info->info.page)
  396.     safefree(info->info.page);
  397.     if (info->info.dispose)
  398.     safefree(info->info.dispose);
  399.     if (info->info.delay)
  400.     safefree(info->info.delay);
  401.     if (info->info.iterations)
  402.     safefree(info->info.iterations);
  403.     if (info->info.texture)
  404.     safefree(info->info.texture);
  405.     if (info->info.undercolor)
  406.     safefree(info->info.undercolor);
  407.     safefree((char *) info);
  408. }
  409.  
  410. /*
  411.  *  Replace libmagick's warning handler.  This stores the (possibly
  412.  *  multiple) messages in a Perl variable for later returning.  If the
  413.  *  IM_do_warn variable is set, it also calls the Perl warn routine.
  414.  */
  415.  
  416. static void
  417. warninghandler(const char *message, const char *qual)
  418. {
  419.     char b[400];
  420.     int magick_status;
  421.     int en = errno;
  422.     errno = 0;
  423.  
  424.     if (!message)
  425.     return;
  426.  
  427.     magick_status = SetErrorStatus(0);
  428.     sprintf(b, "Warning %d: %.128s%s%.128s%s%s%.64s%s",
  429.         magick_status, message,
  430.         qual? " (" : "", qual? qual : "", qual? ")" : "",
  431.         en? " [" : "", en? strerror(en) : "", en? "]" : "");
  432.  
  433.     if (im_er_mes == NULL || IM_do_warn)    /* message buffer not set up */
  434.     {
  435.     warn("%s", b);
  436.     if (im_er_mes == NULL)
  437.         return;
  438.     }
  439.  
  440.     if (SvCUR(im_er_mes))    /* add \n separator between messages */
  441.     sv_catpv(im_er_mes, "\n");
  442.     sv_catpv(im_er_mes, b);
  443. }
  444.  
  445. /*
  446.  *  Replace libmagick's fatal error handler.  This stores the message
  447.  *  in a Perl variable, and longjmp's to return the error to perl.  If
  448.  *  the IM_do_warn variable is set, it also calls the Perl warn routine.
  449.  *  Note that this doesn't exit but returns control to Perl; the
  450.  *  Image::Magick handle may be left in a bad state.
  451.  */
  452. static void
  453. errorhandler(const char *message, const char *qual)
  454. {
  455.     char b[400];
  456.     int magick_status;
  457.     int en = errno;
  458.     errno = 0;
  459.  
  460.     magick_status = SetErrorStatus(0);
  461.     sprintf(b, "Error %d: %.128s%s%.128s%s%s%.64s%s",
  462.         magick_status,
  463.         (message ? message : "ERROR"),
  464.         qual? " (" : "", qual? qual : "", qual? ")" : "",
  465.         en? " [" : "", en? strerror(en) : "", en? "]" : "");
  466.     if (im_er_mes == NULL || im_er_jmp == NULL || IM_do_warn)
  467.     {
  468.     warn("%s", b);
  469.     if (im_er_jmp == NULL)
  470.         exit(magick_status % 100);
  471.     }
  472.  
  473.     if (im_er_mes)
  474.     {
  475.     if (SvCUR(im_er_mes))    /* add \n separator between messages */
  476.         sv_catpv(im_er_mes, "\n");
  477.     sv_catpv(im_er_mes, b);
  478.     }
  479.  
  480.     longjmp(*im_er_jmp, magick_status);    /* tell caller to return error */
  481. }
  482.  
  483. /*  Used mostly for the info structure members.  It sets those elements
  484.  *  that are malloc'd strings.
  485.  */
  486. static void
  487. newval(char **dest, char *src)
  488. {
  489.     char *m = (char *) safemalloc(Extent(src) + 1);
  490.     if (*dest)
  491.     safefree(*dest);
  492.     *dest = m;
  493.     strcpy(*dest, src);
  494. }
  495.  
  496. /*  Look up or create an info structure for the given Image__Magick
  497.  *  reference.  If it does create a new one, the information in
  498.  *  oldinfo is used to initialize it.
  499.  */
  500. static struct info *
  501. getinfo(void *rref, struct info *oldinfo)
  502. {
  503.     SV *sv;
  504.     char b[80];
  505.     struct info *info;
  506.  
  507.     sprintf(b, "%s::A_%lx_Z", IM_packname, (long)rref);
  508.     sv = perl_get_sv(b, (TRUE | 0x2));
  509.     if (!sv)
  510.     {
  511.     warning(ResourceLimitWarning, "Can't create info variable", b);
  512.     return oldinfo;
  513.     }
  514.     if (SvREFCNT(sv) == 0)
  515.     SvREFCNT_inc(sv);
  516.     if (SvIOKp(sv) && (info = (struct info *)SvIV(sv)))
  517.     return info;
  518.  
  519.     info = copy_info(oldinfo);
  520.     sv_setiv(sv, (IV)info);
  521.     return info;
  522. }
  523.  
  524. /*  Sets the attribute named attr to the value in sval.  This can change
  525.  *  either or both of image or info.
  526.  */
  527. static void
  528. SetAttribute(struct info *info, Image *image, char *attr, SV *sval)
  529. {
  530.     /* switch on first letter of attribute.  'break' will go */
  531.     /* to "unknown" error; use 'return' for known options */
  532.     switch (*attr)
  533.     {
  534.     case 'A': case 'a':
  535.     if (strEQcase(attr, "adjoin"))
  536.     {
  537.         int sp = SvPOK(sval) ? LookupStr(p_boolean, SvPV(sval, na))
  538.                  : SvIV(sval);
  539.         if (sp < 0)
  540.         {
  541.         warning(OptionWarning, "Unknown adjoin type", SvPV(sval, na));
  542.         return;
  543.         }
  544.         if (info)
  545.         info->info.adjoin = sp;
  546.         return;
  547.     }
  548.     break;
  549.     case 'B': case 'b':
  550.     if (strEQcase(attr, "background"))
  551.     {
  552.         XColor target_color;
  553.  
  554.         (void) XQueryColorDatabase(SvPV(sval, na), &target_color);
  555.         for ( ; image; image = image->next) {
  556.         image->background_color.red = XDownScale(target_color.red);
  557.         image->background_color.green = XDownScale(target_color.green);
  558.         image->background_color.blue = XDownScale(target_color.blue);
  559.         }
  560.         return;
  561.     }
  562.     if (strEQcase(attr, "bordercolor"))
  563.     {
  564.         XColor target_color;
  565.  
  566.         (void) XQueryColorDatabase(SvPV(sval, na), &target_color);
  567.         for ( ; image; image = image->next) {
  568.         image->border_color.red = XDownScale(target_color.red);
  569.         image->border_color.green = XDownScale(target_color.green);
  570.         image->border_color.blue = XDownScale(target_color.blue);
  571.         }
  572.         return;
  573.     }
  574.     if (strEQcase(attr, "box"))
  575.     {
  576.         if (info)
  577.         newval(&info->info.box, SvPV(sval, na));
  578.         return;
  579.     }
  580.     break;
  581.     case 'C': case 'c':
  582.     if (strEQcase(attr, "colormap"))
  583.     {
  584.         ColorPacket *cp;
  585.         int i, red, green, blue;
  586.  
  587.         for ( ; image; image = image->next)
  588.         {
  589.         if (image->class == DirectClass)
  590.             continue;
  591.         i = 0;
  592.         (void) sscanf(attr, "%*[^[][%d", &i);
  593.         if (i > image->colors)
  594.             i %= image->colors;
  595.         cp = image->colormap+i;
  596.         red = cp->red;
  597.         blue = cp->green;
  598.         green = cp->blue;
  599.         (void) sscanf(SvPV(sval, na), "%d,%d,%d", &red, &green, &blue);
  600.         cp->red = red;
  601.         cp->blue = green;
  602.         cp->green = blue;
  603.         }
  604.         return;
  605.     }
  606.     if (strEQcase(attr, "colorsp"))
  607.     {
  608.         if (info)
  609.         {
  610.         int sp = SvPOK(sval) ? LookupStr(p_colorspaces, SvPV(sval, na))
  611.                      : SvIV(sval);
  612.         if (sp < 0)
  613.         {
  614.             warning(OptionWarning, "Unknown colorspace type",
  615.                        SvPV(sval, na));
  616.             return;
  617.         }
  618.         info->quant.colorspace = (ColorspaceType) sp;
  619.         }
  620.         return;
  621.     }
  622.     if (strEQcase(attr, "colors"))
  623.     {
  624.     colors:
  625.         if (info && !(info->quant.number_colors = SvIV(sval)))
  626.         info->quant.number_colors = MaxRGB+1;
  627.         return;
  628.     }
  629.     if (strEQcase(attr, "compres"))
  630.     {
  631.         int com = SvPOK(sval) ? LookupStr(p_compressions, SvPV(sval, na))
  632.                   : SvIV(sval);
  633.         if (com < 0)
  634.         {
  635.         warning(OptionWarning, "Unknown compression type",
  636.                     SvPV(sval, na));
  637.         return;
  638.         }
  639.         if (info)
  640.         info->info.compression = (CompressionType) com;
  641.         for ( ; image; image = image->next)
  642.         image->compression = (CompressionType) com;
  643.         return;
  644.     }
  645.     break;
  646.     case 'D': case 'd':
  647.     if (strEQcase(attr, "density"))
  648.     {
  649.         char *p = SvPV(sval, na);
  650.         if (!IsGeometry(p))
  651.         {
  652.         warning(OptionWarning, "bad geometry on density", p);
  653.         return;
  654.         }
  655.         if (info)
  656.         newval(&info->info.density, p);
  657.         return;
  658.     }
  659.     if (strEQcase(attr, "dispose"))
  660.     {
  661.         if (info)
  662.         newval(&info->info.dispose, SvPV(sval, na));
  663.         for (; image; image = image->next)
  664.         image->dispose = SvIV(sval);
  665.         return;
  666.     }
  667.     if (strEQcase(attr, "delay"))
  668.     {
  669.         if (info)
  670.         newval(&info->info.delay, SvPV(sval, na));
  671.         for ( ; image; image = image->next)
  672.         image->delay = SvIV(sval);
  673.         return;
  674.     }
  675.     if (strEQcase(attr, "dither"))
  676.     {
  677.         if (info)
  678.         {
  679.         int sp = SvPOK(sval) ? LookupStr(p_boolean, SvPV(sval, na))
  680.                      : SvIV(sval);
  681.         if (sp < 0)
  682.         {
  683.             warning(OptionWarning, "Unknown dither type",
  684.                         SvPV(sval, na));
  685.             return;
  686.         }
  687.         info->info.dither = sp;
  688.         info->quant.dither = sp;
  689.         }
  690.         return;
  691.     }
  692.     if (strEQcase(attr, "display"))    /* same as server */
  693.     {
  694.     display:
  695.         if (info)
  696.         newval(&info->info.server_name, SvPV(sval, na));
  697.         return;
  698.     }
  699.     break;
  700.     case 'E': case 'e':
  701.     break;
  702.     case 'F': case 'f':
  703.     if (strEQcase(attr, "file"))
  704.     {
  705.         char *p = SvPV(sval, na);
  706.         if (info)
  707.         strncpy(info->info.filename, p, MaxTextExtent - 1);
  708.         for ( ; image; image = image->next)
  709.         strncpy(image->filename, p, MaxTextExtent - 1);
  710.         return;
  711.     }
  712.     if (strEQcase(attr, "font"))
  713.     {
  714.         if (info)
  715.         newval(&info->info.font, SvPV(sval, na));
  716.         return;
  717.     }
  718.     if (strEQcase(attr, "format"))    /* same as magick */
  719.     {
  720.     format:
  721.         if (info)
  722.         {
  723.         sprintf(info->info.filename, "%.70s:", SvPV(sval, na));
  724.         SetImageInfo((ImageInfo *) &info->info, 1);
  725.         if (*info->info.magick == '\0')
  726.             warning(OptionWarning, "Unrecognized image format",
  727.                         info->info.filename);
  728.         }
  729.         return;
  730.     }
  731.     break;
  732.     case 'G': case 'g':
  733.     break;
  734.     case 'H': case 'h':
  735.     break;
  736.     case 'I': case 'i':
  737.     if (strEQcase(attr, "iterat"))    /* same as loop */
  738.     {
  739.     iterations:
  740.         if (info)
  741.         newval(&info->info.iterations, SvPV(sval, na));
  742.         for ( ; image; image = image->next)
  743.         image->iterations = SvIV(sval);
  744.         return;
  745.     }
  746.     if (strEQcase(attr, "interla"))
  747.     {
  748.         int in = SvPOK(sval) ? LookupStr(p_interlaces, SvPV(sval, na))
  749.                  : SvIV(sval);
  750.         if (in < 0)
  751.         warning(OptionWarning, "Unknown interlace value", SvPV(sval,na))
  752.         else if (info)
  753.         info->info.interlace = (InterlaceType) in;
  754.         return;
  755.     }
  756.     break;
  757.     case 'J': case 'j':
  758.     break;
  759.     case 'K': case 'k':
  760.     break;
  761.     case 'L': case 'l':
  762.     if (strEQcase(attr, "loop"))    /* same as iterations */
  763.         goto iterations;
  764.     break;
  765.     case 'M': case 'm':
  766.     if (strEQcase(attr, "magick"))    /* same as format */
  767.         goto format;
  768.     if (strEQcase(attr, "mattecolor"))
  769.     {
  770.         XColor target_color;
  771.  
  772.         (void) XQueryColorDatabase(SvPV(sval, na), &target_color);
  773.         for ( ; image; image = image->next) {
  774.         image->matte_color.red = XDownScale(target_color.red);
  775.         image->matte_color.green = XDownScale(target_color.green);
  776.         image->matte_color.blue = XDownScale(target_color.blue);
  777.         }
  778.         return;
  779.     }
  780.     if (strEQcase(attr, "matte"))
  781.     {
  782.         int sp = SvPOK(sval) ? LookupStr(p_boolean, SvPV(sval, na))
  783.                  : SvIV(sval);
  784.         if (sp < 0)
  785.         {
  786.         warning(OptionWarning, "Unknown verbose type", SvPV(sval, na));
  787.         return;
  788.         }
  789.         if (image)
  790.         image->matte = sp;
  791.         return;
  792.     }
  793.     if (strEQcase(attr, "monoch"))
  794.     {
  795.         int sp = SvPOK(sval) ? LookupStr(p_boolean, SvPV(sval, na))
  796.                  : SvIV(sval);
  797.         if (sp < 0)
  798.         {
  799.         warning(OptionWarning, "Unknown monochrome type",
  800.                     SvPV(sval, na));
  801.         return;
  802.         }
  803.         if (info)
  804.         info->info.monochrome = sp;
  805.         return;
  806.     }
  807.     break;
  808.     case 'N': case 'n':
  809.     if (strEQcase(attr, "number"))    /* same as colors */
  810.         goto colors;
  811.     break;
  812.     case 'O': case 'o':
  813.     break;
  814.     case 'P': case 'p':
  815.     if (strEQcase(attr, "page"))
  816.     {
  817.         char *p = PostscriptGeometry(SvPV(sval, na));
  818.         if (!p)
  819.         return;
  820.         for ( ; image; image = image->next)
  821.         newval(&image->page, p);
  822.         if (info)
  823.         newval(&info->info.page, p);
  824.         free(p);
  825.         return;
  826.     }
  827.     if (strEQcase(attr, "pen"))
  828.     {
  829.         if (info)
  830.         newval(&info->info.pen, SvPV(sval, na));
  831.         return;
  832.     }
  833.     if (strEQcase(attr, "pixel"))
  834.     {
  835.         RunlengthPacket *cp;
  836.         int x, y;
  837.  
  838.         for ( ; image; image = image->next)
  839.         {
  840.         if (!UncondenseImage(image))
  841.             continue;
  842.         x = 0;
  843.         y = 0;
  844.         (void) sscanf(attr, "%*[^[][%d,%d", &x, &y);
  845.         if (y > image->rows)
  846.             y %= image->rows;
  847.         if (x > image->columns)
  848.             x %= image->columns;
  849.         cp = image->pixels+(y*image->columns+x);
  850.         image->class = DirectClass;
  851.         if (strchr(SvPV(sval, na), ',') == 0)
  852.         {
  853.             XColor xc;
  854.  
  855.             XQueryColorDatabase(SvPV(sval, na), &xc);
  856.             cp->red = XDownScale(xc.red);
  857.             cp->blue = XDownScale(xc.green);
  858.             cp->green = XDownScale(xc.blue);
  859.         }
  860.         else
  861.         {
  862.             int red, green, blue, index;
  863.  
  864.             red = cp->red;
  865.             blue = cp->green;
  866.             green = cp->blue;
  867.             index = cp->index;
  868.             (void) sscanf(SvPV(sval, na), "%d,%d,%d,%d",
  869.                         &red, &green, &blue, &index);
  870.             cp->red=(Quantum)
  871.              ((red < 0) ? 0 : (red > MaxRGB) ? MaxRGB : red);
  872.             cp->green=(Quantum)
  873.             ((green < 0) ? 0 : (green > MaxRGB) ? MaxRGB : green);
  874.             cp->blue=(Quantum)
  875.              ((blue < 0) ? 0 : (blue > MaxRGB) ? MaxRGB : blue);
  876.             cp->index=(unsigned short) ((index < Transparent) ?
  877.              Transparent : (index > Opaque) ? Opaque : index);
  878.         }
  879.         }
  880.         return;
  881.     }
  882.     if (strEQcase(attr, "points"))
  883.     {
  884.         if (info && (info->info.pointsize = SvIV(sval)) <= 0)
  885.         info->info.pointsize = atoi(DefaultPointSize);
  886.         return;
  887.     }
  888.     if (strEQcase(attr, "preview"))
  889.     {
  890.         int sp = SvPOK(sval) ? LookupStr(p_previews, SvPV(sval, na))
  891.                  : SvIV(sval);
  892.         if (sp < 0)
  893.         {
  894.         warning(OptionWarning, "Unknown preview type", SvPV(sval, na));
  895.         return;
  896.         }
  897.         if (info)
  898.         info->info.preview_type = (PreviewType) sp;
  899.         return;
  900.     }
  901.     break;
  902.     case 'Q': case 'q':
  903.     if (strEQcase(attr, "qualit"))
  904.     {
  905.         if (info && (info->info.quality = SvIV(sval)) <= 0)
  906.         info->info.quality = atoi(DefaultImageQuality);
  907.         return;
  908.     }
  909.     break;
  910.     case 'R': case 'r':
  911.     break;
  912.     case 'S': case 's':
  913.     if (strEQcase(attr, "scene"))
  914.     {
  915.         for ( ; image; image = image->next)
  916.         image->scene = SvIV(sval);
  917.         return;
  918.     }
  919.     if (strEQcase(attr, "subim"))
  920.     {
  921.         if (info)
  922.         info->info.subimage = SvIV(sval);
  923.         return;
  924.     }
  925.     if (strEQcase(attr, "subra"))
  926.     {
  927.         if (info)
  928.         info->info.subrange = SvIV(sval);
  929.         return;
  930.     }
  931.     if (strEQcase(attr, "server"))    /* same as display */
  932.         goto display;
  933.     if (strEQcase(attr, "size"))
  934.     {
  935.         if (info)
  936.         {
  937.         char *p = SvPV(sval, na);
  938.         if (!IsGeometry(p))
  939.         {
  940.             warning(OptionWarning, "Bad geometry on size", p);
  941.             return;
  942.         }
  943.         newval(&info->info.size, p);
  944.         }
  945.         return;
  946.     }
  947.     break;
  948.     case 'T': case 't':
  949.     if (strEQcase(attr, "tile"))
  950.     {
  951.         if (info)
  952.         newval(&info->info.tile, SvPV(sval, na));
  953.         return;
  954.     }
  955.     if (strEQcase(attr, "texture"))
  956.     {
  957.         if (info)
  958.         newval(&info->info.texture, SvPV(sval, na));
  959.         return;
  960.     }
  961.     if (strEQcase(attr, "tree"))
  962.     {
  963.         if (info)
  964.         info->quant.tree_depth = SvIV(sval);
  965.         return;
  966.     }
  967.     break;
  968.     case 'U': case 'u':
  969.     if (strEQcase(attr, "underc"))
  970.     {
  971.         if (info)
  972.         newval(&info->info.undercolor, SvPV(sval, na));
  973.         return;
  974.     }
  975.     break;
  976.     case 'V': case 'v':
  977.     if (strEQcase(attr, "verbose"))
  978.     {
  979.         int sp = SvPOK(sval) ? LookupStr(p_boolean, SvPV(sval, na))
  980.                  : SvIV(sval);
  981.         if (sp < 0)
  982.         {
  983.         warning(OptionWarning, "Unknown verbose type", SvPV(sval, na));
  984.         return;
  985.         }
  986.         if (info)
  987.         info->info.verbose = sp;
  988.         return;
  989.     }
  990.     if (strEQcase(attr, "view"))
  991.     {
  992.         if (info)
  993.         newval(&info->info.view, SvPV(sval, na));
  994.         return;
  995.     }
  996.     break;
  997.     case 'W': case 'w':
  998.     break;
  999.     case 'X': case 'x':
  1000.     break;
  1001.     case 'Y': case 'y':
  1002.     break;
  1003.     case 'Z': case 'z':
  1004.     break;
  1005.     }
  1006.  
  1007.     /* fall through to here for unknown attributes */
  1008.     warning(OptionWarning, "Unknown attribute", attr);
  1009. }
  1010.  
  1011. /*  This recursive routine is called by setup_list to traverse the
  1012.  *  Image__Magick reference.  If building an svarr (see setup_list),
  1013.  *  *cur is the current position in *svarr and *last is the final
  1014.  *  entry in *svarr.
  1015.  */
  1016. static Image *
  1017. get_list(SV *rref, SV ***svarr, int *cur, int *last)
  1018. {
  1019.     if (!rref)
  1020.     return NULL;
  1021.  
  1022.     switch (SvTYPE(rref))
  1023.     {
  1024.     case SVt_PVAV:    /* array of images */
  1025.     {
  1026.         Image *prev = NULL, *head = NULL;
  1027.         AV *av = (AV *)rref;
  1028.         int i, n = av_len(av);
  1029.  
  1030.         for (i = 0; i <= n; i++)    /* look through entire array */
  1031.         {
  1032.         SV **rv = av_fetch(av, i, 0);
  1033.         if (rv && *rv && sv_isobject(*rv))
  1034.         {
  1035.             Image *image = get_list(SvRV(*rv), svarr, cur, last);
  1036.             if (!image)
  1037.             continue;
  1038.             image->previous = prev;
  1039.             *(prev? &prev->next : &head) = image;
  1040.             for (prev = image; prev->next; prev = prev->next)
  1041.             ;
  1042.         }
  1043.         }
  1044.         return head;
  1045.     }
  1046.  
  1047.     case SVt_PVMG:    /* blessed scalar, one image */
  1048.     {
  1049.         Image *image = (Image *)SvIV(rref);
  1050.         if (!image)
  1051.         return NULL;
  1052.  
  1053.         image->previous = image->next = NULL;
  1054.         if (svarr)
  1055.         {
  1056.         if (*cur == *last)
  1057.         {
  1058.             *last += 256;
  1059.             if (*svarr) 
  1060.             *svarr = (SV **)saferealloc((char *) *svarr, *last * sizeof *svarr);
  1061.             else
  1062.             *svarr = (SV **)safemalloc(*last * sizeof *svarr);
  1063.         }
  1064.         (*svarr)[*cur] = rref;
  1065.         (*svarr)[++(*cur)] = NULL;
  1066.         }
  1067.         return image;
  1068.     }
  1069.     }
  1070.     fprintf(stderr, "setup_list: unknown ref type %ld\n", SvTYPE(rref));
  1071.     return NULL;
  1072. }
  1073.  
  1074. /*  This routine returns the list of all the images linked by their
  1075.  *  image->next and image->previous link lists for use with ImageMagick.
  1076.  *  If info is non-NULL, an info structure is returned in *info.  If
  1077.  *  svarr is non-NULL, an array of SV* are returned in *svarr.  svarr
  1078.  *  is used when the images are going to be replaced with new Image* 's.
  1079.  */
  1080. static Image *
  1081. setup_list(SV *rref, struct info **info, SV ***svarr)
  1082. {
  1083.     int cur = 0, last = 0;
  1084.     Image *image;
  1085.  
  1086.     if (svarr)
  1087.     *svarr = NULL;
  1088.     if (info)
  1089.     *info = NULL;
  1090.  
  1091.     image = get_list(rref, svarr, &cur, &last);
  1092.  
  1093.     if (info && SvTYPE(rref) == SVt_PVAV)
  1094.     *info = getinfo((void *) rref, (struct info *) NULV);
  1095.  
  1096.     return image;
  1097. }
  1098.  
  1099. /* From here on is processed by XS */
  1100.  
  1101. MODULE = Image::Magick        PACKAGE = Image::Magick
  1102.  
  1103. PROTOTYPES: ENABLE
  1104.  
  1105. BOOT:
  1106.     SetWarningHandler(warninghandler);
  1107.     SetErrorHandler(errorhandler);
  1108.  
  1109. double
  1110. constant(name,arg)
  1111.     char *        name
  1112.     int        arg
  1113.  
  1114. void
  1115. DESTROY(ref)
  1116.     Image::Magick    ref = NO_INIT
  1117.     PPCODE:
  1118.     {
  1119.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1120.  
  1121.         if (!sv_isobject(ST(0)))
  1122.         croak(complain);
  1123.         rref = SvRV(ST(0));
  1124.  
  1125.         switch (SvTYPE(rref))
  1126.         {
  1127.         case SVt_PVAV:    /* array (AV *)rref */
  1128.         {
  1129.             SV *sv;
  1130.             char b[80];
  1131.             struct info *info;
  1132.  
  1133.             sprintf(b, "%s::A_%lx_Z", IM_packname, (long)rref);
  1134.             if ((sv = perl_get_sv(b, FALSE)))
  1135.             {
  1136.             if (SvREFCNT(sv) == 1 && SvIOK(sv)
  1137.                     && (info = (struct info *)SvIV(sv)))
  1138.             {
  1139.                 destroy_info(info);
  1140.                 sv_setiv(sv, 0);
  1141.             }
  1142.             /*SvREFCNT_dec(sv);*/
  1143.             }
  1144.             break;
  1145.         }
  1146.         case SVt_PVMG:    /* blessed scalar = (Image *)SvIV(rref) */
  1147.         {
  1148.             Image *image = (Image *)SvIV(rref);
  1149.             if (image)
  1150.             {
  1151.             image->orphan = 1;
  1152.             DestroyImage(image);
  1153.             sv_setiv(rref, 0);
  1154.             }
  1155.             break;
  1156.         }
  1157.         default:
  1158.         break;
  1159.         }
  1160.     }
  1161.  
  1162. void
  1163. Animate(ref, ...)
  1164.     Image::Magick    ref = NO_INIT
  1165.     ALIAS:
  1166.         AnimateImage    = 1
  1167.         animate        = 2
  1168.         animateimage    = 3
  1169.     PPCODE:
  1170.     {
  1171.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1172.         Image *image;
  1173.         struct info *info;
  1174.         struct info *temp = NULL;
  1175.         XResourceInfo resource;
  1176.         XrmDatabase resource_database;
  1177.         Display *display;
  1178.         char *resource_value;
  1179.         int n;
  1180.         jmp_buf error_jmp;
  1181.         volatile int retval = 0;
  1182.  
  1183.         im_er_mes = newSVpv("", 0);
  1184.  
  1185.         if (!sv_isobject(ST(0)))
  1186.         {
  1187.         warning(OptionWarning, complain, IM_packname);
  1188.         goto badreturn;
  1189.         }
  1190.         rref = SvRV(ST(0));
  1191.  
  1192.         im_er_jmp = &error_jmp;
  1193.         if ((retval = setjmp(error_jmp)))
  1194.         goto badreturn;
  1195.  
  1196.         if (!(image = setup_list(rref, &info, (SV ***) NULV)))
  1197.         {
  1198.         warning(OptionWarning, "No images to Display", NULV);
  1199.         goto badreturn;
  1200.         }
  1201.  
  1202.         temp = copy_info(info);
  1203.         *temp->info.filename = '\0';
  1204.  
  1205.         if (items == 2)
  1206.         SetAttribute(temp, NULL, "server", ST(1));
  1207.         else if (items > 2)
  1208.         for (n = 2; n < items; n += 2)
  1209.             SetAttribute(temp, NULL, SvPV(ST(n-1), na), ST(n));
  1210.  
  1211.         display = XOpenDisplay(info->info.server_name);
  1212.         if (display)
  1213.         {
  1214.         XSetErrorHandler(XError);
  1215.         resource_database = XGetResourceDatabase(display, client_name);
  1216.         XGetResourceInfo(resource_database, client_name, &resource);
  1217.         resource.image_info = info->info;
  1218.         resource.quantize_info = info->quant;
  1219.  
  1220.         (void) XAnimateImages(display, &resource, (char **) NULL, 0,
  1221.             image);
  1222.  
  1223.         XCloseDisplay(display);
  1224.         }
  1225.  
  1226.     badreturn:
  1227.         if (temp)
  1228.         destroy_info(temp);
  1229.         sv_setiv(im_er_mes, (IV)(retval ? retval : SvCUR(im_er_mes) != 0));
  1230.         SvPOK_on(im_er_mes);
  1231.         ST(0) = sv_2mortal(im_er_mes);
  1232.         im_er_mes = NULL, im_er_jmp = NULL;
  1233.         XSRETURN(1);
  1234.     }
  1235.  
  1236. void
  1237. Average(ref)
  1238.     Image::Magick    ref = NO_INIT
  1239.     ALIAS:
  1240.         AverageImage   = 1
  1241.         average        = 2
  1242.         averageimage   = 3
  1243.     PPCODE:
  1244.     {
  1245.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1246.         Image *image;
  1247.         jmp_buf error_jmp;
  1248.         char *p;
  1249.         struct info *info;
  1250.         SV *sv, *rv;
  1251.         AV *av;
  1252.         HV *hv;
  1253.         volatile int retval = 0;
  1254.  
  1255.         im_er_mes = newSVpv("", 0);
  1256.  
  1257.         if (!sv_isobject(ST(0)))
  1258.         {
  1259.         warning(OptionWarning, complain, IM_packname);
  1260.         goto badreturn;
  1261.         }
  1262.         rref = SvRV(ST(0));
  1263.         hv = SvSTASH(rref);
  1264.  
  1265.         im_er_jmp = &error_jmp;
  1266.         if (retval = setjmp(error_jmp))
  1267.         goto badreturn;
  1268.  
  1269.         if (!(image = setup_list(rref, &info, (SV ***) NULV)))
  1270.         {
  1271.         warning(OptionWarning, "No images to Average", NULV);
  1272.         goto badreturn;
  1273.         }
  1274.  
  1275.         image = AverageImages(image);
  1276.         if (!image)
  1277.         goto badreturn;
  1278.  
  1279.         /* create blessed Perl array for the returned image */
  1280.         av = newAV();
  1281.         ST(0) = sv_2mortal(sv_bless(newRV((SV *)av), hv));
  1282.         SvREFCNT_dec(av);
  1283.  
  1284.         sv = newSViv((IV)image);
  1285.         rv = newRV(sv);
  1286.         av_push(av, sv_bless(rv, hv));
  1287.         SvREFCNT_dec(sv);
  1288.         /*SvREFCNT_dec(rv);*/
  1289.  
  1290.         info = getinfo((void *) av, info);
  1291.         sprintf(info->info.filename, "average-%.*s", MaxTextExtent - 9,
  1292.         ((p = strrchr(image->filename, '/')) ? p+1 : image->filename));
  1293.         strcpy(image->filename, info->info.filename);
  1294.         SetImageInfo(&info->info, False);
  1295.  
  1296.         SvREFCNT_dec(im_er_mes);
  1297.         im_er_jmp = NULL;
  1298.         XSRETURN(1);
  1299.  
  1300.     badreturn:
  1301.         sv_setiv(im_er_mes, (IV)(retval ? retval : SvCUR(im_er_mes) != 0));
  1302.         SvPOK_on(im_er_mes);    /* return messages in string context */
  1303.         ST(0) = sv_2mortal(im_er_mes);
  1304.         im_er_mes = NULL, im_er_jmp = NULL;
  1305.         XSRETURN(1);
  1306.     }
  1307.  
  1308. void
  1309. Copy(ref)
  1310.     Image::Magick    ref = NO_INIT
  1311.     ALIAS:
  1312.         CopyImage   = 1
  1313.         copy        = 2
  1314.         copyimage   = 3
  1315.         CloneImage  = 4
  1316.         clone       = 5
  1317.         cloneimage  = 6
  1318.         Clone    = 7
  1319.     PPCODE:
  1320.     {
  1321.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1322.         Image *im, *image;
  1323.         jmp_buf error_jmp;
  1324.         char *p;
  1325.         struct info *info;
  1326.         SV *sv, *rv;
  1327.         AV *av;
  1328.         HV *hv;
  1329.         volatile int retval = 0;
  1330.  
  1331.         im_er_mes = newSVpv("", 0);
  1332.  
  1333.         if (!sv_isobject(ST(0)))
  1334.         {
  1335.         warning(OptionWarning, complain, IM_packname);
  1336.         goto badreturn;
  1337.         }
  1338.         rref = SvRV(ST(0));
  1339.         hv = SvSTASH(rref);
  1340.  
  1341.         im_er_jmp = &error_jmp;
  1342.         if (retval = setjmp(error_jmp))
  1343.         goto badreturn;
  1344.  
  1345.         if (!(image = setup_list(rref, &info, (SV ***) NULV)))
  1346.         {
  1347.         warning(OptionWarning, "No images to Copy", NULV);
  1348.         goto badreturn;
  1349.         }
  1350.  
  1351.         /* create blessed Perl array for the returned image */
  1352.         av = newAV();
  1353.         ST(0) = sv_2mortal(sv_bless(newRV((SV *)av), hv));
  1354.         SvREFCNT_dec(av);
  1355.  
  1356.         for (im = image; im; im = im->next)
  1357.         {
  1358.         image = CloneImage(im, im->columns, im->rows, True);
  1359.         if (!image)
  1360.             break;
  1361.         sv = newSViv((IV)image);
  1362.         rv = newRV(sv);
  1363.         av_push(av, sv_bless(rv, hv));
  1364.         SvREFCNT_dec(sv);
  1365.         }
  1366.  
  1367.         info = getinfo((void *) av, info);
  1368.         SvREFCNT_dec(im_er_mes);
  1369.         im_er_jmp = NULL;
  1370.         XSRETURN(1);
  1371.  
  1372.     badreturn:
  1373.         sv_setiv(im_er_mes, (IV)(retval ? retval : SvCUR(im_er_mes) != 0));
  1374.         SvPOK_on(im_er_mes);    /* return messages in string context */
  1375.         ST(0) = sv_2mortal(im_er_mes);
  1376.         im_er_mes = NULL, im_er_jmp = NULL;
  1377.         XSRETURN(1);
  1378.     }
  1379.  
  1380. void
  1381. Display(ref, ...)
  1382.     Image::Magick    ref = NO_INIT
  1383.     ALIAS:
  1384.         DisplayImage    = 1
  1385.         display        = 2
  1386.         displayimage    = 3
  1387.     PPCODE:
  1388.     {
  1389.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1390.         Image *image, *im;
  1391.         struct info *info;
  1392.         struct info *temp = NULL;
  1393.         XResourceInfo resource;
  1394.         XrmDatabase resource_database;
  1395.         unsigned long state;
  1396.         Display *display;
  1397.         char *resource_value;
  1398.         int n;
  1399.         jmp_buf error_jmp;
  1400.         volatile int retval = 0;
  1401.  
  1402.         im_er_mes = newSVpv("", 0);
  1403.         if (!sv_isobject(ST(0)))
  1404.         {
  1405.         warning(OptionWarning, complain, IM_packname);
  1406.         goto badreturn;
  1407.         }
  1408.         rref = SvRV(ST(0));
  1409.  
  1410.         im_er_jmp = &error_jmp;
  1411.         if ((retval = setjmp(error_jmp)))
  1412.         goto badreturn;
  1413.  
  1414.         if (!(image = setup_list(rref, &info, (SV ***) NULV)))
  1415.         {
  1416.         warning(OptionWarning, "No images to Display", NULV);
  1417.         goto badreturn;
  1418.         }
  1419.  
  1420.         temp = copy_info(info);
  1421.         *temp->info.filename = '\0';
  1422.  
  1423.         if (items == 2)
  1424.         SetAttribute(temp, NULL, "server", ST(1));
  1425.         else if (items > 2)
  1426.         for (n = 2; n < items; n += 2)
  1427.             SetAttribute(temp, NULL, SvPV(ST(n-1), na), ST(n));
  1428.  
  1429.         display = XOpenDisplay(info->info.server_name);
  1430.         if (display)
  1431.         {
  1432.         XSetErrorHandler(XError);
  1433.         resource_database = XGetResourceDatabase(display, client_name);
  1434.         XGetResourceInfo(resource_database, client_name, &resource);
  1435.         resource.image_info = temp->info;
  1436.         resource.quantize_info = temp->quant;
  1437.  
  1438.         for (im = image; im; im = im->next)
  1439.         {
  1440.             if (temp->info.delay)
  1441.             resource.delay = atoi(temp->info.delay);
  1442.             state=DefaultState;
  1443.             (void) XDisplayImage(display, &resource, (char **) NULL, 0,
  1444.                                    &im, &state);
  1445.             if (state & ExitState)
  1446.             break;
  1447.         }
  1448.  
  1449.         XCloseDisplay(display);
  1450.         }
  1451.  
  1452.     badreturn:
  1453.         if (temp)
  1454.         destroy_info(temp);
  1455.         sv_setiv(im_er_mes, (IV)retval);
  1456.         SvPOK_on(im_er_mes);
  1457.         ST(0) = sv_2mortal(im_er_mes);
  1458.         im_er_mes = NULL, im_er_jmp = NULL;
  1459.         XSRETURN(1);
  1460.     }
  1461.  
  1462. void
  1463. Montage(ref, ...)
  1464.     Image::Magick    ref = NO_INIT
  1465.     ALIAS:
  1466.         MontageImage    = 1
  1467.         montage        = 2
  1468.         montageimage    = 3
  1469.     PPCODE:
  1470.     {
  1471.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1472.         XResourceInfo resource;
  1473.         XrmDatabase resource_database;
  1474.         XMontageInfo montage;
  1475.         Display *display;
  1476.         int n, concatenate;
  1477.         char *transparent_color, *resource_value, *p, *arg = NULL;
  1478.         jmp_buf error_jmp;
  1479.         Image *im, *image;
  1480.         struct info *info;
  1481.         volatile int retval = 0;
  1482.         SV **svarr = NULL;
  1483.         AV *av = NULL;
  1484.         HV *hv;
  1485.         SV *sv, *rv, *avref;
  1486.  
  1487.         im_er_mes = newSVpv("", 0);
  1488.  
  1489.         if (!sv_isobject(ST(0)))
  1490.         {
  1491.         warning(OptionWarning, complain, IM_packname);
  1492.         goto badreturn;
  1493.         }
  1494.         rref = SvRV(ST(0));
  1495.         hv = SvSTASH(rref);
  1496.  
  1497.         av = newAV();
  1498.         avref = sv_2mortal(sv_bless(newRV((SV *)av), hv));
  1499.         SvREFCNT_dec(av);
  1500.  
  1501.         transparent_color = montage.tile = montage.texture = NULL;
  1502.  
  1503.         im_er_jmp = &error_jmp;
  1504.         if ((retval = setjmp(error_jmp)))
  1505.         goto badreturn;
  1506.  
  1507.         if (!(image = setup_list(rref, &info, &svarr)))
  1508.         {
  1509.         warning(OptionWarning, "No images to Montage", NULV);
  1510.         goto badreturn;
  1511.         }
  1512.         info = getinfo((void *) av, info);
  1513.  
  1514.         /*
  1515.           Get user defaults from X resource database.
  1516.         */
  1517.         XGetMontageInfo(&montage);
  1518.         sprintf(montage.filename, "montage-%.*s", MaxTextExtent - 9,
  1519.         ((p = strrchr(image->filename, '/')) ? p+1 : image->filename));
  1520.  
  1521.         display = XOpenDisplay(info->info.server_name);
  1522.         if (display)
  1523.         XSetErrorHandler(XError);
  1524.         resource_database = XGetResourceDatabase(display, client_name);
  1525.         XGetResourceInfo(resource_database, client_name, &resource);
  1526.         resource.image_info = info->info;
  1527.         resource.quantize_info = info->quant;
  1528.         resource.background_color = XGetResourceInstance(resource_database,
  1529.         client_name, "background", DefaultTileBackground);
  1530.         resource.foreground_color = XGetResourceInstance(resource_database,
  1531.         client_name, "foreground", DefaultTileForeground);
  1532.         resource.image_geometry = XGetResourceInstance(resource_database,
  1533.         client_name, "imageGeometry", DefaultTileGeometry);
  1534.         resource.matte_color = XGetResourceInstance(resource_database,
  1535.         client_name, "mattecolor", DefaultTileMatte);
  1536.         resource_value = XGetResourceClass(resource_database, client_name,
  1537.         "pointsize", DefaultPointSize);
  1538.         montage.pointsize = atoi(resource_value);
  1539.         resource_value = XGetResourceClass(resource_database, client_name,
  1540.         "shadow", "False");
  1541.         montage.shadow = IsTrue(resource_value);
  1542.         montage.tile = XGetResourceClass(resource_database, client_name,
  1543.         "tile", montage.tile);
  1544.         if (display)
  1545.         XCloseDisplay(display);
  1546.  
  1547.         concatenate = 0;
  1548.         for (n = 2; n < items; n += 2)
  1549.         {
  1550.         arg = (char *)SvPV(ST(n-1), na);
  1551.  
  1552.         /* switch on first letter of attribute.  'break' will go */
  1553.         /* to "unknown" error; use 'continue' for known options */
  1554.         switch (*arg)
  1555.         {
  1556.         case 'B': case 'b':
  1557.             if (strEQcase(arg, "background"))
  1558.             {
  1559.             resource.background_color = SvPV(ST(n), na);
  1560.             continue;
  1561.             }
  1562.             if (strEQcase(arg, "bordercolor"))
  1563.             {
  1564.             resource.border_color = SvPV(ST(n), na);
  1565.             continue;
  1566.             }
  1567.             if (strEQcase(arg, "borderwidth"))
  1568.             {
  1569.             resource.border_width = SvIV(ST(n));
  1570.             continue;
  1571.             }
  1572.         case 'C': case 'c':
  1573.             if (strEQcase(arg, "compos"))
  1574.             {
  1575.             int sp = !SvPOK(ST(n)) ? SvIV(ST(n)) :
  1576.                   LookupStr(p_composites, SvPV(ST(n), na));
  1577.             if (sp < 0)
  1578.             {
  1579.                 warning(OptionWarning, "Unknown composite type",
  1580.                             SvPV(ST(n), na));
  1581.                 continue;
  1582.             }
  1583.             montage.compose = (CompositeOperator) sp;
  1584.             continue;
  1585.             }
  1586.             break;
  1587.         case 'F': case 'f':
  1588.             if (strEQcase(arg, "frame"))
  1589.             {
  1590.             char *p = SvPV(ST(n), na);
  1591.             if (!IsGeometry(p))
  1592.             {
  1593.                 warning(OptionWarning, "bad geometry on frame", p);
  1594.                 continue;
  1595.             }
  1596.             montage.frame = p;
  1597.             if (*p == '\0')
  1598.               montage.frame = (char *) NULL;
  1599.             continue;
  1600.             }
  1601.             if (strEQcase(arg, "file"))
  1602.             {
  1603.             strncpy(montage.filename, SvPV(ST(n), na),
  1604.                           sizeof montage.filename);
  1605.             continue;
  1606.             }
  1607.             if (strEQcase(arg, "filter"))
  1608.             {
  1609.             int sp = !SvPOK(ST(n)) ? SvIV(ST(n)) :
  1610.                   LookupStr(p_filters, SvPV(ST(n), na));
  1611.             if (sp < 0)
  1612.             {
  1613.                 warning(OptionWarning, "Unknown filter type",
  1614.                             SvPV(ST(n), na));
  1615.                 continue;
  1616.             }
  1617.             montage.filter = (FilterType) sp;
  1618.             continue;
  1619.             }
  1620.             if (strEQcase(arg, "font"))
  1621.             {
  1622.             resource.font = SvPV(ST(n), na);
  1623.             continue;
  1624.             }
  1625.             if (strEQcase(arg, "foreground"))
  1626.             {
  1627.             resource.foreground_color = SvPV(ST(n), na);
  1628.             continue;
  1629.             }
  1630.             break;
  1631.         case 'G': case 'g':
  1632.             if (strEQcase(arg, "geometry"))
  1633.             {
  1634.             char *p = SvPV(ST(n), na);
  1635.             if (!IsGeometry(p))
  1636.             {
  1637.                 warning(OptionWarning, "bad geometry on geometry", p);
  1638.                 continue;
  1639.             }
  1640.             resource.image_geometry = p;
  1641.             if (*p == '\0')
  1642.               resource.image_geometry = (char *) NULL;
  1643.             continue;
  1644.             }
  1645.             if (strEQcase(arg, "gravity"))
  1646.             {
  1647.             int in = !SvPOK(ST(n)) ? SvIV(ST(n)) :
  1648.                   LookupStr(p_gravities, SvPV(ST(n), na));
  1649.                 if (in < 0)
  1650.             {
  1651.                 warning(OptionWarning, "Unknown gravity type",
  1652.                             SvPV(ST(n), na));
  1653.                 return;
  1654.             }
  1655.             resource.gravity = in;
  1656.             continue;
  1657.             }
  1658.             break;
  1659.         case 'L': case 'l':
  1660.             if (strEQcase(arg, "label"))
  1661.             {
  1662.             for (im = image; im; im = im->next)
  1663.                 LabelImage(im, SvPV(ST(n), na));
  1664.             continue;
  1665.             }
  1666.             break;
  1667.         case 'M': case 'm':
  1668.             if (strEQcase(arg, "mattec"))
  1669.             {
  1670.             resource.matte_color = SvPV(ST(n), na);
  1671.             continue;
  1672.             }
  1673.             if (strEQcase(arg, "mode"))
  1674.             {
  1675.             int in = !SvPOK(ST(n)) ? SvIV(ST(n)) :
  1676.                   LookupStr(p_modes, SvPV(ST(n), na));
  1677.             switch (in)
  1678.             {
  1679.             default:
  1680.                 warning(OptionWarning, "Unknown mode value",
  1681.                             SvPV(ST(n), na));
  1682.                 break;
  1683.             case FrameMode:
  1684.                 montage.frame = DefaultTileFrame;
  1685.                 montage.shadow = True;
  1686.                 break;
  1687.             case UnframeMode:
  1688.                 montage.frame = (char *) NULL;
  1689.                 montage.shadow = False;
  1690.                 resource.border_width = 0;
  1691.                 break;
  1692.             case ConcatenateMode:
  1693.                 montage.frame = (char *) NULL;
  1694.                 montage.shadow = False;
  1695.                 resource.image_geometry = "+0+0";
  1696.                 resource.border_width = 0;
  1697.                     concatenate = 1;
  1698.             }
  1699.             continue;
  1700.             }
  1701.             break;
  1702.         case 'P': case 'p':
  1703.             if (strEQcase(arg, "pen"))
  1704.             {
  1705.             if (info)
  1706.               newval(&info->info.pen, SvPV(ST(n), na));
  1707.             continue;
  1708.             }
  1709.             if (strEQcase(arg, "point"))
  1710.             {
  1711.             montage.pointsize = SvIV(ST(n));
  1712.             continue;
  1713.             }
  1714.             break;
  1715.         case 'S': case 's':
  1716.             if (strEQcase(arg, "shadow"))
  1717.             {
  1718.             int sp = !SvPOK(ST(n)) ? SvIV(ST(n)) :
  1719.                   LookupStr(p_boolean, SvPV(ST(n), na));
  1720.             if (sp < 0)
  1721.             {
  1722.                 warning(OptionWarning, "Unknown shadow type",
  1723.                             SvPV(ST(n), na));
  1724.                 continue;
  1725.             }
  1726.             montage.shadow = sp;
  1727.             continue;
  1728.             }
  1729.             break;
  1730.         case 'T': case 't':
  1731.             if (strEQcase(arg, "texture"))
  1732.             {
  1733.             montage.texture = SvPV(ST(n), na);
  1734.             continue;
  1735.             }
  1736.             if (strEQcase(arg, "tile"))
  1737.             {
  1738.             char *p = SvPV(ST(n), na);
  1739.             if (!IsGeometry(p))
  1740.             {
  1741.                 warning(OptionWarning, "bad geometry on tile", p);
  1742.                 continue;
  1743.             }
  1744.             montage.tile = p;
  1745.             if (*p == '\0')
  1746.               montage.tile = (char *) NULL;
  1747.             continue;
  1748.             }
  1749.             if (strEQcase(arg, "title"))
  1750.             {
  1751.             resource.title = SvPV(ST(n), na);
  1752.             continue;
  1753.             }
  1754.             if (strEQcase(arg, "trans"))
  1755.             {
  1756.             transparent_color = SvPV(ST(n), na);
  1757.             continue;
  1758.             }
  1759.             break;
  1760.         }
  1761.         /* fall through to here for unknown attributes */
  1762.         warning(OptionWarning, "Unknown attribute", arg);
  1763.         }
  1764.         resource.image_info = info->info;
  1765.  
  1766.         image = XMontageImages(&resource, &montage, image);
  1767.         if (!image)
  1768.         goto badreturn;
  1769.  
  1770.         if (transparent_color)
  1771.         for (im = image; im; im = im->next)
  1772.             TransparentImage(im, transparent_color);
  1773.  
  1774.         strcpy(info->info.filename, montage.filename);
  1775.         SetImageInfo(&info->info, False);
  1776.  
  1777.         for (im = image; im; im = im->next)
  1778.         {
  1779.         sv = newSViv((IV)im);
  1780.         rv = newRV(sv);
  1781.         av_push(av, sv_bless(rv, hv));
  1782.         SvREFCNT_dec(sv);
  1783.         }
  1784.  
  1785.         ST(0) = avref;
  1786.         im_er_jmp = NULL;
  1787.         SvREFCNT_dec(im_er_mes);    /* can't return warning messages */
  1788.         im_er_mes = NULL;
  1789.         XSRETURN(1);
  1790.  
  1791.     badreturn:
  1792.         im_er_jmp = NULL;
  1793.         sv_setiv(im_er_mes, (IV)(retval ? retval : SvCUR(im_er_mes) != 0));
  1794.         SvPOK_on(im_er_mes);
  1795.         ST(0) = sv_2mortal(im_er_mes);
  1796.         im_er_mes = NULL, im_er_jmp = NULL;
  1797.         XSRETURN(1);
  1798.     }
  1799.  
  1800. void
  1801. Read(ref, ...)
  1802.     Image::Magick    ref = NO_INIT
  1803.     ALIAS:
  1804.         ReadImage    = 1
  1805.         read    = 2
  1806.         readimage    = 3
  1807.     PPCODE:
  1808.     {
  1809.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1810.         jmp_buf error_jmp;
  1811.         int n, ac;
  1812.         volatile int nimg = 0;
  1813.         char **list, **keep, **kp;
  1814.         struct info *info;
  1815.         Image *image;
  1816.         AV *av;
  1817.         HV *hv;
  1818.  
  1819.         im_er_mes = newSVpv("", 0);
  1820.  
  1821.         ac = (items < 2) ? 1 : items - 1;
  1822.         list = (char **) safemalloc((ac + 1) * sizeof *list);
  1823.  
  1824.         if (!sv_isobject(ST(0)))
  1825.         {
  1826.         warning(OptionWarning, complain, IM_packname);
  1827.         goto return_it;
  1828.         }
  1829.         rref = SvRV(ST(0));
  1830.         hv = SvSTASH(rref);
  1831.  
  1832.         if (SvTYPE(rref) != SVt_PVAV)    /* array of images */
  1833.         {
  1834.         warning(OptionWarning, "Can't (yet) read into a single image", NULV);
  1835.         goto return_it;
  1836.         }
  1837.  
  1838.         av = (AV *)rref;
  1839.         info = getinfo((void *) av, (struct info *) NULV);
  1840.  
  1841.         if (items <= 1)
  1842.         *list = *info->info.filename ? info->info.filename : "XC:black";
  1843.         else
  1844.         for (n = 0; n < ac; n++)
  1845.             list[n] = (char *)SvPV(ST(n+1), na);
  1846.  
  1847.         list[ac] = NULL;
  1848.         keep = list;
  1849.  
  1850.         im_er_jmp = &error_jmp;
  1851.         if (setjmp(error_jmp))
  1852.         goto return_it;
  1853.  
  1854.         ExpandFilenames(&ac, &list);
  1855.  
  1856.         for (n = nimg = 0; n < ac; n++)
  1857.         {
  1858.         strncpy(info->info.filename, list[n], MaxTextExtent - 1);
  1859.  
  1860.         for (image = ReadImage(&info->info); image; image = image->next)
  1861.         {
  1862.             SV *sv = newSViv((IV)image);
  1863.             SV *rv = newRV(sv);
  1864.             av_push(av, sv_bless(rv, hv));
  1865.             SvREFCNT_dec(sv);
  1866.             ++nimg;
  1867.         }
  1868.         }
  1869.  
  1870.         for (n = 0; n < ac; n++)    /* free memory from ExpandFilenames */
  1871.         if (list[n])
  1872.             for (kp = keep; list[n] != *kp++; )
  1873.             if (*kp == NULL)
  1874.             {
  1875.                 free(list[n]);
  1876.                 break;
  1877.             }
  1878.     return_it:
  1879.         safefree((char *) list);
  1880.         sv_setiv(im_er_mes, (IV)nimg);
  1881.         SvPOK_on(im_er_mes);    /* return messages in string context */
  1882.         ST(0) = sv_2mortal(im_er_mes);
  1883.         im_er_mes = NULL, im_er_jmp = NULL;
  1884.         XSRETURN(1);
  1885.     }
  1886.  
  1887. void
  1888. Write(ref, ...)
  1889.     Image::Magick    ref = NO_INIT
  1890.     ALIAS:
  1891.         WriteImage        = 1
  1892.         write        = 2
  1893.         writeimage        = 3
  1894.     PPCODE:
  1895.     {
  1896.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  1897.         Image *image, *im;
  1898.         struct info *info;
  1899.         struct info *temp = NULL;
  1900.         int n;
  1901.         jmp_buf error_jmp;
  1902.         volatile int nimg = 0;
  1903.         char filename[MaxTextExtent];
  1904.  
  1905.         im_er_mes = newSVpv("", 0);
  1906.         if (!sv_isobject(ST(0)))
  1907.         {
  1908.         warning(OptionWarning, complain, IM_packname);
  1909.         goto badreturn;
  1910.         }
  1911.         rref = SvRV(ST(0));
  1912.  
  1913.         im_er_jmp = &error_jmp;
  1914.         if (setjmp(error_jmp))
  1915.         goto badreturn;
  1916.  
  1917.         if (!(image = setup_list(rref, &info, (SV ***) NULV)))
  1918.         {
  1919.         warning(OptionWarning, "No images to Write", NULV);
  1920.         goto badreturn;
  1921.         }
  1922.  
  1923.         temp = copy_info(info);
  1924.         *temp->info.filename = '\0';
  1925.  
  1926.         if (items == 2)
  1927.         SetAttribute(temp, NULL, "file", ST(1));
  1928.         else if (items > 2)
  1929.         for (n = 2; n < items; n += 2)
  1930.             SetAttribute(temp, NULL, SvPV(ST(n-1), na), ST(n));
  1931.  
  1932.         strcpy(filename, temp->info.filename);
  1933.         SetImageInfo((void *)&temp->info, True);    /* sets adjoin */
  1934.  
  1935.         for (n = 0, im = image; im; im = im->next, n++)
  1936.         {
  1937.         strcpy(im->filename, filename);
  1938.         im->scene = n;
  1939.         }
  1940.  
  1941.         for (im = image; im; im = im->next)
  1942.         {
  1943.         if (WriteImage((ImageInfo *) &temp->info, im))
  1944.             ++nimg;
  1945.  
  1946.         if (temp->info.adjoin)
  1947.             break;
  1948.         }
  1949.  
  1950.     badreturn:
  1951.         if (temp)
  1952.         destroy_info(temp);
  1953.         sv_setiv(im_er_mes, (IV)nimg);
  1954.         SvPOK_on(im_er_mes);
  1955.         ST(0) = sv_2mortal(im_er_mes);
  1956.         im_er_mes = NULL, im_er_jmp = NULL;
  1957.         XSRETURN(1);
  1958.     }
  1959.  
  1960.  #
  1961.  # Mogrify: operate on an image
  1962.  #
  1963. void
  1964. Mogrify(ref, ...)
  1965.     Image::Magick    ref = NO_INIT
  1966.     ALIAS:
  1967.         Comment            =   1
  1968.         CommentImage        =   2
  1969.         Label            =   3
  1970.         LabelImage        =   4
  1971.         AddNoise        =   5
  1972.         AddNoiseImage        =   6
  1973.         Colorize        =   7
  1974.         ColorizeImage        =   8
  1975.         Border            =   9
  1976.         BorderImage        =  10
  1977.         Blur            =  11
  1978.         BlurImage        =  12
  1979.         Chop            =  13
  1980.         ChopImage        =  14
  1981.         Crop            =  15
  1982.         CropImage        =  16
  1983.         Despeckle        =  17
  1984.         DespeckleImage        =  18
  1985.         Edge            =  19
  1986.         EdgeImage        =  20
  1987.         Emboss            =  21
  1988.         EmbossImage        =  22
  1989.         Enhance            =  23
  1990.         EnhanceImage        =  24
  1991.         Flip            =  25
  1992.         FlipImage        =  26
  1993.         Flop            =  27
  1994.         FlopImage        =  28
  1995.         Frame            =  29
  1996.         FrameImage        =  30
  1997.         Implode            =  31
  1998.         ImplodeImage        =  32
  1999.         Magnify            =  33
  2000.         MagnifyImage        =  34
  2001.         MedianFilter        =  35
  2002.         MedianFilterImage    =  36
  2003.         Minify            =  37
  2004.         MinifyImage        =  38
  2005.         OilPaint        =  39
  2006.         OilPaintImage        =  40
  2007.         ReduceNoise        =  41
  2008.         ReduceNoiseImage    =  42
  2009.         Roll            =  43
  2010.         RollImage        =  44
  2011.         Rotate            =  45
  2012.         RotateImage        =  46
  2013.         Sample            =  47
  2014.         SampleImage        =  48
  2015.         Scale            =  49
  2016.         ScaleImage        =  50
  2017.         Shade            =  51
  2018.         ShadeImage        =  52
  2019.         Sharpen            =  53
  2020.         SharpenImage        =  54
  2021.         Shear            =  55
  2022.         ShearImage        =  56
  2023.         Spread            =  57
  2024.         SpreadImage        =  58
  2025.         Swirl            =  59
  2026.         SwirlImage        =  60
  2027.         Zoom            =  61
  2028.         ZoomImage        =  62
  2029.         IsGray            =  63
  2030.         IsGrayImage        =  64
  2031.         Annotate        =  65
  2032.         AnnotateImage        =  66
  2033.         ColorFloodfill        =  67
  2034.         ColorFloodfillImage    =  68
  2035.         Composite        =  69
  2036.         CompositeImage        =  70
  2037.         Contrast        =  71
  2038.         ContrastImage        =  72
  2039.         CycleColormap        =  73
  2040.         CycleColormapImage    =  74
  2041.         Draw            =  75
  2042.         DrawImage        =  76
  2043.         Equalize        =  77
  2044.         EqualizeImage        =  78
  2045.         Gamma            =  79
  2046.         GammaImage        =  80
  2047.         Map            =  81
  2048.         MapImage        =  82
  2049.         MatteFloodfill        =  83
  2050.         MatteFloodfillImage    =  84
  2051.         Modulate        =  85
  2052.         ModulateImage        =  86
  2053.         Negate            =  87
  2054.         NegateImage        =  88
  2055.         Normalize        =  89
  2056.         NormalizeImage        =  90
  2057.         NumberColors        =  91
  2058.         NumberColorsImage    =  92
  2059.         Opaque            =  93
  2060.         OpaqueImage        =  94
  2061.         Quantize        =  95
  2062.         QuantizeImage        =  96
  2063.         Raise            =  97
  2064.         RaiseImage        =  98
  2065.         Segment            =  99
  2066.         SegmentImage        = 100
  2067.         Signature        = 101
  2068.         SignatureImage        = 102
  2069.         Solarize        = 103
  2070.         SolarizeImage        = 104
  2071.         Sync            = 105
  2072.         SyncImage        = 106
  2073.         Texture            = 107
  2074.         TextureImage        = 108
  2075.         Transform        = 109
  2076.         TransformImage        = 110
  2077.         Transparent        = 111
  2078.         TransparentImage    = 112
  2079.         Threshold        = 113
  2080.         ThresholdImage        = 114
  2081.         Charcoal        = 115
  2082.         CharcoalImage        = 116
  2083.         Trim            = 117
  2084.         TrimImage        = 118
  2085.         Wave            = 119
  2086.         WaveImage        = 120
  2087.         Layer            = 121
  2088.         LayerImage        = 122
  2089.         MogrifyRegion        = 666
  2090.     PPCODE:
  2091.     {
  2092.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  2093.         int n, base = 2;
  2094.         struct routines *rp;
  2095.         union alist alist[ARGMAX];
  2096.         char aflag[ARGMAX];
  2097.         jmp_buf error_jmp;
  2098.         volatile int nimg = 0;
  2099.         int first;
  2100.         RectangleInfo br, rg;
  2101.         AnnotateInfo annotate;
  2102.         FrameInfo fr;
  2103.         XColor xc;
  2104.         char *arg;
  2105.         Image *image, *next, *region_image = NULL;
  2106.         struct info *info, *temp = NULL;
  2107.         char b[80];
  2108.         SV **svarr = NULL, **pv;
  2109.         char *commands[10];
  2110.  
  2111.         im_er_mes = newSVpv("", 0);
  2112.         if (!sv_isobject(ST(0)))
  2113.         {
  2114.         warning(OptionWarning, complain, IM_packname);
  2115.         goto return_it;
  2116.         }
  2117.         rref = SvRV(ST(0));
  2118.  
  2119.         rg.width = 0;
  2120.         rg.height = 0;
  2121.         rg.x = 0;
  2122.         rg.y = 0;
  2123.  
  2124.         if (ix && (ix != 666))    /* called as Routinename(...) */
  2125.         {
  2126.         ix = (ix+1)/2;
  2127.         rp = &routines[ix-1];
  2128.         arg = rp->name;
  2129.         }
  2130.         else    /* called as Mogrify("Routinename", ...) */
  2131.         {
  2132.         arg = (char *)SvPV(ST(1), na);
  2133.         if (ix)
  2134.         {
  2135.             int f = XParseGeometry(arg, &rg.x, &rg.y,
  2136.                         &rg.width, &rg.height);
  2137.             if (!(f & HeightValue))
  2138.             rg.height = rg.width;
  2139.             arg = (char *)SvPV(ST(2), na);
  2140.             base++;
  2141.         }
  2142.         for (rp = routines; ; rp++)
  2143.         {
  2144.             if (rp >= ENDOF(routines))
  2145.             {
  2146.             warning(OptionWarning, "Unknown Magick routine", arg);
  2147.             goto return_it;
  2148.             }
  2149.             if (strEQcase(arg, rp->name))
  2150.             break;
  2151.         }
  2152.  
  2153.         ix = rp - routines + 1;
  2154.         base++;
  2155.         }
  2156.  
  2157.         if (!(image = setup_list(rref, &info, &svarr)))
  2158.         {
  2159.         warning(OptionWarning, "No Images to Mogrify", arg);
  2160.         goto return_it;
  2161.         }
  2162.  
  2163.         Zero(&alist, NUMBEROF(alist), union alist);
  2164.         Zero(&aflag, NUMBEROF(aflag), char);
  2165.  
  2166.         /* Look through the argument list Perl passed to us, and
  2167.          * try to match against the routine's parameter names.
  2168.          * The Perl list looks something like:
  2169.          *      i->routine(comment => "..comments..", x => 100, ...)
  2170.          * (which is the same as:)
  2171.          *    i->routine("comment", "..comments..", "x", 100, ...)
  2172.          * As a special case, if only one argument then it
  2173.          * matches the first parameter:
  2174.          *    i->routine("..comments..")
  2175.          */
  2176.         for (n = base; n < items || n == items && base == items; n += 2)
  2177.         {
  2178.         struct args *pp = rp->args;
  2179.         struct args *qq = rp->args;
  2180.         union alist *al;
  2181.         int longest = 0;
  2182.         SV *sv;
  2183.  
  2184.         if (n == items)        /* single argument */
  2185.             sv = ST(n-1);
  2186.         else
  2187.             for (sv = ST(n), arg = (char *)SvPV(ST(n-1), na); ; qq++)
  2188.             {
  2189.             if (qq >= ENDOF(rp->args) || qq->varname == NULL)
  2190.             {
  2191.                 if (longest > 0)
  2192.                 break;
  2193.                 goto continue_outer_loop;
  2194.             }
  2195.             if (strEQcase(arg, qq->varname) > longest)
  2196.             {
  2197.                 pp = qq;
  2198.                 longest = strEQcase(arg, qq->varname);
  2199.             }
  2200.             }
  2201.         al = &alist[pp - rp->args];
  2202.  
  2203.         if (pp->type == P_INT)
  2204.             al->t_int = SvIV(sv);
  2205.         else if (pp->type == P_STR)
  2206.             al->t_str = (char *)SvPV(sv, na);
  2207.         else if (pp->type == P_DBL)
  2208.             al->t_dbl = SvNV(sv);
  2209.         else if (pp->type == P_IMG)
  2210.         {
  2211.             if (!sv_isobject(sv) ||
  2212.             !(al->t_img = setup_list(SvRV(sv),
  2213.                 (struct info **) NULV, (SV ***) NULV)))
  2214.             {
  2215.             warning(OptionWarning, complain, IM_packname);
  2216.             goto return_it;
  2217.             }
  2218.         }
  2219.         else if (!SvPOK(sv))    /* not a string; just get number */
  2220.             al->t_int = SvIV(sv);
  2221.         else            /* is a string; look up name */
  2222.         {
  2223.             al->t_int = LookupStr(pp->type, SvPV(sv, na));
  2224.             if (al->t_int < 0 && (al->t_int = SvIV(sv)) <= 0)
  2225.             {
  2226.             char b[80];
  2227.             sprintf(b, "Invalid %.60s value", pp->varname);
  2228.             warning(OptionWarning, b, arg);
  2229.             goto continue_outer_loop;
  2230.             }
  2231.         }
  2232.         ++aflag[pp - rp->args];    /* indicate that parameter was given */
  2233.  
  2234.     continue_outer_loop: ;
  2235.         }
  2236.  
  2237.         im_er_jmp = &error_jmp;
  2238.         if (setjmp(error_jmp))
  2239.         goto return_it;
  2240.  
  2241.         first = 1;
  2242.         for (next = image, pv = svarr; next; first = 0, next = next->next)
  2243.         {
  2244.         image = next;
  2245.  
  2246.         if ((rg.width*rg.height) != 0)
  2247.         {
  2248.             region_image = image;
  2249.             image = CropImage(image, &rg);
  2250.             if (!image)
  2251.             continue;
  2252.         }
  2253.  
  2254.         br.width = image->columns;
  2255.         br.height = image->rows;
  2256.         br.x = br.y = 0;
  2257.  
  2258.         switch (ix)
  2259.         {
  2260.         default:
  2261.             sprintf(b, "%d", (int)ix);
  2262.             warning(OptionWarning, "Routine value out of range", b);
  2263.             goto return_it;
  2264.         case  1:    /* Comment */
  2265.             if (!aflag[0])
  2266.             alist[0].t_str = NULL;
  2267.             CommentImage(image, alist[0].t_str);
  2268.             break;
  2269.         case  2:    /* Label */
  2270.             if (!aflag[0])
  2271.             alist[0].t_str = NULL;
  2272.             LabelImage(image, alist[0].t_str);
  2273.             break;
  2274.         case  3:    /* AddNoise */
  2275.             if (!aflag[0])
  2276.             alist[0].t_int = UniformNoise;
  2277.             image = AddNoiseImage(image, (NoiseType) alist[0].t_int);
  2278.             break;
  2279.         case  4:    /* Colorize */
  2280.             if (!aflag[0])
  2281.             alist[0].t_str = NULL;
  2282.             if (!aflag[1])
  2283.             alist[1].t_str = "black";
  2284.             ColorizeImage(image, alist[0].t_str, alist[1].t_str);
  2285.             break;
  2286.         case  5:    /* Border */
  2287.             if (first)
  2288.             {
  2289.             br.height = br.width = 6;
  2290.  
  2291.             if (aflag[0])
  2292.             {
  2293.                 int f = XParseGeometry(alist[0].t_str, &br.x,
  2294.                     &br.y, &br.width, &br.height);
  2295.                 if (!(f & HeightValue))
  2296.                 br.height = br.width;
  2297.             }
  2298.  
  2299.             if (aflag[1])
  2300.                 br.height = alist[1].t_int;
  2301.             if (aflag[2])
  2302.                 br.width = alist[2].t_int;
  2303.             if (aflag[3])
  2304.                 XQueryColorDatabase(alist[3].t_str, &xc);
  2305.             }
  2306.  
  2307.             if (aflag[3])
  2308.             {
  2309.             image->border_color.red = XDownScale(xc.red);
  2310.             image->border_color.green = XDownScale(xc.green);
  2311.             image->border_color.blue = XDownScale(xc.blue);
  2312.             }
  2313.             image = BorderImage(image, &br);
  2314.             break;
  2315.         case  6:    /* Blur */
  2316.             if (!aflag[0])
  2317.             alist[0].t_dbl = 0.6;
  2318.             image = BlurImage(image, alist[0].t_dbl);
  2319.             break;
  2320.         case  7:    /* Chop */
  2321.             if (aflag[0])
  2322.             {
  2323.             int f = XParseGeometry(alist[0].t_str, &br.x, &br.y,
  2324.                             &br.width, &br.height);
  2325.             if (!(f & HeightValue))
  2326.                 br.height = br.width;
  2327.             }
  2328.             if (aflag[1])
  2329.             br.width = alist[1].t_int;
  2330.             if (aflag[2])
  2331.             br.height = alist[2].t_int;
  2332.             if (aflag[3])
  2333.             br.x = alist[3].t_int;
  2334.             if (aflag[4])
  2335.             br.y = alist[4].t_int;
  2336.  
  2337.             image = ChopImage(image, &br);
  2338.             break;
  2339.         case 59:    /* Trim */
  2340.             ++aflag[0];
  2341.             alist[0].t_str = "0x0";
  2342.         case  8:    /* Crop */
  2343.             if (aflag[0])
  2344.             {
  2345.             int f = XParseGeometry(alist[0].t_str, &br.x, &br.y,
  2346.                             &br.width, &br.height);
  2347.             if (!(f & HeightValue))
  2348.                 br.height = br.width;
  2349.             }
  2350.             if (aflag[1])
  2351.             br.width = alist[1].t_int;
  2352.             if (aflag[2])
  2353.             br.height = alist[2].t_int;
  2354.             if (aflag[3])
  2355.             br.x = alist[3].t_int;
  2356.             if (aflag[4])
  2357.             br.y = alist[4].t_int;
  2358.  
  2359.             image = CropImage(image, &br);
  2360.             break;
  2361.         case  9:    /* Despeckle */
  2362.             image = DespeckleImage(image);
  2363.             break;
  2364.         case 10:    /* Edge */
  2365.             if (!aflag[0])
  2366.             alist[0].t_dbl = .5;
  2367.             image = EdgeImage(image, alist[0].t_dbl);
  2368.             break;
  2369.         case 11:    /* Emboss */
  2370.             image = EmbossImage(image);
  2371.             break;
  2372.         case 12:    /* Enhance */
  2373.             image = EnhanceImage(image);
  2374.             break;
  2375.         case 13:    /* Flip */
  2376.             image = FlipImage(image);
  2377.             break;
  2378.         case 14:    /* Flop */
  2379.             image = FlopImage(image);
  2380.             break;
  2381.         case 15:    /* Frame */
  2382.             if (first)
  2383.             {
  2384.             fr.height = fr.width = 25;
  2385.             fr.x = fr.y = 0;
  2386.             fr.inner_bevel = fr.outer_bevel = 6;
  2387.             if (aflag[0])
  2388.             {
  2389.                 int f = XParseGeometry(alist[0].t_str,
  2390.                         &fr.outer_bevel, &fr.inner_bevel,
  2391.                         &fr.width, &fr.height);
  2392.                 if (!(f & HeightValue))
  2393.                 fr.height = fr.width;
  2394.                 if (!(f & XValue))
  2395.                 fr.outer_bevel = (fr.width >> 2) + 1;
  2396.                 if (!(f & YValue))
  2397.                 fr.inner_bevel = (fr.height >> 2) + 1;
  2398.             }
  2399.             if (aflag[1])
  2400.                 fr.width = alist[1].t_int;
  2401.             if (aflag[2])
  2402.                 fr.height = alist[2].t_int;
  2403.             if (aflag[3])
  2404.                 fr.inner_bevel = alist[3].t_int;
  2405.             if (aflag[4])
  2406.                 fr.outer_bevel = alist[4].t_int;
  2407.             fr.x = fr.width;
  2408.             fr.y = fr.height;
  2409.             if (aflag[5])
  2410.                 XQueryColorDatabase(alist[5].t_str, &xc);
  2411.             }
  2412.             fr.width = image->columns + (fr.x << 1);
  2413.             fr.height = image->rows + (fr.y << 1);
  2414.             if (aflag[5])
  2415.             {
  2416.             image->matte_color.red = XDownScale(xc.red);
  2417.             image->matte_color.green = XDownScale(xc.green);
  2418.             image->matte_color.blue = XDownScale(xc.blue);
  2419.             }
  2420.             image = FrameImage(image, &fr);
  2421.             break;
  2422.         case 16:    /* Implode */
  2423.             if (!aflag[0])
  2424.             alist[0].t_dbl = 30.0;
  2425.             image = ImplodeImage(image, alist[0].t_dbl);
  2426.             break;
  2427.         case 17:    /* Magnify */
  2428.             image = MagnifyImage(image);
  2429.             break;
  2430.         case 18:    /* MedianFilter */
  2431.             break;
  2432.         case 19:    /* Minify */
  2433.             image = MinifyImage(image);
  2434.             break;
  2435.         case 20:    /* OilPaint */
  2436.             if (!aflag[0])
  2437.             alist[0].t_int = 3;
  2438.             image = OilPaintImage(image, alist[0].t_int);
  2439.             break;
  2440.         case 21:    /* ReduceNoise */
  2441.             image = ReduceNoiseImage(image);
  2442.             break;
  2443.         case 22:    /* Roll */
  2444.             if (aflag[1])
  2445.             br.x = alist[1].t_int;
  2446.             if (aflag[2])
  2447.             br.y = alist[2].t_int;
  2448.             if (aflag[0])
  2449.             (void) XParseGeometry(alist[0].t_str, &br.x, &br.y,
  2450.                              &br.width, &br.height);
  2451.  
  2452.             image = RollImage(image, br.x, br.y);
  2453.             break;
  2454.         case 23:    /* Rotate */
  2455.             if (!aflag[0])
  2456.             alist[0].t_dbl = 90.0;
  2457.             if (!aflag[1])
  2458.             alist[1].t_int = 0;
  2459.             if (!aflag[2])
  2460.             alist[2].t_int = 1;
  2461.  
  2462.             image = RotateImage(image, alist[0].t_dbl,
  2463.                     alist[1].t_int, alist[2].t_int);
  2464.             break;
  2465.         case 24:    /* Sample */
  2466.             if (aflag[1])
  2467.             br.width = alist[1].t_int;
  2468.             if (aflag[2])
  2469.             br.height = alist[2].t_int;
  2470.             if (aflag[0])
  2471.             (void) ParseImageGeometry(alist[0].t_str, &br.x, &br.y,
  2472.                              &br.width, &br.height);
  2473.             image = SampleImage(image, br.width, br.height);
  2474.             break;
  2475.         case 25:    /* Scale */
  2476.             if (aflag[1])
  2477.             br.width = alist[1].t_int;
  2478.             if (aflag[2])
  2479.             br.height = alist[2].t_int;
  2480.             if (aflag[0])
  2481.             (void) ParseImageGeometry(alist[0].t_str, &br.x, &br.y,
  2482.                              &br.width, &br.height);
  2483.             image = ScaleImage(image, br.width, br.height);
  2484.             break;
  2485.         case 26:    /* Shade */
  2486.         {
  2487.             float azimuth, elevation;
  2488.  
  2489.             azimuth = 30.0;
  2490.             elevation = 30.0;
  2491.             if (aflag[1])
  2492.             azimuth = alist[1].t_dbl;
  2493.             if (aflag[2])
  2494.             elevation = alist[2].t_dbl;
  2495.             if (aflag[0])
  2496.             (void) sscanf(alist[0].t_str, "%fx%f", &azimuth,
  2497.                                    &elevation);
  2498.  
  2499.             image = ShadeImage(image, alist[3].t_int, azimuth,
  2500.                                   elevation);
  2501.             break;
  2502.         }
  2503.         case 27:    /* Sharpen */
  2504.             if (!aflag[0])
  2505.             alist[0].t_dbl = 30.0;
  2506.             image = SharpenImage(image, alist[0].t_dbl);
  2507.             break;
  2508.         case 28:    /* Shear */
  2509.         {
  2510.             float x_shear, y_shear;
  2511.  
  2512.             x_shear = 45.0;
  2513.             y_shear = 45.0;
  2514.             if (aflag[1])
  2515.             x_shear = alist[1].t_dbl;
  2516.             if (aflag[2])
  2517.             y_shear = alist[2].t_dbl;
  2518.             if (aflag[0])
  2519.             (void) sscanf(alist[0].t_str, "%fx%f", &x_shear,
  2520.                                    &y_shear);
  2521.  
  2522.             image = ShearImage(image, x_shear, y_shear, alist[3].t_int);
  2523.             break;
  2524.         }
  2525.         case 29:    /* Spread */
  2526.             if (!aflag[0])
  2527.             alist[0].t_int = 3;
  2528.             image = SpreadImage(image, alist[0].t_int);
  2529.             break;
  2530.         case 30:    /* Swirl */
  2531.             if (!aflag[0])
  2532.             alist[0].t_dbl = .5;
  2533.             image = SwirlImage(image, alist[0].t_dbl);
  2534.             break;
  2535.         case 31:    /* Zoom */
  2536.             if (aflag[1])
  2537.             br.width = alist[1].t_int;
  2538.             if (aflag[2])
  2539.             br.height = alist[2].t_int;
  2540.             if (aflag[0])
  2541.             (void) ParseImageGeometry(alist[0].t_str, &br.x, &br.y,
  2542.                              &br.width, &br.height);
  2543.             if (!aflag[3])
  2544.             alist[3].t_int = 1;
  2545.             image = ZoomImage(image, br.width, br.height,
  2546.                           (FilterType) alist[3].t_int);
  2547.             break;
  2548.         case 32:    /* IsGrayImage */
  2549.             break;
  2550.         case 33:    /* Annotate */
  2551.             if (first)
  2552.             {
  2553.             GetAnnotateInfo(&annotate);
  2554.             temp = copy_info(info);
  2555.             annotate.image_info = &temp->info;
  2556.             if (aflag[0])
  2557.                 newval(&temp->info.server_name, alist[0].t_str);
  2558.             if (aflag[1])
  2559.                 newval(&temp->info.font, alist[1].t_str);
  2560.             if (aflag[2])
  2561.                 temp->info.pointsize = alist[2].t_int;
  2562.             if (aflag[3])
  2563.                 newval(&temp->info.density, alist[3].t_str);
  2564.             if (aflag[4])
  2565.                 newval(&temp->info.box, alist[4].t_str);
  2566.             if (aflag[5])
  2567.                 newval(&temp->info.pen, alist[5].t_str);
  2568.             if (aflag[6])
  2569.                 annotate.geometry = alist[6].t_str;
  2570.             if (aflag[7])
  2571.                 annotate.text = alist[7].t_str;
  2572.             if (aflag[8] || aflag[9])
  2573.             {
  2574.                 if (!aflag[8])
  2575.                 alist[8].t_int = 0;
  2576.                 if (!aflag[9])
  2577.                 alist[9].t_int = 0;
  2578.                 sprintf(b, "%+d%+d",
  2579.                     alist[8].t_int, alist[9].t_int);
  2580.                 annotate.geometry = b;
  2581.             }
  2582.             if (aflag[10])
  2583.                 annotate.alignment = (AlignmentType) alist[10].t_int;
  2584.             }
  2585.             AnnotateImage(image, &annotate);
  2586.             break;
  2587.         case 34:    /* ColorFloodfill */
  2588.             break;
  2589.         case 35:    /* Composite */
  2590.             if (!aflag[0])
  2591.             alist[0].t_int = 2;
  2592.             if (!aflag[3])
  2593.             alist[3].t_int = 1;
  2594.             if (!aflag[4])
  2595.             alist[4].t_int = 1;
  2596.  
  2597.             br.x = alist[3].t_int;
  2598.             br.y = alist[4].t_int;
  2599.             if (aflag[2])
  2600.             {
  2601.             int f = ParseImageGeometry(alist[2].t_str, &br.x, &br.y,
  2602.                              &br.width, &br.height);
  2603.             if (!(f & HeightValue))
  2604.                br.height = br.width;
  2605.             }
  2606.             if (!aflag[1])
  2607.             {
  2608.             warning(OptionWarning, "Missing image in Composite",
  2609.                 NULV);
  2610.             goto return_it;
  2611.             }
  2612.             if (aflag[5])
  2613.             {
  2614.             /*
  2615.               Gravitate image as specified by the gravity.
  2616.             */
  2617.             switch (alist[5].t_int)
  2618.             {
  2619.               case NorthWestGravity:
  2620.               {
  2621.                 br.x=0;
  2622.                 br.y=0;
  2623.                 break;
  2624.               }
  2625.               case NorthGravity:
  2626.               {
  2627.                 br.x=(image->columns-alist[1].t_img->columns) >> 1;
  2628.                 br.y=0;
  2629.                 break;
  2630.               }
  2631.               case NorthEastGravity:
  2632.               {
  2633.                 br.x=image->columns-alist[1].t_img->columns;
  2634.                 br.y=0;
  2635.                 break;
  2636.               }
  2637.               case WestGravity:
  2638.               {
  2639.                 br.x=0;
  2640.                 br.y=(image->rows-alist[1].t_img->rows) >> 1;
  2641.                 break;
  2642.               }
  2643.               case ForgetGravity:
  2644.               case StaticGravity:
  2645.               case CenterGravity:
  2646.               default:
  2647.               {
  2648.                 br.x=(image->columns-alist[1].t_img->columns) >> 1;
  2649.                 br.y=(image->rows-alist[1].t_img->rows) >> 1;
  2650.                 break;
  2651.               }
  2652.               case EastGravity:
  2653.               {
  2654.                 br.x=image->columns-alist[1].t_img->columns;
  2655.                 br.y=(image->rows-alist[1].t_img->rows) >> 1;
  2656.                 break;
  2657.               }
  2658.               case SouthWestGravity:
  2659.               {
  2660.                 br.x=0;
  2661.                 br.y=image->rows-alist[1].t_img->rows;
  2662.                 break;
  2663.               }
  2664.               case SouthGravity:
  2665.               {
  2666.                 br.x=(image->columns-alist[1].t_img->columns) >> 1;
  2667.                 br.y=image->rows-alist[1].t_img->rows;
  2668.                 break;
  2669.               }
  2670.               case SouthEastGravity:
  2671.               {
  2672.                 br.x=image->columns-alist[1].t_img->columns;
  2673.                 br.y=image->rows-alist[1].t_img->rows;
  2674.                 break;
  2675.               }
  2676.             }
  2677.             }
  2678.             CompositeImage(image, (CompositeOperator) alist[0].t_int,
  2679.                     alist[1].t_img, br.x, br.y);
  2680.             break;
  2681.         case 36:    /* Contrast */
  2682.             if (aflag[0])
  2683.             alist[0].t_int = 1;
  2684.             ContrastImage(image, alist[0].t_int);
  2685.             break;
  2686.         case 37:    /* CycleColormap */
  2687.             if (aflag[0])
  2688.             alist[0].t_int = 6;
  2689.             CycleColormapImage(image, alist[0].t_int);
  2690.             break;
  2691.         case 38:    /* Draw */
  2692.             if (first)
  2693.             {
  2694.             GetAnnotateInfo(&annotate);
  2695.             temp = copy_info(info);
  2696.             annotate.image_info = &temp->info;
  2697.             if (aflag[3])
  2698.                 newval(&temp->info.pen, alist[3].t_str);
  2699.             if (aflag[4])
  2700.                 annotate.linewidth = alist[4].t_int;
  2701.             if (aflag[5])
  2702.                 newval(&temp->info.server_name,
  2703.                 alist[5].t_str);
  2704.             }
  2705.             n = MaxTextExtent;
  2706.             if (aflag[1])
  2707.             n += Extent(alist[1].t_str);
  2708.                 if (aflag[0] && (alist[0].t_int > 0))
  2709.             annotate.primitive =
  2710.                 strcpy(safemalloc(n), p_primitives[alist[0].t_int]);
  2711.             else
  2712.             annotate.primitive = strcpy(safemalloc(n), " ");
  2713.             if (aflag[1])
  2714.             {
  2715.             strcat(annotate.primitive, " ");
  2716.             strcat(annotate.primitive, alist[1].t_str);
  2717.             }
  2718.             if (aflag[2])
  2719.             {
  2720.             strcat(annotate.primitive, " ");
  2721.             strcat(annotate.primitive, p_methods[alist[2].t_int]);
  2722.             }
  2723.             DrawImage(image, &annotate);
  2724.             safefree(annotate.primitive);
  2725.             annotate.primitive = NULL;
  2726.             break;
  2727.         case 39:    /* Equalize */
  2728.             EqualizeImage(image);
  2729.             break;
  2730.         case 40:    /* Gamma */
  2731.             if (first)
  2732.             {
  2733.             if (!aflag[1])
  2734.                 alist[1].t_dbl = 1.0;
  2735.             if (!aflag[2])
  2736.                 alist[2].t_dbl = 1.0;
  2737.             if (!aflag[3])
  2738.                 alist[3].t_dbl = 1.0;
  2739.             if (!aflag[0])
  2740.             {
  2741.                 sprintf(b, "%f/%f/%f", alist[1].t_dbl,
  2742.                     alist[2].t_dbl, alist[3].t_dbl);
  2743.                 alist[0].t_str = b;
  2744.             }
  2745.             }
  2746.             GammaImage(image, alist[0].t_str);
  2747.             break;
  2748.         case 41:    /* Map */
  2749.             if (!aflag[1])
  2750.             alist[1].t_int = 1;
  2751.             if (!aflag[0])
  2752.             {
  2753.             warning(OptionWarning, "Missing image in Map", NULV);
  2754.             goto return_it;
  2755.             }
  2756.             MapImage(image, alist[0].t_img, alist[1].t_int);
  2757.             break;
  2758.         case 42:    /* MatteFloodfill */
  2759.             break;
  2760.         case 43:    /* Modulate */
  2761.             if (first)
  2762.             {
  2763.             if (!aflag[1])
  2764.                 alist[1].t_dbl = 1.0;
  2765.             if (!aflag[2])
  2766.                 alist[2].t_dbl = 1.0;
  2767.             if (!aflag[3])
  2768.                 alist[3].t_dbl = 1.0;
  2769.             sprintf(b, "%f/%f/%f", alist[1].t_dbl,
  2770.                 alist[2].t_dbl, alist[3].t_dbl);
  2771.             if (!aflag[0])
  2772.                 alist[0].t_str = b;
  2773.             }
  2774.             ModulateImage(image, alist[0].t_str);
  2775.             break;
  2776.         case 44:    /* Negate */
  2777.             if (!aflag[0])
  2778.             alist[0].t_int = 0;
  2779.             NegateImage(image, alist[0].t_int);
  2780.             break;
  2781.         case 45:    /* Normalize */
  2782.             NormalizeImage(image);
  2783.             break;
  2784.         case 46:    /* NumberColors */
  2785.             break;
  2786.         case 47:    /* Opaque */
  2787.             if (!aflag[0])
  2788.             alist[0].t_str = "black";
  2789.             if (!aflag[1])
  2790.             alist[1].t_str = "white";
  2791.  
  2792.             OpaqueImage(image, alist[0].t_str, alist[1].t_str);
  2793.             break;
  2794.         case 48:    /* Quantize */
  2795.             {
  2796.             QuantizeInfo quan;
  2797.             quan.number_colors = aflag[0] ? alist[0].t_int :
  2798.                 (info? info->quant.number_colors : MaxRGB + 1);
  2799.             quan.tree_depth = aflag[1] ? alist[1].t_int :
  2800.                 (info? info->quant.tree_depth : 8);
  2801.             quan.colorspace = (ColorspaceType)
  2802.                 (aflag[2] ? alist[2].t_int :
  2803.                 (info? info->quant.colorspace : RGBColorspace));
  2804.             quan.dither = aflag[3] ? alist[3].t_int :
  2805.                 (info? info->quant.dither : False);
  2806.             QuantizeImages(&quan, image);
  2807.             QuantizationError(image);
  2808.                 SyncImage(image);
  2809.             goto return_it;
  2810.             }
  2811.         case 49:    /* Raise */
  2812.             if (first)
  2813.             {
  2814.             br.height = br.width = 6;
  2815.  
  2816.             if (aflag[0])
  2817.             {
  2818.                 int f = XParseGeometry(alist[0].t_str, &br.x,
  2819.                     &br.y, &br.width, &br.height);
  2820.                 if (!(f & HeightValue))
  2821.                 br.height = br.width;
  2822.             }
  2823.  
  2824.             if (aflag[1])
  2825.                 br.width = alist[1].t_int;
  2826.             if (aflag[2])
  2827.                 br.height = alist[2].t_int;
  2828.             if (aflag[3])
  2829.                 br.x = alist[3].t_int;
  2830.             if (aflag[4])
  2831.                 br.y = alist[4].t_int;
  2832.             if (aflag[5])
  2833.                 alist[5].t_int = 1;
  2834.             }
  2835.  
  2836.             RaiseImage(image, &br, alist[5].t_int);
  2837.             break;
  2838.         case 50:    /* Segment */
  2839.             if (first)
  2840.             {
  2841.             if (!aflag[0])
  2842.                 alist[0].t_int = info ? info->quant.colorspace
  2843.                           : RGBColorspace;
  2844.             if (!aflag[1])
  2845.                 alist[1].t_int = 0;
  2846.             if (!aflag[2])
  2847.                 alist[2].t_dbl = 1.0;
  2848.             if (!aflag[3])
  2849.                 alist[3].t_dbl = 1.5;
  2850.             }
  2851.             SegmentImage(image, alist[0].t_int, alist[1].t_int,
  2852.                     alist[2].t_dbl, alist[3].t_dbl);
  2853.             QuantizationError(image);
  2854.             SyncImage(image);
  2855.             break;
  2856.         case 51:    /* Signature */
  2857.             SignatureImage(image);
  2858.             break;
  2859.         case 52:    /* Solarize */
  2860.             if (!aflag[0])
  2861.             alist[0].t_dbl = 50.0;
  2862.             SolarizeImage(image, alist[0].t_dbl);
  2863.             break;
  2864.         case 53:    /* Sync */
  2865.             SyncImage(image);
  2866.             break;
  2867.         case 54:    /* Texture */
  2868.             if (!aflag[0])
  2869.             alist[0].t_str = NULL;
  2870.             TextureImage(image, alist[0].t_str);
  2871.             break;
  2872.         case 55:    /* Transform */
  2873.             break;
  2874.         case 56:    /* Transparent */
  2875.             if (!aflag[0])
  2876.             alist[0].t_str = NULL;
  2877.             TransparentImage(image, alist[0].t_str);
  2878.             break;
  2879.         case 57:    /* Threshold */
  2880.             if (!aflag[0])
  2881.             alist[0].t_dbl = (MaxRGB+1)/2.0;
  2882.             ThresholdImage(image, alist[0].t_dbl);
  2883.             break;
  2884.         case 58:    /* Charcoal */
  2885.             if (first)
  2886.             {
  2887.             temp = copy_info(info);
  2888.             if (!aflag[0])
  2889.                 alist[0].t_str = "50";
  2890.             GetQuantizeInfo(&temp->quant);
  2891.             if (info)
  2892.                 temp->quant.dither = info->quant.dither;
  2893.             temp->quant.colorspace = GRAYColorspace;
  2894.             commands[0] = client_name;
  2895.             commands[1] = "-edge";
  2896.             commands[2] = alist[0].t_str;
  2897.             commands[3] = "-blur";
  2898.             commands[4] = alist[0].t_str;
  2899.             commands[5] = "-normalize";
  2900.             commands[6] = "-negate";
  2901.             }
  2902.             QuantizeImage(&temp->quant, image);
  2903.             QuantizationError(image);
  2904.             SyncImage(image);
  2905.             MogrifyImage(&info->info, 7, commands, &image);
  2906.             if (next != image)
  2907.             next = NULL;  /* 'cause it's been blown away */
  2908.             break;
  2909.         case 60:    /* Wave */
  2910.             {
  2911.             float amplitude, wavelength;
  2912.  
  2913.             amplitude = 10.0;
  2914.             wavelength = 10.0;
  2915.             if (aflag[1])
  2916.                 amplitude = alist[1].t_dbl;
  2917.             if (aflag[2])
  2918.                 wavelength = alist[2].t_dbl;
  2919.             if (aflag[0])
  2920.                 (void) sscanf(alist[0].t_str, "%fx%f",
  2921.                             &litude, &wavelength);
  2922.  
  2923.             image = WaveImage(image, amplitude, wavelength);
  2924.             break;
  2925.             }
  2926.         case 61:    /* Layer */
  2927.             if (!aflag[0])
  2928.             alist[0].t_int = 0;
  2929.             LayerImage(image, alist[0].t_int);
  2930.             break;
  2931.         }
  2932.  
  2933.         if (region_image != (Image *) NULL)
  2934.         {
  2935.             /*
  2936.               Composite region.
  2937.             */
  2938.             CompositeImage(region_image, ReplaceCompositeOp, image,
  2939.             rg.x, rg.y);
  2940.             image->orphan = 1;
  2941.             DestroyImage(image);
  2942.             image = region_image;
  2943.         }
  2944.  
  2945.         if (image)
  2946.         {
  2947.             ++nimg;
  2948.             if (next && next != image)
  2949.             {
  2950.             image->next = next->next;
  2951.             next->orphan = 1;
  2952.             DestroyImage(next);
  2953.             }
  2954.             sv_setiv(*pv, (IV)image);
  2955.             next = image;
  2956.         }
  2957.         if (*pv)
  2958.             ++pv;
  2959.         }
  2960.  
  2961.         if (temp)
  2962.         destroy_info(temp);
  2963.  
  2964.     return_it:
  2965.         if (svarr)
  2966.         safefree((char *) svarr);
  2967.         sv_setiv(im_er_mes, (IV)nimg);
  2968.         SvPOK_on(im_er_mes); /* return messages in string context */
  2969.         ST(0) = sv_2mortal(im_er_mes);
  2970.         im_er_mes = NULL, im_er_jmp = NULL;
  2971.         XSRETURN(1);
  2972.     }
  2973.  
  2974. void
  2975. Set(ref, ...)
  2976.     Image::Magick    ref = NO_INIT
  2977.     ALIAS:
  2978.         SetAttributes    = 1
  2979.         SetAttribute    = 2
  2980.         set        = 3
  2981.         setattributes    = 4
  2982.         setattribute    = 5
  2983.     PPCODE:
  2984.     {
  2985.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  2986.         int n;
  2987.         Image *image;
  2988.         struct info *info;
  2989.  
  2990.         im_er_mes = newSVpv("", 0);
  2991.         if (!sv_isobject(ST(0)))
  2992.         {
  2993.         warning(OptionWarning, complain, IM_packname);
  2994.         goto badreturn;
  2995.         }
  2996.         rref = SvRV(ST(0));
  2997.  
  2998.         image = setup_list(rref, &info, (SV ***) NULV);
  2999.  
  3000.         for (n = 2; n < items; n += 2)
  3001.         SetAttribute(info, image, SvPV(ST(n-1), na), ST(n));
  3002.  
  3003.     badreturn:
  3004.         sv_setiv(im_er_mes, (IV)(SvCUR(im_er_mes) != 0));
  3005.         SvPOK_on(im_er_mes); /* return messages in string context */
  3006.  
  3007.         ST(0) = sv_2mortal(im_er_mes);
  3008.         im_er_mes = NULL;
  3009.         XSRETURN(1);
  3010.     }
  3011.  
  3012.  #  Get returns the values of the given attributes.  It takes a list of
  3013.  #  atrribute names and returns an equal length list of their corresponding
  3014.  #  values.  Unknown attributes return as undef.
  3015.  
  3016. void
  3017. Get(ref, ...)
  3018.     Image::Magick    ref = NO_INIT
  3019.     ALIAS:
  3020.         GetAttributes = 1
  3021.         GetAttribute  = 2
  3022.         get          = 3
  3023.         getattributes = 4
  3024.         getattribute  = 5
  3025.     PPCODE:
  3026.     {
  3027.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  3028.         int n;
  3029.         char *arg;    /* the attribute name */
  3030.         IV i;
  3031.         SV *s;
  3032.         char b[80];
  3033.         Image *image;
  3034.         struct info *info;
  3035.  
  3036.         if (!sv_isobject(ST(0)))
  3037.         {
  3038.         warning(OptionWarning, complain, IM_packname);
  3039.         XSRETURN_EMPTY;
  3040.         }
  3041.         rref = SvRV(ST(0));
  3042.  
  3043.         image = setup_list(rref, &info, (SV ***) NULV);
  3044.         if (!image && !info)
  3045.         {
  3046.         warning(OptionWarning, "Nothing to GetAttributes from", NULV);
  3047.         XSRETURN_EMPTY;
  3048.         }
  3049.         EXTEND(sp, items - 1);
  3050.  
  3051.         for (n = 1; n < items; n++)
  3052.         {
  3053.         arg = (char *)SvPV(ST(n), na);
  3054.         s = NULL;        /* unknown attributes return this */
  3055.  
  3056.         switch (*arg)
  3057.         {
  3058.         case 'A': case 'a':
  3059.             if (strEQcase(arg, "affirm"))
  3060.             {
  3061.             if (info)
  3062.                 s = newSViv(info->info.affirm);
  3063.             }
  3064.             else if (strEQcase(arg, "adjoin"))
  3065.             {
  3066.             if (info)
  3067.                 s = newSViv(info->info.adjoin);
  3068.             }
  3069.             break;
  3070.         case 'B': case 'b':
  3071.             if (strEQcase(arg, "base_column"))
  3072.             {
  3073.             if (image)
  3074.                 s = newSViv(image->magick_columns);
  3075.             }
  3076.             if (strEQcase(arg, "base_filename"))
  3077.             {
  3078.             if (image)
  3079.                 s = newSVpv(image->magick_filename, 0);
  3080.             }
  3081.             if (strEQcase(arg, "base_height"))
  3082.             {
  3083.             if (image)
  3084.                 s = newSViv(image->magick_rows);
  3085.             }
  3086.             if (strEQcase(arg, "base_row"))
  3087.             {
  3088.             if (image)
  3089.                 s = newSViv(image->magick_rows);
  3090.             }
  3091.             if (strEQcase(arg, "base_width"))
  3092.             {
  3093.             if (image)
  3094.                 s = newSViv(image->magick_columns);
  3095.             }
  3096.             else if (strEQcase(arg, "border"))
  3097.             {
  3098.             if (!image)
  3099.                 break;
  3100.             sprintf(b, "%u,%u,%u,%u",
  3101.                     image->border_color.red,
  3102.                     image->border_color.green,
  3103.                     image->border_color.blue,
  3104.                     image->border_color.index);
  3105.             s = newSVpv(b, 0);
  3106.             }
  3107.             else if (strEQcase(arg, "background"))
  3108.             {
  3109.             if (!image)
  3110.                 break;
  3111.             sprintf(b, "%u,%u,%u,%u",
  3112.                     image->background_color.red,
  3113.                     image->background_color.green,
  3114.                     image->background_color.blue,
  3115.                     image->background_color.index);
  3116.             s = newSVpv(b, 0);
  3117.             }
  3118.             break;
  3119.         case 'C': case 'c':
  3120.             if (strEQcase(arg, "comment"))
  3121.             {
  3122.             if (image && image->comments)
  3123.                 s = newSVpv(image->comments, 0);
  3124.             }
  3125.             else if (strEQcase(arg, "class"))
  3126.             {
  3127.             if (!image)
  3128.                 break;
  3129. #if defined(__cplusplus) || defined(c_plusplus)
  3130.             i = image->c_class;
  3131. #else
  3132.             i = image->class;
  3133. #endif
  3134.             s = newSViv(i);
  3135.             if (i >= 0 && i < NUMBEROF(p_classes) - 1)
  3136.             {
  3137.                 sv_setpv(s, p_classes[i]);
  3138.                 SvIOK_on(s);
  3139.             }
  3140.             }
  3141.             else if (strEQcase(arg, "compress"))
  3142.             {
  3143.             i = info ? info->info.compression : image->compression;
  3144.             s = newSViv(i);
  3145.             if (i >= 0 && i < NUMBEROF(p_compressions) - 1)
  3146.             {
  3147.                 sv_setpv(s, p_compressions[i]);
  3148.                 SvIOK_on(s);
  3149.             }
  3150.             }
  3151.             else if (strEQcase(arg, "colorspace"))
  3152.             {
  3153.             i = info ? info->quant.colorspace : RGBColorspace;
  3154.             s = newSViv(i);
  3155.             if (i >= 0 && i < NUMBEROF(p_colorspaces) - 1)
  3156.             {
  3157.                 sv_setpv(s, p_colorspaces[i]);
  3158.                 SvIOK_on(s);
  3159.             }
  3160.             }
  3161.             else if (strEQcase(arg, "colors"))    /* same as number */
  3162.             {
  3163.             if (info && info->quant.number_colors)
  3164.                 s = newSViv(info->quant.number_colors);
  3165.             else if (image)
  3166.                 s = newSViv(image->colors);
  3167.             }
  3168.             else if (strEQcase(arg, "colormap"))
  3169.             {
  3170.             ColorPacket *cp;
  3171.  
  3172.             if (!image || !image->colormap)
  3173.                 break;
  3174.             i = 0;
  3175.             (void) sscanf(arg, "%*[^[][%d,%d", &i );
  3176.             if (i > image->colors)
  3177.                 i %= image->colors;
  3178.             cp = image->colormap+i;
  3179.             sprintf(b, "%u,%u,%u\n", cp->red, cp->green, cp->blue);
  3180.             s = newSVpv(b, 0);
  3181.             }
  3182.             else if (strEQcase(arg, "column"))
  3183.             {
  3184.             if (image)
  3185.                 s = newSViv(image->columns);
  3186.             }
  3187.             break;
  3188.         case 'D': case 'd':
  3189.             if (strEQcase(arg, "density"))
  3190.             {
  3191.             if (info && info->info.density)
  3192.                 s = newSVpv(info->info.density, 0);
  3193.             }
  3194.             else if (strEQcase(arg, "dispose"))
  3195.             {
  3196.             if (info && info->info.dispose)
  3197.                 s = newSVpv(info->info.dispose, 0);
  3198.             else if (image)
  3199.                 s = newSViv(image->dispose);
  3200.             }
  3201.             else if (strEQcase(arg, "delay"))
  3202.             {
  3203.             if (info && info->info.delay)
  3204.                 s = newSVpv(info->info.delay, 0);
  3205.             else if (image)
  3206.                 s = newSViv(image->delay);
  3207.             }
  3208.             else if (strEQcase(arg, "dither"))
  3209.             {
  3210.             if (info)
  3211.                 s = newSViv(info->info.dither);
  3212.             }
  3213.             else if (strEQcase(arg, "display"))    /* same as server */
  3214.             {
  3215.             if (info && info->info.server_name)
  3216.                 s = newSVpv(info->info.server_name, 0);
  3217.             }
  3218.             else if (strEQcase(arg, "depth"))
  3219.             {
  3220.             if (image)
  3221.                 s = newSViv(image->depth);
  3222.             }
  3223.             else if (strEQcase(arg, "directory"))
  3224.             {
  3225.             if (image && image->directory)
  3226.                 s = newSVpv(image->directory, 0);
  3227.             }
  3228.             break;
  3229.         case 'E': case 'e':
  3230.             break;
  3231.         case 'F': case 'f':
  3232.             if (strEQcase(arg, "filesize"))
  3233.             {
  3234.             if (image)
  3235.                 s = newSViv(image->filesize);
  3236.             }
  3237.             else if (strEQcase(arg, "file"))
  3238.             {
  3239.             if (image)
  3240.                 s = newSVpv(image->filename, 0);
  3241.             else if (info && info->info.filename &&
  3242.                  *info->info.filename)
  3243.                 s = newSVpv(info->info.filename, 0);
  3244.             }
  3245.             else if (strEQcase(arg, "font"))
  3246.             {
  3247.             if (info && info->info.font)
  3248.                 s = newSVpv(info->info.font, 0);
  3249.             }
  3250.             else if (strEQcase(arg, "format"))    /* same as magick */
  3251.             {
  3252.             if (info && *info->info.magick)
  3253.                 s = newSVpv(info->info.magick, 0);
  3254.             else if (image)
  3255.                 s = newSVpv(image->magick, 0);
  3256.             }
  3257.             break;
  3258.         case 'G': case 'g':
  3259.             if (strEQcase(arg, "gamma"))
  3260.             {
  3261.             if (image)
  3262.                 s = newSVnv(image->gamma);
  3263.             }
  3264.             else if (strEQcase(arg, "geom"))
  3265.             {
  3266.             if (image && image->geometry)
  3267.                 s = newSVpv(image->geometry, 0);
  3268.             }
  3269.             break;
  3270.         case 'H': case 'h':
  3271.             if (strEQcase(arg, "height"))
  3272.             {
  3273.             if (image)
  3274.                 s = newSViv(image->rows);
  3275.             }
  3276.             break;
  3277.         case 'I': case 'i':
  3278.             if (strEQcase(arg, "iterat"))    /* same as loop */
  3279.             {
  3280.             if (info && info->info.iterations)
  3281.                 s = newSVpv(info->info.iterations, 0);
  3282.             else if (image)
  3283.                 s = newSViv(image->iterations);
  3284.             }
  3285.             else if (strEQcase(arg, "interlace"))
  3286.             {
  3287.             i = info ? info->info.interlace : image->interlace;
  3288.             s = newSViv(i);
  3289.             if (i >= 0 && i < NUMBEROF(p_interlaces) - 1)
  3290.             {
  3291.                 sv_setpv(s, p_interlaces[i]);
  3292.                 SvIOK_on(s);
  3293.             }
  3294.             }
  3295.             break;
  3296.         case 'J': case 'j':
  3297.             break;
  3298.         case 'K': case 'k':
  3299.             break;
  3300.         case 'L': case 'l':
  3301.             if (strEQcase(arg, "label"))
  3302.             {
  3303.             if (image && image->label)
  3304.                 s = newSVpv(image->label, 0);
  3305.             }
  3306.             else if (strEQcase(arg, "loop"))    /* same as iterations */
  3307.             {
  3308.             if (info && info->info.iterations)
  3309.                 s = newSVpv(info->info.iterations, 0);
  3310.             else if (image)
  3311.                 s = newSViv(image->iterations);
  3312.             }
  3313.             break;
  3314.         case 'M': case 'm':
  3315.             if (strEQcase(arg, "magick"))    /* same as format */
  3316.             {
  3317.             if (info && *info->info.magick)
  3318.                 s = newSVpv(info->info.magick, 0);
  3319.             else if (image)
  3320.                 s = newSVpv(image->magick, 0);
  3321.             }
  3322.             else if (strEQcase(arg, "monoch"))
  3323.             {
  3324.             i = info?
  3325.                 info->info.monochrome : IsMonochromeImage(image);
  3326.             s = newSViv(i);
  3327.             }
  3328.             else if (strEQcase(arg, "mattecolor") ||
  3329.                  strEQcase(arg, "matte_color"))
  3330.             {
  3331.             if (!image)
  3332.                 break;
  3333.             sprintf(b, "%u,%u,%u,%u",
  3334.                     image->border_color.red,
  3335.                     image->border_color.green,
  3336.                     image->border_color.blue,
  3337.                     image->border_color.index);
  3338.             s = newSVpv(b, 0);
  3339.             }
  3340.             else if (strEQcase(arg, "matte"))
  3341.             {
  3342.             if (image)
  3343.                 s = newSViv(image->matte);
  3344.             }
  3345.             else if (strEQcase(arg, "montage"))
  3346.             {
  3347.             if (image && image->montage)
  3348.                 s = newSVpv(image->montage, 0);
  3349.             }
  3350.             else if (strEQcase(arg, "mean"))
  3351.             {
  3352.             if (image)
  3353.                 s = newSViv(image->mean_error_per_pixel);
  3354.             }
  3355.             break;
  3356.         case 'N': case 'n':
  3357.             if (strEQcase(arg, "number"))    /* same as colors */
  3358.             {
  3359.             if (info && info->quant.number_colors)
  3360.                 s = newSViv(info->quant.number_colors);
  3361.             else if (image)
  3362.                 s = newSViv(image->colors);
  3363.             }
  3364.             else if (strEQcase(arg, "normalized_max") ||
  3365.                  strEQcase(arg, "normalizedmax"))
  3366.             {
  3367.             if (image)
  3368.                 s = newSVnv(image->normalized_maximum_error);
  3369.             }
  3370.             else if (strEQcase(arg, "normalized_mean") ||
  3371.                  strEQcase(arg, "normalizedmean"))
  3372.             {
  3373.             if (image)
  3374.                 s = newSVnv(image->normalized_mean_error);
  3375.             }
  3376.             break;
  3377.         case 'O': case 'o':
  3378.             break;
  3379.         case 'P': case 'p':
  3380.             if (strEQcase(arg, "packetsize") ||
  3381.             strEQcase(arg, "packet_size"))
  3382.             {
  3383.             if (image)
  3384.                 s = newSViv(image->packet_size);
  3385.             }
  3386.             else if (strEQcase(arg, "packet"))
  3387.             {
  3388.             if (image)
  3389.                 s = newSViv(image->packets);
  3390.             }
  3391.             else if (strEQcase(arg, "page"))
  3392.             {
  3393.             if (info && info->info.page)
  3394.                 s = newSVpv(info->info.page, 0);
  3395.             else if (image && image->page)
  3396.                 s = newSVpv(image->page, 0);
  3397.             }
  3398.             else if (strEQcase(arg, "pipe"))
  3399.             {
  3400.             if (image)
  3401.                 s = newSViv(image->pipe);
  3402.             }
  3403.             else if (strEQcase(arg, "pixel"))
  3404.             {
  3405.             RunlengthPacket *cp;
  3406.             int x, y;
  3407.  
  3408.             if (!image || !image->pixels)
  3409.                 break;
  3410.             if (!UncondenseImage(image))
  3411.                 break;
  3412.             x = 0;
  3413.             y = 0;
  3414.             (void) sscanf(arg, "%*[^[][%d,%d", &x, &y);
  3415.             if (y > image->rows)
  3416.                 y %= image->rows;
  3417.             if (x > image->columns)
  3418.                 x %= image->columns;
  3419.             cp = image->pixels+(y*image->columns+x);
  3420.             sprintf(b, "%u,%u,%u,%u\n", cp->red, cp->green,
  3421.                             cp->blue, cp->index);
  3422.             s = newSVpv(b, 0);
  3423.             }
  3424.             else if (strEQcase(arg, "points"))
  3425.             {
  3426.             if (info)
  3427.                 s = newSViv(info->info.pointsize);
  3428.             }
  3429.             else if (strEQcase(arg, "preview"))
  3430.             {
  3431.             s = newSViv(info->info.preview_type);
  3432.             if (info->info.preview_type >= 0 &&
  3433.                 info->info.preview_type < NUMBEROF(p_previews) - 1)
  3434.             {
  3435.                 sv_setpv(s, p_previews[info->info.preview_type]);
  3436.                 SvIOK_on(s);
  3437.             }
  3438.             }
  3439.             break;
  3440.         case 'Q': case 'q':
  3441.             if (strEQcase(arg, "quality"))
  3442.             {
  3443.             if (info)
  3444.                 s = newSViv(info->info.quality);
  3445.             }
  3446.             break;
  3447.         case 'R': case 'r':
  3448.             if (strEQcase(arg, "runlength"))
  3449.             {
  3450.             if (image)
  3451.                 s = newSViv(image->runlength);
  3452.             }
  3453.             else if (strEQcase(arg, "row"))
  3454.             {
  3455.             if (image)
  3456.                 s = newSViv(image->rows);
  3457.             }
  3458.             break;
  3459.         case 'S': case 's':
  3460.             if (strEQcase(arg, "status"))
  3461.             {
  3462.             if (image)
  3463.                 s = newSViv(image->status);
  3464.             }
  3465.             else if (strEQcase(arg, "subimage"))
  3466.             {
  3467.             if (info)
  3468.                 s = newSViv(info->info.subimage);
  3469.             }
  3470.             else if (strEQcase(arg, "subrange"))
  3471.             {
  3472.             if (info)
  3473.                 s = newSViv(info->info.subrange);
  3474.             }
  3475.             else if (strEQcase(arg, "server"))    /* same as display */
  3476.             {
  3477.             if (info && info->info.server_name)
  3478.                 s = newSVpv(info->info.server_name, 0);
  3479.             }
  3480.             else if (strEQcase(arg, "size"))
  3481.             {
  3482.             if (info && info->info.size)
  3483.                 s = newSVpv(info->info.size, 0);
  3484.             }
  3485.             else if (strEQcase(arg, "scene"))
  3486.             {
  3487.             if (image)
  3488.                 s = newSViv(image->scene);
  3489.             }
  3490.             else if (strEQcase(arg, "sign"))
  3491.             {
  3492.             if (image && image->signature)
  3493.                 s = newSVpv(image->signature, 0);
  3494.             }
  3495.             break;
  3496.         case 'T': case 't':
  3497.             if (strEQcase(arg, "temp"))
  3498.             {
  3499.             if (image)
  3500.                 s = newSViv(image->temporary);
  3501.             }
  3502.             else if (strEQcase(arg, "tile"))
  3503.             {
  3504.             if (info && info->info.tile)
  3505.                 s = newSVpv(info->info.tile, 0);
  3506.             }
  3507.             else if (strEQcase(arg, "texture"))
  3508.             {
  3509.             if (info && info->info.texture)
  3510.                 s = newSVpv(info->info.texture, 0);
  3511.             }
  3512.             else if (strEQcase(arg, "text"))
  3513.             {
  3514.             if (image && image->text)
  3515.                 s = newSVpv(image->text, 0);
  3516.             }
  3517.             else if (strEQcase(arg, "total"))
  3518.             {
  3519.             if (image)
  3520.                 {
  3521.                 NumberColors(image,(FILE *) NULL);
  3522.                 s = newSViv(image->total_colors);
  3523.                 }
  3524.             }
  3525.             else if (strEQcase(arg, "type"))
  3526.             {
  3527.             if (image)
  3528.                 if (info && info->info.colorspace == CMYKColorspace)
  3529.                 s = newSVpv("color separation", 0);
  3530.                 else if (IsMonochromeImage(image))
  3531.                 s = newSVpv("bilevel", 0);
  3532.                 else if (IsGrayImage(image))
  3533.                 s = newSVpv("grayscale", 0);
  3534.                 else if (IsPseudoClass(image))
  3535.                 s = newSVpv("palette", 0);
  3536.                 else if (!image->matte)
  3537.                 s = newSVpv("true color", 0);
  3538.                 else
  3539.                 s = newSVpv("true color with transparency", 0);
  3540.             }
  3541.             break;
  3542.         case 'U': case 'u':
  3543.             if (strEQcase(arg, "undercolor"))
  3544.             {
  3545.             if (info && info->info.undercolor)
  3546.                 s = newSVpv(info->info.undercolor, 0);
  3547.             }
  3548.             else if (strEQcase(arg, "units"))
  3549.             {
  3550.             if (!image)
  3551.                 break;
  3552.             s = newSViv(image->units);
  3553.             if (image->units >= 0 &&
  3554.                     image->units < NUMBEROF(p_units) - 1)
  3555.             {
  3556.                 sv_setpv(s, p_units[image->units]);
  3557.                 SvIOK_on(s);
  3558.             }
  3559.             }
  3560.             break;
  3561.         case 'V': case 'v':
  3562.             if (strEQcase(arg, "verbose"))
  3563.             {
  3564.             if (info)
  3565.                 s = newSViv(info->info.verbose);
  3566.             }
  3567.             break;
  3568.         case 'W': case 'w':
  3569.             if (strEQcase(arg, "width"))
  3570.             {
  3571.             if (image)
  3572.                 s = newSViv(image->columns);
  3573.             }
  3574.             break;
  3575.         case 'X': case 'x':
  3576.             if (strEQcase(arg, "x"))
  3577.             {
  3578.             if (image)
  3579.                 s = newSVnv(image->x_resolution);
  3580.             }
  3581.             break;
  3582.         case 'Y': case 'y':
  3583.             if (strEQcase(arg, "y"))
  3584.             {
  3585.             if (image)
  3586.                 s = newSVnv(image->y_resolution);
  3587.             }
  3588.             break;
  3589.         case 'Z': case 'z':
  3590.             break;
  3591.         }
  3592.  
  3593.         /* push return value onto stack */
  3594.         PUSHs(s? sv_2mortal(s) : &sv_undef);
  3595.         }
  3596.     }
  3597.  
  3598. # Ping image
  3599.  
  3600. void
  3601. Ping(ref, ...)
  3602.     Image::Magick    ref = NO_INIT
  3603.     ALIAS:
  3604.         PingImage    = 1
  3605.         ping    = 2
  3606.         pingimage    = 3
  3607.     PPCODE:
  3608.     {
  3609.         SV *rref;    /* rref is the SV* of ref=SvIV(rref) */
  3610.         int n;
  3611.         char b[80];
  3612.         struct info *info;
  3613.         unsigned int columns, rows, filesize;
  3614.         AV *av;
  3615.         SV *s;
  3616.  
  3617.         EXTEND(sp, items - 1);
  3618.         im_er_mes = newSVpv("", 0);
  3619.  
  3620.         rref = SvRV(ST(0));
  3621.         av = (AV *)rref;
  3622.         info = getinfo((void *) av, (struct info *) NULV);
  3623.         for (n = 1; n < items; n++)
  3624.         {
  3625.         strcpy(info->info.filename, (char *)SvPV(ST(n), na));
  3626.         filesize=PingImage(&info->info, &columns, &rows);
  3627.         if (filesize == 0)
  3628.             s = &sv_undef;
  3629.         else {
  3630.             sprintf(b, "%u,%u,%u,%s", columns, rows, filesize,
  3631.                 info->info.magick);
  3632.             s = sv_2mortal(newSVpv(b, 0));
  3633.         }
  3634.         PUSHs(s);
  3635.         }
  3636.  
  3637.         SvREFCNT_dec(im_er_mes);    /* throw away all errors */
  3638.         im_er_mes = NULL;
  3639.     }
  3640.  
  3641. # lookup a color by its name
  3642.  
  3643. void
  3644. QueryColor(ref, ...)
  3645.     Image::Magick    ref = NO_INIT
  3646.     ALIAS:
  3647.         querycolor = 1
  3648.     PPCODE:
  3649.     {
  3650.         int n;
  3651.         char *arg;    /* the color name */
  3652.         XColor target_color;
  3653.         char b[80];
  3654.         SV *s;
  3655.  
  3656.         EXTEND(sp, items - 1);
  3657.         im_er_mes = newSVpv("", 0);
  3658.  
  3659.         for (n = 1; n < items; n++)
  3660.         {
  3661.         arg = (char *)SvPV(ST(n), na);
  3662.  
  3663.         if (!XQueryColorDatabase(arg, &target_color))
  3664.             s = &sv_undef;
  3665.         else {
  3666.             sprintf(b, "%u,%u,%u", XDownScale(target_color.red),
  3667.                        XDownScale(target_color.green),
  3668.                        XDownScale(target_color.blue));
  3669.             s = sv_2mortal(newSVpv(b, 0));
  3670.         }
  3671.         PUSHs(s);
  3672.         }
  3673.  
  3674.         SvREFCNT_dec(im_er_mes);    /* throw away all errors */
  3675.         im_er_mes = NULL;
  3676.     }
  3677.