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