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