display.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
00007 %               D   D    I    SS     P   P  L      A   A   Y Y                %
00008 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
00009 %               D   D    I       SS  P      L      A   A    Y                 %
00010 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
00011 %                                                                             %
00012 %                                                                             %
00013 %        MagickCore Methods to Interactively Display and Edit an Image        %
00014 %                                                                             %
00015 %                             Software Design                                 %
00016 %                               John Cristy                                   %
00017 %                                July 1992                                    %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %
00037 */
00038 
00039 /*
00040   Include declarations.
00041 */
00042 #include "magick/studio.h"
00043 #include "magick/artifact.h"
00044 #include "magick/blob.h"
00045 #include "magick/cache.h"
00046 #include "magick/client.h"
00047 #include "magick/color.h"
00048 #include "magick/colorspace.h"
00049 #include "magick/composite.h"
00050 #include "magick/constitute.h"
00051 #include "magick/decorate.h"
00052 #include "magick/delegate.h"
00053 #include "magick/display.h"
00054 #include "magick/display-private.h"
00055 #include "magick/draw.h"
00056 #include "magick/effect.h"
00057 #include "magick/enhance.h"
00058 #include "magick/exception.h"
00059 #include "magick/exception-private.h"
00060 #include "magick/fx.h"
00061 #include "magick/geometry.h"
00062 #include "magick/image.h"
00063 #include "magick/image-private.h"
00064 #include "magick/list.h"
00065 #include "magick/log.h"
00066 #include "magick/magick.h"
00067 #include "magick/memory_.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/montage.h"
00071 #include "magick/option.h"
00072 #include "magick/paint.h"
00073 #include "magick/pixel.h"
00074 #include "magick/pixel-private.h"
00075 #include "magick/PreRvIcccm.h"
00076 #include "magick/property.h"
00077 #include "magick/quantum.h"
00078 #include "magick/resize.h"
00079 #include "magick/resource_.h"
00080 #include "magick/shear.h"
00081 #include "magick/segment.h"
00082 #include "magick/string_.h"
00083 #include "magick/string-private.h"
00084 #include "magick/transform.h"
00085 #include "magick/threshold.h"
00086 #include "magick/utility.h"
00087 #include "magick/version.h"
00088 #include "magick/widget.h"
00089 #include "magick/xwindow-private.h"
00090 
00091 #if defined(MAGICKCORE_X11_DELEGATE)
00092 /*
00093   Define declarations.
00094 */
00095 #define MaxColors  MagickMin(windows->visual_info->colormap_size,256L)
00096 
00097 /*
00098   Constant declarations.
00099 */
00100 static const unsigned char
00101   HighlightBitmap[8] =
00102   {
00103     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
00104   },
00105   ShadowBitmap[8] =
00106   {
00107     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
00108   };
00109 
00110 static const char
00111   *PageSizes[] =
00112   {
00113     "Letter",
00114     "Tabloid",
00115     "Ledger",
00116     "Legal",
00117     "Statement",
00118     "Executive",
00119     "A3",
00120     "A4",
00121     "A5",
00122     "B4",
00123     "B5",
00124     "Folio",
00125     "Quarto",
00126     "10x14",
00127     (char *) NULL
00128   };
00129 
00130 /*
00131   Help widget declarations.
00132 */
00133 static const char
00134   *ImageAnnotateHelp[] =
00135   {
00136     "In annotate mode, the Command widget has these options:",
00137     "",
00138     "    Font Name",
00139     "      fixed",
00140     "      variable",
00141     "      5x8",
00142     "      6x10",
00143     "      7x13bold",
00144     "      8x13bold",
00145     "      9x15bold",
00146     "      10x20",
00147     "      12x24",
00148     "      Browser...",
00149     "    Font Color",
00150     "      black",
00151     "      blue",
00152     "      cyan",
00153     "      green",
00154     "      gray",
00155     "      red",
00156     "      magenta",
00157     "      yellow",
00158     "      white",
00159     "      transparent",
00160     "      Browser...",
00161     "    Font Color",
00162     "      black",
00163     "      blue",
00164     "      cyan",
00165     "      green",
00166     "      gray",
00167     "      red",
00168     "      magenta",
00169     "      yellow",
00170     "      white",
00171     "      transparent",
00172     "      Browser...",
00173     "    Rotate Text",
00174     "      -90",
00175     "      -45",
00176     "      -30",
00177     "      0",
00178     "      30",
00179     "      45",
00180     "      90",
00181     "      180",
00182     "      Dialog...",
00183     "    Help",
00184     "    Dismiss",
00185     "",
00186     "Choose a font name from the Font Name sub-menu.  Additional",
00187     "font names can be specified with the font browser.  You can",
00188     "change the menu names by setting the X resources font1",
00189     "through font9.",
00190     "",
00191     "Choose a font color from the Font Color sub-menu.",
00192     "Additional font colors can be specified with the color",
00193     "browser.  You can change the menu colors by setting the X",
00194     "resources pen1 through pen9.",
00195     "",
00196     "If you select the color browser and press Grab, you can",
00197     "choose the font color by moving the pointer to the desired",
00198     "color on the screen and press any button.",
00199     "",
00200     "If you choose to rotate the text, choose Rotate Text from the",
00201     "menu and select an angle.  Typically you will only want to",
00202     "rotate one line of text at a time.  Depending on the angle you",
00203     "choose, subsequent lines may end up overwriting each other.",
00204     "",
00205     "Choosing a font and its color is optional.  The default font",
00206     "is fixed and the default color is black.  However, you must",
00207     "choose a location to begin entering text and press button 1.",
00208     "An underscore character will appear at the location of the",
00209     "pointer.  The cursor changes to a pencil to indicate you are",
00210     "in text mode.  To exit immediately, press Dismiss.",
00211     "",
00212     "In text mode, any key presses will display the character at",
00213     "the location of the underscore and advance the underscore",
00214     "cursor.  Enter your text and once completed press Apply to",
00215     "finish your image annotation.  To correct errors press BACK",
00216     "SPACE.  To delete an entire line of text, press DELETE.  Any",
00217     "text that exceeds the boundaries of the image window is",
00218     "automagically continued onto the next line.",
00219     "",
00220     "The actual color you request for the font is saved in the",
00221     "image.  However, the color that appears in your image window",
00222     "may be different.  For example, on a monochrome screen the",
00223     "text will appear black or white even if you choose the color",
00224     "red as the font color.  However, the image saved to a file",
00225     "with -write is written with red lettering.  To assure the",
00226     "correct color text in the final image, any PseudoClass image",
00227     "is promoted to DirectClass (see miff(5)).  To force a",
00228     "PseudoClass image to remain PseudoClass, use -colors.",
00229     (char *) NULL,
00230   },
00231   *ImageChopHelp[] =
00232   {
00233     "In chop mode, the Command widget has these options:",
00234     "",
00235     "    Direction",
00236     "      horizontal",
00237     "      vertical",
00238     "    Help",
00239     "    Dismiss",
00240     "",
00241     "If the you choose the horizontal direction (this the",
00242     "default), the area of the image between the two horizontal",
00243     "endpoints of the chop line is removed.  Otherwise, the area",
00244     "of the image between the two vertical endpoints of the chop",
00245     "line is removed.",
00246     "",
00247     "Select a location within the image window to begin your chop,",
00248     "press and hold any button.  Next, move the pointer to",
00249     "another location in the image.  As you move a line will",
00250     "connect the initial location and the pointer.  When you",
00251     "release the button, the area within the image to chop is",
00252     "determined by which direction you choose from the Command",
00253     "widget.",
00254     "",
00255     "To cancel the image chopping, move the pointer back to the",
00256     "starting point of the line and release the button.",
00257     (char *) NULL,
00258   },
00259   *ImageColorEditHelp[] =
00260   {
00261     "In color edit mode, the Command widget has these options:",
00262     "",
00263     "    Method",
00264     "      point",
00265     "      replace",
00266     "      floodfill",
00267     "      filltoborder",
00268     "      reset",
00269     "    Pixel Color",
00270     "      black",
00271     "      blue",
00272     "      cyan",
00273     "      green",
00274     "      gray",
00275     "      red",
00276     "      magenta",
00277     "      yellow",
00278     "      white",
00279     "      Browser...",
00280     "    Border Color",
00281     "      black",
00282     "      blue",
00283     "      cyan",
00284     "      green",
00285     "      gray",
00286     "      red",
00287     "      magenta",
00288     "      yellow",
00289     "      white",
00290     "      Browser...",
00291     "    Fuzz",
00292     "      0%",
00293     "      2%",
00294     "      5%",
00295     "      10%",
00296     "      15%",
00297     "      Dialog...",
00298     "    Undo",
00299     "    Help",
00300     "    Dismiss",
00301     "",
00302     "Choose a color editing method from the Method sub-menu",
00303     "of the Command widget.  The point method recolors any pixel",
00304     "selected with the pointer until the button is released.  The",
00305     "replace method recolors any pixel that matches the color of",
00306     "the pixel you select with a button press.  Floodfill recolors",
00307     "any pixel that matches the color of the pixel you select with",
00308     "a button press and is a neighbor.  Whereas filltoborder recolors",
00309     "any neighbor pixel that is not the border color.  Finally reset",
00310     "changes the entire image to the designated color.",
00311     "",
00312     "Next, choose a pixel color from the Pixel Color sub-menu.",
00313     "Additional pixel colors can be specified with the color",
00314     "browser.  You can change the menu colors by setting the X",
00315     "resources pen1 through pen9.",
00316     "",
00317     "Now press button 1 to select a pixel within the image window",
00318     "to change its color.  Additional pixels may be recolored as",
00319     "prescribed by the method you choose.",
00320     "",
00321     "If the Magnify widget is mapped, it can be helpful in positioning",
00322     "your pointer within the image (refer to button 2).",
00323     "",
00324     "The actual color you request for the pixels is saved in the",
00325     "image.  However, the color that appears in your image window",
00326     "may be different.  For example, on a monochrome screen the",
00327     "pixel will appear black or white even if you choose the",
00328     "color red as the pixel color.  However, the image saved to a",
00329     "file with -write is written with red pixels.  To assure the",
00330     "correct color text in the final image, any PseudoClass image",
00331     "is promoted to DirectClass (see miff(5)).  To force a",
00332     "PseudoClass image to remain PseudoClass, use -colors.",
00333     (char *) NULL,
00334   },
00335   *ImageCompositeHelp[] =
00336   {
00337     "First a widget window is displayed requesting you to enter an",
00338     "image name. Press Composite, Grab or type a file name.",
00339     "Press Cancel if you choose not to create a composite image.",
00340     "When you choose Grab, move the pointer to the desired window",
00341     "and press any button.",
00342     "",
00343     "If the Composite image does not have any matte information,",
00344     "you are informed and the file browser is displayed again.",
00345     "Enter the name of a mask image.  The image is typically",
00346     "grayscale and the same size as the composite image.  If the",
00347     "image is not grayscale, it is converted to grayscale and the",
00348     "resulting intensities are used as matte information.",
00349     "",
00350     "A small window appears showing the location of the cursor in",
00351     "the image window. You are now in composite mode.  To exit",
00352     "immediately, press Dismiss.  In composite mode, the Command",
00353     "widget has these options:",
00354     "",
00355     "    Operators",
00356     "      Over",
00357     "      In",
00358     "      Out",
00359     "      Atop",
00360     "      Xor",
00361     "      Plus",
00362     "      Minus",
00363     "      Add",
00364     "      Subtract",
00365     "      Difference",
00366     "      Multiply",
00367     "      Bumpmap",
00368     "      Copy",
00369     "      CopyRed",
00370     "      CopyGreen",
00371     "      CopyBlue",
00372     "      CopyOpacity",
00373     "      Clear",
00374     "    Dissolve",
00375     "    Displace",
00376     "    Help",
00377     "    Dismiss",
00378     "",
00379     "Choose a composite operation from the Operators sub-menu of",
00380     "the Command widget.  How each operator behaves is described",
00381     "below.  Image window is the image currently displayed on",
00382     "your X server and image is the image obtained with the File",
00383     "Browser widget.",
00384     "",
00385     "Over     The result is the union of the two image shapes,",
00386     "         with image obscuring image window in the region of",
00387     "         overlap.",
00388     "",
00389     "In       The result is simply image cut by the shape of",
00390     "         image window.  None of the image data of image",
00391     "         window is in the result.",
00392     "",
00393     "Out      The resulting image is image with the shape of",
00394     "         image window cut out.",
00395     "",
00396     "Atop     The result is the same shape as image image window,",
00397     "         with image obscuring image window where the image",
00398     "         shapes overlap.  Note this differs from over",
00399     "         because the portion of image outside image window's",
00400     "         shape does not appear in the result.",
00401     "",
00402     "Xor      The result is the image data from both image and",
00403     "         image window that is outside the overlap region.",
00404     "         The overlap region is blank.",
00405     "",
00406     "Plus     The result is just the sum of the image data.",
00407     "         Output values are cropped to QuantumRange (no overflow).",
00408     "",
00409     "Minus    The result of image - image window, with underflow",
00410     "         cropped to zero.",
00411     "",
00412     "Add      The result of image + image window, with overflow",
00413     "         wrapping around (mod 256).",
00414     "",
00415     "Subtract The result of image - image window, with underflow",
00416     "         wrapping around (mod 256).  The add and subtract",
00417     "         operators can be used to perform reversible",
00418     "         transformations.",
00419     "",
00420     "Difference",
00421     "         The result of abs(image - image window).  This",
00422     "         useful for comparing two very similar images.",
00423     "",
00424     "Multiply",
00425     "         The result of image * image window.  This",
00426     "         useful for the creation of drop-shadows.",
00427     "",
00428     "Bumpmap  The result of surface normals from image * image",
00429     "         window.",
00430     "",
00431     "Copy     The resulting image is image window replaced with",
00432     "         image.  Here the matte information is ignored.",
00433     "",
00434     "CopyRed  The red layer of the image window is replace with",
00435     "         the red layer of the image.  The other layers are",
00436     "         untouched.",
00437     "",
00438     "CopyGreen",
00439     "         The green layer of the image window is replace with",
00440     "         the green layer of the image.  The other layers are",
00441     "         untouched.",
00442     "",
00443     "CopyBlue The blue layer of the image window is replace with",
00444     "         the blue layer of the image.  The other layers are",
00445     "         untouched.",
00446     "",
00447     "CopyOpacity",
00448     "         The matte layer of the image window is replace with",
00449     "         the matte layer of the image.  The other layers are",
00450     "         untouched.",
00451     "",
00452     "The image compositor requires a matte, or alpha channel in",
00453     "the image for some operations.  This extra channel usually",
00454     "defines a mask which represents a sort of a cookie-cutter",
00455     "for the image.  This the case when matte is opaque (full",
00456     "coverage) for pixels inside the shape, zero outside, and",
00457     "between 0 and QuantumRange on the boundary.  If image does not",
00458     "have a matte channel, it is initialized with 0 for any pixel",
00459     "matching in color to pixel location (0,0), otherwise QuantumRange.",
00460     "",
00461     "If you choose Dissolve, the composite operator becomes Over.  The",
00462     "image matte channel percent transparency is initialized to factor.",
00463     "The image window is initialized to (100-factor). Where factor is the",
00464     "value you specify in the Dialog widget.",
00465     "",
00466     "Displace shifts the image pixels as defined by a displacement",
00467     "map.  With this option, image is used as a displacement map.",
00468     "Black, within the displacement map, is a maximum positive",
00469     "displacement.  White is a maximum negative displacement and",
00470     "middle gray is neutral.  The displacement is scaled to determine",
00471     "the pixel shift.  By default, the displacement applies in both the",
00472     "horizontal and vertical directions.  However, if you specify a mask,",
00473     "image is the horizontal X displacement and mask the vertical Y",
00474     "displacement.",
00475     "",
00476     "Note that matte information for image window is not retained",
00477     "for colormapped X server visuals (e.g. StaticColor,",
00478     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
00479     "behavior may require a TrueColor or DirectColor visual or a",
00480     "Standard Colormap.",
00481     "",
00482     "Choosing a composite operator is optional.  The default",
00483     "operator is replace.  However, you must choose a location to",
00484     "composite your image and press button 1.  Press and hold the",
00485     "button before releasing and an outline of the image will",
00486     "appear to help you identify your location.",
00487     "",
00488     "The actual colors of the composite image is saved.  However,",
00489     "the color that appears in image window may be different.",
00490     "For example, on a monochrome screen image window will appear",
00491     "black or white even though your composited image may have",
00492     "many colors.  If the image is saved to a file it is written",
00493     "with the correct colors.  To assure the correct colors are",
00494     "saved in the final image, any PseudoClass image is promoted",
00495     "to DirectClass (see miff(5)).  To force a PseudoClass image",
00496     "to remain PseudoClass, use -colors.",
00497     (char *) NULL,
00498   },
00499   *ImageCutHelp[] =
00500   {
00501     "In cut mode, the Command widget has these options:",
00502     "",
00503     "    Help",
00504     "    Dismiss",
00505     "",
00506     "To define a cut region, press button 1 and drag.  The",
00507     "cut region is defined by a highlighted rectangle that",
00508     "expands or contracts as it follows the pointer.  Once you",
00509     "are satisfied with the cut region, release the button.",
00510     "You are now in rectify mode.  In rectify mode, the Command",
00511     "widget has these options:",
00512     "",
00513     "    Cut",
00514     "    Help",
00515     "    Dismiss",
00516     "",
00517     "You can make adjustments by moving the pointer to one of the",
00518     "cut rectangle corners, pressing a button, and dragging.",
00519     "Finally, press Cut to commit your copy region.  To",
00520     "exit without cutting the image, press Dismiss.",
00521     (char *) NULL,
00522   },
00523   *ImageCopyHelp[] =
00524   {
00525     "In copy mode, the Command widget has these options:",
00526     "",
00527     "    Help",
00528     "    Dismiss",
00529     "",
00530     "To define a copy region, press button 1 and drag.  The",
00531     "copy region is defined by a highlighted rectangle that",
00532     "expands or contracts as it follows the pointer.  Once you",
00533     "are satisfied with the copy region, release the button.",
00534     "You are now in rectify mode.  In rectify mode, the Command",
00535     "widget has these options:",
00536     "",
00537     "    Copy",
00538     "    Help",
00539     "    Dismiss",
00540     "",
00541     "You can make adjustments by moving the pointer to one of the",
00542     "copy rectangle corners, pressing a button, and dragging.",
00543     "Finally, press Copy to commit your copy region.  To",
00544     "exit without copying the image, press Dismiss.",
00545     (char *) NULL,
00546   },
00547   *ImageCropHelp[] =
00548   {
00549     "In crop mode, the Command widget has these options:",
00550     "",
00551     "    Help",
00552     "    Dismiss",
00553     "",
00554     "To define a cropping region, press button 1 and drag.  The",
00555     "cropping region is defined by a highlighted rectangle that",
00556     "expands or contracts as it follows the pointer.  Once you",
00557     "are satisfied with the cropping region, release the button.",
00558     "You are now in rectify mode.  In rectify mode, the Command",
00559     "widget has these options:",
00560     "",
00561     "    Crop",
00562     "    Help",
00563     "    Dismiss",
00564     "",
00565     "You can make adjustments by moving the pointer to one of the",
00566     "cropping rectangle corners, pressing a button, and dragging.",
00567     "Finally, press Crop to commit your cropping region.  To",
00568     "exit without cropping the image, press Dismiss.",
00569     (char *) NULL,
00570   },
00571   *ImageDrawHelp[] =
00572   {
00573     "The cursor changes to a crosshair to indicate you are in",
00574     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
00575     "the Command widget has these options:",
00576     "",
00577     "    Element",
00578     "      point",
00579     "      line",
00580     "      rectangle",
00581     "      fill rectangle",
00582     "      circle",
00583     "      fill circle",
00584     "      ellipse",
00585     "      fill ellipse",
00586     "      polygon",
00587     "      fill polygon",
00588     "    Color",
00589     "      black",
00590     "      blue",
00591     "      cyan",
00592     "      green",
00593     "      gray",
00594     "      red",
00595     "      magenta",
00596     "      yellow",
00597     "      white",
00598     "      transparent",
00599     "      Browser...",
00600     "    Stipple",
00601     "      Brick",
00602     "      Diagonal",
00603     "      Scales",
00604     "      Vertical",
00605     "      Wavy",
00606     "      Translucent",
00607     "      Opaque",
00608     "      Open...",
00609     "    Width",
00610     "      1",
00611     "      2",
00612     "      4",
00613     "      8",
00614     "      16",
00615     "      Dialog...",
00616     "    Undo",
00617     "    Help",
00618     "    Dismiss",
00619     "",
00620     "Choose a drawing primitive from the Element sub-menu.",
00621     "",
00622     "Choose a color from the Color sub-menu.  Additional",
00623     "colors can be specified with the color browser.",
00624     "",
00625     "If you choose the color browser and press Grab, you can",
00626     "select the color by moving the pointer to the desired",
00627     "color on the screen and press any button.  The transparent",
00628     "color updates the image matte channel and is useful for",
00629     "image compositing.",
00630     "",
00631     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
00632     "Additional stipples can be specified with the file browser.",
00633     "Stipples obtained from the file browser must be on disk in the",
00634     "X11 bitmap format.",
00635     "",
00636     "Choose a width, if appropriate, from the Width sub-menu.  To",
00637     "choose a specific width select the Dialog widget.",
00638     "",
00639     "Choose a point in the Image window and press button 1 and",
00640     "hold.  Next, move the pointer to another location in the",
00641     "image.  As you move, a line connects the initial location and",
00642     "the pointer.  When you release the button, the image is",
00643     "updated with the primitive you just drew.  For polygons, the",
00644     "image is updated when you press and release the button without",
00645     "moving the pointer.",
00646     "",
00647     "To cancel image drawing, move the pointer back to the",
00648     "starting point of the line and release the button.",
00649     (char *) NULL,
00650   },
00651   *DisplayHelp[] =
00652   {
00653     "BUTTONS",
00654     "  The effects of each button press is described below.  Three",
00655     "  buttons are required.  If you have a two button mouse,",
00656     "  button 1 and 3 are returned.  Press ALT and button 3 to",
00657     "  simulate button 2.",
00658     "",
00659     "  1    Press this button to map or unmap the Command widget.",
00660     "",
00661     "  2    Press and drag to define a region of the image to",
00662     "       magnify.",
00663     "",
00664     "  3    Press and drag to choose from a select set of commands.",
00665     "       This button behaves differently if the image being",
00666     "       displayed is a visual image directory.  Here, choose a",
00667     "       particular tile of the directory and press this button and",
00668     "       drag to select a command from a pop-up menu.  Choose from",
00669     "       these menu items:",
00670     "",
00671     "           Open",
00672     "           Next",
00673     "           Former",
00674     "           Delete",
00675     "           Update",
00676     "",
00677     "       If you choose Open, the image represented by the tile is",
00678     "       displayed.  To return to the visual image directory, choose",
00679     "       Next from the Command widget.  Next and Former moves to the",
00680     "       next or former image respectively.  Choose Delete to delete",
00681     "       a particular image tile.  Finally, choose Update to",
00682     "       synchronize all the image tiles with their respective",
00683     "       images.",
00684     "",
00685     "COMMAND WIDGET",
00686     "  The Command widget lists a number of sub-menus and commands.",
00687     "  They are",
00688     "",
00689     "      File",
00690     "        Open...",
00691     "        Next",
00692     "        Former",
00693     "        Select...",
00694     "        Save...",
00695     "        Print...",
00696     "        Delete...",
00697     "        New...",
00698     "        Visual Directory...",
00699     "        Quit",
00700     "      Edit",
00701     "        Undo",
00702     "        Redo",
00703     "        Cut",
00704     "        Copy",
00705     "        Paste",
00706     "      View",
00707     "        Half Size",
00708     "        Original Size",
00709     "        Double Size",
00710     "        Resize...",
00711     "        Apply",
00712     "        Refresh",
00713     "        Restore",
00714     "      Transform",
00715     "        Crop",
00716     "        Chop",
00717     "        Flop",
00718     "        Flip",
00719     "        Rotate Right",
00720     "        Rotate Left",
00721     "        Rotate...",
00722     "        Shear...",
00723     "        Roll...",
00724     "        Trim Edges",
00725     "      Enhance",
00726     "        Brightness...",
00727     "        Saturation...",
00728     "        Hue...",
00729     "        Gamma...",
00730     "        Sharpen...",
00731     "        Dull",
00732     "        Contrast Stretch...",
00733     "        Sigmoidal Contrast...",
00734     "        Normalize",
00735     "        Equalize",
00736     "        Negate",
00737     "        Grayscale",
00738     "        Map...",
00739     "        Quantize...",
00740     "      Effects",
00741     "        Despeckle",
00742     "        Emboss",
00743     "        Reduce Noise",
00744     "        Add Noise",
00745     "        Sharpen...",
00746     "        Blur...",
00747     "        Threshold...",
00748     "        Edge Detect...",
00749     "        Spread...",
00750     "        Shade...",
00751     "        Painting...",
00752     "        Segment...",
00753     "      F/X",
00754     "        Solarize...",
00755     "        Sepia Tone...",
00756     "        Swirl...",
00757     "        Implode...",
00758     "        Vignette...",
00759     "        Wave...",
00760     "        Oil Painting...",
00761     "        Charcoal Drawing...",
00762     "      Image Edit",
00763     "        Annotate...",
00764     "        Draw...",
00765     "        Color...",
00766     "        Matte...",
00767     "        Composite...",
00768     "        Add Border...",
00769     "        Add Frame...",
00770     "        Comment...",
00771     "        Launch...",
00772     "        Region of Interest...",
00773     "      Miscellany",
00774     "        Image Info",
00775     "        Zoom Image",
00776     "        Show Preview...",
00777     "        Show Histogram",
00778     "        Show Matte",
00779     "        Background...",
00780     "        Slide Show",
00781     "        Preferences...",
00782     "      Help",
00783     "        Overview",
00784     "        Browse Documentation",
00785     "        About Display",
00786     "",
00787     "  Menu items with a indented triangle have a sub-menu.  They",
00788     "  are represented above as the indented items.  To access a",
00789     "  sub-menu item, move the pointer to the appropriate menu and",
00790     "  press a button and drag.  When you find the desired sub-menu",
00791     "  item, release the button and the command is executed.  Move",
00792     "  the pointer away from the sub-menu if you decide not to",
00793     "  execute a particular command.",
00794     "",
00795     "KEYBOARD ACCELERATORS",
00796     "  Accelerators are one or two key presses that effect a",
00797     "  particular command.  The keyboard accelerators that",
00798     "  display(1) understands is:",
00799     "",
00800     "  Ctl+O     Press to open an image from a file.",
00801     "",
00802     "  space     Press to display the next image.",
00803     "",
00804     "            If the image is a multi-paged document such as a Postscript",
00805     "            document, you can skip ahead several pages by preceding",
00806     "            this command with a number.  For example to display the",
00807     "            third page beyond the current page, press 3<space>.",
00808     "",
00809     "  backspace Press to display the former image.",
00810     "",
00811     "            If the image is a multi-paged document such as a Postscript",
00812     "            document, you can skip behind several pages by preceding",
00813     "            this command with a number.  For example to display the",
00814     "            third page preceding the current page, press 3<backspace>.",
00815     "",
00816     "  Ctl+S     Press to write the image to a file.",
00817     "",
00818     "  Ctl+P     Press to print the image to a Postscript printer.",
00819     "",
00820     "  Ctl+D     Press to delete an image file.",
00821     "",
00822     "  Ctl+N     Press to create a blank canvas.",
00823     "",
00824     "  Ctl+Q     Press to discard all images and exit program.",
00825     "",
00826     "  Ctl+Z     Press to undo last image transformation.",
00827     "",
00828     "  Ctl+R     Press to redo last image transformation.",
00829     "",
00830     "  Ctl+X     Press to cut a region of the image.",
00831     "",
00832     "  Ctl+C     Press to copy a region of the image.",
00833     "",
00834     "  Ctl+V     Press to paste a region to the image.",
00835     "",
00836     "  <         Press to half the image size.",
00837     "",
00838     "  -         Press to return to the original image size.",
00839     "",
00840     "  >         Press to double the image size.",
00841     "",
00842     "  %         Press to resize the image to a width and height you",
00843     "            specify.",
00844     "",
00845     "Cmd-A       Press to make any image transformations permanent."
00846     "",
00847     "            By default, any image size transformations are applied",
00848     "            to the original image to create the image displayed on",
00849     "            the X server.  However, the transformations are not",
00850     "            permanent (i.e. the original image does not change",
00851     "            size only the X image does).  For example, if you",
00852     "            press > the X image will appear to double in size,",
00853     "            but the original image will in fact remain the same size.",
00854     "            To force the original image to double in size, press >",
00855     "            followed by Cmd-A.",
00856     "",
00857     "  @         Press to refresh the image window.",
00858     "",
00859     "  C         Press to cut out a rectangular region of the image.",
00860     "",
00861     "  [         Press to chop the image.",
00862     "",
00863     "  H         Press to flop image in the horizontal direction.",
00864     "",
00865     "  V         Press to flip image in the vertical direction.",
00866     "",
00867     "  /         Press to rotate the image 90 degrees clockwise.",
00868     "",
00869     " \\         Press to rotate the image 90 degrees counter-clockwise.",
00870     "",
00871     "  *         Press to rotate the image the number of degrees you",
00872     "            specify.",
00873     "",
00874     "  S         Press to shear the image the number of degrees you",
00875     "            specify.",
00876     "",
00877     "  R         Press to roll the image.",
00878     "",
00879     "  T         Press to trim the image edges.",
00880     "",
00881     "  Shft-H    Press to vary the image hue.",
00882     "",
00883     "  Shft-S    Press to vary the color saturation.",
00884     "",
00885     "  Shft-L    Press to vary the color brightness.",
00886     "",
00887     "  Shft-G    Press to gamma correct the image.",
00888     "",
00889     "  Shft-C    Press to sharpen the image contrast.",
00890     "",
00891     "  Shft-Z    Press to dull the image contrast.",
00892     "",
00893     "  =         Press to perform histogram equalization on the image.",
00894     "",
00895     "  Shft-N    Press to perform histogram normalization on the image.",
00896     "",
00897     "  Shft-~    Press to negate the colors of the image.",
00898     "",
00899     "  .         Press to convert the image colors to gray.",
00900     "",
00901     "  Shft-#    Press to set the maximum number of unique colors in the",
00902     "            image.",
00903     "",
00904     "  F2        Press to reduce the speckles in an image.",
00905     "",
00906     "  F3        Press to eliminate peak noise from an image.",
00907     "",
00908     "  F4        Press to add noise to an image.",
00909     "",
00910     "  F5        Press to sharpen an image.",
00911     "",
00912     "  F6        Press to delete an image file.",
00913     "",
00914     "  F7        Press to threshold the image.",
00915     "",
00916     "  F8        Press to detect edges within an image.",
00917     "",
00918     "  F9        Press to emboss an image.",
00919     "",
00920     "  F10       Press to displace pixels by a random amount.",
00921     "",
00922     "  F11       Press to negate all pixels above the threshold level.",
00923     "",
00924     "  F12       Press to shade the image using a distant light source.",
00925     "",
00926     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
00927     "",
00928     "  F14       Press to segment the image by color.",
00929     "",
00930     "  Meta-S    Press to swirl image pixels about the center.",
00931     "",
00932     "  Meta-I    Press to implode image pixels about the center.",
00933     "",
00934     "  Meta-W    Press to alter an image along a sine wave.",
00935     "",
00936     "  Meta-P    Press to simulate an oil painting.",
00937     "",
00938     "  Meta-C    Press to simulate a charcoal drawing.",
00939     "",
00940     "  Alt-A     Press to annotate the image with text.",
00941     "",
00942     "  Alt-D     Press to draw on an image.",
00943     "",
00944     "  Alt-P     Press to edit an image pixel color.",
00945     "",
00946     "  Alt-M     Press to edit the image matte information.",
00947     "",
00948     "  Alt-V     Press to composite the image with another.",
00949     "",
00950     "  Alt-B     Press to add a border to the image.",
00951     "",
00952     "  Alt-F     Press to add an ornamental border to the image.",
00953     "",
00954     "  Alt-Shft-!",
00955     "            Press to add an image comment.",
00956     "",
00957     "  Ctl-A     Press to apply image processing techniques to a region",
00958     "            of interest.",
00959     "",
00960     "  Shft-?    Press to display information about the image.",
00961     "",
00962     "  Shft-+    Press to map the zoom image window.",
00963     "",
00964     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
00965     "",
00966     "  F1        Press to display helpful information about display(1).",
00967     "",
00968     "  Find      Press to browse documentation about ImageMagick.",
00969     "",
00970     "  1-9       Press to change the level of magnification.",
00971     "",
00972     "  Use the arrow keys to move the image one pixel up, down,",
00973     "  left, or right within the magnify window.  Be sure to first",
00974     "  map the magnify window by pressing button 2.",
00975     "",
00976     "  Press ALT and one of the arrow keys to trim off one pixel",
00977     "  from any side of the image.",
00978     (char *) NULL,
00979   },
00980   *ImageMatteEditHelp[] =
00981   {
00982     "Matte information within an image is useful for some",
00983     "operations such as image compositing (See IMAGE",
00984     "COMPOSITING).  This extra channel usually defines a mask",
00985     "which represents a sort of a cookie-cutter for the image.",
00986     "This the case when matte is opaque (full coverage) for",
00987     "pixels inside the shape, zero outside, and between 0 and",
00988     "QuantumRange on the boundary.",
00989     "",
00990     "A small window appears showing the location of the cursor in",
00991     "the image window. You are now in matte edit mode.  To exit",
00992     "immediately, press Dismiss.  In matte edit mode, the Command",
00993     "widget has these options:",
00994     "",
00995     "    Method",
00996     "      point",
00997     "      replace",
00998     "      floodfill",
00999     "      filltoborder",
01000     "      reset",
01001     "    Border Color",
01002     "      black",
01003     "      blue",
01004     "      cyan",
01005     "      green",
01006     "      gray",
01007     "      red",
01008     "      magenta",
01009     "      yellow",
01010     "      white",
01011     "      Browser...",
01012     "    Fuzz",
01013     "      0%",
01014     "      2%",
01015     "      5%",
01016     "      10%",
01017     "      15%",
01018     "      Dialog...",
01019     "    Matte",
01020     "      Opaque",
01021     "      Transparent",
01022     "      Dialog...",
01023     "    Undo",
01024     "    Help",
01025     "    Dismiss",
01026     "",
01027     "Choose a matte editing method from the Method sub-menu of",
01028     "the Command widget.  The point method changes the matte value",
01029     "of any pixel selected with the pointer until the button is",
01030     "is released.  The replace method changes the matte value of",
01031     "any pixel that matches the color of the pixel you select with",
01032     "a button press.  Floodfill changes the matte value of any pixel",
01033     "that matches the color of the pixel you select with a button",
01034     "press and is a neighbor.  Whereas filltoborder changes the matte",
01035     "value any neighbor pixel that is not the border color.  Finally",
01036     "reset changes the entire image to the designated matte value.",
01037     "",
01038     "Choose Matte Value and pick Opaque or Transarent.  For other values",
01039     "select the Dialog entry.  Here a dialog appears requesting a matte",
01040     "value.  The value you select is assigned as the opacity value of the",
01041     "selected pixel or pixels.",
01042     "",
01043     "Now, press any button to select a pixel within the image",
01044     "window to change its matte value.",
01045     "",
01046     "If the Magnify widget is mapped, it can be helpful in positioning",
01047     "your pointer within the image (refer to button 2).",
01048     "",
01049     "Matte information is only valid in a DirectClass image.",
01050     "Therefore, any PseudoClass image is promoted to DirectClass",
01051     "(see miff(5)).  Note that matte information for PseudoClass",
01052     "is not retained for colormapped X server visuals (e.g.",
01053     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
01054     "immediately save your image to a file (refer to Write).",
01055     "Correct matte editing behavior may require a TrueColor or",
01056     "DirectColor visual or a Standard Colormap.",
01057     (char *) NULL,
01058   },
01059   *ImagePanHelp[] =
01060   {
01061     "When an image exceeds the width or height of the X server",
01062     "screen, display maps a small panning icon.  The rectangle",
01063     "within the panning icon shows the area that is currently",
01064     "displayed in the image window.  To pan about the image,",
01065     "press any button and drag the pointer within the panning",
01066     "icon.  The pan rectangle moves with the pointer and the",
01067     "image window is updated to reflect the location of the",
01068     "rectangle within the panning icon.  When you have selected",
01069     "the area of the image you wish to view, release the button.",
01070     "",
01071     "Use the arrow keys to pan the image one pixel up, down,",
01072     "left, or right within the image window.",
01073     "",
01074     "The panning icon is withdrawn if the image becomes smaller",
01075     "than the dimensions of the X server screen.",
01076     (char *) NULL,
01077   },
01078   *ImagePasteHelp[] =
01079   {
01080     "A small window appears showing the location of the cursor in",
01081     "the image window. You are now in paste mode.  To exit",
01082     "immediately, press Dismiss.  In paste mode, the Command",
01083     "widget has these options:",
01084     "",
01085     "    Operators",
01086     "      over",
01087     "      in",
01088     "      out",
01089     "      atop",
01090     "      xor",
01091     "      plus",
01092     "      minus",
01093     "      add",
01094     "      subtract",
01095     "      difference",
01096     "      replace",
01097     "    Help",
01098     "    Dismiss",
01099     "",
01100     "Choose a composite operation from the Operators sub-menu of",
01101     "the Command widget.  How each operator behaves is described",
01102     "below.  Image window is the image currently displayed on",
01103     "your X server and image is the image obtained with the File",
01104     "Browser widget.",
01105     "",
01106     "Over     The result is the union of the two image shapes,",
01107     "         with image obscuring image window in the region of",
01108     "         overlap.",
01109     "",
01110     "In       The result is simply image cut by the shape of",
01111     "         image window.  None of the image data of image",
01112     "         window is in the result.",
01113     "",
01114     "Out      The resulting image is image with the shape of",
01115     "         image window cut out.",
01116     "",
01117     "Atop     The result is the same shape as image image window,",
01118     "         with image obscuring image window where the image",
01119     "         shapes overlap.  Note this differs from over",
01120     "         because the portion of image outside image window's",
01121     "         shape does not appear in the result.",
01122     "",
01123     "Xor      The result is the image data from both image and",
01124     "         image window that is outside the overlap region.",
01125     "         The overlap region is blank.",
01126     "",
01127     "Plus     The result is just the sum of the image data.",
01128     "         Output values are cropped to QuantumRange (no overflow).",
01129     "         This operation is independent of the matte",
01130     "         channels.",
01131     "",
01132     "Minus    The result of image - image window, with underflow",
01133     "         cropped to zero.",
01134     "",
01135     "Add      The result of image + image window, with overflow",
01136     "         wrapping around (mod 256).",
01137     "",
01138     "Subtract The result of image - image window, with underflow",
01139     "         wrapping around (mod 256).  The add and subtract",
01140     "         operators can be used to perform reversible",
01141     "         transformations.",
01142     "",
01143     "Difference",
01144     "         The result of abs(image - image window).  This",
01145     "         useful for comparing two very similar images.",
01146     "",
01147     "Copy     The resulting image is image window replaced with",
01148     "         image.  Here the matte information is ignored.",
01149     "",
01150     "CopyRed  The red layer of the image window is replace with",
01151     "         the red layer of the image.  The other layers are",
01152     "         untouched.",
01153     "",
01154     "CopyGreen",
01155     "         The green layer of the image window is replace with",
01156     "         the green layer of the image.  The other layers are",
01157     "         untouched.",
01158     "",
01159     "CopyBlue The blue layer of the image window is replace with",
01160     "         the blue layer of the image.  The other layers are",
01161     "         untouched.",
01162     "",
01163     "CopyOpacity",
01164     "         The matte layer of the image window is replace with",
01165     "         the matte layer of the image.  The other layers are",
01166     "         untouched.",
01167     "",
01168     "The image compositor requires a matte, or alpha channel in",
01169     "the image for some operations.  This extra channel usually",
01170     "defines a mask which represents a sort of a cookie-cutter",
01171     "for the image.  This the case when matte is opaque (full",
01172     "coverage) for pixels inside the shape, zero outside, and",
01173     "between 0 and QuantumRange on the boundary.  If image does not",
01174     "have a matte channel, it is initialized with 0 for any pixel",
01175     "matching in color to pixel location (0,0), otherwise QuantumRange.",
01176     "",
01177     "Note that matte information for image window is not retained",
01178     "for colormapped X server visuals (e.g. StaticColor,",
01179     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
01180     "behavior may require a TrueColor or DirectColor visual or a",
01181     "Standard Colormap.",
01182     "",
01183     "Choosing a composite operator is optional.  The default",
01184     "operator is replace.  However, you must choose a location to",
01185     "paste your image and press button 1.  Press and hold the",
01186     "button before releasing and an outline of the image will",
01187     "appear to help you identify your location.",
01188     "",
01189     "The actual colors of the pasted image is saved.  However,",
01190     "the color that appears in image window may be different.",
01191     "For example, on a monochrome screen image window will appear",
01192     "black or white even though your pasted image may have",
01193     "many colors.  If the image is saved to a file it is written",
01194     "with the correct colors.  To assure the correct colors are",
01195     "saved in the final image, any PseudoClass image is promoted",
01196     "to DirectClass (see miff(5)).  To force a PseudoClass image",
01197     "to remain PseudoClass, use -colors.",
01198     (char *) NULL,
01199   },
01200   *ImageROIHelp[] =
01201   {
01202     "In region of interest mode, the Command widget has these",
01203     "options:",
01204     "",
01205     "    Help",
01206     "    Dismiss",
01207     "",
01208     "To define a region of interest, press button 1 and drag.",
01209     "The region of interest is defined by a highlighted rectangle",
01210     "that expands or contracts as it follows the pointer.  Once",
01211     "you are satisfied with the region of interest, release the",
01212     "button.  You are now in apply mode.  In apply mode the",
01213     "Command widget has these options:",
01214     "",
01215     "      File",
01216     "        Save...",
01217     "        Print...",
01218     "      Edit",
01219     "        Undo",
01220     "        Redo",
01221     "      Transform",
01222     "        Flop",
01223     "        Flip",
01224     "        Rotate Right",
01225     "        Rotate Left",
01226     "      Enhance",
01227     "        Hue...",
01228     "        Saturation...",
01229     "        Brightness...",
01230     "        Gamma...",
01231     "        Spiff",
01232     "        Dull",
01233     "        Contrast Stretch",
01234     "        Sigmoidal Contrast...",
01235     "        Normalize",
01236     "        Equalize",
01237     "        Negate",
01238     "        Grayscale",
01239     "        Map...",
01240     "        Quantize...",
01241     "      Effects",
01242     "        Despeckle",
01243     "        Emboss",
01244     "        Reduce Noise",
01245     "        Sharpen...",
01246     "        Blur...",
01247     "        Threshold...",
01248     "        Edge Detect...",
01249     "        Spread...",
01250     "        Shade...",
01251     "        Raise...",
01252     "        Segment...",
01253     "      F/X",
01254     "        Solarize...",
01255     "        Sepia Tone...",
01256     "        Swirl...",
01257     "        Implode...",
01258     "        Vignette...",
01259     "        Wave...",
01260     "        Oil Painting...",
01261     "        Charcoal Drawing...",
01262     "      Miscellany",
01263     "        Image Info",
01264     "        Zoom Image",
01265     "        Show Preview...",
01266     "        Show Histogram",
01267     "        Show Matte",
01268     "      Help",
01269     "      Dismiss",
01270     "",
01271     "You can make adjustments to the region of interest by moving",
01272     "the pointer to one of the rectangle corners, pressing a",
01273     "button, and dragging.  Finally, choose an image processing",
01274     "technique from the Command widget.  You can choose more than",
01275     "one image processing technique to apply to an area.",
01276     "Alternatively, you can move the region of interest before",
01277     "applying another image processing technique.  To exit, press",
01278     "Dismiss.",
01279     (char *) NULL,
01280   },
01281   *ImageRotateHelp[] =
01282   {
01283     "In rotate mode, the Command widget has these options:",
01284     "",
01285     "    Pixel Color",
01286     "      black",
01287     "      blue",
01288     "      cyan",
01289     "      green",
01290     "      gray",
01291     "      red",
01292     "      magenta",
01293     "      yellow",
01294     "      white",
01295     "      Browser...",
01296     "    Direction",
01297     "      horizontal",
01298     "      vertical",
01299     "    Help",
01300     "    Dismiss",
01301     "",
01302     "Choose a background color from the Pixel Color sub-menu.",
01303     "Additional background colors can be specified with the color",
01304     "browser.  You can change the menu colors by setting the X",
01305     "resources pen1 through pen9.",
01306     "",
01307     "If you choose the color browser and press Grab, you can",
01308     "select the background color by moving the pointer to the",
01309     "desired color on the screen and press any button.",
01310     "",
01311     "Choose a point in the image window and press this button and",
01312     "hold.  Next, move the pointer to another location in the",
01313     "image.  As you move a line connects the initial location and",
01314     "the pointer.  When you release the button, the degree of",
01315     "image rotation is determined by the slope of the line you",
01316     "just drew.  The slope is relative to the direction you",
01317     "choose from the Direction sub-menu of the Command widget.",
01318     "",
01319     "To cancel the image rotation, move the pointer back to the",
01320     "starting point of the line and release the button.",
01321     (char *) NULL,
01322   };
01323 
01324 /*
01325   Enumeration declarations.
01326 */
01327 typedef enum
01328 {
01329   CopyMode,
01330   CropMode,
01331   CutMode
01332 } ClipboardMode;
01333 
01334 typedef enum
01335 {
01336   OpenCommand,
01337   NextCommand,
01338   FormerCommand,
01339   SelectCommand,
01340   SaveCommand,
01341   PrintCommand,
01342   DeleteCommand,
01343   NewCommand,
01344   VisualDirectoryCommand,
01345   QuitCommand,
01346   UndoCommand,
01347   RedoCommand,
01348   CutCommand,
01349   CopyCommand,
01350   PasteCommand,
01351   HalfSizeCommand,
01352   OriginalSizeCommand,
01353   DoubleSizeCommand,
01354   ResizeCommand,
01355   ApplyCommand,
01356   RefreshCommand,
01357   RestoreCommand,
01358   CropCommand,
01359   ChopCommand,
01360   FlopCommand,
01361   FlipCommand,
01362   RotateRightCommand,
01363   RotateLeftCommand,
01364   RotateCommand,
01365   ShearCommand,
01366   RollCommand,
01367   TrimCommand,
01368   HueCommand,
01369   SaturationCommand,
01370   BrightnessCommand,
01371   GammaCommand,
01372   SpiffCommand,
01373   DullCommand,
01374   ContrastStretchCommand,
01375   SigmoidalContrastCommand,
01376   NormalizeCommand,
01377   EqualizeCommand,
01378   NegateCommand,
01379   GrayscaleCommand,
01380   MapCommand,
01381   QuantizeCommand,
01382   DespeckleCommand,
01383   EmbossCommand,
01384   ReduceNoiseCommand,
01385   AddNoiseCommand,
01386   SharpenCommand,
01387   BlurCommand,
01388   ThresholdCommand,
01389   EdgeDetectCommand,
01390   SpreadCommand,
01391   ShadeCommand,
01392   RaiseCommand,
01393   SegmentCommand,
01394   SolarizeCommand,
01395   SepiaToneCommand,
01396   SwirlCommand,
01397   ImplodeCommand,
01398   VignetteCommand,
01399   WaveCommand,
01400   OilPaintCommand,
01401   CharcoalDrawCommand,
01402   AnnotateCommand,
01403   DrawCommand,
01404   ColorCommand,
01405   MatteCommand,
01406   CompositeCommand,
01407   AddBorderCommand,
01408   AddFrameCommand,
01409   CommentCommand,
01410   LaunchCommand,
01411   RegionofInterestCommand,
01412   ROIHelpCommand,
01413   ROIDismissCommand,
01414   InfoCommand,
01415   ZoomCommand,
01416   ShowPreviewCommand,
01417   ShowHistogramCommand,
01418   ShowMatteCommand,
01419   BackgroundCommand,
01420   SlideShowCommand,
01421   PreferencesCommand,
01422   HelpCommand,
01423   BrowseDocumentationCommand,
01424   VersionCommand,
01425   SaveToUndoBufferCommand,
01426   FreeBuffersCommand,
01427   NullCommand
01428 } CommandType;
01429 
01430 typedef enum
01431 {
01432   AnnotateNameCommand,
01433   AnnotateFontColorCommand,
01434   AnnotateBackgroundColorCommand,
01435   AnnotateRotateCommand,
01436   AnnotateHelpCommand,
01437   AnnotateDismissCommand,
01438   TextHelpCommand,
01439   TextApplyCommand,
01440   ChopDirectionCommand,
01441   ChopHelpCommand,
01442   ChopDismissCommand,
01443   HorizontalChopCommand,
01444   VerticalChopCommand,
01445   ColorEditMethodCommand,
01446   ColorEditColorCommand,
01447   ColorEditBorderCommand,
01448   ColorEditFuzzCommand,
01449   ColorEditUndoCommand,
01450   ColorEditHelpCommand,
01451   ColorEditDismissCommand,
01452   CompositeOperatorsCommand,
01453   CompositeDissolveCommand,
01454   CompositeDisplaceCommand,
01455   CompositeHelpCommand,
01456   CompositeDismissCommand,
01457   CropHelpCommand,
01458   CropDismissCommand,
01459   RectifyCopyCommand,
01460   RectifyHelpCommand,
01461   RectifyDismissCommand,
01462   DrawElementCommand,
01463   DrawColorCommand,
01464   DrawStippleCommand,
01465   DrawWidthCommand,
01466   DrawUndoCommand,
01467   DrawHelpCommand,
01468   DrawDismissCommand,
01469   MatteEditMethod,
01470   MatteEditBorderCommand,
01471   MatteEditFuzzCommand,
01472   MatteEditValueCommand,
01473   MatteEditUndoCommand,
01474   MatteEditHelpCommand,
01475   MatteEditDismissCommand,
01476   PasteOperatorsCommand,
01477   PasteHelpCommand,
01478   PasteDismissCommand,
01479   RotateColorCommand,
01480   RotateDirectionCommand,
01481   RotateCropCommand,
01482   RotateSharpenCommand,
01483   RotateHelpCommand,
01484   RotateDismissCommand,
01485   HorizontalRotateCommand,
01486   VerticalRotateCommand,
01487   TileLoadCommand,
01488   TileNextCommand,
01489   TileFormerCommand,
01490   TileDeleteCommand,
01491   TileUpdateCommand
01492 } ModeType;
01493 
01494 /*
01495   Stipples.
01496 */
01497 #define BricksWidth  20
01498 #define BricksHeight  20
01499 #define DiagonalWidth  16
01500 #define DiagonalHeight  16
01501 #define HighlightWidth  8
01502 #define HighlightHeight  8
01503 #define ScalesWidth  16
01504 #define ScalesHeight  16
01505 #define ShadowWidth  8
01506 #define ShadowHeight  8
01507 #define VerticalWidth  16
01508 #define VerticalHeight  16
01509 #define WavyWidth  16
01510 #define WavyHeight  16
01511 
01512 /*
01513   Constant declaration.
01514 */
01515 static const int
01516   RoiDelta = 8;
01517 
01518 static const unsigned char
01519   BricksBitmap[] =
01520   {
01521     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
01522     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
01523     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
01524     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
01525     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
01526   },
01527   DiagonalBitmap[] =
01528   {
01529     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
01530     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
01531     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
01532   },
01533   ScalesBitmap[] =
01534   {
01535     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
01536     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
01537     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
01538   },
01539   VerticalBitmap[] =
01540   {
01541     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
01542     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
01543     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
01544   },
01545   WavyBitmap[] =
01546   {
01547     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
01548     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
01549     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
01550   };
01551 
01552 /*
01553   Function prototypes.
01554 */
01555 static CommandType
01556   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
01557     const MagickStatusType,KeySym,Image **);
01558 
01559 static Image
01560   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
01561     Image **),
01562   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
01563   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
01564   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
01565 
01566 static MagickBooleanType
01567   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
01568   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01569   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
01570   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
01571   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
01572   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01573   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
01574   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
01575   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
01576   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
01577   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
01578   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
01579   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
01580   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
01581   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
01582 
01583 static void
01584   XDrawPanRectangle(Display *,XWindows *),
01585   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
01586   XMagnifyImage(Display *,XWindows *,XEvent *),
01587   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
01588   XPanImage(Display *,XWindows *,XEvent *),
01589   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
01590     const KeySym),
01591   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
01592   XScreenEvent(Display *,XWindows *,XEvent *),
01593   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
01594 
01595 /*
01596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01597 %                                                                             %
01598 %                                                                             %
01599 %                                                                             %
01600 %   D i s p l a y I m a g e s                                                 %
01601 %                                                                             %
01602 %                                                                             %
01603 %                                                                             %
01604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01605 %
01606 %  DisplayImages() displays an image sequence to any X window screen.  It
01607 %  returns a value other than 0 if successful.  Check the exception member
01608 %  of image to determine the reason for any failure.
01609 %
01610 %  The format of the DisplayImages method is:
01611 %
01612 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
01613 %        Image *images)
01614 %
01615 %  A description of each parameter follows:
01616 %
01617 %    o image_info: the image info.
01618 %
01619 %    o image: the image.
01620 %
01621 */
01622 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
01623   Image *images)
01624 {
01625   char
01626     *argv[1];
01627 
01628   Display
01629     *display;
01630 
01631   Image
01632     *image;
01633 
01634   register long
01635     i;
01636 
01637   unsigned long
01638     state;
01639 
01640   XrmDatabase
01641     resource_database;
01642 
01643   XResourceInfo
01644     resource_info;
01645 
01646   assert(image_info != (const ImageInfo *) NULL);
01647   assert(image_info->signature == MagickSignature);
01648   assert(images != (Image *) NULL);
01649   assert(images->signature == MagickSignature);
01650   if (images->debug != MagickFalse)
01651     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
01652   display=XOpenDisplay(image_info->server_name);
01653   if (display == (Display *) NULL)
01654     {
01655       (void) ThrowMagickException(&images->exception,GetMagickModule(),
01656         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
01657         image_info->server_name));
01658       return(MagickFalse);
01659     }
01660   if (images->exception.severity != UndefinedException)
01661     CatchException(&images->exception);
01662   (void) XSetErrorHandler(XError);
01663   resource_database=XGetResourceDatabase(display,GetClientName());
01664   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
01665   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
01666   if (image_info->page != (char *) NULL)
01667     resource_info.image_geometry=AcquireString(image_info->page);
01668   resource_info.immutable=MagickTrue;
01669   argv[0]=AcquireString(GetClientName());
01670   state=DefaultState;
01671   for (i=0; (state & ExitState) == 0; i++)
01672   {
01673     if ((images->iterations != 0) && (i >= (long) images->iterations))
01674       break;
01675     image=GetImageFromList(images,i % GetImageListLength(images));
01676     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
01677   }
01678   argv[0]=DestroyString(argv[0]);
01679   (void) XCloseDisplay(display);
01680   XDestroyResourceInfo(&resource_info);
01681   if (images->exception.severity != UndefinedException)
01682     return(MagickFalse);
01683   return(MagickTrue);
01684 }
01685 
01686 /*
01687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01688 %                                                                             %
01689 %                                                                             %
01690 %                                                                             %
01691 %   R e m o t e D i s p l a y C o m m a n d                                   %
01692 %                                                                             %
01693 %                                                                             %
01694 %                                                                             %
01695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01696 %
01697 %  RemoteDisplayCommand() encourages a remote display program to display the
01698 %  specified image filename.
01699 %
01700 %  The format of the RemoteDisplayCommand method is:
01701 %
01702 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
01703 %        const char *window,const char *filename,ExceptionInfo *exception)
01704 %
01705 %  A description of each parameter follows:
01706 %
01707 %    o image_info: the image info.
01708 %
01709 %    o window: Specifies the name or id of an X window.
01710 %
01711 %    o filename: the name of the image filename to display.
01712 %
01713 %    o exception: return any errors or warnings in this structure.
01714 %
01715 */
01716 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
01717   const char *window,const char *filename,ExceptionInfo *exception)
01718 {
01719   Display
01720     *display;
01721 
01722   MagickStatusType
01723     status;
01724 
01725   assert(image_info != (const ImageInfo *) NULL);
01726   assert(image_info->signature == MagickSignature);
01727   assert(filename != (char *) NULL);
01728   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
01729   display=XOpenDisplay(image_info->server_name);
01730   if (display == (Display *) NULL)
01731     {
01732       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
01733         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
01734       return(MagickFalse);
01735     }
01736   (void) XSetErrorHandler(XError);
01737   status=XRemoteCommand(display,window,filename);
01738   (void) XCloseDisplay(display);
01739   return(status != 0 ? MagickTrue : MagickFalse);
01740 }
01741 
01742 /*
01743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01744 %                                                                             %
01745 %                                                                             %
01746 %                                                                             %
01747 +   X A n n o t a t e E d i t I m a g e                                       %
01748 %                                                                             %
01749 %                                                                             %
01750 %                                                                             %
01751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01752 %
01753 %  XAnnotateEditImage() annotates the image with text.
01754 %
01755 %  The format of the XAnnotateEditImage method is:
01756 %
01757 %      MagickBooleanType XAnnotateEditImage(Display *display,
01758 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
01759 %
01760 %  A description of each parameter follows:
01761 %
01762 %    o display: Specifies a connection to an X server;  returned from
01763 %      XOpenDisplay.
01764 %
01765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
01766 %
01767 %    o windows: Specifies a pointer to a XWindows structure.
01768 %
01769 %    o image: the image; returned from ReadImage.
01770 %
01771 */
01772 
01773 static inline long MagickMax(const long x,const long y)
01774 {
01775   if (x > y)
01776     return(x);
01777   return(y);
01778 }
01779 
01780 static inline long MagickMin(const long x,const long y)
01781 {
01782   if (x < y)
01783     return(x);
01784   return(y);
01785 }
01786 
01787 static MagickBooleanType XAnnotateEditImage(Display *display,
01788   XResourceInfo *resource_info,XWindows *windows,Image *image)
01789 {
01790   static const char
01791     *AnnotateMenu[] =
01792     {
01793       "Font Name",
01794       "Font Color",
01795       "Box Color",
01796       "Rotate Text",
01797       "Help",
01798       "Dismiss",
01799       (char *) NULL
01800     },
01801     *TextMenu[] =
01802     {
01803       "Help",
01804       "Apply",
01805       (char *) NULL
01806     };
01807 
01808   static const ModeType
01809     AnnotateCommands[] =
01810     {
01811       AnnotateNameCommand,
01812       AnnotateFontColorCommand,
01813       AnnotateBackgroundColorCommand,
01814       AnnotateRotateCommand,
01815       AnnotateHelpCommand,
01816       AnnotateDismissCommand
01817     },
01818     TextCommands[] =
01819     {
01820       TextHelpCommand,
01821       TextApplyCommand
01822     };
01823 
01824   static MagickBooleanType
01825     transparent_box = MagickTrue,
01826     transparent_pen = MagickFalse;
01827 
01828   static MagickRealType
01829     degrees = 0.0;
01830 
01831   static unsigned int
01832     box_id = MaxNumberPens-2,
01833     font_id = 0,
01834     pen_id = 0;
01835 
01836   char
01837     command[MaxTextExtent],
01838     text[MaxTextExtent];
01839 
01840   const char
01841     *ColorMenu[MaxNumberPens+1];
01842 
01843   Cursor
01844     cursor;
01845 
01846   GC
01847     annotate_context;
01848 
01849   int
01850     id,
01851     pen_number,
01852     status,
01853     x,
01854     y;
01855 
01856   KeySym
01857     key_symbol;
01858 
01859   register char
01860     *p;
01861 
01862   register long
01863     i;
01864 
01865   unsigned int
01866     height,
01867     width;
01868 
01869   unsigned long
01870     state;
01871 
01872   XAnnotateInfo
01873     *annotate_info,
01874     *previous_info;
01875 
01876   XColor
01877     color;
01878 
01879   XFontStruct
01880     *font_info;
01881 
01882   XEvent
01883     event,
01884     text_event;
01885 
01886   /*
01887     Map Command widget.
01888   */
01889   (void) CloneString(&windows->command.name,"Annotate");
01890   windows->command.data=4;
01891   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
01892   (void) XMapRaised(display,windows->command.id);
01893   XClientMessage(display,windows->image.id,windows->im_protocols,
01894     windows->im_update_widget,CurrentTime);
01895   /*
01896     Track pointer until button 1 is pressed.
01897   */
01898   XQueryPosition(display,windows->image.id,&x,&y);
01899   (void) XSelectInput(display,windows->image.id,
01900     windows->image.attributes.event_mask | PointerMotionMask);
01901   cursor=XCreateFontCursor(display,XC_left_side);
01902   (void) XCheckDefineCursor(display,windows->image.id,cursor);
01903   state=DefaultState;
01904   do
01905   {
01906     if (windows->info.mapped != MagickFalse)
01907       {
01908         /*
01909           Display pointer position.
01910         */
01911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
01912           x+windows->image.x,y+windows->image.y);
01913         XInfoWidget(display,windows,text);
01914       }
01915     /*
01916       Wait for next event.
01917     */
01918     XScreenEvent(display,windows,&event);
01919     if (event.xany.window == windows->command.id)
01920       {
01921         /*
01922           Select a command from the Command widget.
01923         */
01924         id=XCommandWidget(display,windows,AnnotateMenu,&event);
01925         (void) XCheckDefineCursor(display,windows->image.id,cursor);
01926         if (id < 0)
01927           continue;
01928         switch (AnnotateCommands[id])
01929         {
01930           case AnnotateNameCommand:
01931           {
01932             const char
01933               *FontMenu[MaxNumberFonts];
01934 
01935             int
01936               font_number;
01937 
01938             /*
01939               Initialize menu selections.
01940             */
01941             for (i=0; i < MaxNumberFonts; i++)
01942               FontMenu[i]=resource_info->font_name[i];
01943             FontMenu[MaxNumberFonts-2]="Browser...";
01944             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
01945             /*
01946               Select a font name from the pop-up menu.
01947             */
01948             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
01949               (const char **) FontMenu,command);
01950             if (font_number < 0)
01951               break;
01952             if (font_number == (MaxNumberFonts-2))
01953               {
01954                 static char
01955                   font_name[MaxTextExtent] = "fixed";
01956 
01957                 /*
01958                   Select a font name from a browser.
01959                 */
01960                 resource_info->font_name[font_number]=font_name;
01961                 XFontBrowserWidget(display,windows,"Select",font_name);
01962                 if (*font_name == '\0')
01963                   break;
01964               }
01965             /*
01966               Initialize font info.
01967             */
01968             font_info=XLoadQueryFont(display,resource_info->font_name[
01969               font_number]);
01970             if (font_info == (XFontStruct *) NULL)
01971               {
01972                 XNoticeWidget(display,windows,"Unable to load font:",
01973                   resource_info->font_name[font_number]);
01974                 break;
01975               }
01976             font_id=(unsigned int) font_number;
01977             (void) XFreeFont(display,font_info);
01978             break;
01979           }
01980           case AnnotateFontColorCommand:
01981           {
01982             /*
01983               Initialize menu selections.
01984             */
01985             for (i=0; i < (int) (MaxNumberPens-2); i++)
01986               ColorMenu[i]=resource_info->pen_colors[i];
01987             ColorMenu[MaxNumberPens-2]="transparent";
01988             ColorMenu[MaxNumberPens-1]="Browser...";
01989             ColorMenu[MaxNumberPens]=(const char *) NULL;
01990             /*
01991               Select a pen color from the pop-up menu.
01992             */
01993             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
01994               (const char **) ColorMenu,command);
01995             if (pen_number < 0)
01996               break;
01997             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
01998               MagickFalse;
01999             if (transparent_pen != MagickFalse)
02000               break;
02001             if (pen_number == (MaxNumberPens-1))
02002               {
02003                 static char
02004                   color_name[MaxTextExtent] = "gray";
02005 
02006                 /*
02007                   Select a pen color from a dialog.
02008                 */
02009                 resource_info->pen_colors[pen_number]=color_name;
02010                 XColorBrowserWidget(display,windows,"Select",color_name);
02011                 if (*color_name == '\0')
02012                   break;
02013               }
02014             /*
02015               Set pen color.
02016             */
02017             (void) XParseColor(display,windows->map_info->colormap,
02018               resource_info->pen_colors[pen_number],&color);
02019             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
02020               (unsigned int) MaxColors,&color);
02021             windows->pixel_info->pen_colors[pen_number]=color;
02022             pen_id=(unsigned int) pen_number;
02023             break;
02024           }
02025           case AnnotateBackgroundColorCommand:
02026           {
02027             /*
02028               Initialize menu selections.
02029             */
02030             for (i=0; i < (int) (MaxNumberPens-2); i++)
02031               ColorMenu[i]=resource_info->pen_colors[i];
02032             ColorMenu[MaxNumberPens-2]="transparent";
02033             ColorMenu[MaxNumberPens-1]="Browser...";
02034             ColorMenu[MaxNumberPens]=(const char *) NULL;
02035             /*
02036               Select a pen color from the pop-up menu.
02037             */
02038             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
02039               (const char **) ColorMenu,command);
02040             if (pen_number < 0)
02041               break;
02042             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
02043               MagickFalse;
02044             if (transparent_box != MagickFalse)
02045               break;
02046             if (pen_number == (MaxNumberPens-1))
02047               {
02048                 static char
02049                   color_name[MaxTextExtent] = "gray";
02050 
02051                 /*
02052                   Select a pen color from a dialog.
02053                 */
02054                 resource_info->pen_colors[pen_number]=color_name;
02055                 XColorBrowserWidget(display,windows,"Select",color_name);
02056                 if (*color_name == '\0')
02057                   break;
02058               }
02059             /*
02060               Set pen color.
02061             */
02062             (void) XParseColor(display,windows->map_info->colormap,
02063               resource_info->pen_colors[pen_number],&color);
02064             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
02065               (unsigned int) MaxColors,&color);
02066             windows->pixel_info->pen_colors[pen_number]=color;
02067             box_id=(unsigned int) pen_number;
02068             break;
02069           }
02070           case AnnotateRotateCommand:
02071           {
02072             int
02073               entry;
02074 
02075             static char
02076               angle[MaxTextExtent] = "30.0";
02077 
02078             static const char
02079               *RotateMenu[] =
02080               {
02081                 "-90",
02082                 "-45",
02083                 "-30",
02084                 "0",
02085                 "30",
02086                 "45",
02087                 "90",
02088                 "180",
02089                 "Dialog...",
02090                 (char *) NULL,
02091               };
02092 
02093             /*
02094               Select a command from the pop-up menu.
02095             */
02096             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
02097               command);
02098             if (entry < 0)
02099               break;
02100             if (entry != 8)
02101               {
02102                 degrees=StringToDouble(RotateMenu[entry]);
02103                 break;
02104               }
02105             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
02106               angle);
02107             if (*angle == '\0')
02108               break;
02109             degrees=StringToDouble(angle);
02110             break;
02111           }
02112           case AnnotateHelpCommand:
02113           {
02114             XTextViewWidget(display,resource_info,windows,MagickFalse,
02115               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02116             break;
02117           }
02118           case AnnotateDismissCommand:
02119           {
02120             /*
02121               Prematurely exit.
02122             */
02123             state|=EscapeState;
02124             state|=ExitState;
02125             break;
02126           }
02127           default:
02128             break;
02129         }
02130         continue;
02131       }
02132     switch (event.type)
02133     {
02134       case ButtonPress:
02135       {
02136         if (event.xbutton.button != Button1)
02137           break;
02138         if (event.xbutton.window != windows->image.id)
02139           break;
02140         /*
02141           Change to text entering mode.
02142         */
02143         x=event.xbutton.x;
02144         y=event.xbutton.y;
02145         state|=ExitState;
02146         break;
02147       }
02148       case ButtonRelease:
02149         break;
02150       case Expose:
02151         break;
02152       case KeyPress:
02153       {
02154         if (event.xkey.window != windows->image.id)
02155           break;
02156         /*
02157           Respond to a user key press.
02158         */
02159         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
02160           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02161         switch ((int) key_symbol)
02162         {
02163           case XK_Escape:
02164           case XK_F20:
02165           {
02166             /*
02167               Prematurely exit.
02168             */
02169             state|=EscapeState;
02170             state|=ExitState;
02171             break;
02172           }
02173           case XK_F1:
02174           case XK_Help:
02175           {
02176             XTextViewWidget(display,resource_info,windows,MagickFalse,
02177               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02178             break;
02179           }
02180           default:
02181           {
02182             (void) XBell(display,0);
02183             break;
02184           }
02185         }
02186         break;
02187       }
02188       case MotionNotify:
02189       {
02190         /*
02191           Map and unmap Info widget as cursor crosses its boundaries.
02192         */
02193         x=event.xmotion.x;
02194         y=event.xmotion.y;
02195         if (windows->info.mapped != MagickFalse)
02196           {
02197             if ((x < (int) (windows->info.x+windows->info.width)) &&
02198                 (y < (int) (windows->info.y+windows->info.height)))
02199               (void) XWithdrawWindow(display,windows->info.id,
02200                 windows->info.screen);
02201           }
02202         else
02203           if ((x > (int) (windows->info.x+windows->info.width)) ||
02204               (y > (int) (windows->info.y+windows->info.height)))
02205             (void) XMapWindow(display,windows->info.id);
02206         break;
02207       }
02208       default:
02209         break;
02210     }
02211   } while ((state & ExitState) == 0);
02212   (void) XSelectInput(display,windows->image.id,
02213     windows->image.attributes.event_mask);
02214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
02215   if ((state & EscapeState) != 0)
02216     return(MagickTrue);
02217   /*
02218     Set font info and check boundary conditions.
02219   */
02220   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
02221   if (font_info == (XFontStruct *) NULL)
02222     {
02223       XNoticeWidget(display,windows,"Unable to load font:",
02224         resource_info->font_name[font_id]);
02225       font_info=windows->font_info;
02226     }
02227   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
02228     x=(int) windows->image.width-font_info->max_bounds.width;
02229   if (y < (int) (font_info->ascent+font_info->descent))
02230     y=(int) font_info->ascent+font_info->descent;
02231   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
02232       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
02233     return(MagickFalse);
02234   /*
02235     Initialize annotate structure.
02236   */
02237   annotate_info=(XAnnotateInfo *) AcquireAlignedMemory(1,sizeof(*annotate_info));
02238   if (annotate_info == (XAnnotateInfo *) NULL)
02239     return(MagickFalse);
02240   XGetAnnotateInfo(annotate_info);
02241   annotate_info->x=x;
02242   annotate_info->y=y;
02243   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
02244     annotate_info->stencil=OpaqueStencil;
02245   else
02246     if (transparent_box == MagickFalse)
02247       annotate_info->stencil=BackgroundStencil;
02248     else
02249       annotate_info->stencil=ForegroundStencil;
02250   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
02251   annotate_info->degrees=degrees;
02252   annotate_info->font_info=font_info;
02253   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02254     windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02255     sizeof(*annotate_info->text));
02256   if (annotate_info->text == (char *) NULL)
02257     return(MagickFalse);
02258   /*
02259     Create cursor and set graphic context.
02260   */
02261   cursor=XCreateFontCursor(display,XC_pencil);
02262   (void) XCheckDefineCursor(display,windows->image.id,cursor);
02263   annotate_context=windows->image.annotate_context;
02264   (void) XSetFont(display,annotate_context,font_info->fid);
02265   (void) XSetBackground(display,annotate_context,
02266     windows->pixel_info->pen_colors[box_id].pixel);
02267   (void) XSetForeground(display,annotate_context,
02268     windows->pixel_info->pen_colors[pen_id].pixel);
02269   /*
02270     Begin annotating the image with text.
02271   */
02272   (void) CloneString(&windows->command.name,"Text");
02273   windows->command.data=0;
02274   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
02275   state=DefaultState;
02276   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
02277   text_event.xexpose.width=(int) font_info->max_bounds.width;
02278   text_event.xexpose.height=font_info->max_bounds.ascent+
02279     font_info->max_bounds.descent;
02280   p=annotate_info->text;
02281   do
02282   {
02283     /*
02284       Display text cursor.
02285     */
02286     *p='\0';
02287     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
02288     /*
02289       Wait for next event.
02290     */
02291     XScreenEvent(display,windows,&event);
02292     if (event.xany.window == windows->command.id)
02293       {
02294         /*
02295           Select a command from the Command widget.
02296         */
02297         (void) XSetBackground(display,annotate_context,
02298           windows->pixel_info->background_color.pixel);
02299         (void) XSetForeground(display,annotate_context,
02300           windows->pixel_info->foreground_color.pixel);
02301         id=XCommandWidget(display,windows,AnnotateMenu,&event);
02302         (void) XSetBackground(display,annotate_context,
02303           windows->pixel_info->pen_colors[box_id].pixel);
02304         (void) XSetForeground(display,annotate_context,
02305           windows->pixel_info->pen_colors[pen_id].pixel);
02306         if (id < 0)
02307           continue;
02308         switch (TextCommands[id])
02309         {
02310           case TextHelpCommand:
02311           {
02312             XTextViewWidget(display,resource_info,windows,MagickFalse,
02313               "Help Viewer - Image Annotation",ImageAnnotateHelp);
02314             (void) XCheckDefineCursor(display,windows->image.id,cursor);
02315             break;
02316           }
02317           case TextApplyCommand:
02318           {
02319             /*
02320               Finished annotating.
02321             */
02322             annotate_info->width=(unsigned int) XTextWidth(font_info,
02323               annotate_info->text,(int) strlen(annotate_info->text));
02324             XRefreshWindow(display,&windows->image,&text_event);
02325             state|=ExitState;
02326             break;
02327           }
02328           default:
02329             break;
02330         }
02331         continue;
02332       }
02333     /*
02334       Erase text cursor.
02335     */
02336     text_event.xexpose.x=x;
02337     text_event.xexpose.y=y-font_info->max_bounds.ascent;
02338     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
02339       (unsigned int) text_event.xexpose.width,(unsigned int)
02340       text_event.xexpose.height,MagickFalse);
02341     XRefreshWindow(display,&windows->image,&text_event);
02342     switch (event.type)
02343     {
02344       case ButtonPress:
02345       {
02346         if (event.xbutton.window != windows->image.id)
02347           break;
02348         if (event.xbutton.button == Button2)
02349           {
02350             /*
02351               Request primary selection.
02352             */
02353             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
02354               windows->image.id,CurrentTime);
02355             break;
02356           }
02357         break;
02358       }
02359       case Expose:
02360       {
02361         if (event.xexpose.count == 0)
02362           {
02363             XAnnotateInfo
02364               *text_info;
02365 
02366             /*
02367               Refresh Image window.
02368             */
02369             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
02370             text_info=annotate_info;
02371             while (text_info != (XAnnotateInfo *) NULL)
02372             {
02373               if (annotate_info->stencil == ForegroundStencil)
02374                 (void) XDrawString(display,windows->image.id,annotate_context,
02375                   text_info->x,text_info->y,text_info->text,
02376                   (int) strlen(text_info->text));
02377               else
02378                 (void) XDrawImageString(display,windows->image.id,
02379                   annotate_context,text_info->x,text_info->y,text_info->text,
02380                   (int) strlen(text_info->text));
02381               text_info=text_info->previous;
02382             }
02383             (void) XDrawString(display,windows->image.id,annotate_context,
02384               x,y,"_",1);
02385           }
02386         break;
02387       }
02388       case KeyPress:
02389       {
02390         int
02391           length;
02392 
02393         if (event.xkey.window != windows->image.id)
02394           break;
02395         /*
02396           Respond to a user key press.
02397         */
02398         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
02399           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02400         *(command+length)='\0';
02401         if (((event.xkey.state & ControlMask) != 0) ||
02402             ((event.xkey.state & Mod1Mask) != 0))
02403           state|=ModifierState;
02404         if ((state & ModifierState) != 0)
02405           switch ((int) key_symbol)
02406           {
02407             case XK_u:
02408             case XK_U:
02409             {
02410               key_symbol=DeleteCommand;
02411               break;
02412             }
02413             default:
02414               break;
02415           }
02416         switch ((int) key_symbol)
02417         {
02418           case XK_BackSpace:
02419           {
02420             /*
02421               Erase one character.
02422             */
02423             if (p == annotate_info->text)
02424               {
02425                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
02426                   break;
02427                 else
02428                   {
02429                     /*
02430                       Go to end of the previous line of text.
02431                     */
02432                     annotate_info=annotate_info->previous;
02433                     p=annotate_info->text;
02434                     x=annotate_info->x+annotate_info->width;
02435                     y=annotate_info->y;
02436                     if (annotate_info->width != 0)
02437                       p+=strlen(annotate_info->text);
02438                     break;
02439                   }
02440               }
02441             p--;
02442             x-=XTextWidth(font_info,p,1);
02443             text_event.xexpose.x=x;
02444             text_event.xexpose.y=y-font_info->max_bounds.ascent;
02445             XRefreshWindow(display,&windows->image,&text_event);
02446             break;
02447           }
02448           case XK_bracketleft:
02449           {
02450             key_symbol=XK_Escape;
02451             break;
02452           }
02453           case DeleteCommand:
02454           {
02455             /*
02456               Erase the entire line of text.
02457             */
02458             while (p != annotate_info->text)
02459             {
02460               p--;
02461               x-=XTextWidth(font_info,p,1);
02462               text_event.xexpose.x=x;
02463               XRefreshWindow(display,&windows->image,&text_event);
02464             }
02465             break;
02466           }
02467           case XK_Escape:
02468           case XK_F20:
02469           {
02470             /*
02471               Finished annotating.
02472             */
02473             annotate_info->width=(unsigned int) XTextWidth(font_info,
02474               annotate_info->text,(int) strlen(annotate_info->text));
02475             XRefreshWindow(display,&windows->image,&text_event);
02476             state|=ExitState;
02477             break;
02478           }
02479           default:
02480           {
02481             /*
02482               Draw a single character on the Image window.
02483             */
02484             if ((state & ModifierState) != 0)
02485               break;
02486             if (*command == '\0')
02487               break;
02488             *p=(*command);
02489             if (annotate_info->stencil == ForegroundStencil)
02490               (void) XDrawString(display,windows->image.id,annotate_context,
02491                 x,y,p,1);
02492             else
02493               (void) XDrawImageString(display,windows->image.id,
02494                 annotate_context,x,y,p,1);
02495             x+=XTextWidth(font_info,p,1);
02496             p++;
02497             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
02498               break;
02499           }
02500           case XK_Return:
02501           case XK_KP_Enter:
02502           {
02503             /*
02504               Advance to the next line of text.
02505             */
02506             *p='\0';
02507             annotate_info->width=(unsigned int) XTextWidth(font_info,
02508               annotate_info->text,(int) strlen(annotate_info->text));
02509             if (annotate_info->next != (XAnnotateInfo *) NULL)
02510               {
02511                 /*
02512                   Line of text already exists.
02513                 */
02514                 annotate_info=annotate_info->next;
02515                 x=annotate_info->x;
02516                 y=annotate_info->y;
02517                 p=annotate_info->text;
02518                 break;
02519               }
02520             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
02521               sizeof(*annotate_info->next));
02522             if (annotate_info->next == (XAnnotateInfo *) NULL)
02523               return(MagickFalse);
02524             *annotate_info->next=(*annotate_info);
02525             annotate_info->next->previous=annotate_info;
02526             annotate_info=annotate_info->next;
02527             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02528               windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02529               sizeof(*annotate_info->text));
02530             if (annotate_info->text == (char *) NULL)
02531               return(MagickFalse);
02532             annotate_info->y+=annotate_info->height;
02533             if (annotate_info->y > (int) windows->image.height)
02534               annotate_info->y=(int) annotate_info->height;
02535             annotate_info->next=(XAnnotateInfo *) NULL;
02536             x=annotate_info->x;
02537             y=annotate_info->y;
02538             p=annotate_info->text;
02539             break;
02540           }
02541         }
02542         break;
02543       }
02544       case KeyRelease:
02545       {
02546         /*
02547           Respond to a user key release.
02548         */
02549         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
02550           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
02551         state&=(~ModifierState);
02552         break;
02553       }
02554       case SelectionNotify:
02555       {
02556         Atom
02557           type;
02558 
02559         int
02560           format;
02561 
02562         unsigned char
02563           *data;
02564 
02565         unsigned long
02566           after,
02567           length;
02568 
02569         /*
02570           Obtain response from primary selection.
02571         */
02572         if (event.xselection.property == (Atom) None)
02573           break;
02574         status=XGetWindowProperty(display,event.xselection.requestor,
02575           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
02576           &type,&format,&length,&after,&data);
02577         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
02578             (length == 0))
02579           break;
02580         /*
02581           Annotate Image window with primary selection.
02582         */
02583         for (i=0; i < (long) length; i++)
02584         {
02585           if ((char) data[i] != '\n')
02586             {
02587               /*
02588                 Draw a single character on the Image window.
02589               */
02590               *p=(char) data[i];
02591               (void) XDrawString(display,windows->image.id,annotate_context,
02592                 x,y,p,1);
02593               x+=XTextWidth(font_info,p,1);
02594               p++;
02595               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
02596                 continue;
02597             }
02598           /*
02599             Advance to the next line of text.
02600           */
02601           *p='\0';
02602           annotate_info->width=(unsigned int) XTextWidth(font_info,
02603             annotate_info->text,(int) strlen(annotate_info->text));
02604           if (annotate_info->next != (XAnnotateInfo *) NULL)
02605             {
02606               /*
02607                 Line of text already exists.
02608               */
02609               annotate_info=annotate_info->next;
02610               x=annotate_info->x;
02611               y=annotate_info->y;
02612               p=annotate_info->text;
02613               continue;
02614             }
02615           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
02616             sizeof(*annotate_info->next));
02617           if (annotate_info->next == (XAnnotateInfo *) NULL)
02618             return(MagickFalse);
02619           *annotate_info->next=(*annotate_info);
02620           annotate_info->next->previous=annotate_info;
02621           annotate_info=annotate_info->next;
02622           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
02623             windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
02624             sizeof(*annotate_info->text));
02625           if (annotate_info->text == (char *) NULL)
02626             return(MagickFalse);
02627           annotate_info->y+=annotate_info->height;
02628           if (annotate_info->y > (int) windows->image.height)
02629             annotate_info->y=(int) annotate_info->height;
02630           annotate_info->next=(XAnnotateInfo *) NULL;
02631           x=annotate_info->x;
02632           y=annotate_info->y;
02633           p=annotate_info->text;
02634         }
02635         (void) XFree((void *) data);
02636         break;
02637       }
02638       default:
02639         break;
02640     }
02641   } while ((state & ExitState) == 0);
02642   (void) XFreeCursor(display,cursor);
02643   /*
02644     Annotation is relative to image configuration.
02645   */
02646   width=(unsigned int) image->columns;
02647   height=(unsigned int) image->rows;
02648   x=0;
02649   y=0;
02650   if (windows->image.crop_geometry != (char *) NULL)
02651     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
02652   /*
02653     Initialize annotated image.
02654   */
02655   XSetCursorState(display,windows,MagickTrue);
02656   XCheckRefreshWindows(display,windows);
02657   while (annotate_info != (XAnnotateInfo *) NULL)
02658   {
02659     if (annotate_info->width == 0)
02660       {
02661         /*
02662           No text on this line--  go to the next line of text.
02663         */
02664         previous_info=annotate_info->previous;
02665         annotate_info->text=(char *)
02666           RelinquishMagickMemory(annotate_info->text);
02667         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
02668         annotate_info=previous_info;
02669         continue;
02670       }
02671     /*
02672       Determine pixel index for box and pen color.
02673     */
02674     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
02675     if (windows->pixel_info->colors != 0)
02676       for (i=0; i < (long) windows->pixel_info->colors; i++)
02677         if (windows->pixel_info->pixels[i] ==
02678             windows->pixel_info->pen_colors[box_id].pixel)
02679           {
02680             windows->pixel_info->box_index=(unsigned short) i;
02681             break;
02682           }
02683     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
02684     if (windows->pixel_info->colors != 0)
02685       for (i=0; i < (long) windows->pixel_info->colors; i++)
02686         if (windows->pixel_info->pixels[i] ==
02687             windows->pixel_info->pen_colors[pen_id].pixel)
02688           {
02689             windows->pixel_info->pen_index=(unsigned short) i;
02690             break;
02691           }
02692     /*
02693       Define the annotate geometry string.
02694     */
02695     annotate_info->x=(int)
02696       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
02697     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
02698       windows->image.y)/windows->image.ximage->height;
02699     (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
02700       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
02701       height*annotate_info->height/windows->image.ximage->height,
02702       annotate_info->x+x,annotate_info->y+y);
02703     /*
02704       Annotate image with text.
02705     */
02706     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
02707     if (status == 0)
02708       return(MagickFalse);
02709     /*
02710       Free up memory.
02711     */
02712     previous_info=annotate_info->previous;
02713     annotate_info->text=DestroyString(annotate_info->text);
02714     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
02715     annotate_info=previous_info;
02716   }
02717   (void) XSetForeground(display,annotate_context,
02718     windows->pixel_info->foreground_color.pixel);
02719   (void) XSetBackground(display,annotate_context,
02720     windows->pixel_info->background_color.pixel);
02721   (void) XSetFont(display,annotate_context,windows->font_info->fid);
02722   XSetCursorState(display,windows,MagickFalse);
02723   (void) XFreeFont(display,font_info);
02724   /*
02725     Update image configuration.
02726   */
02727   XConfigureImageColormap(display,resource_info,windows,image);
02728   (void) XConfigureImage(display,resource_info,windows,image);
02729   return(MagickTrue);
02730 }
02731 
02732 /*
02733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02734 %                                                                             %
02735 %                                                                             %
02736 %                                                                             %
02737 +   X B a c k g r o u n d I m a g e                                           %
02738 %                                                                             %
02739 %                                                                             %
02740 %                                                                             %
02741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02742 %
02743 %  XBackgroundImage() displays the image in the background of a window.
02744 %
02745 %  The format of the XBackgroundImage method is:
02746 %
02747 %      MagickBooleanType XBackgroundImage(Display *display,
02748 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
02749 %
02750 %  A description of each parameter follows:
02751 %
02752 %    o display: Specifies a connection to an X server; returned from
02753 %      XOpenDisplay.
02754 %
02755 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
02756 %
02757 %    o windows: Specifies a pointer to a XWindows structure.
02758 %
02759 %    o image: the image.
02760 %
02761 */
02762 static MagickBooleanType XBackgroundImage(Display *display,
02763   XResourceInfo *resource_info,XWindows *windows,Image **image)
02764 {
02765 #define BackgroundImageTag  "Background/Image"
02766 
02767   int
02768     status;
02769 
02770   static char
02771     window_id[MaxTextExtent] = "root";
02772 
02773   XResourceInfo
02774     background_resources;
02775 
02776   /*
02777     Put image in background.
02778   */
02779   status=XDialogWidget(display,windows,"Background",
02780     "Enter window id (id 0x00 selects window with pointer):",window_id);
02781   if (*window_id == '\0')
02782     return(MagickFalse);
02783   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
02784   XInfoWidget(display,windows,BackgroundImageTag);
02785   XSetCursorState(display,windows,MagickTrue);
02786   XCheckRefreshWindows(display,windows);
02787   background_resources=(*resource_info);
02788   background_resources.window_id=window_id;
02789   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
02790   status=XDisplayBackgroundImage(display,&background_resources,*image);
02791   if (status != MagickFalse)
02792     XClientMessage(display,windows->image.id,windows->im_protocols,
02793       windows->im_retain_colors,CurrentTime);
02794   XSetCursorState(display,windows,MagickFalse);
02795   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
02796   return(MagickTrue);
02797 }
02798 
02799 /*
02800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02801 %                                                                             %
02802 %                                                                             %
02803 %                                                                             %
02804 +   X C h o p I m a g e                                                       %
02805 %                                                                             %
02806 %                                                                             %
02807 %                                                                             %
02808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
02809 %
02810 %  XChopImage() chops the X image.
02811 %
02812 %  The format of the XChopImage method is:
02813 %
02814 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
02815 %      XWindows *windows,Image **image)
02816 %
02817 %  A description of each parameter follows:
02818 %
02819 %    o display: Specifies a connection to an X server; returned from
02820 %      XOpenDisplay.
02821 %
02822 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
02823 %
02824 %    o windows: Specifies a pointer to a XWindows structure.
02825 %
02826 %    o image: the image.
02827 %
02828 */
02829 static MagickBooleanType XChopImage(Display *display,
02830   XResourceInfo *resource_info,XWindows *windows,Image **image)
02831 {
02832   static const char
02833     *ChopMenu[] =
02834     {
02835       "Direction",
02836       "Help",
02837       "Dismiss",
02838       (char *) NULL
02839     };
02840 
02841   static ModeType
02842     direction = HorizontalChopCommand;
02843 
02844   static const ModeType
02845     ChopCommands[] =
02846     {
02847       ChopDirectionCommand,
02848       ChopHelpCommand,
02849       ChopDismissCommand
02850     },
02851     DirectionCommands[] =
02852     {
02853       HorizontalChopCommand,
02854       VerticalChopCommand
02855     };
02856 
02857   char
02858     text[MaxTextExtent];
02859 
02860   Image
02861     *chop_image;
02862 
02863   int
02864     id,
02865     x,
02866     y;
02867 
02868   MagickRealType
02869     scale_factor;
02870 
02871   RectangleInfo
02872     chop_info;
02873 
02874   unsigned int
02875     distance,
02876     height,
02877     width;
02878 
02879   unsigned long
02880     state;
02881 
02882   XEvent
02883     event;
02884 
02885   XSegment
02886     segment_info;
02887 
02888   /*
02889     Map Command widget.
02890   */
02891   (void) CloneString(&windows->command.name,"Chop");
02892   windows->command.data=1;
02893   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
02894   (void) XMapRaised(display,windows->command.id);
02895   XClientMessage(display,windows->image.id,windows->im_protocols,
02896     windows->im_update_widget,CurrentTime);
02897   /*
02898     Track pointer until button 1 is pressed.
02899   */
02900   XQueryPosition(display,windows->image.id,&x,&y);
02901   (void) XSelectInput(display,windows->image.id,
02902     windows->image.attributes.event_mask | PointerMotionMask);
02903   state=DefaultState;
02904   do
02905   {
02906     if (windows->info.mapped != MagickFalse)
02907       {
02908         /*
02909           Display pointer position.
02910         */
02911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
02912           x+windows->image.x,y+windows->image.y);
02913         XInfoWidget(display,windows,text);
02914       }
02915     /*
02916       Wait for next event.
02917     */
02918     XScreenEvent(display,windows,&event);
02919     if (event.xany.window == windows->command.id)
02920       {
02921         /*
02922           Select a command from the Command widget.
02923         */
02924         id=XCommandWidget(display,windows,ChopMenu,&event);
02925         if (id < 0)
02926           continue;
02927         switch (ChopCommands[id])
02928         {
02929           case ChopDirectionCommand:
02930           {
02931             char
02932               command[MaxTextExtent];
02933 
02934             static const char
02935               *Directions[] =
02936               {
02937                 "horizontal",
02938                 "vertical",
02939                 (char *) NULL,
02940               };
02941 
02942             /*
02943               Select a command from the pop-up menu.
02944             */
02945             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
02946             if (id >= 0)
02947               direction=DirectionCommands[id];
02948             break;
02949           }
02950           case ChopHelpCommand:
02951           {
02952             XTextViewWidget(display,resource_info,windows,MagickFalse,
02953               "Help Viewer - Image Chop",ImageChopHelp);
02954             break;
02955           }
02956           case ChopDismissCommand:
02957           {
02958             /*
02959               Prematurely exit.
02960             */
02961             state|=EscapeState;
02962             state|=ExitState;
02963             break;
02964           }
02965           default:
02966             break;
02967         }
02968         continue;
02969       }
02970     switch (event.type)
02971     {
02972       case ButtonPress:
02973       {
02974         if (event.xbutton.button != Button1)
02975           break;
02976         if (event.xbutton.window != windows->image.id)
02977           break;
02978         /*
02979           User has committed to start point of chopping line.
02980         */
02981         segment_info.x1=(short int) event.xbutton.x;
02982         segment_info.x2=(short int) event.xbutton.x;
02983         segment_info.y1=(short int) event.xbutton.y;
02984         segment_info.y2=(short int) event.xbutton.y;
02985         state|=ExitState;
02986         break;
02987       }
02988       case ButtonRelease:
02989         break;
02990       case Expose:
02991         break;
02992       case KeyPress:
02993       {
02994         char
02995           command[MaxTextExtent];
02996 
02997         KeySym
02998           key_symbol;
02999 
03000         if (event.xkey.window != windows->image.id)
03001           break;
03002         /*
03003           Respond to a user key press.
03004         */
03005         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
03006           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
03007         switch ((int) key_symbol)
03008         {
03009           case XK_Escape:
03010           case XK_F20:
03011           {
03012             /*
03013               Prematurely exit.
03014             */
03015             state|=EscapeState;
03016             state|=ExitState;
03017             break;
03018           }
03019           case XK_F1:
03020           case XK_Help:
03021           {
03022             (void) XSetFunction(display,windows->image.highlight_context,
03023               GXcopy);
03024             XTextViewWidget(display,resource_info,windows,MagickFalse,
03025               "Help Viewer - Image Chop",ImageChopHelp);
03026             (void) XSetFunction(display,windows->image.highlight_context,
03027               GXinvert);
03028             break;
03029           }
03030           default:
03031           {
03032             (void) XBell(display,0);
03033             break;
03034           }
03035         }
03036         break;
03037       }
03038       case MotionNotify:
03039       {
03040         /*
03041           Map and unmap Info widget as text cursor crosses its boundaries.
03042         */
03043         x=event.xmotion.x;
03044         y=event.xmotion.y;
03045         if (windows->info.mapped != MagickFalse)
03046           {
03047             if ((x < (int) (windows->info.x+windows->info.width)) &&
03048                 (y < (int) (windows->info.y+windows->info.height)))
03049               (void) XWithdrawWindow(display,windows->info.id,
03050                 windows->info.screen);
03051           }
03052         else
03053           if ((x > (int) (windows->info.x+windows->info.width)) ||
03054               (y > (int) (windows->info.y+windows->info.height)))
03055             (void) XMapWindow(display,windows->info.id);
03056       }
03057     }
03058   } while ((state & ExitState) == 0);
03059   (void) XSelectInput(display,windows->image.id,
03060     windows->image.attributes.event_mask);
03061   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03062   if ((state & EscapeState) != 0)
03063     return(MagickTrue);
03064   /*
03065     Draw line as pointer moves until the mouse button is released.
03066   */
03067   chop_info.width=0;
03068   chop_info.height=0;
03069   chop_info.x=0;
03070   chop_info.y=0;
03071   distance=0;
03072   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
03073   state=DefaultState;
03074   do
03075   {
03076     if (distance > 9)
03077       {
03078         /*
03079           Display info and draw chopping line.
03080         */
03081         if (windows->info.mapped == MagickFalse)
03082           (void) XMapWindow(display,windows->info.id);
03083         (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
03084           chop_info.width,chop_info.height,chop_info.x,chop_info.y);
03085         XInfoWidget(display,windows,text);
03086         XHighlightLine(display,windows->image.id,
03087           windows->image.highlight_context,&segment_info);
03088       }
03089     else
03090       if (windows->info.mapped != MagickFalse)
03091         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03092     /*
03093       Wait for next event.
03094     */
03095     XScreenEvent(display,windows,&event);
03096     if (distance > 9)
03097       XHighlightLine(display,windows->image.id,
03098         windows->image.highlight_context,&segment_info);
03099     switch (event.type)
03100     {
03101       case ButtonPress:
03102       {
03103         segment_info.x2=(short int) event.xmotion.x;
03104         segment_info.y2=(short int) event.xmotion.y;
03105         break;
03106       }
03107       case ButtonRelease:
03108       {
03109         /*
03110           User has committed to chopping line.
03111         */
03112         segment_info.x2=(short int) event.xbutton.x;
03113         segment_info.y2=(short int) event.xbutton.y;
03114         state|=ExitState;
03115         break;
03116       }
03117       case Expose:
03118         break;
03119       case MotionNotify:
03120       {
03121         segment_info.x2=(short int) event.xmotion.x;
03122         segment_info.y2=(short int) event.xmotion.y;
03123       }
03124       default:
03125         break;
03126     }
03127     /*
03128       Check boundary conditions.
03129     */
03130     if (segment_info.x2 < 0)
03131       segment_info.x2=0;
03132     else
03133       if (segment_info.x2 > windows->image.ximage->width)
03134         segment_info.x2=windows->image.ximage->width;
03135     if (segment_info.y2 < 0)
03136       segment_info.y2=0;
03137     else
03138       if (segment_info.y2 > windows->image.ximage->height)
03139         segment_info.y2=windows->image.ximage->height;
03140     distance=(unsigned int)
03141       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
03142        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
03143     /*
03144       Compute chopping geometry.
03145     */
03146     if (direction == HorizontalChopCommand)
03147       {
03148         chop_info.width=(unsigned long) (segment_info.x2-segment_info.x1+1);
03149         chop_info.x=windows->image.x+segment_info.x1;
03150         chop_info.height=0;
03151         chop_info.y=0;
03152         if (segment_info.x1 > (int) segment_info.x2)
03153           {
03154             chop_info.width=(unsigned long) (segment_info.x1-segment_info.x2+1);
03155             chop_info.x=windows->image.x+segment_info.x2;
03156           }
03157       }
03158     else
03159       {
03160         chop_info.width=0;
03161         chop_info.height=(unsigned long) (segment_info.y2-segment_info.y1+1);
03162         chop_info.x=0;
03163         chop_info.y=windows->image.y+segment_info.y1;
03164         if (segment_info.y1 > segment_info.y2)
03165           {
03166             chop_info.height=(unsigned long)
03167               (segment_info.y1-segment_info.y2+1);
03168             chop_info.y=windows->image.y+segment_info.y2;
03169           }
03170       }
03171   } while ((state & ExitState) == 0);
03172   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
03173   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
03174   if (distance <= 9)
03175     return(MagickTrue);
03176   /*
03177     Image chopping is relative to image configuration.
03178   */
03179   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
03180   XSetCursorState(display,windows,MagickTrue);
03181   XCheckRefreshWindows(display,windows);
03182   windows->image.window_changes.width=windows->image.ximage->width-
03183     (unsigned int) chop_info.width;
03184   windows->image.window_changes.height=windows->image.ximage->height-
03185     (unsigned int) chop_info.height;
03186   width=(unsigned int) (*image)->columns;
03187   height=(unsigned int) (*image)->rows;
03188   x=0;
03189   y=0;
03190   if (windows->image.crop_geometry != (char *) NULL)
03191     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
03192   scale_factor=(MagickRealType) width/windows->image.ximage->width;
03193   chop_info.x+=x;
03194   chop_info.x=(int) (scale_factor*chop_info.x+0.5);
03195   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
03196   scale_factor=(MagickRealType) height/windows->image.ximage->height;
03197   chop_info.y+=y;
03198   chop_info.y=(int) (scale_factor*chop_info.y+0.5);
03199   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
03200   /*
03201     Chop image.
03202   */
03203   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
03204   XSetCursorState(display,windows,MagickFalse);
03205   if (chop_image == (Image *) NULL)
03206     return(MagickFalse);
03207   *image=DestroyImage(*image);
03208   *image=chop_image;
03209   /*
03210     Update image configuration.
03211   */
03212   XConfigureImageColormap(display,resource_info,windows,*image);
03213   (void) XConfigureImage(display,resource_info,windows,*image);
03214   return(MagickTrue);
03215 }
03216 
03217 /*
03218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03219 %                                                                             %
03220 %                                                                             %
03221 %                                                                             %
03222 +   X C o l o r E d i t I m a g e                                             %
03223 %                                                                             %
03224 %                                                                             %
03225 %                                                                             %
03226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03227 %
03228 %  XColorEditImage() allows the user to interactively change the color of one
03229 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
03230 %
03231 %  The format of the XColorEditImage method is:
03232 %
03233 %      MagickBooleanType XColorEditImage(Display *display,
03234 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
03235 %
03236 %  A description of each parameter follows:
03237 %
03238 %    o display: Specifies a connection to an X server;  returned from
03239 %      XOpenDisplay.
03240 %
03241 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
03242 %
03243 %    o windows: Specifies a pointer to a XWindows structure.
03244 %
03245 %    o image: the image; returned from ReadImage.
03246 %
03247 */
03248 
03249 
03250 static MagickBooleanType XColorEditImage(Display *display,
03251   XResourceInfo *resource_info,XWindows *windows,Image **image)
03252 {
03253   static const char
03254     *ColorEditMenu[] =
03255     {
03256       "Method",
03257       "Pixel Color",
03258       "Border Color",
03259       "Fuzz",
03260       "Undo",
03261       "Help",
03262       "Dismiss",
03263       (char *) NULL
03264     };
03265 
03266   static const ModeType
03267     ColorEditCommands[] =
03268     {
03269       ColorEditMethodCommand,
03270       ColorEditColorCommand,
03271       ColorEditBorderCommand,
03272       ColorEditFuzzCommand,
03273       ColorEditUndoCommand,
03274       ColorEditHelpCommand,
03275       ColorEditDismissCommand
03276     };
03277 
03278   static PaintMethod
03279     method = PointMethod;
03280 
03281   static unsigned int
03282     pen_id = 0;
03283 
03284   static XColor
03285     border_color = { 0, 0, 0, 0, 0, 0 };
03286 
03287   char
03288     command[MaxTextExtent],
03289     text[MaxTextExtent];
03290 
03291   Cursor
03292     cursor;
03293 
03294   ExceptionInfo
03295     *exception;
03296 
03297   int
03298     entry,
03299     id,
03300     x,
03301     x_offset,
03302     y,
03303     y_offset;
03304 
03305   register PixelPacket
03306     *q;
03307 
03308   register long
03309     i;
03310 
03311   unsigned int
03312     height,
03313     width;
03314 
03315   unsigned long
03316     state;
03317 
03318   XColor
03319     color;
03320 
03321   XEvent
03322     event;
03323 
03324   /*
03325     Map Command widget.
03326   */
03327   (void) CloneString(&windows->command.name,"Color Edit");
03328   windows->command.data=4;
03329   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
03330   (void) XMapRaised(display,windows->command.id);
03331   XClientMessage(display,windows->image.id,windows->im_protocols,
03332     windows->im_update_widget,CurrentTime);
03333   /*
03334     Make cursor.
03335   */
03336   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
03337     resource_info->background_color,resource_info->foreground_color);
03338   (void) XCheckDefineCursor(display,windows->image.id,cursor);
03339   /*
03340     Track pointer until button 1 is pressed.
03341   */
03342   XQueryPosition(display,windows->image.id,&x,&y);
03343   (void) XSelectInput(display,windows->image.id,
03344     windows->image.attributes.event_mask | PointerMotionMask);
03345   state=DefaultState;
03346   do
03347   {
03348     if (windows->info.mapped != MagickFalse)
03349       {
03350         /*
03351           Display pointer position.
03352         */
03353         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
03354           x+windows->image.x,y+windows->image.y);
03355         XInfoWidget(display,windows,text);
03356       }
03357     /*
03358       Wait for next event.
03359     */
03360     XScreenEvent(display,windows,&event);
03361     if (event.xany.window == windows->command.id)
03362       {
03363         /*
03364           Select a command from the Command widget.
03365         */
03366         id=XCommandWidget(display,windows,ColorEditMenu,&event);
03367         if (id < 0)
03368           {
03369             (void) XCheckDefineCursor(display,windows->image.id,cursor);
03370             continue;
03371           }
03372         switch (ColorEditCommands[id])
03373         {
03374           case ColorEditMethodCommand:
03375           {
03376             char
03377               **methods;
03378 
03379             /*
03380               Select a method from the pop-up menu.
03381             */
03382             methods=(char **) GetMagickOptions(MagickMethodOptions);
03383             if (methods == (char **) NULL)
03384               break;
03385             entry=XMenuWidget(display,windows,ColorEditMenu[id],
03386               (const char **) methods,command);
03387             if (entry >= 0)
03388               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
03389                 MagickFalse,methods[entry]);
03390             methods=DestroyStringList(methods);
03391             break;
03392           }
03393           case ColorEditColorCommand:
03394           {
03395             const char
03396               *ColorMenu[MaxNumberPens];
03397 
03398             int
03399               pen_number;
03400 
03401             /*
03402               Initialize menu selections.
03403             */
03404             for (i=0; i < (int) (MaxNumberPens-2); i++)
03405               ColorMenu[i]=resource_info->pen_colors[i];
03406             ColorMenu[MaxNumberPens-2]="Browser...";
03407             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
03408             /*
03409               Select a pen color from the pop-up menu.
03410             */
03411             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
03412               (const char **) ColorMenu,command);
03413             if (pen_number < 0)
03414               break;
03415             if (pen_number == (MaxNumberPens-2))
03416               {
03417                 static char
03418                   color_name[MaxTextExtent] = "gray";
03419 
03420                 /*
03421                   Select a pen color from a dialog.
03422                 */
03423                 resource_info->pen_colors[pen_number]=color_name;
03424                 XColorBrowserWidget(display,windows,"Select",color_name);
03425                 if (*color_name == '\0')
03426                   break;
03427               }
03428             /*
03429               Set pen color.
03430             */
03431             (void) XParseColor(display,windows->map_info->colormap,
03432               resource_info->pen_colors[pen_number],&color);
03433             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
03434               (unsigned int) MaxColors,&color);
03435             windows->pixel_info->pen_colors[pen_number]=color;
03436             pen_id=(unsigned int) pen_number;
03437             break;
03438           }
03439           case ColorEditBorderCommand:
03440           {
03441             const char
03442               *ColorMenu[MaxNumberPens];
03443 
03444             int
03445               pen_number;
03446 
03447             /*
03448               Initialize menu selections.
03449             */
03450             for (i=0; i < (int) (MaxNumberPens-2); i++)
03451               ColorMenu[i]=resource_info->pen_colors[i];
03452             ColorMenu[MaxNumberPens-2]="Browser...";
03453             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
03454             /*
03455               Select a pen color from the pop-up menu.
03456             */
03457             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
03458               (const char **) ColorMenu,command);
03459             if (pen_number < 0)
03460               break;
03461             if (pen_number == (MaxNumberPens-2))
03462               {
03463                 static char
03464                   color_name[MaxTextExtent] = "gray";
03465 
03466                 /*
03467                   Select a pen color from a dialog.
03468                 */
03469                 resource_info->pen_colors[pen_number]=color_name;
03470                 XColorBrowserWidget(display,windows,"Select",color_name);
03471                 if (*color_name == '\0')
03472                   break;
03473               }
03474             /*
03475               Set border color.
03476             */
03477             (void) XParseColor(display,windows->map_info->colormap,
03478               resource_info->pen_colors[pen_number],&border_color);
03479             break;
03480           }
03481           case ColorEditFuzzCommand:
03482           {
03483             static char
03484               fuzz[MaxTextExtent];
03485 
03486             static const char
03487               *FuzzMenu[] =
03488               {
03489                 "0%",
03490                 "2%",
03491                 "5%",
03492                 "10%",
03493                 "15%",
03494                 "Dialog...",
03495                 (char *) NULL,
03496               };
03497 
03498             /*
03499               Select a command from the pop-up menu.
03500             */
03501             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
03502               command);
03503             if (entry < 0)
03504               break;
03505             if (entry != 5)
03506               {
03507                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
03508                   QuantumRange+1.0);
03509                 break;
03510               }
03511             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
03512             (void) XDialogWidget(display,windows,"Ok",
03513               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
03514             if (*fuzz == '\0')
03515               break;
03516             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
03517             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
03518             break;
03519           }
03520           case ColorEditUndoCommand:
03521           {
03522             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
03523               image);
03524             break;
03525           }
03526           case ColorEditHelpCommand:
03527           default:
03528           {
03529             XTextViewWidget(display,resource_info,windows,MagickFalse,
03530               "Help Viewer - Image Annotation",ImageColorEditHelp);
03531             break;
03532           }
03533           case ColorEditDismissCommand:
03534           {
03535             /*
03536               Prematurely exit.
03537             */
03538             state|=EscapeState;
03539             state|=ExitState;
03540             break;
03541           }
03542         }
03543         (void) XCheckDefineCursor(display,windows->image.id,cursor);
03544         continue;
03545       }
03546     switch (event.type)
03547     {
03548       case ButtonPress:
03549       {
03550         if (event.xbutton.button != Button1)
03551           break;
03552         if ((event.xbutton.window != windows->image.id) &&
03553             (event.xbutton.window != windows->magnify.id))
03554           break;
03555         /*
03556           exit loop.
03557         */
03558         x=event.xbutton.x;
03559         y=event.xbutton.y;
03560         (void) XMagickCommand(display,resource_info,windows,
03561           SaveToUndoBufferCommand,image);
03562         state|=UpdateConfigurationState;
03563         break;
03564       }
03565       case ButtonRelease:
03566       {
03567         if (event.xbutton.button != Button1)
03568           break;
03569         if ((event.xbutton.window != windows->image.id) &&
03570             (event.xbutton.window != windows->magnify.id))
03571           break;
03572         /*
03573           Update colormap information.
03574         */
03575         x=event.xbutton.x;
03576         y=event.xbutton.y;
03577         XConfigureImageColormap(display,resource_info,windows,*image);
03578         (void) XConfigureImage(display,resource_info,windows,*image);
03579         XInfoWidget(display,windows,text);
03580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
03581         state&=(~UpdateConfigurationState);
03582         break;
03583       }
03584       case Expose:
03585         break;
03586       case KeyPress:
03587       {
03588         KeySym
03589           key_symbol;
03590 
03591         if (event.xkey.window == windows->magnify.id)
03592           {
03593             Window
03594               window;
03595 
03596             window=windows->magnify.id;
03597             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
03598           }
03599         if (event.xkey.window != windows->image.id)
03600           break;
03601         /*
03602           Respond to a user key press.
03603         */
03604         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
03605           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
03606         switch ((int) key_symbol)
03607         {
03608           case XK_Escape:
03609           case XK_F20:
03610           {
03611             /*
03612               Prematurely exit.
03613             */
03614             state|=ExitState;
03615             break;
03616           }
03617           case XK_F1:
03618           case XK_Help:
03619           {
03620             XTextViewWidget(display,resource_info,windows,MagickFalse,
03621               "Help Viewer - Image Annotation",ImageColorEditHelp);
03622             break;
03623           }
03624           default:
03625           {
03626             (void) XBell(display,0);
03627             break;
03628           }
03629         }
03630         break;
03631       }
03632       case MotionNotify:
03633       {
03634         /*
03635           Map and unmap Info widget as cursor crosses its boundaries.
03636         */
03637         x=event.xmotion.x;
03638         y=event.xmotion.y;
03639         if (windows->info.mapped != MagickFalse)
03640           {
03641             if ((x < (int) (windows->info.x+windows->info.width)) &&
03642                 (y < (int) (windows->info.y+windows->info.height)))
03643               (void) XWithdrawWindow(display,windows->info.id,
03644                 windows->info.screen);
03645           }
03646         else
03647           if ((x > (int) (windows->info.x+windows->info.width)) ||
03648               (y > (int) (windows->info.y+windows->info.height)))
03649             (void) XMapWindow(display,windows->info.id);
03650         break;
03651       }
03652       default:
03653         break;
03654     }
03655     if (event.xany.window == windows->magnify.id)
03656       {
03657         x=windows->magnify.x-windows->image.x;
03658         y=windows->magnify.y-windows->image.y;
03659       }
03660     x_offset=x;
03661     y_offset=y;
03662     if ((state & UpdateConfigurationState) != 0)
03663       {
03664         int
03665           x,
03666           y;
03667 
03668         /*
03669           Pixel edit is relative to image configuration.
03670         */
03671         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
03672           MagickTrue);
03673         color=windows->pixel_info->pen_colors[pen_id];
03674         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
03675         width=(unsigned int) (*image)->columns;
03676         height=(unsigned int) (*image)->rows;
03677         x=0;
03678         y=0;
03679         if (windows->image.crop_geometry != (char *) NULL)
03680           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
03681             &width,&height);
03682         x_offset=(int)
03683           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
03684         y_offset=(int)
03685           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
03686         if ((x_offset < 0) || (y_offset < 0))
03687           continue;
03688         if ((x_offset >= (long) (*image)->columns) ||
03689             (y_offset >= (long) (*image)->rows))
03690           continue;
03691         exception=(&(*image)->exception);
03692         switch (method)
03693         {
03694           case PointMethod:
03695           default:
03696           {
03697             /*
03698               Update color information using point algorithm.
03699             */
03700             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
03701               return(MagickFalse);
03702             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
03703             if (q == (PixelPacket *) NULL)
03704               break;
03705             q->red=ScaleShortToQuantum(color.red);
03706             q->green=ScaleShortToQuantum(color.green);
03707             q->blue=ScaleShortToQuantum(color.blue);
03708             (void) SyncAuthenticPixels(*image,&(*image)->exception);
03709             break;
03710           }
03711           case ReplaceMethod:
03712           {
03713             PixelPacket
03714               target;
03715 
03716             /*
03717               Update color information using replace algorithm.
03718             */
03719             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
03720               &(*image)->exception);
03721             if ((*image)->storage_class == DirectClass)
03722               {
03723                 for (y=0; y < (long) (*image)->rows; y++)
03724                 {
03725                   q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
03726                     exception);
03727                   if (q == (PixelPacket *) NULL)
03728                     break;
03729                   for (x=0; x < (int) (*image)->columns; x++)
03730                   {
03731                     if (IsColorSimilar(*image,q,&target))
03732                       {
03733                         q->red=ScaleShortToQuantum(color.red);
03734                         q->green=ScaleShortToQuantum(color.green);
03735                         q->blue=ScaleShortToQuantum(color.blue);
03736                       }
03737                     q++;
03738                   }
03739                   if (SyncAuthenticPixels(*image,exception) == MagickFalse)
03740                     break;
03741                 }
03742               }
03743             else
03744               {
03745                 for (i=0; i < (int) (*image)->colors; i++)
03746                   if (IsColorSimilar(*image,(*image)->colormap+i,&target))
03747                     {
03748                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
03749                       (*image)->colormap[i].green=ScaleShortToQuantum(
03750                         color.green);
03751                       (*image)->colormap[i].blue=ScaleShortToQuantum(
03752                         color.blue);
03753                     }
03754                 (void) SyncImage(*image);
03755               }
03756             break;
03757           }
03758           case FloodfillMethod:
03759           case FillToBorderMethod:
03760           {
03761             DrawInfo
03762               *draw_info;
03763 
03764             MagickPixelPacket
03765               target;
03766 
03767             /*
03768               Update color information using floodfill algorithm.
03769             */
03770             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
03771               exception);
03772             if (method == FillToBorderMethod)
03773               {
03774                 target.red=(MagickRealType)
03775                   ScaleShortToQuantum(border_color.red);
03776                 target.green=(MagickRealType)
03777                   ScaleShortToQuantum(border_color.green);
03778                 target.blue=(MagickRealType)
03779                   ScaleShortToQuantum(border_color.blue);
03780               }
03781             draw_info=CloneDrawInfo(resource_info->image_info,
03782               (DrawInfo *) NULL);
03783             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
03784               &draw_info->fill,exception);
03785             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
03786               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
03787               MagickTrue);
03788             draw_info=DestroyDrawInfo(draw_info);
03789             break;
03790           }
03791           case ResetMethod:
03792           {
03793             /*
03794               Update color information using reset algorithm.
03795             */
03796             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
03797               return(MagickFalse);
03798             for (y=0; y < (long) (*image)->rows; y++)
03799             {
03800               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
03801               if (q == (PixelPacket *) NULL)
03802                 break;
03803               for (x=0; x < (int) (*image)->columns; x++)
03804               {
03805                 q->red=ScaleShortToQuantum(color.red);
03806                 q->green=ScaleShortToQuantum(color.green);
03807                 q->blue=ScaleShortToQuantum(color.blue);
03808                 q++;
03809               }
03810               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
03811                 break;
03812             }
03813             break;
03814           }
03815         }
03816         state&=(~UpdateConfigurationState);
03817       }
03818   } while ((state & ExitState) == 0);
03819   (void) XSelectInput(display,windows->image.id,
03820     windows->image.attributes.event_mask);
03821   XSetCursorState(display,windows,MagickFalse);
03822   (void) XFreeCursor(display,cursor);
03823   return(MagickTrue);
03824 }
03825 
03826 /*
03827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03828 %                                                                             %
03829 %                                                                             %
03830 %                                                                             %
03831 +   X C o m p o s i t e I m a g e                                             %
03832 %                                                                             %
03833 %                                                                             %
03834 %                                                                             %
03835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
03836 %
03837 %  XCompositeImage() requests an image name from the user, reads the image and
03838 %  composites it with the X window image at a location the user chooses with
03839 %  the pointer.
03840 %
03841 %  The format of the XCompositeImage method is:
03842 %
03843 %      MagickBooleanType XCompositeImage(Display *display,
03844 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
03845 %
03846 %  A description of each parameter follows:
03847 %
03848 %    o display: Specifies a connection to an X server;  returned from
03849 %      XOpenDisplay.
03850 %
03851 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
03852 %
03853 %    o windows: Specifies a pointer to a XWindows structure.
03854 %
03855 %    o image: the image; returned from ReadImage.
03856 %
03857 */
03858 static MagickBooleanType XCompositeImage(Display *display,
03859   XResourceInfo *resource_info,XWindows *windows,Image *image)
03860 {
03861   static char
03862     displacement_geometry[MaxTextExtent] = "30x30",
03863     filename[MaxTextExtent] = "\0";
03864 
03865   static const char
03866     *CompositeMenu[] =
03867     {
03868       "Operators",
03869       "Dissolve",
03870       "Displace",
03871       "Help",
03872       "Dismiss",
03873       (char *) NULL
03874     };
03875 
03876   static CompositeOperator
03877     compose = CopyCompositeOp;
03878 
03879   static const ModeType
03880     CompositeCommands[] =
03881     {
03882       CompositeOperatorsCommand,
03883       CompositeDissolveCommand,
03884       CompositeDisplaceCommand,
03885       CompositeHelpCommand,
03886       CompositeDismissCommand
03887     };
03888 
03889   char
03890     text[MaxTextExtent];
03891 
03892   Cursor
03893     cursor;
03894 
03895   Image
03896     *composite_image;
03897 
03898   int
03899     entry,
03900     id,
03901     x,
03902     y;
03903 
03904   MagickRealType
03905     blend,
03906     scale_factor;
03907 
03908   RectangleInfo
03909     highlight_info,
03910     composite_info;
03911 
03912   unsigned int
03913     height,
03914     width;
03915 
03916   unsigned long
03917     state;
03918 
03919   XEvent
03920     event;
03921 
03922   /*
03923     Request image file name from user.
03924   */
03925   XFileBrowserWidget(display,windows,"Composite",filename);
03926   if (*filename == '\0')
03927     return(MagickTrue);
03928   /*
03929     Read image.
03930   */
03931   XSetCursorState(display,windows,MagickTrue);
03932   XCheckRefreshWindows(display,windows);
03933   (void) CopyMagickString(resource_info->image_info->filename,filename,
03934     MaxTextExtent);
03935   composite_image=ReadImage(resource_info->image_info,&image->exception);
03936   CatchException(&image->exception);
03937   XSetCursorState(display,windows,MagickFalse);
03938   if (composite_image == (Image *) NULL)
03939     return(MagickFalse);
03940   /*
03941     Map Command widget.
03942   */
03943   (void) CloneString(&windows->command.name,"Composite");
03944   windows->command.data=1;
03945   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
03946   (void) XMapRaised(display,windows->command.id);
03947   XClientMessage(display,windows->image.id,windows->im_protocols,
03948     windows->im_update_widget,CurrentTime);
03949   /*
03950     Track pointer until button 1 is pressed.
03951   */
03952   XQueryPosition(display,windows->image.id,&x,&y);
03953   (void) XSelectInput(display,windows->image.id,
03954     windows->image.attributes.event_mask | PointerMotionMask);
03955   composite_info.x=windows->image.x+x;
03956   composite_info.y=windows->image.y+y;
03957   composite_info.width=0;
03958   composite_info.height=0;
03959   cursor=XCreateFontCursor(display,XC_ul_angle);
03960   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
03961   blend=0.0;
03962   state=DefaultState;
03963   do
03964   {
03965     if (windows->info.mapped != MagickFalse)
03966       {
03967         /*
03968           Display pointer position.
03969         */
03970         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
03971           composite_info.x,composite_info.y);
03972         XInfoWidget(display,windows,text);
03973       }
03974     highlight_info=composite_info;
03975     highlight_info.x=composite_info.x-windows->image.x;
03976     highlight_info.y=composite_info.y-windows->image.y;
03977     XHighlightRectangle(display,windows->image.id,
03978       windows->image.highlight_context,&highlight_info);
03979     /*
03980       Wait for next event.
03981     */
03982     XScreenEvent(display,windows,&event);
03983     XHighlightRectangle(display,windows->image.id,
03984       windows->image.highlight_context,&highlight_info);
03985     if (event.xany.window == windows->command.id)
03986       {
03987         /*
03988           Select a command from the Command widget.
03989         */
03990         id=XCommandWidget(display,windows,CompositeMenu,&event);
03991         if (id < 0)
03992           continue;
03993         switch (CompositeCommands[id])
03994         {
03995           case CompositeOperatorsCommand:
03996           {
03997             char
03998               command[MaxTextExtent],
03999               **operators;
04000 
04001             /*
04002               Select a command from the pop-up menu.
04003             */
04004             operators=GetMagickOptions(MagickComposeOptions);
04005             if (operators == (char **) NULL)
04006               break;
04007             entry=XMenuWidget(display,windows,CompositeMenu[id],
04008               (const char **) operators,command);
04009             if (entry >= 0)
04010               compose=(CompositeOperator) ParseMagickOption(
04011                 MagickComposeOptions,MagickFalse,operators[entry]);
04012             operators=DestroyStringList(operators);
04013             break;
04014           }
04015           case CompositeDissolveCommand:
04016           {
04017             static char
04018               factor[MaxTextExtent] = "20.0";
04019 
04020             /*
04021               Dissolve the two images a given percent.
04022             */
04023             (void) XSetFunction(display,windows->image.highlight_context,
04024               GXcopy);
04025             (void) XDialogWidget(display,windows,"Dissolve",
04026               "Enter the blend factor (0.0 - 99.9%):",factor);
04027             (void) XSetFunction(display,windows->image.highlight_context,
04028               GXinvert);
04029             if (*factor == '\0')
04030               break;
04031             blend=StringToDouble(factor);
04032             compose=DissolveCompositeOp;
04033             break;
04034           }
04035           case CompositeDisplaceCommand:
04036           {
04037             /*
04038               Get horizontal and vertical scale displacement geometry.
04039             */
04040             (void) XSetFunction(display,windows->image.highlight_context,
04041               GXcopy);
04042             (void) XDialogWidget(display,windows,"Displace",
04043               "Enter the horizontal and vertical scale:",displacement_geometry);
04044             (void) XSetFunction(display,windows->image.highlight_context,
04045               GXinvert);
04046             if (*displacement_geometry == '\0')
04047               break;
04048             compose=DisplaceCompositeOp;
04049             break;
04050           }
04051           case CompositeHelpCommand:
04052           {
04053             (void) XSetFunction(display,windows->image.highlight_context,
04054               GXcopy);
04055             XTextViewWidget(display,resource_info,windows,MagickFalse,
04056               "Help Viewer - Image Composite",ImageCompositeHelp);
04057             (void) XSetFunction(display,windows->image.highlight_context,
04058               GXinvert);
04059             break;
04060           }
04061           case CompositeDismissCommand:
04062           {
04063             /*
04064               Prematurely exit.
04065             */
04066             state|=EscapeState;
04067             state|=ExitState;
04068             break;
04069           }
04070           default:
04071             break;
04072         }
04073         continue;
04074       }
04075     switch (event.type)
04076     {
04077       case ButtonPress:
04078       {
04079         if (image->debug != MagickFalse)
04080           (void) LogMagickEvent(X11Event,GetMagickModule(),
04081             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
04082             event.xbutton.button,event.xbutton.x,event.xbutton.y);
04083         if (event.xbutton.button != Button1)
04084           break;
04085         if (event.xbutton.window != windows->image.id)
04086           break;
04087         /*
04088           Change cursor.
04089         */
04090         composite_info.width=composite_image->columns;
04091         composite_info.height=composite_image->rows;
04092         (void) XCheckDefineCursor(display,windows->image.id,cursor);
04093         composite_info.x=windows->image.x+event.xbutton.x;
04094         composite_info.y=windows->image.y+event.xbutton.y;
04095         break;
04096       }
04097       case ButtonRelease:
04098       {
04099         if (image->debug != MagickFalse)
04100           (void) LogMagickEvent(X11Event,GetMagickModule(),
04101             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
04102             event.xbutton.button,event.xbutton.x,event.xbutton.y);
04103         if (event.xbutton.button != Button1)
04104           break;
04105         if (event.xbutton.window != windows->image.id)
04106           break;
04107         if ((composite_info.width != 0) && (composite_info.height != 0))
04108           {
04109             /*
04110               User has selected the location of the composite image.
04111             */
04112             composite_info.x=windows->image.x+event.xbutton.x;
04113             composite_info.y=windows->image.y+event.xbutton.y;
04114             state|=ExitState;
04115           }
04116         break;
04117       }
04118       case Expose:
04119         break;
04120       case KeyPress:
04121       {
04122         char
04123           command[MaxTextExtent];
04124 
04125         KeySym
04126           key_symbol;
04127 
04128         int
04129           length;
04130 
04131         if (event.xkey.window != windows->image.id)
04132           break;
04133         /*
04134           Respond to a user key press.
04135         */
04136         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
04137           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
04138         *(command+length)='\0';
04139         if (image->debug != MagickFalse)
04140           (void) LogMagickEvent(X11Event,GetMagickModule(),
04141             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
04142         switch ((int) key_symbol)
04143         {
04144           case XK_Escape:
04145           case XK_F20:
04146           {
04147             /*
04148               Prematurely exit.
04149             */
04150             composite_image=DestroyImage(composite_image);
04151             state|=EscapeState;
04152             state|=ExitState;
04153             break;
04154           }
04155           case XK_F1:
04156           case XK_Help:
04157           {
04158             (void) XSetFunction(display,windows->image.highlight_context,
04159               GXcopy);
04160             XTextViewWidget(display,resource_info,windows,MagickFalse,
04161               "Help Viewer - Image Composite",ImageCompositeHelp);
04162             (void) XSetFunction(display,windows->image.highlight_context,
04163               GXinvert);
04164             break;
04165           }
04166           default:
04167           {
04168             (void) XBell(display,0);
04169             break;
04170           }
04171         }
04172         break;
04173       }
04174       case MotionNotify:
04175       {
04176         /*
04177           Map and unmap Info widget as text cursor crosses its boundaries.
04178         */
04179         x=event.xmotion.x;
04180         y=event.xmotion.y;
04181         if (windows->info.mapped != MagickFalse)
04182           {
04183             if ((x < (int) (windows->info.x+windows->info.width)) &&
04184                 (y < (int) (windows->info.y+windows->info.height)))
04185               (void) XWithdrawWindow(display,windows->info.id,
04186                 windows->info.screen);
04187           }
04188         else
04189           if ((x > (int) (windows->info.x+windows->info.width)) ||
04190               (y > (int) (windows->info.y+windows->info.height)))
04191             (void) XMapWindow(display,windows->info.id);
04192         composite_info.x=windows->image.x+x;
04193         composite_info.y=windows->image.y+y;
04194         break;
04195       }
04196       default:
04197       {
04198         if (image->debug != MagickFalse)
04199           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
04200             event.type);
04201         break;
04202       }
04203     }
04204   } while ((state & ExitState) == 0);
04205   (void) XSelectInput(display,windows->image.id,
04206     windows->image.attributes.event_mask);
04207   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
04208   XSetCursorState(display,windows,MagickFalse);
04209   (void) XFreeCursor(display,cursor);
04210   if ((state & EscapeState) != 0)
04211     return(MagickTrue);
04212   /*
04213     Image compositing is relative to image configuration.
04214   */
04215   XSetCursorState(display,windows,MagickTrue);
04216   XCheckRefreshWindows(display,windows);
04217   width=(unsigned int) image->columns;
04218   height=(unsigned int) image->rows;
04219   x=0;
04220   y=0;
04221   if (windows->image.crop_geometry != (char *) NULL)
04222     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
04223   scale_factor=(MagickRealType) width/windows->image.ximage->width;
04224   composite_info.x+=x;
04225   composite_info.x=(int) (scale_factor*composite_info.x+0.5);
04226   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
04227   scale_factor=(MagickRealType) height/windows->image.ximage->height;
04228   composite_info.y+=y;
04229   composite_info.y=(int) (scale_factor*composite_info.y+0.5);
04230   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
04231   if ((composite_info.width != composite_image->columns) ||
04232       (composite_info.height != composite_image->rows))
04233     {
04234       Image
04235         *resize_image;
04236 
04237       /*
04238         Scale composite image.
04239       */
04240       resize_image=ZoomImage(composite_image,composite_info.width,
04241         composite_info.height,&image->exception);
04242       composite_image=DestroyImage(composite_image);
04243       if (resize_image == (Image *) NULL)
04244         {
04245           XSetCursorState(display,windows,MagickFalse);
04246           return(MagickFalse);
04247         }
04248       composite_image=resize_image;
04249     }
04250   if (compose == DisplaceCompositeOp)
04251     (void) SetImageArtifact(composite_image,"compose:args",
04252       displacement_geometry);
04253   if (blend != 0.0)
04254     {
04255       ExceptionInfo
04256         *exception;
04257 
04258       int
04259         y;
04260 
04261       Quantum
04262         opacity;
04263 
04264       register int
04265         x;
04266 
04267       register PixelPacket
04268         *q;
04269 
04270       /*
04271         Create mattes for blending.
04272       */
04273       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
04274       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
04275         ((long) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
04276       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
04277         return(MagickFalse);
04278       image->matte=MagickTrue;
04279       exception=(&image->exception);
04280       for (y=0; y < (long) image->rows; y++)
04281       {
04282         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
04283         if (q == (PixelPacket *) NULL)
04284           break;
04285         for (x=0; x < (int) image->columns; x++)
04286         {
04287           q->opacity=opacity;
04288           q++;
04289         }
04290         if (SyncAuthenticPixels(image,exception) == MagickFalse)
04291           break;
04292       }
04293     }
04294   /*
04295     Composite image with X Image window.
04296   */
04297   (void) CompositeImage(image,compose,composite_image,composite_info.x,
04298     composite_info.y);
04299   composite_image=DestroyImage(composite_image);
04300   XSetCursorState(display,windows,MagickFalse);
04301   /*
04302     Update image configuration.
04303   */
04304   XConfigureImageColormap(display,resource_info,windows,image);
04305   (void) XConfigureImage(display,resource_info,windows,image);
04306   return(MagickTrue);
04307 }
04308 
04309 /*
04310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04311 %                                                                             %
04312 %                                                                             %
04313 %                                                                             %
04314 +   X C o n f i g u r e I m a g e                                             %
04315 %                                                                             %
04316 %                                                                             %
04317 %                                                                             %
04318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
04319 %
04320 %  XConfigureImage() creates a new X image.  It also notifies the window
04321 %  manager of the new image size and configures the transient widows.
04322 %
04323 %  The format of the XConfigureImage method is:
04324 %
04325 %      MagickBooleanType XConfigureImage(Display *display,
04326 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
04327 %
04328 %  A description of each parameter follows:
04329 %
04330 %    o display: Specifies a connection to an X server; returned from
04331 %      XOpenDisplay.
04332 %
04333 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
04334 %
04335 %    o windows: Specifies a pointer to a XWindows structure.
04336 %
04337 %    o image: the image.
04338 %
04339 %
04340 */
04341 static MagickBooleanType XConfigureImage(Display *display,
04342   XResourceInfo *resource_info,XWindows *windows,Image *image)
04343 {
04344   char
04345     geometry[MaxTextExtent];
04346 
04347   long
04348     x,
04349     y;
04350 
04351   MagickStatusType
04352     status;
04353 
04354   unsigned long
04355     mask,
04356     height,
04357     width;
04358 
04359   XSizeHints
04360     *size_hints;
04361 
04362   XWindowChanges
04363     window_changes;
04364 
04365   /*
04366     Dismiss if window dimensions are zero.
04367   */
04368   width=(unsigned int) windows->image.window_changes.width;
04369   height=(unsigned int) windows->image.window_changes.height;
04370   if (image->debug != MagickFalse)
04371     (void) LogMagickEvent(X11Event,GetMagickModule(),
04372       "Configure Image: %dx%d=>%lux%lu",windows->image.ximage->width,
04373       windows->image.ximage->height,width,height);
04374   if ((width*height) == 0)
04375     return(MagickTrue);
04376   x=0;
04377   y=0;
04378   /*
04379     Resize image to fit Image window dimensions.
04380   */
04381   XSetCursorState(display,windows,MagickTrue);
04382   (void) XFlush(display);
04383   if (((int) width != windows->image.ximage->width) ||
04384       ((int) height != windows->image.ximage->height))
04385     image->taint=MagickTrue;
04386   windows->magnify.x=(int)
04387     width*windows->magnify.x/windows->image.ximage->width;
04388   windows->magnify.y=(int)
04389     height*windows->magnify.y/windows->image.ximage->height;
04390   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
04391   windows->image.y=(int)
04392     (height*windows->image.y/windows->image.ximage->height);
04393   status=XMakeImage(display,resource_info,&windows->image,image,
04394     (unsigned int) width,(unsigned int) height);
04395   if (status == MagickFalse)
04396     XNoticeWidget(display,windows,"Unable to configure X image:",
04397       windows->image.name);
04398   /*
04399     Notify window manager of the new configuration.
04400   */
04401   if (resource_info->image_geometry != (char *) NULL)
04402     (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
04403       resource_info->image_geometry);
04404   else
04405     (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
04406       XDisplayWidth(display,windows->image.screen),
04407       XDisplayHeight(display,windows->image.screen));
04408   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
04409   window_changes.width=(int) width;
04410   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
04411     window_changes.width=XDisplayWidth(display,windows->image.screen);
04412   window_changes.height=(int) height;
04413   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
04414     window_changes.height=XDisplayHeight(display,windows->image.screen);
04415   mask=(unsigned long) (CWWidth | CWHeight);
04416   if (resource_info->backdrop)
04417     {
04418       mask|=CWX | CWY;
04419       window_changes.x=(int)
04420         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
04421       window_changes.y=(int)
04422         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
04423     }
04424   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
04425     (unsigned int) mask,&window_changes);
04426   (void) XClearWindow(display,windows->image.id);
04427   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
04428   /*
04429     Update Magnify window configuration.
04430   */
04431   if (windows->magnify.mapped != MagickFalse)
04432     XMakeMagnifyImage(display,windows);
04433   windows->pan.crop_geometry=windows->image.crop_geometry;
04434   XBestIconSize(display,&windows->pan,image);
04435   while (((windows->pan.width << 1) < MaxIconSize) &&
04436          ((windows->pan.height << 1) < MaxIconSize))
04437   {
04438     windows->pan.width<<=1;
04439     windows->pan.height<<=1;
04440   }
04441   if (windows->pan.geometry != (char *) NULL)
04442     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
04443       &windows->pan.width,&windows->pan.height);
04444   window_changes.width=(int) windows->pan.width;
04445   window_changes.height=(int) windows->pan.