• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                                  Cristy                                     %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2020 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/timer-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/transform-private.h"
93 #include "MagickCore/threshold.h"
94 #include "MagickCore/utility.h"
95 #include "MagickCore/utility-private.h"
96 #include "MagickCore/version.h"
97 #include "MagickCore/visual-effects.h"
98 #include "MagickCore/widget.h"
99 #include "MagickCore/widget-private.h"
100 #include "MagickCore/xwindow.h"
101 #include "MagickCore/xwindow-private.h"
102 
103 #if defined(MAGICKCORE_X11_DELEGATE)
104 /*
105   Define declarations.
106 */
107 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
108 
109 /*
110   Constant declarations.
111 */
112 static const unsigned char
113   HighlightBitmap[8] =
114   {
115     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
116   },
117   OpaqueBitmap[8] =
118   {
119     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
120   },
121   ShadowBitmap[8] =
122   {
123     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
124   };
125 
126 /*
127   Help widget declarations.
128 */
129 static const char
130   ImageAnnotateHelp[] =
131   {
132     "In annotate mode, the Command widget has these options:\n"
133     "\n"
134     "    Font Name\n"
135     "      fixed\n"
136     "      variable\n"
137     "      5x8\n"
138     "      6x10\n"
139     "      7x13bold\n"
140     "      8x13bold\n"
141     "      9x15bold\n"
142     "      10x20\n"
143     "      12x24\n"
144     "      Browser...\n"
145     "    Font Color\n"
146     "      black\n"
147     "      blue\n"
148     "      cyan\n"
149     "      green\n"
150     "      gray\n"
151     "      red\n"
152     "      magenta\n"
153     "      yellow\n"
154     "      white\n"
155     "      transparent\n"
156     "      Browser...\n"
157     "    Font Color\n"
158     "      black\n"
159     "      blue\n"
160     "      cyan\n"
161     "      green\n"
162     "      gray\n"
163     "      red\n"
164     "      magenta\n"
165     "      yellow\n"
166     "      white\n"
167     "      transparent\n"
168     "      Browser...\n"
169     "    Rotate Text\n"
170     "      -90\n"
171     "      -45\n"
172     "      -30\n"
173     "      0\n"
174     "      30\n"
175     "      45\n"
176     "      90\n"
177     "      180\n"
178     "      Dialog...\n"
179     "    Help\n"
180     "    Dismiss\n"
181     "\n"
182     "Choose a font name from the Font Name sub-menu.  Additional\n"
183     "font names can be specified with the font browser.  You can\n"
184     "change the menu names by setting the X resources font1\n"
185     "through font9.\n"
186     "\n"
187     "Choose a font color from the Font Color sub-menu.\n"
188     "Additional font colors can be specified with the color\n"
189     "browser.  You can change the menu colors by setting the X\n"
190     "resources pen1 through pen9.\n"
191     "\n"
192     "If you select the color browser and press Grab, you can\n"
193     "choose the font color by moving the pointer to the desired\n"
194     "color on the screen and press any button.\n"
195     "\n"
196     "If you choose to rotate the text, choose Rotate Text from the\n"
197     "menu and select an angle.  Typically you will only want to\n"
198     "rotate one line of text at a time.  Depending on the angle you\n"
199     "choose, subsequent lines may end up overwriting each other.\n"
200     "\n"
201     "Choosing a font and its color is optional.  The default font\n"
202     "is fixed and the default color is black.  However, you must\n"
203     "choose a location to begin entering text and press button 1.\n"
204     "An underscore character will appear at the location of the\n"
205     "pointer.  The cursor changes to a pencil to indicate you are\n"
206     "in text mode.  To exit immediately, press Dismiss.\n"
207     "\n"
208     "In text mode, any key presses will display the character at\n"
209     "the location of the underscore and advance the underscore\n"
210     "cursor.  Enter your text and once completed press Apply to\n"
211     "finish your image annotation.  To correct errors press BACK\n"
212     "SPACE.  To delete an entire line of text, press DELETE.  Any\n"
213     "text that exceeds the boundaries of the image window is\n"
214     "automagically continued onto the next line.\n"
215     "\n"
216     "The actual color you request for the font is saved in the\n"
217     "image.  However, the color that appears in your image window\n"
218     "may be different.  For example, on a monochrome screen the\n"
219     "text will appear black or white even if you choose the color\n"
220     "red as the font color.  However, the image saved to a file\n"
221     "with -write is written with red lettering.  To assure the\n"
222     "correct color text in the final image, any PseudoClass image\n"
223     "is promoted to DirectClass (see miff(5)).  To force a\n"
224     "PseudoClass image to remain PseudoClass, use -colors.\n"
225   },
226   ImageChopHelp[] =
227   {
228     "In chop mode, the Command widget has these options:\n"
229     "\n"
230     "    Direction\n"
231     "      horizontal\n"
232     "      vertical\n"
233     "    Help\n"
234     "    Dismiss\n"
235     "\n"
236     "If the you choose the horizontal direction (this the\n"
237     "default), the area of the image between the two horizontal\n"
238     "endpoints of the chop line is removed.  Otherwise, the area\n"
239     "of the image between the two vertical endpoints of the chop\n"
240     "line is removed.\n"
241     "\n"
242     "Select a location within the image window to begin your chop,\n"
243     "press and hold any button.  Next, move the pointer to\n"
244     "another location in the image.  As you move a line will\n"
245     "connect the initial location and the pointer.  When you\n"
246     "release the button, the area within the image to chop is\n"
247     "determined by which direction you choose from the Command\n"
248     "widget.\n"
249     "\n"
250     "To cancel the image chopping, move the pointer back to the\n"
251     "starting point of the line and release the button.\n"
252   },
253   ImageColorEditHelp[] =
254   {
255     "In color edit mode, the Command widget has these options:\n"
256     "\n"
257     "    Method\n"
258     "      point\n"
259     "      replace\n"
260     "      floodfill\n"
261     "      filltoborder\n"
262     "      reset\n"
263     "    Pixel Color\n"
264     "      black\n"
265     "      blue\n"
266     "      cyan\n"
267     "      green\n"
268     "      gray\n"
269     "      red\n"
270     "      magenta\n"
271     "      yellow\n"
272     "      white\n"
273     "      Browser...\n"
274     "    Border Color\n"
275     "      black\n"
276     "      blue\n"
277     "      cyan\n"
278     "      green\n"
279     "      gray\n"
280     "      red\n"
281     "      magenta\n"
282     "      yellow\n"
283     "      white\n"
284     "      Browser...\n"
285     "    Fuzz\n"
286     "      0%\n"
287     "      2%\n"
288     "      5%\n"
289     "      10%\n"
290     "      15%\n"
291     "      Dialog...\n"
292     "    Undo\n"
293     "    Help\n"
294     "    Dismiss\n"
295     "\n"
296     "Choose a color editing method from the Method sub-menu\n"
297     "of the Command widget.  The point method recolors any pixel\n"
298     "selected with the pointer until the button is released.  The\n"
299     "replace method recolors any pixel that matches the color of\n"
300     "the pixel you select with a button press.  Floodfill recolors\n"
301     "any pixel that matches the color of the pixel you select with\n"
302     "a button press and is a neighbor.  Whereas filltoborder recolors\n"
303     "any neighbor pixel that is not the border color.  Finally reset\n"
304     "changes the entire image to the designated color.\n"
305     "\n"
306     "Next, choose a pixel color from the Pixel Color sub-menu.\n"
307     "Additional pixel colors can be specified with the color\n"
308     "browser.  You can change the menu colors by setting the X\n"
309     "resources pen1 through pen9.\n"
310     "\n"
311     "Now press button 1 to select a pixel within the image window\n"
312     "to change its color.  Additional pixels may be recolored as\n"
313     "prescribed by the method you choose.\n"
314     "\n"
315     "If the Magnify widget is mapped, it can be helpful in positioning\n"
316     "your pointer within the image (refer to button 2).\n"
317     "\n"
318     "The actual color you request for the pixels is saved in the\n"
319     "image.  However, the color that appears in your image window\n"
320     "may be different.  For example, on a monochrome screen the\n"
321     "pixel will appear black or white even if you choose the\n"
322     "color red as the pixel color.  However, the image saved to a\n"
323     "file with -write is written with red pixels.  To assure the\n"
324     "correct color text in the final image, any PseudoClass image\n"
325     "is promoted to DirectClass (see miff(5)).  To force a\n"
326     "PseudoClass image to remain PseudoClass, use -colors.\n"
327   },
328   ImageCompositeHelp[] =
329   {
330     "First a widget window is displayed requesting you to enter an\n"
331     "image name. Press Composite, Grab or type a file name.\n"
332     "Press Cancel if you choose not to create a composite image.\n"
333     "When you choose Grab, move the pointer to the desired window\n"
334     "and press any button.\n"
335     "\n"
336     "If the Composite image does not have any matte information,\n"
337     "you are informed and the file browser is displayed again.\n"
338     "Enter the name of a mask image.  The image is typically\n"
339     "grayscale and the same size as the composite image.  If the\n"
340     "image is not grayscale, it is converted to grayscale and the\n"
341     "resulting intensities are used as matte information.\n"
342     "\n"
343     "A small window appears showing the location of the cursor in\n"
344     "the image window. You are now in composite mode.  To exit\n"
345     "immediately, press Dismiss.  In composite mode, the Command\n"
346     "widget has these options:\n"
347     "\n"
348     "    Operators\n"
349     "      Over\n"
350     "      In\n"
351     "      Out\n"
352     "      Atop\n"
353     "      Xor\n"
354     "      Plus\n"
355     "      Minus\n"
356     "      Add\n"
357     "      Subtract\n"
358     "      Difference\n"
359     "      Multiply\n"
360     "      Bumpmap\n"
361     "      Copy\n"
362     "      CopyRed\n"
363     "      CopyGreen\n"
364     "      CopyBlue\n"
365     "      CopyOpacity\n"
366     "      Clear\n"
367     "    Dissolve\n"
368     "    Displace\n"
369     "    Help\n"
370     "    Dismiss\n"
371     "\n"
372     "Choose a composite operation from the Operators sub-menu of\n"
373     "the Command widget.  How each operator behaves is described\n"
374     "below.  Image window is the image currently displayed on\n"
375     "your X server and image is the image obtained with the File\n"
376     "Browser widget.\n"
377     "\n"
378     "Over     The result is the union of the two image shapes,\n"
379     "         with image obscuring image window in the region of\n"
380     "         overlap.\n"
381     "\n"
382     "In       The result is simply image cut by the shape of\n"
383     "         image window.  None of the image data of image\n"
384     "         window is in the result.\n"
385     "\n"
386     "Out      The resulting image is image with the shape of\n"
387     "         image window cut out.\n"
388     "\n"
389     "Atop     The result is the same shape as image image window,\n"
390     "         with image obscuring image window where the image\n"
391     "         shapes overlap.  Note this differs from over\n"
392     "         because the portion of image outside image window's\n"
393     "         shape does not appear in the result.\n"
394     "\n"
395     "Xor      The result is the image data from both image and\n"
396     "         image window that is outside the overlap region.\n"
397     "         The overlap region is blank.\n"
398     "\n"
399     "Plus     The result is just the sum of the image data.\n"
400     "         Output values are cropped to QuantumRange (no overflow).\n"
401     "\n"
402     "Minus    The result of image - image window, with underflow\n"
403     "         cropped to zero.\n"
404     "\n"
405     "Add      The result of image + image window, with overflow\n"
406     "         wrapping around (mod 256).\n"
407     "\n"
408     "Subtract The result of image - image window, with underflow\n"
409     "         wrapping around (mod 256).  The add and subtract\n"
410     "         operators can be used to perform reversible\n"
411     "         transformations.\n"
412     "\n"
413     "Difference\n"
414     "         The result of abs(image - image window).  This\n"
415     "         useful for comparing two very similar images.\n"
416     "\n"
417     "Multiply\n"
418     "         The result of image * image window.  This\n"
419     "         useful for the creation of drop-shadows.\n"
420     "\n"
421     "Bumpmap  The result of surface normals from image * image\n"
422     "         window.\n"
423     "\n"
424     "Copy     The resulting image is image window replaced with\n"
425     "         image.  Here the matte information is ignored.\n"
426     "\n"
427     "CopyRed  The red layer of the image window is replace with\n"
428     "         the red layer of the image.  The other layers are\n"
429     "         untouched.\n"
430     "\n"
431     "CopyGreen\n"
432     "         The green layer of the image window is replace with\n"
433     "         the green layer of the image.  The other layers are\n"
434     "         untouched.\n"
435     "\n"
436     "CopyBlue The blue layer of the image window is replace with\n"
437     "         the blue layer of the image.  The other layers are\n"
438     "         untouched.\n"
439     "\n"
440     "CopyOpacity\n"
441     "         The matte layer of the image window is replace with\n"
442     "         the matte layer of the image.  The other layers are\n"
443     "         untouched.\n"
444     "\n"
445     "The image compositor requires a matte, or alpha channel in\n"
446     "the image for some operations.  This extra channel usually\n"
447     "defines a mask which represents a sort of a cookie-cutter\n"
448     "for the image.  This the case when matte is opaque (full\n"
449     "coverage) for pixels inside the shape, zero outside, and\n"
450     "between 0 and QuantumRange on the boundary.  If image does not\n"
451     "have a matte channel, it is initialized with 0 for any pixel\n"
452     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
453     "\n"
454     "If you choose Dissolve, the composite operator becomes Over.  The\n"
455     "image matte channel percent transparency is initialized to factor.\n"
456     "The image window is initialized to (100-factor). Where factor is the\n"
457     "value you specify in the Dialog widget.\n"
458     "\n"
459     "Displace shifts the image pixels as defined by a displacement\n"
460     "map.  With this option, image is used as a displacement map.\n"
461     "Black, within the displacement map, is a maximum positive\n"
462     "displacement.  White is a maximum negative displacement and\n"
463     "middle gray is neutral.  The displacement is scaled to determine\n"
464     "the pixel shift.  By default, the displacement applies in both the\n"
465     "horizontal and vertical directions.  However, if you specify a mask,\n"
466     "image is the horizontal X displacement and mask the vertical Y\n"
467     "displacement.\n"
468     "\n"
469     "Note that matte information for image window is not retained\n"
470     "for colormapped X server visuals (e.g. StaticColor,\n"
471     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
472     "behavior may require a TrueColor or DirectColor visual or a\n"
473     "Standard Colormap.\n"
474     "\n"
475     "Choosing a composite operator is optional.  The default\n"
476     "operator is replace.  However, you must choose a location to\n"
477     "composite your image and press button 1.  Press and hold the\n"
478     "button before releasing and an outline of the image will\n"
479     "appear to help you identify your location.\n"
480     "\n"
481     "The actual colors of the composite image is saved.  However,\n"
482     "the color that appears in image window may be different.\n"
483     "For example, on a monochrome screen image window will appear\n"
484     "black or white even though your composited image may have\n"
485     "many colors.  If the image is saved to a file it is written\n"
486     "with the correct colors.  To assure the correct colors are\n"
487     "saved in the final image, any PseudoClass image is promoted\n"
488     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
489     "to remain PseudoClass, use -colors.\n"
490   },
491   ImageCutHelp[] =
492   {
493     "In cut mode, the Command widget has these options:\n"
494     "\n"
495     "    Help\n"
496     "    Dismiss\n"
497     "\n"
498     "To define a cut region, press button 1 and drag.  The\n"
499     "cut region is defined by a highlighted rectangle that\n"
500     "expands or contracts as it follows the pointer.  Once you\n"
501     "are satisfied with the cut region, release the button.\n"
502     "You are now in rectify mode.  In rectify mode, the Command\n"
503     "widget has these options:\n"
504     "\n"
505     "    Cut\n"
506     "    Help\n"
507     "    Dismiss\n"
508     "\n"
509     "You can make adjustments by moving the pointer to one of the\n"
510     "cut rectangle corners, pressing a button, and dragging.\n"
511     "Finally, press Cut to commit your copy region.  To\n"
512     "exit without cutting the image, press Dismiss.\n"
513   },
514   ImageCopyHelp[] =
515   {
516     "In copy mode, the Command widget has these options:\n"
517     "\n"
518     "    Help\n"
519     "    Dismiss\n"
520     "\n"
521     "To define a copy region, press button 1 and drag.  The\n"
522     "copy region is defined by a highlighted rectangle that\n"
523     "expands or contracts as it follows the pointer.  Once you\n"
524     "are satisfied with the copy region, release the button.\n"
525     "You are now in rectify mode.  In rectify mode, the Command\n"
526     "widget has these options:\n"
527     "\n"
528     "    Copy\n"
529     "    Help\n"
530     "    Dismiss\n"
531     "\n"
532     "You can make adjustments by moving the pointer to one of the\n"
533     "copy rectangle corners, pressing a button, and dragging.\n"
534     "Finally, press Copy to commit your copy region.  To\n"
535     "exit without copying the image, press Dismiss.\n"
536   },
537   ImageCropHelp[] =
538   {
539     "In crop mode, the Command widget has these options:\n"
540     "\n"
541     "    Help\n"
542     "    Dismiss\n"
543     "\n"
544     "To define a cropping region, press button 1 and drag.  The\n"
545     "cropping region is defined by a highlighted rectangle that\n"
546     "expands or contracts as it follows the pointer.  Once you\n"
547     "are satisfied with the cropping region, release the button.\n"
548     "You are now in rectify mode.  In rectify mode, the Command\n"
549     "widget has these options:\n"
550     "\n"
551     "    Crop\n"
552     "    Help\n"
553     "    Dismiss\n"
554     "\n"
555     "You can make adjustments by moving the pointer to one of the\n"
556     "cropping rectangle corners, pressing a button, and dragging.\n"
557     "Finally, press Crop to commit your cropping region.  To\n"
558     "exit without cropping the image, press Dismiss.\n"
559   },
560   ImageDrawHelp[] =
561   {
562     "The cursor changes to a crosshair to indicate you are in\n"
563     "draw mode.  To exit immediately, press Dismiss.  In draw mode,\n"
564     "the Command widget has these options:\n"
565     "\n"
566     "    Element\n"
567     "      point\n"
568     "      line\n"
569     "      rectangle\n"
570     "      fill rectangle\n"
571     "      circle\n"
572     "      fill circle\n"
573     "      ellipse\n"
574     "      fill ellipse\n"
575     "      polygon\n"
576     "      fill polygon\n"
577     "    Color\n"
578     "      black\n"
579     "      blue\n"
580     "      cyan\n"
581     "      green\n"
582     "      gray\n"
583     "      red\n"
584     "      magenta\n"
585     "      yellow\n"
586     "      white\n"
587     "      transparent\n"
588     "      Browser...\n"
589     "    Stipple\n"
590     "      Brick\n"
591     "      Diagonal\n"
592     "      Scales\n"
593     "      Vertical\n"
594     "      Wavy\n"
595     "      Translucent\n"
596     "      Opaque\n"
597     "      Open...\n"
598     "    Width\n"
599     "      1\n"
600     "      2\n"
601     "      4\n"
602     "      8\n"
603     "      16\n"
604     "      Dialog...\n"
605     "    Undo\n"
606     "    Help\n"
607     "    Dismiss\n"
608     "\n"
609     "Choose a drawing primitive from the Element sub-menu.\n"
610     "\n"
611     "Choose a color from the Color sub-menu.  Additional\n"
612     "colors can be specified with the color browser.\n"
613     "\n"
614     "If you choose the color browser and press Grab, you can\n"
615     "select the color by moving the pointer to the desired\n"
616     "color on the screen and press any button.  The transparent\n"
617     "color updates the image matte channel and is useful for\n"
618     "image compositing.\n"
619     "\n"
620     "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
621     "Additional stipples can be specified with the file browser.\n"
622     "Stipples obtained from the file browser must be on disk in the\n"
623     "X11 bitmap format.\n"
624     "\n"
625     "Choose a width, if appropriate, from the Width sub-menu.  To\n"
626     "choose a specific width select the Dialog widget.\n"
627     "\n"
628     "Choose a point in the Image window and press button 1 and\n"
629     "hold.  Next, move the pointer to another location in the\n"
630     "image.  As you move, a line connects the initial location and\n"
631     "the pointer.  When you release the button, the image is\n"
632     "updated with the primitive you just drew.  For polygons, the\n"
633     "image is updated when you press and release the button without\n"
634     "moving the pointer.\n"
635     "\n"
636     "To cancel image drawing, move the pointer back to the\n"
637     "starting point of the line and release the button.\n"
638   },
639   DisplayHelp[] =
640   {
641     "BUTTONS\n"
642     "  The effects of each button press is described below.  Three\n"
643     "  buttons are required.  If you have a two button mouse,\n"
644     "  button 1 and 3 are returned.  Press ALT and button 3 to\n"
645     "  simulate button 2.\n"
646     "\n"
647     "  1    Press this button to map or unmap the Command widget.\n"
648     "\n"
649     "  2    Press and drag to define a region of the image to\n"
650     "       magnify.\n"
651     "\n"
652     "  3    Press and drag to choose from a select set of commands.\n"
653     "       This button behaves differently if the image being\n"
654     "       displayed is a visual image directory.  Here, choose a\n"
655     "       particular tile of the directory and press this button and\n"
656     "       drag to select a command from a pop-up menu.  Choose from\n"
657     "       these menu items:\n"
658     "\n"
659     "           Open\n"
660     "           Next\n"
661     "           Former\n"
662     "           Delete\n"
663     "           Update\n"
664     "\n"
665     "       If you choose Open, the image represented by the tile is\n"
666     "       displayed.  To return to the visual image directory, choose\n"
667     "       Next from the Command widget.  Next and Former moves to the\n"
668     "       next or former image respectively.  Choose Delete to delete\n"
669     "       a particular image tile.  Finally, choose Update to\n"
670     "       synchronize all the image tiles with their respective\n"
671     "       images.\n"
672     "\n"
673     "COMMAND WIDGET\n"
674     "  The Command widget lists a number of sub-menus and commands.\n"
675     "  They are\n"
676     "\n"
677     "      File\n"
678     "        Open...\n"
679     "        Next\n"
680     "        Former\n"
681     "        Select...\n"
682     "        Save...\n"
683     "        Print...\n"
684     "        Delete...\n"
685     "        New...\n"
686     "        Visual Directory...\n"
687     "        Quit\n"
688     "      Edit\n"
689     "        Undo\n"
690     "        Redo\n"
691     "        Cut\n"
692     "        Copy\n"
693     "        Paste\n"
694     "      View\n"
695     "        Half Size\n"
696     "        Original Size\n"
697     "        Double Size\n"
698     "        Resize...\n"
699     "        Apply\n"
700     "        Refresh\n"
701     "        Restore\n"
702     "      Transform\n"
703     "        Crop\n"
704     "        Chop\n"
705     "        Flop\n"
706     "        Flip\n"
707     "        Rotate Right\n"
708     "        Rotate Left\n"
709     "        Rotate...\n"
710     "        Shear...\n"
711     "        Roll...\n"
712     "        Trim Edges\n"
713     "      Enhance\n"
714     "        Brightness...\n"
715     "        Saturation...\n"
716     "        Hue...\n"
717     "        Gamma...\n"
718     "        Sharpen...\n"
719     "        Dull\n"
720     "        Contrast Stretch...\n"
721     "        Sigmoidal Contrast...\n"
722     "        Normalize\n"
723     "        Equalize\n"
724     "        Negate\n"
725     "        Grayscale\n"
726     "        Map...\n"
727     "        Quantize...\n"
728     "      Effects\n"
729     "        Despeckle\n"
730     "        Emboss\n"
731     "        Reduce Noise\n"
732     "        Add Noise\n"
733     "        Sharpen...\n"
734     "        Blur...\n"
735     "        Threshold...\n"
736     "        Edge Detect...\n"
737     "        Spread...\n"
738     "        Shade...\n"
739     "        Painting...\n"
740     "        Segment...\n"
741     "      F/X\n"
742     "        Solarize...\n"
743     "        Sepia Tone...\n"
744     "        Swirl...\n"
745     "        Implode...\n"
746     "        Vignette...\n"
747     "        Wave...\n"
748     "        Oil Painting...\n"
749     "        Charcoal Drawing...\n"
750     "      Image Edit\n"
751     "        Annotate...\n"
752     "        Draw...\n"
753     "        Color...\n"
754     "        Matte...\n"
755     "        Composite...\n"
756     "        Add Border...\n"
757     "        Add Frame...\n"
758     "        Comment...\n"
759     "        Launch...\n"
760     "        Region of Interest...\n"
761     "      Miscellany\n"
762     "        Image Info\n"
763     "        Zoom Image\n"
764     "        Show Preview...\n"
765     "        Show Histogram\n"
766     "        Show Matte\n"
767     "        Background...\n"
768     "        Slide Show\n"
769     "        Preferences...\n"
770     "      Help\n"
771     "        Overview\n"
772     "        Browse Documentation\n"
773     "        About Display\n"
774     "\n"
775     "  Menu items with a indented triangle have a sub-menu.  They\n"
776     "  are represented above as the indented items.  To access a\n"
777     "  sub-menu item, move the pointer to the appropriate menu and\n"
778     "  press a button and drag.  When you find the desired sub-menu\n"
779     "  item, release the button and the command is executed.  Move\n"
780     "  the pointer away from the sub-menu if you decide not to\n"
781     "  execute a particular command.\n"
782     "\n"
783     "KEYBOARD ACCELERATORS\n"
784     "  Accelerators are one or two key presses that effect a\n"
785     "  particular command.  The keyboard accelerators that\n"
786     "  display(1) understands is:\n"
787     "\n"
788     "  Ctl+O     Press to open an image from a file.\n"
789     "\n"
790     "  space     Press to display the next image.\n"
791     "\n"
792     "            If the image is a multi-paged document such as a Postscript\n"
793     "            document, you can skip ahead several pages by preceding\n"
794     "            this command with a number.  For example to display the\n"
795     "            third page beyond the current page, press 3<space>.\n"
796     "\n"
797     "  backspace Press to display the former image.\n"
798     "\n"
799     "            If the image is a multi-paged document such as a Postscript\n"
800     "            document, you can skip behind several pages by preceding\n"
801     "            this command with a number.  For example to display the\n"
802     "            third page preceding the current page, press 3<backspace>.\n"
803     "\n"
804     "  Ctl+S     Press to write the image to a file.\n"
805     "\n"
806     "  Ctl+P     Press to print the image to a Postscript printer.\n"
807     "\n"
808     "  Ctl+D     Press to delete an image file.\n"
809     "\n"
810     "  Ctl+N     Press to create a blank canvas.\n"
811     "\n"
812     "  Ctl+Q     Press to discard all images and exit program.\n"
813     "\n"
814     "  Ctl+Z     Press to undo last image transformation.\n"
815     "\n"
816     "  Ctl+R     Press to redo last image transformation.\n"
817     "\n"
818     "  Ctl+X     Press to cut a region of the image.\n"
819     "\n"
820     "  Ctl+C     Press to copy a region of the image.\n"
821     "\n"
822     "  Ctl+V     Press to paste a region to the image.\n"
823     "\n"
824     "  <         Press to half the image size.\n"
825     "\n"
826     "  -         Press to return to the original image size.\n"
827     "\n"
828     "  >         Press to double the image size.\n"
829     "\n"
830     "  %         Press to resize the image to a width and height you\n"
831     "            specify.\n"
832     "\n"
833     "Cmd-A       Press to make any image transformations permanent."
834     "\n"
835     "            By default, any image size transformations are applied\n"
836     "            to the original image to create the image displayed on\n"
837     "            the X server.  However, the transformations are not\n"
838     "            permanent (i.e. the original image does not change\n"
839     "            size only the X image does).  For example, if you\n"
840     "            press > the X image will appear to double in size,\n"
841     "            but the original image will in fact remain the same size.\n"
842     "            To force the original image to double in size, press >\n"
843     "            followed by Cmd-A.\n"
844     "\n"
845     "  @         Press to refresh the image window.\n"
846     "\n"
847     "  C         Press to cut out a rectangular region of the image.\n"
848     "\n"
849     "  [         Press to chop the image.\n"
850     "\n"
851     "  H         Press to flop image in the horizontal direction.\n"
852     "\n"
853     "  V         Press to flip image in the vertical direction.\n"
854     "\n"
855     "  /         Press to rotate the image 90 degrees clockwise.\n"
856     "\n"
857     " \\         Press to rotate the image 90 degrees counter-clockwise.\n"
858     "\n"
859     "  *         Press to rotate the image the number of degrees you\n"
860     "            specify.\n"
861     "\n"
862     "  S         Press to shear the image the number of degrees you\n"
863     "            specify.\n"
864     "\n"
865     "  R         Press to roll the image.\n"
866     "\n"
867     "  T         Press to trim the image edges.\n"
868     "\n"
869     "  Shft-H    Press to vary the image hue.\n"
870     "\n"
871     "  Shft-S    Press to vary the color saturation.\n"
872     "\n"
873     "  Shft-L    Press to vary the color brightness.\n"
874     "\n"
875     "  Shft-G    Press to gamma correct the image.\n"
876     "\n"
877     "  Shft-C    Press to sharpen the image contrast.\n"
878     "\n"
879     "  Shft-Z    Press to dull the image contrast.\n"
880     "\n"
881     "  =         Press to perform histogram equalization on the image.\n"
882     "\n"
883     "  Shft-N    Press to perform histogram normalization on the image.\n"
884     "\n"
885     "  Shft-~    Press to negate the colors of the image.\n"
886     "\n"
887     "  .         Press to convert the image colors to gray.\n"
888     "\n"
889     "  Shft-#    Press to set the maximum number of unique colors in the\n"
890     "            image.\n"
891     "\n"
892     "  F2        Press to reduce the speckles in an image.\n"
893     "\n"
894     "  F3        Press to eliminate peak noise from an image.\n"
895     "\n"
896     "  F4        Press to add noise to an image.\n"
897     "\n"
898     "  F5        Press to sharpen an image.\n"
899     "\n"
900     "  F6        Press to delete an image file.\n"
901     "\n"
902     "  F7        Press to threshold the image.\n"
903     "\n"
904     "  F8        Press to detect edges within an image.\n"
905     "\n"
906     "  F9        Press to emboss an image.\n"
907     "\n"
908     "  F10       Press to displace pixels by a random amount.\n"
909     "\n"
910     "  F11       Press to negate all pixels above the threshold level.\n"
911     "\n"
912     "  F12       Press to shade the image using a distant light source.\n"
913     "\n"
914     "  F13       Press to lighten or darken image edges to create a 3-D effect.\n"
915     "\n"
916     "  F14       Press to segment the image by color.\n"
917     "\n"
918     "  Meta-S    Press to swirl image pixels about the center.\n"
919     "\n"
920     "  Meta-I    Press to implode image pixels about the center.\n"
921     "\n"
922     "  Meta-W    Press to alter an image along a sine wave.\n"
923     "\n"
924     "  Meta-P    Press to simulate an oil painting.\n"
925     "\n"
926     "  Meta-C    Press to simulate a charcoal drawing.\n"
927     "\n"
928     "  Alt-A     Press to annotate the image with text.\n"
929     "\n"
930     "  Alt-D     Press to draw on an image.\n"
931     "\n"
932     "  Alt-P     Press to edit an image pixel color.\n"
933     "\n"
934     "  Alt-M     Press to edit the image matte information.\n"
935     "\n"
936     "  Alt-V     Press to composite the image with another.\n"
937     "\n"
938     "  Alt-B     Press to add a border to the image.\n"
939     "\n"
940     "  Alt-F     Press to add an ornamental border to the image.\n"
941     "\n"
942     "  Alt-Shft-!\n"
943     "            Press to add an image comment.\n"
944     "\n"
945     "  Ctl-A     Press to apply image processing techniques to a region\n"
946     "            of interest.\n"
947     "\n"
948     "  Shft-?    Press to display information about the image.\n"
949     "\n"
950     "  Shft-+    Press to map the zoom image window.\n"
951     "\n"
952     "  Shft-P    Press to preview an image enhancement, effect, or f/x.\n"
953     "\n"
954     "  F1        Press to display helpful information about display(1).\n"
955     "\n"
956     "  Find      Press to browse documentation about ImageMagick.\n"
957     "\n"
958     "  1-9       Press to change the level of magnification.\n"
959     "\n"
960     "  Use the arrow keys to move the image one pixel up, down,\n"
961     "  left, or right within the magnify window.  Be sure to first\n"
962     "  map the magnify window by pressing button 2.\n"
963     "\n"
964     "  Press ALT and one of the arrow keys to trim off one pixel\n"
965     "  from any side of the image.\n"
966   },
967   ImageMatteEditHelp[] =
968   {
969     "Matte information within an image is useful for some\n"
970     "operations such as image compositing (See IMAGE\n"
971     "COMPOSITING).  This extra channel usually defines a mask\n"
972     "which represents a sort of a cookie-cutter for the image.\n"
973     "This the case when matte is opaque (full coverage) for\n"
974     "pixels inside the shape, zero outside, and between 0 and\n"
975     "QuantumRange on the boundary.\n"
976     "\n"
977     "A small window appears showing the location of the cursor in\n"
978     "the image window. You are now in matte edit mode.  To exit\n"
979     "immediately, press Dismiss.  In matte edit mode, the Command\n"
980     "widget has these options:\n"
981     "\n"
982     "    Method\n"
983     "      point\n"
984     "      replace\n"
985     "      floodfill\n"
986     "      filltoborder\n"
987     "      reset\n"
988     "    Border Color\n"
989     "      black\n"
990     "      blue\n"
991     "      cyan\n"
992     "      green\n"
993     "      gray\n"
994     "      red\n"
995     "      magenta\n"
996     "      yellow\n"
997     "      white\n"
998     "      Browser...\n"
999     "    Fuzz\n"
1000     "      0%\n"
1001     "      2%\n"
1002     "      5%\n"
1003     "      10%\n"
1004     "      15%\n"
1005     "      Dialog...\n"
1006     "    Matte\n"
1007     "      Opaque\n"
1008     "      Transparent\n"
1009     "      Dialog...\n"
1010     "    Undo\n"
1011     "    Help\n"
1012     "    Dismiss\n"
1013     "\n"
1014     "Choose a matte editing method from the Method sub-menu of\n"
1015     "the Command widget.  The point method changes the matte value\n"
1016     "of any pixel selected with the pointer until the button is\n"
1017     "is released.  The replace method changes the matte value of\n"
1018     "any pixel that matches the color of the pixel you select with\n"
1019     "a button press.  Floodfill changes the matte value of any pixel\n"
1020     "that matches the color of the pixel you select with a button\n"
1021     "press and is a neighbor.  Whereas filltoborder changes the matte\n"
1022     "value any neighbor pixel that is not the border color.  Finally\n"
1023     "reset changes the entire image to the designated matte value.\n"
1024     "\n"
1025     "Choose Matte Value and pick Opaque or Transarent.  For other values\n"
1026     "select the Dialog entry.  Here a dialog appears requesting a matte\n"
1027     "value.  The value you select is assigned as the opacity value of the\n"
1028     "selected pixel or pixels.\n"
1029     "\n"
1030     "Now, press any button to select a pixel within the image\n"
1031     "window to change its matte value.\n"
1032     "\n"
1033     "If the Magnify widget is mapped, it can be helpful in positioning\n"
1034     "your pointer within the image (refer to button 2).\n"
1035     "\n"
1036     "Matte information is only valid in a DirectClass image.\n"
1037     "Therefore, any PseudoClass image is promoted to DirectClass\n"
1038     "(see miff(5)).  Note that matte information for PseudoClass\n"
1039     "is not retained for colormapped X server visuals (e.g.\n"
1040     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1041     "immediately save your image to a file (refer to Write).\n"
1042     "Correct matte editing behavior may require a TrueColor or\n"
1043     "DirectColor visual or a Standard Colormap.\n"
1044   },
1045   ImagePanHelp[] =
1046   {
1047     "When an image exceeds the width or height of the X server\n"
1048     "screen, display maps a small panning icon.  The rectangle\n"
1049     "within the panning icon shows the area that is currently\n"
1050     "displayed in the image window.  To pan about the image,\n"
1051     "press any button and drag the pointer within the panning\n"
1052     "icon.  The pan rectangle moves with the pointer and the\n"
1053     "image window is updated to reflect the location of the\n"
1054     "rectangle within the panning icon.  When you have selected\n"
1055     "the area of the image you wish to view, release the button.\n"
1056     "\n"
1057     "Use the arrow keys to pan the image one pixel up, down,\n"
1058     "left, or right within the image window.\n"
1059     "\n"
1060     "The panning icon is withdrawn if the image becomes smaller\n"
1061     "than the dimensions of the X server screen.\n"
1062   },
1063   ImagePasteHelp[] =
1064   {
1065     "A small window appears showing the location of the cursor in\n"
1066     "the image window. You are now in paste mode.  To exit\n"
1067     "immediately, press Dismiss.  In paste mode, the Command\n"
1068     "widget has these options:\n"
1069     "\n"
1070     "    Operators\n"
1071     "      over\n"
1072     "      in\n"
1073     "      out\n"
1074     "      atop\n"
1075     "      xor\n"
1076     "      plus\n"
1077     "      minus\n"
1078     "      add\n"
1079     "      subtract\n"
1080     "      difference\n"
1081     "      replace\n"
1082     "    Help\n"
1083     "    Dismiss\n"
1084     "\n"
1085     "Choose a composite operation from the Operators sub-menu of\n"
1086     "the Command widget.  How each operator behaves is described\n"
1087     "below.  Image window is the image currently displayed on\n"
1088     "your X server and image is the image obtained with the File\n"
1089     "Browser widget.\n"
1090     "\n"
1091     "Over     The result is the union of the two image shapes,\n"
1092     "         with image obscuring image window in the region of\n"
1093     "         overlap.\n"
1094     "\n"
1095     "In       The result is simply image cut by the shape of\n"
1096     "         image window.  None of the image data of image\n"
1097     "         window is in the result.\n"
1098     "\n"
1099     "Out      The resulting image is image with the shape of\n"
1100     "         image window cut out.\n"
1101     "\n"
1102     "Atop     The result is the same shape as image image window,\n"
1103     "         with image obscuring image window where the image\n"
1104     "         shapes overlap.  Note this differs from over\n"
1105     "         because the portion of image outside image window's\n"
1106     "         shape does not appear in the result.\n"
1107     "\n"
1108     "Xor      The result is the image data from both image and\n"
1109     "         image window that is outside the overlap region.\n"
1110     "         The overlap region is blank.\n"
1111     "\n"
1112     "Plus     The result is just the sum of the image data.\n"
1113     "         Output values are cropped to QuantumRange (no overflow).\n"
1114     "         This operation is independent of the matte\n"
1115     "         channels.\n"
1116     "\n"
1117     "Minus    The result of image - image window, with underflow\n"
1118     "         cropped to zero.\n"
1119     "\n"
1120     "Add      The result of image + image window, with overflow\n"
1121     "         wrapping around (mod 256).\n"
1122     "\n"
1123     "Subtract The result of image - image window, with underflow\n"
1124     "         wrapping around (mod 256).  The add and subtract\n"
1125     "         operators can be used to perform reversible\n"
1126     "         transformations.\n"
1127     "\n"
1128     "Difference\n"
1129     "         The result of abs(image - image window).  This\n"
1130     "         useful for comparing two very similar images.\n"
1131     "\n"
1132     "Copy     The resulting image is image window replaced with\n"
1133     "         image.  Here the matte information is ignored.\n"
1134     "\n"
1135     "CopyRed  The red layer of the image window is replace with\n"
1136     "         the red layer of the image.  The other layers are\n"
1137     "         untouched.\n"
1138     "\n"
1139     "CopyGreen\n"
1140     "         The green layer of the image window is replace with\n"
1141     "         the green layer of the image.  The other layers are\n"
1142     "         untouched.\n"
1143     "\n"
1144     "CopyBlue The blue layer of the image window is replace with\n"
1145     "         the blue layer of the image.  The other layers are\n"
1146     "         untouched.\n"
1147     "\n"
1148     "CopyOpacity\n"
1149     "         The matte layer of the image window is replace with\n"
1150     "         the matte layer of the image.  The other layers are\n"
1151     "         untouched.\n"
1152     "\n"
1153     "The image compositor requires a matte, or alpha channel in\n"
1154     "the image for some operations.  This extra channel usually\n"
1155     "defines a mask which represents a sort of a cookie-cutter\n"
1156     "for the image.  This the case when matte is opaque (full\n"
1157     "coverage) for pixels inside the shape, zero outside, and\n"
1158     "between 0 and QuantumRange on the boundary.  If image does not\n"
1159     "have a matte channel, it is initialized with 0 for any pixel\n"
1160     "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1161     "\n"
1162     "Note that matte information for image window is not retained\n"
1163     "for colormapped X server visuals (e.g. StaticColor,\n"
1164     "StaticColor, GrayScale, PseudoColor).  Correct compositing\n"
1165     "behavior may require a TrueColor or DirectColor visual or a\n"
1166     "Standard Colormap.\n"
1167     "\n"
1168     "Choosing a composite operator is optional.  The default\n"
1169     "operator is replace.  However, you must choose a location to\n"
1170     "paste your image and press button 1.  Press and hold the\n"
1171     "button before releasing and an outline of the image will\n"
1172     "appear to help you identify your location.\n"
1173     "\n"
1174     "The actual colors of the pasted image is saved.  However,\n"
1175     "the color that appears in image window may be different.\n"
1176     "For example, on a monochrome screen image window will appear\n"
1177     "black or white even though your pasted image may have\n"
1178     "many colors.  If the image is saved to a file it is written\n"
1179     "with the correct colors.  To assure the correct colors are\n"
1180     "saved in the final image, any PseudoClass image is promoted\n"
1181     "to DirectClass (see miff(5)).  To force a PseudoClass image\n"
1182     "to remain PseudoClass, use -colors.\n"
1183   },
1184   ImageROIHelp[] =
1185   {
1186     "In region of interest mode, the Command widget has these\n"
1187     "options:\n"
1188     "\n"
1189     "    Help\n"
1190     "    Dismiss\n"
1191     "\n"
1192     "To define a region of interest, press button 1 and drag.\n"
1193     "The region of interest is defined by a highlighted rectangle\n"
1194     "that expands or contracts as it follows the pointer.  Once\n"
1195     "you are satisfied with the region of interest, release the\n"
1196     "button.  You are now in apply mode.  In apply mode the\n"
1197     "Command widget has these options:\n"
1198     "\n"
1199     "      File\n"
1200     "        Save...\n"
1201     "        Print...\n"
1202     "      Edit\n"
1203     "        Undo\n"
1204     "        Redo\n"
1205     "      Transform\n"
1206     "        Flop\n"
1207     "        Flip\n"
1208     "        Rotate Right\n"
1209     "        Rotate Left\n"
1210     "      Enhance\n"
1211     "        Hue...\n"
1212     "        Saturation...\n"
1213     "        Brightness...\n"
1214     "        Gamma...\n"
1215     "        Spiff\n"
1216     "        Dull\n"
1217     "        Contrast Stretch\n"
1218     "        Sigmoidal Contrast...\n"
1219     "        Normalize\n"
1220     "        Equalize\n"
1221     "        Negate\n"
1222     "        Grayscale\n"
1223     "        Map...\n"
1224     "        Quantize...\n"
1225     "      Effects\n"
1226     "        Despeckle\n"
1227     "        Emboss\n"
1228     "        Reduce Noise\n"
1229     "        Sharpen...\n"
1230     "        Blur...\n"
1231     "        Threshold...\n"
1232     "        Edge Detect...\n"
1233     "        Spread...\n"
1234     "        Shade...\n"
1235     "        Raise...\n"
1236     "        Segment...\n"
1237     "      F/X\n"
1238     "        Solarize...\n"
1239     "        Sepia Tone...\n"
1240     "        Swirl...\n"
1241     "        Implode...\n"
1242     "        Vignette...\n"
1243     "        Wave...\n"
1244     "        Oil Painting...\n"
1245     "        Charcoal Drawing...\n"
1246     "      Miscellany\n"
1247     "        Image Info\n"
1248     "        Zoom Image\n"
1249     "        Show Preview...\n"
1250     "        Show Histogram\n"
1251     "        Show Matte\n"
1252     "      Help\n"
1253     "      Dismiss\n"
1254     "\n"
1255     "You can make adjustments to the region of interest by moving\n"
1256     "the pointer to one of the rectangle corners, pressing a\n"
1257     "button, and dragging.  Finally, choose an image processing\n"
1258     "technique from the Command widget.  You can choose more than\n"
1259     "one image processing technique to apply to an area.\n"
1260     "Alternatively, you can move the region of interest before\n"
1261     "applying another image processing technique.  To exit, press\n"
1262     "Dismiss.\n"
1263   },
1264   ImageRotateHelp[] =
1265   {
1266     "In rotate mode, the Command widget has these options:\n"
1267     "\n"
1268     "    Pixel Color\n"
1269     "      black\n"
1270     "      blue\n"
1271     "      cyan\n"
1272     "      green\n"
1273     "      gray\n"
1274     "      red\n"
1275     "      magenta\n"
1276     "      yellow\n"
1277     "      white\n"
1278     "      Browser...\n"
1279     "    Direction\n"
1280     "      horizontal\n"
1281     "      vertical\n"
1282     "    Help\n"
1283     "    Dismiss\n"
1284     "\n"
1285     "Choose a background color from the Pixel Color sub-menu.\n"
1286     "Additional background colors can be specified with the color\n"
1287     "browser.  You can change the menu colors by setting the X\n"
1288     "resources pen1 through pen9.\n"
1289     "\n"
1290     "If you choose the color browser and press Grab, you can\n"
1291     "select the background color by moving the pointer to the\n"
1292     "desired color on the screen and press any button.\n"
1293     "\n"
1294     "Choose a point in the image window and press this button and\n"
1295     "hold.  Next, move the pointer to another location in the\n"
1296     "image.  As you move a line connects the initial location and\n"
1297     "the pointer.  When you release the button, the degree of\n"
1298     "image rotation is determined by the slope of the line you\n"
1299     "just drew.  The slope is relative to the direction you\n"
1300     "choose from the Direction sub-menu of the Command widget.\n"
1301     "\n"
1302     "To cancel the image rotation, move the pointer back to the\n"
1303     "starting point of the line and release the button.\n"
1304   };
1305 
1306 /*
1307   Enumeration declarations.
1308 */
1309 typedef enum
1310 {
1311   CopyMode,
1312   CropMode,
1313   CutMode
1314 } ClipboardMode;
1315 
1316 typedef enum
1317 {
1318   OpenCommand,
1319   NextCommand,
1320   FormerCommand,
1321   SelectCommand,
1322   SaveCommand,
1323   PrintCommand,
1324   DeleteCommand,
1325   NewCommand,
1326   VisualDirectoryCommand,
1327   QuitCommand,
1328   UndoCommand,
1329   RedoCommand,
1330   CutCommand,
1331   CopyCommand,
1332   PasteCommand,
1333   HalfSizeCommand,
1334   OriginalSizeCommand,
1335   DoubleSizeCommand,
1336   ResizeCommand,
1337   ApplyCommand,
1338   RefreshCommand,
1339   RestoreCommand,
1340   CropCommand,
1341   ChopCommand,
1342   FlopCommand,
1343   FlipCommand,
1344   RotateRightCommand,
1345   RotateLeftCommand,
1346   RotateCommand,
1347   ShearCommand,
1348   RollCommand,
1349   TrimCommand,
1350   HueCommand,
1351   SaturationCommand,
1352   BrightnessCommand,
1353   GammaCommand,
1354   SpiffCommand,
1355   DullCommand,
1356   ContrastStretchCommand,
1357   SigmoidalContrastCommand,
1358   NormalizeCommand,
1359   EqualizeCommand,
1360   NegateCommand,
1361   GrayscaleCommand,
1362   MapCommand,
1363   QuantizeCommand,
1364   DespeckleCommand,
1365   EmbossCommand,
1366   ReduceNoiseCommand,
1367   AddNoiseCommand,
1368   SharpenCommand,
1369   BlurCommand,
1370   ThresholdCommand,
1371   EdgeDetectCommand,
1372   SpreadCommand,
1373   ShadeCommand,
1374   RaiseCommand,
1375   SegmentCommand,
1376   SolarizeCommand,
1377   SepiaToneCommand,
1378   SwirlCommand,
1379   ImplodeCommand,
1380   VignetteCommand,
1381   WaveCommand,
1382   OilPaintCommand,
1383   CharcoalDrawCommand,
1384   AnnotateCommand,
1385   DrawCommand,
1386   ColorCommand,
1387   MatteCommand,
1388   CompositeCommand,
1389   AddBorderCommand,
1390   AddFrameCommand,
1391   CommentCommand,
1392   LaunchCommand,
1393   RegionofInterestCommand,
1394   ROIHelpCommand,
1395   ROIDismissCommand,
1396   InfoCommand,
1397   ZoomCommand,
1398   ShowPreviewCommand,
1399   ShowHistogramCommand,
1400   ShowMatteCommand,
1401   BackgroundCommand,
1402   SlideShowCommand,
1403   PreferencesCommand,
1404   HelpCommand,
1405   BrowseDocumentationCommand,
1406   VersionCommand,
1407   SaveToUndoBufferCommand,
1408   FreeBuffersCommand,
1409   NullCommand
1410 } CommandType;
1411 
1412 typedef enum
1413 {
1414   AnnotateNameCommand,
1415   AnnotateFontColorCommand,
1416   AnnotateBackgroundColorCommand,
1417   AnnotateRotateCommand,
1418   AnnotateHelpCommand,
1419   AnnotateDismissCommand,
1420   TextHelpCommand,
1421   TextApplyCommand,
1422   ChopDirectionCommand,
1423   ChopHelpCommand,
1424   ChopDismissCommand,
1425   HorizontalChopCommand,
1426   VerticalChopCommand,
1427   ColorEditMethodCommand,
1428   ColorEditColorCommand,
1429   ColorEditBorderCommand,
1430   ColorEditFuzzCommand,
1431   ColorEditUndoCommand,
1432   ColorEditHelpCommand,
1433   ColorEditDismissCommand,
1434   CompositeOperatorsCommand,
1435   CompositeDissolveCommand,
1436   CompositeDisplaceCommand,
1437   CompositeHelpCommand,
1438   CompositeDismissCommand,
1439   CropHelpCommand,
1440   CropDismissCommand,
1441   RectifyCopyCommand,
1442   RectifyHelpCommand,
1443   RectifyDismissCommand,
1444   DrawElementCommand,
1445   DrawColorCommand,
1446   DrawStippleCommand,
1447   DrawWidthCommand,
1448   DrawUndoCommand,
1449   DrawHelpCommand,
1450   DrawDismissCommand,
1451   MatteEditMethod,
1452   MatteEditBorderCommand,
1453   MatteEditFuzzCommand,
1454   MatteEditValueCommand,
1455   MatteEditUndoCommand,
1456   MatteEditHelpCommand,
1457   MatteEditDismissCommand,
1458   PasteOperatorsCommand,
1459   PasteHelpCommand,
1460   PasteDismissCommand,
1461   RotateColorCommand,
1462   RotateDirectionCommand,
1463   RotateCropCommand,
1464   RotateSharpenCommand,
1465   RotateHelpCommand,
1466   RotateDismissCommand,
1467   HorizontalRotateCommand,
1468   VerticalRotateCommand,
1469   TileLoadCommand,
1470   TileNextCommand,
1471   TileFormerCommand,
1472   TileDeleteCommand,
1473   TileUpdateCommand
1474 } ModeType;
1475 
1476 /*
1477   Stipples.
1478 */
1479 #define BricksWidth  20
1480 #define BricksHeight  20
1481 #define DiagonalWidth  16
1482 #define DiagonalHeight  16
1483 #define HighlightWidth  8
1484 #define HighlightHeight  8
1485 #define OpaqueWidth  8
1486 #define OpaqueHeight  8
1487 #define ScalesWidth  16
1488 #define ScalesHeight  16
1489 #define ShadowWidth  8
1490 #define ShadowHeight  8
1491 #define VerticalWidth  16
1492 #define VerticalHeight  16
1493 #define WavyWidth  16
1494 #define WavyHeight  16
1495 
1496 /*
1497   Constant declaration.
1498 */
1499 static const int
1500   RoiDelta = 8;
1501 
1502 static const unsigned char
1503   BricksBitmap[] =
1504   {
1505     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1506     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1507     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1508     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1509     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1510   },
1511   DiagonalBitmap[] =
1512   {
1513     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1514     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1515     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1516   },
1517   ScalesBitmap[] =
1518   {
1519     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1520     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1521     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1522   },
1523   VerticalBitmap[] =
1524   {
1525     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1526     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1528   },
1529   WavyBitmap[] =
1530   {
1531     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1532     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1533     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1534   };
1535 
1536 /*
1537   Function prototypes.
1538 */
1539 static CommandType
1540   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1541     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1542 
1543 static Image
1544   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1545     Image **,ExceptionInfo *),
1546   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1547   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1548     ExceptionInfo *),
1549   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1550     ExceptionInfo *);
1551 
1552 static MagickBooleanType
1553   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1554     ExceptionInfo *),
1555   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1556     ExceptionInfo *),
1557   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1558     ExceptionInfo *),
1559   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1560     ExceptionInfo *),
1561   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1562     ExceptionInfo *),
1563   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1564     ExceptionInfo *),
1565   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1566   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1567     ExceptionInfo *),
1568   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1569     ExceptionInfo *),
1570   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1571   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1573     ExceptionInfo *),
1574   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1575   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1576   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1577 
1578 static void
1579   XDrawPanRectangle(Display *,XWindows *),
1580   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1581     ExceptionInfo *),
1582   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1583   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1584   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1585   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1586     const KeySym,ExceptionInfo *),
1587   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1588   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1589   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1590 
1591 /*
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593 %                                                                             %
1594 %                                                                             %
1595 %                                                                             %
1596 %   D i s p l a y I m a g e s                                                 %
1597 %                                                                             %
1598 %                                                                             %
1599 %                                                                             %
1600 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1601 %
1602 %  DisplayImages() displays an image sequence to any X window screen.  It
1603 %  returns a value other than 0 if successful.  Check the exception member
1604 %  of image to determine the reason for any failure.
1605 %
1606 %  The format of the DisplayImages method is:
1607 %
1608 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1609 %        Image *images,ExceptionInfo *exception)
1610 %
1611 %  A description of each parameter follows:
1612 %
1613 %    o image_info: the image info.
1614 %
1615 %    o image: the image.
1616 %
1617 %    o exception: return any errors or warnings in this structure.
1618 %
1619 */
DisplayImages(const ImageInfo * image_info,Image * images,ExceptionInfo * exception)1620 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1621   Image *images,ExceptionInfo *exception)
1622 {
1623   char
1624     *argv[1];
1625 
1626   Display
1627     *display;
1628 
1629   Image
1630     *image;
1631 
1632   register ssize_t
1633     i;
1634 
1635   size_t
1636     state;
1637 
1638   XrmDatabase
1639     resource_database;
1640 
1641   XResourceInfo
1642     resource_info;
1643 
1644   assert(image_info != (const ImageInfo *) NULL);
1645   assert(image_info->signature == MagickCoreSignature);
1646   assert(images != (Image *) NULL);
1647   assert(images->signature == MagickCoreSignature);
1648   if (images->debug != MagickFalse )
1649     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1650   display=XOpenDisplay(image_info->server_name);
1651   if (display == (Display *) NULL)
1652     {
1653       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1654         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1655       return(MagickFalse);
1656     }
1657   if (exception->severity != UndefinedException)
1658     CatchException(exception);
1659   (void) XSetErrorHandler(XError);
1660   resource_database=XGetResourceDatabase(display,GetClientName());
1661   (void) memset(&resource_info,0,sizeof(resource_info));
1662   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1663   if (image_info->page != (char *) NULL)
1664     resource_info.image_geometry=AcquireString(image_info->page);
1665   resource_info.immutable=MagickTrue;
1666   argv[0]=AcquireString(GetClientName());
1667   state=DefaultState;
1668   for (i=0; (state & ExitState) == 0; i++)
1669   {
1670     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1671       break;
1672     image=GetImageFromList(images,i % GetImageListLength(images));
1673     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1674   }
1675   (void) SetErrorHandler((ErrorHandler) NULL);
1676   (void) SetWarningHandler((WarningHandler) NULL);
1677   argv[0]=DestroyString(argv[0]);
1678   (void) XCloseDisplay(display);
1679   XDestroyResourceInfo(&resource_info);
1680   if (exception->severity != UndefinedException)
1681     return(MagickFalse);
1682   return(MagickTrue);
1683 }
1684 
1685 /*
1686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1687 %                                                                             %
1688 %                                                                             %
1689 %                                                                             %
1690 %   R e m o t e D i s p l a y C o m m a n d                                   %
1691 %                                                                             %
1692 %                                                                             %
1693 %                                                                             %
1694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695 %
1696 %  RemoteDisplayCommand() encourages a remote display program to display the
1697 %  specified image filename.
1698 %
1699 %  The format of the RemoteDisplayCommand method is:
1700 %
1701 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1702 %        const char *window,const char *filename,ExceptionInfo *exception)
1703 %
1704 %  A description of each parameter follows:
1705 %
1706 %    o image_info: the image info.
1707 %
1708 %    o window: Specifies the name or id of an X window.
1709 %
1710 %    o filename: the name of the image filename to display.
1711 %
1712 %    o exception: return any errors or warnings in this structure.
1713 %
1714 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)1715 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1716   const char *window,const char *filename,ExceptionInfo *exception)
1717 {
1718   Display
1719     *display;
1720 
1721   MagickStatusType
1722     status;
1723 
1724   assert(image_info != (const ImageInfo *) NULL);
1725   assert(image_info->signature == MagickCoreSignature);
1726   assert(filename != (char *) NULL);
1727   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1728   display=XOpenDisplay(image_info->server_name);
1729   if (display == (Display *) NULL)
1730     {
1731       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1732         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1733       return(MagickFalse);
1734     }
1735   (void) XSetErrorHandler(XError);
1736   status=XRemoteCommand(display,window,filename);
1737   (void) XCloseDisplay(display);
1738   return(status != 0 ? MagickTrue : MagickFalse);
1739 }
1740 
1741 /*
1742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1743 %                                                                             %
1744 %                                                                             %
1745 %                                                                             %
1746 +   X A n n o t a t e E d i t I m a g e                                       %
1747 %                                                                             %
1748 %                                                                             %
1749 %                                                                             %
1750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751 %
1752 %  XAnnotateEditImage() annotates the image with text.
1753 %
1754 %  The format of the XAnnotateEditImage method is:
1755 %
1756 %      MagickBooleanType XAnnotateEditImage(Display *display,
1757 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1758 %        ExceptionInfo *exception)
1759 %
1760 %  A description of each parameter follows:
1761 %
1762 %    o display: Specifies a connection to an X server;  returned from
1763 %      XOpenDisplay.
1764 %
1765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1766 %
1767 %    o windows: Specifies a pointer to a XWindows structure.
1768 %
1769 %    o image: the image; returned from ReadImage.
1770 %
1771 */
1772 
XAnnotateEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)1773 static MagickBooleanType XAnnotateEditImage(Display *display,
1774   XResourceInfo *resource_info,XWindows *windows,Image *image,
1775   ExceptionInfo *exception)
1776 {
1777   const char
1778     *const AnnotateMenu[] =
1779     {
1780       "Font Name",
1781       "Font Color",
1782       "Box Color",
1783       "Rotate Text",
1784       "Help",
1785       "Dismiss",
1786       (char *) NULL
1787     },
1788     *const TextMenu[] =
1789     {
1790       "Help",
1791       "Apply",
1792       (char *) NULL
1793     };
1794 
1795   static const ModeType
1796     AnnotateCommands[] =
1797     {
1798       AnnotateNameCommand,
1799       AnnotateFontColorCommand,
1800       AnnotateBackgroundColorCommand,
1801       AnnotateRotateCommand,
1802       AnnotateHelpCommand,
1803       AnnotateDismissCommand
1804     },
1805     TextCommands[] =
1806     {
1807       TextHelpCommand,
1808       TextApplyCommand
1809     };
1810 
1811   static MagickBooleanType
1812     transparent_box = MagickTrue,
1813     transparent_pen = MagickFalse;
1814 
1815   static double
1816     degrees = 0.0;
1817 
1818   static unsigned int
1819     box_id = MaxNumberPens-2,
1820     font_id = 0,
1821     pen_id = 0;
1822 
1823   char
1824     command[MagickPathExtent],
1825     text[MagickPathExtent];
1826 
1827   const char
1828     *ColorMenu[MaxNumberPens+1];
1829 
1830   Cursor
1831     cursor;
1832 
1833   GC
1834     annotate_context;
1835 
1836   int
1837     id,
1838     pen_number,
1839     status,
1840     x,
1841     y;
1842 
1843   KeySym
1844     key_symbol;
1845 
1846   register char
1847     *p;
1848 
1849   register ssize_t
1850     i;
1851 
1852   unsigned int
1853     height,
1854     width;
1855 
1856   size_t
1857     state;
1858 
1859   XAnnotateInfo
1860     *annotate_info,
1861     *previous_info;
1862 
1863   XColor
1864     color;
1865 
1866   XFontStruct
1867     *font_info;
1868 
1869   XEvent
1870     event,
1871     text_event;
1872 
1873   /*
1874     Map Command widget.
1875   */
1876   (void) CloneString(&windows->command.name,"Annotate");
1877   windows->command.data=4;
1878   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1879   (void) XMapRaised(display,windows->command.id);
1880   XClientMessage(display,windows->image.id,windows->im_protocols,
1881     windows->im_update_widget,CurrentTime);
1882   /*
1883     Track pointer until button 1 is pressed.
1884   */
1885   XQueryPosition(display,windows->image.id,&x,&y);
1886   (void) XSelectInput(display,windows->image.id,
1887     windows->image.attributes.event_mask | PointerMotionMask);
1888   cursor=XCreateFontCursor(display,XC_left_side);
1889   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1890   state=DefaultState;
1891   do
1892   {
1893     if (windows->info.mapped != MagickFalse )
1894       {
1895         /*
1896           Display pointer position.
1897         */
1898         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
1899           x+windows->image.x,y+windows->image.y);
1900         XInfoWidget(display,windows,text);
1901       }
1902     /*
1903       Wait for next event.
1904     */
1905     XScreenEvent(display,windows,&event,exception);
1906     if (event.xany.window == windows->command.id)
1907       {
1908         /*
1909           Select a command from the Command widget.
1910         */
1911         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1912         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1913         if (id < 0)
1914           continue;
1915         switch (AnnotateCommands[id])
1916         {
1917           case AnnotateNameCommand:
1918           {
1919             const char
1920               *FontMenu[MaxNumberFonts];
1921 
1922             int
1923               font_number;
1924 
1925             /*
1926               Initialize menu selections.
1927             */
1928             for (i=0; i < MaxNumberFonts; i++)
1929               FontMenu[i]=resource_info->font_name[i];
1930             FontMenu[MaxNumberFonts-2]="Browser...";
1931             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1932             /*
1933               Select a font name from the pop-up menu.
1934             */
1935             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1936               (const char **) FontMenu,command);
1937             if (font_number < 0)
1938               break;
1939             if (font_number == (MaxNumberFonts-2))
1940               {
1941                 static char
1942                   font_name[MagickPathExtent] = "fixed";
1943 
1944                 /*
1945                   Select a font name from a browser.
1946                 */
1947                 resource_info->font_name[font_number]=font_name;
1948                 XFontBrowserWidget(display,windows,"Select",font_name);
1949                 if (*font_name == '\0')
1950                   break;
1951               }
1952             /*
1953               Initialize font info.
1954             */
1955             font_info=XLoadQueryFont(display,resource_info->font_name[
1956               font_number]);
1957             if (font_info == (XFontStruct *) NULL)
1958               {
1959                 XNoticeWidget(display,windows,"Unable to load font:",
1960                   resource_info->font_name[font_number]);
1961                 break;
1962               }
1963             font_id=(unsigned int) font_number;
1964             (void) XFreeFont(display,font_info);
1965             break;
1966           }
1967           case AnnotateFontColorCommand:
1968           {
1969             /*
1970               Initialize menu selections.
1971             */
1972             for (i=0; i < (int) (MaxNumberPens-2); i++)
1973               ColorMenu[i]=resource_info->pen_colors[i];
1974             ColorMenu[MaxNumberPens-2]="transparent";
1975             ColorMenu[MaxNumberPens-1]="Browser...";
1976             ColorMenu[MaxNumberPens]=(const char *) NULL;
1977             /*
1978               Select a pen color from the pop-up menu.
1979             */
1980             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981               (const char **) ColorMenu,command);
1982             if (pen_number < 0)
1983               break;
1984             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1985               MagickFalse;
1986             if (transparent_pen != MagickFalse )
1987               break;
1988             if (pen_number == (MaxNumberPens-1))
1989               {
1990                 static char
1991                   color_name[MagickPathExtent] = "gray";
1992 
1993                 /*
1994                   Select a pen color from a dialog.
1995                 */
1996                 resource_info->pen_colors[pen_number]=color_name;
1997                 XColorBrowserWidget(display,windows,"Select",color_name);
1998                 if (*color_name == '\0')
1999                   break;
2000               }
2001             /*
2002               Set pen color.
2003             */
2004             (void) XParseColor(display,windows->map_info->colormap,
2005               resource_info->pen_colors[pen_number],&color);
2006             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2007               (unsigned int) MaxColors,&color);
2008             windows->pixel_info->pen_colors[pen_number]=color;
2009             pen_id=(unsigned int) pen_number;
2010             break;
2011           }
2012           case AnnotateBackgroundColorCommand:
2013           {
2014             /*
2015               Initialize menu selections.
2016             */
2017             for (i=0; i < (int) (MaxNumberPens-2); i++)
2018               ColorMenu[i]=resource_info->pen_colors[i];
2019             ColorMenu[MaxNumberPens-2]="transparent";
2020             ColorMenu[MaxNumberPens-1]="Browser...";
2021             ColorMenu[MaxNumberPens]=(const char *) NULL;
2022             /*
2023               Select a pen color from the pop-up menu.
2024             */
2025             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026               (const char **) ColorMenu,command);
2027             if (pen_number < 0)
2028               break;
2029             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030               MagickFalse;
2031             if (transparent_box != MagickFalse )
2032               break;
2033             if (pen_number == (MaxNumberPens-1))
2034               {
2035                 static char
2036                   color_name[MagickPathExtent] = "gray";
2037 
2038                 /*
2039                   Select a pen color from a dialog.
2040                 */
2041                 resource_info->pen_colors[pen_number]=color_name;
2042                 XColorBrowserWidget(display,windows,"Select",color_name);
2043                 if (*color_name == '\0')
2044                   break;
2045               }
2046             /*
2047               Set pen color.
2048             */
2049             (void) XParseColor(display,windows->map_info->colormap,
2050               resource_info->pen_colors[pen_number],&color);
2051             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052               (unsigned int) MaxColors,&color);
2053             windows->pixel_info->pen_colors[pen_number]=color;
2054             box_id=(unsigned int) pen_number;
2055             break;
2056           }
2057           case AnnotateRotateCommand:
2058           {
2059             int
2060               entry;
2061 
2062             const char
2063               *const RotateMenu[] =
2064               {
2065                 "-90",
2066                 "-45",
2067                 "-30",
2068                 "0",
2069                 "30",
2070                 "45",
2071                 "90",
2072                 "180",
2073                 "Dialog...",
2074                 (char *) NULL,
2075               };
2076 
2077             static char
2078               angle[MagickPathExtent] = "30.0";
2079 
2080             /*
2081               Select a command from the pop-up menu.
2082             */
2083             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2084               command);
2085             if (entry < 0)
2086               break;
2087             if (entry != 8)
2088               {
2089                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2090                 break;
2091               }
2092             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2093               angle);
2094             if (*angle == '\0')
2095               break;
2096             degrees=StringToDouble(angle,(char **) NULL);
2097             break;
2098           }
2099           case AnnotateHelpCommand:
2100           {
2101             XTextViewHelp(display,resource_info,windows,MagickFalse,
2102               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2103             break;
2104           }
2105           case AnnotateDismissCommand:
2106           {
2107             /*
2108               Prematurely exit.
2109             */
2110             state|=EscapeState;
2111             state|=ExitState;
2112             break;
2113           }
2114           default:
2115             break;
2116         }
2117         continue;
2118       }
2119     switch (event.type)
2120     {
2121       case ButtonPress:
2122       {
2123         if (event.xbutton.button != Button1)
2124           break;
2125         if (event.xbutton.window != windows->image.id)
2126           break;
2127         /*
2128           Change to text entering mode.
2129         */
2130         x=event.xbutton.x;
2131         y=event.xbutton.y;
2132         state|=ExitState;
2133         break;
2134       }
2135       case ButtonRelease:
2136         break;
2137       case Expose:
2138         break;
2139       case KeyPress:
2140       {
2141         if (event.xkey.window != windows->image.id)
2142           break;
2143         /*
2144           Respond to a user key press.
2145         */
2146         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2147           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2148         switch ((int) key_symbol)
2149         {
2150           case XK_Escape:
2151           case XK_F20:
2152           {
2153             /*
2154               Prematurely exit.
2155             */
2156             state|=EscapeState;
2157             state|=ExitState;
2158             break;
2159           }
2160           case XK_F1:
2161           case XK_Help:
2162           {
2163             XTextViewHelp(display,resource_info,windows,MagickFalse,
2164               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2165             break;
2166           }
2167           default:
2168           {
2169             (void) XBell(display,0);
2170             break;
2171           }
2172         }
2173         break;
2174       }
2175       case MotionNotify:
2176       {
2177         /*
2178           Map and unmap Info widget as cursor crosses its boundaries.
2179         */
2180         x=event.xmotion.x;
2181         y=event.xmotion.y;
2182         if (windows->info.mapped != MagickFalse )
2183           {
2184             if ((x < (int) (windows->info.x+windows->info.width)) &&
2185                 (y < (int) (windows->info.y+windows->info.height)))
2186               (void) XWithdrawWindow(display,windows->info.id,
2187                 windows->info.screen);
2188           }
2189         else
2190           if ((x > (int) (windows->info.x+windows->info.width)) ||
2191               (y > (int) (windows->info.y+windows->info.height)))
2192             (void) XMapWindow(display,windows->info.id);
2193         break;
2194       }
2195       default:
2196         break;
2197     }
2198   } while ((state & ExitState) == 0);
2199   (void) XSelectInput(display,windows->image.id,
2200     windows->image.attributes.event_mask);
2201   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2202   if ((state & EscapeState) != 0)
2203     return(MagickTrue);
2204   /*
2205     Set font info and check boundary conditions.
2206   */
2207   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2208   if (font_info == (XFontStruct *) NULL)
2209     {
2210       XNoticeWidget(display,windows,"Unable to load font:",
2211         resource_info->font_name[font_id]);
2212       font_info=windows->font_info;
2213     }
2214   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2215     x=(int) windows->image.width-font_info->max_bounds.width;
2216   if (y < (int) (font_info->ascent+font_info->descent))
2217     y=(int) font_info->ascent+font_info->descent;
2218   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2219       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2220     return(MagickFalse);
2221   /*
2222     Initialize annotate structure.
2223   */
2224   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2225   if (annotate_info == (XAnnotateInfo *) NULL)
2226     return(MagickFalse);
2227   XGetAnnotateInfo(annotate_info);
2228   annotate_info->x=x;
2229   annotate_info->y=y;
2230   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2231     annotate_info->stencil=OpaqueStencil;
2232   else
2233     if (transparent_box == MagickFalse)
2234       annotate_info->stencil=BackgroundStencil;
2235     else
2236       annotate_info->stencil=ForegroundStencil;
2237   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2238   annotate_info->degrees=degrees;
2239   annotate_info->font_info=font_info;
2240   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2241     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2242     sizeof(*annotate_info->text));
2243   if (annotate_info->text == (char *) NULL)
2244     return(MagickFalse);
2245   /*
2246     Create cursor and set graphic context.
2247   */
2248   cursor=XCreateFontCursor(display,XC_pencil);
2249   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2250   annotate_context=windows->image.annotate_context;
2251   (void) XSetFont(display,annotate_context,font_info->fid);
2252   (void) XSetBackground(display,annotate_context,
2253     windows->pixel_info->pen_colors[box_id].pixel);
2254   (void) XSetForeground(display,annotate_context,
2255     windows->pixel_info->pen_colors[pen_id].pixel);
2256   /*
2257     Begin annotating the image with text.
2258   */
2259   (void) CloneString(&windows->command.name,"Text");
2260   windows->command.data=0;
2261   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2262   state=DefaultState;
2263   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2264   text_event.xexpose.width=(int) font_info->max_bounds.width;
2265   text_event.xexpose.height=font_info->max_bounds.ascent+
2266     font_info->max_bounds.descent;
2267   p=annotate_info->text;
2268   do
2269   {
2270     /*
2271       Display text cursor.
2272     */
2273     *p='\0';
2274     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2275     /*
2276       Wait for next event.
2277     */
2278     XScreenEvent(display,windows,&event,exception);
2279     if (event.xany.window == windows->command.id)
2280       {
2281         /*
2282           Select a command from the Command widget.
2283         */
2284         (void) XSetBackground(display,annotate_context,
2285           windows->pixel_info->background_color.pixel);
2286         (void) XSetForeground(display,annotate_context,
2287           windows->pixel_info->foreground_color.pixel);
2288         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2289         (void) XSetBackground(display,annotate_context,
2290           windows->pixel_info->pen_colors[box_id].pixel);
2291         (void) XSetForeground(display,annotate_context,
2292           windows->pixel_info->pen_colors[pen_id].pixel);
2293         if (id < 0)
2294           continue;
2295         switch (TextCommands[id])
2296         {
2297           case TextHelpCommand:
2298           {
2299             XTextViewHelp(display,resource_info,windows,MagickFalse,
2300               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2301             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2302             break;
2303           }
2304           case TextApplyCommand:
2305           {
2306             /*
2307               Finished annotating.
2308             */
2309             annotate_info->width=(unsigned int) XTextWidth(font_info,
2310               annotate_info->text,(int) strlen(annotate_info->text));
2311             XRefreshWindow(display,&windows->image,&text_event);
2312             state|=ExitState;
2313             break;
2314           }
2315           default:
2316             break;
2317         }
2318         continue;
2319       }
2320     /*
2321       Erase text cursor.
2322     */
2323     text_event.xexpose.x=x;
2324     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2325     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2326       (unsigned int) text_event.xexpose.width,(unsigned int)
2327       text_event.xexpose.height,MagickFalse);
2328     XRefreshWindow(display,&windows->image,&text_event);
2329     switch (event.type)
2330     {
2331       case ButtonPress:
2332       {
2333         if (event.xbutton.window != windows->image.id)
2334           break;
2335         if (event.xbutton.button == Button2)
2336           {
2337             /*
2338               Request primary selection.
2339             */
2340             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2341               windows->image.id,CurrentTime);
2342             break;
2343           }
2344         break;
2345       }
2346       case Expose:
2347       {
2348         if (event.xexpose.count == 0)
2349           {
2350             XAnnotateInfo
2351               *text_info;
2352 
2353             /*
2354               Refresh Image window.
2355             */
2356             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2357             text_info=annotate_info;
2358             while (text_info != (XAnnotateInfo *) NULL)
2359             {
2360               if (annotate_info->stencil == ForegroundStencil)
2361                 (void) XDrawString(display,windows->image.id,annotate_context,
2362                   text_info->x,text_info->y,text_info->text,
2363                   (int) strlen(text_info->text));
2364               else
2365                 (void) XDrawImageString(display,windows->image.id,
2366                   annotate_context,text_info->x,text_info->y,text_info->text,
2367                   (int) strlen(text_info->text));
2368               text_info=text_info->previous;
2369             }
2370             (void) XDrawString(display,windows->image.id,annotate_context,
2371               x,y,"_",1);
2372           }
2373         break;
2374       }
2375       case KeyPress:
2376       {
2377         int
2378           length;
2379 
2380         if (event.xkey.window != windows->image.id)
2381           break;
2382         /*
2383           Respond to a user key press.
2384         */
2385         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2386           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2387         *(command+length)='\0';
2388         if (((event.xkey.state & ControlMask) != 0) ||
2389             ((event.xkey.state & Mod1Mask) != 0))
2390           state|=ModifierState;
2391         if ((state & ModifierState) != 0)
2392           switch ((int) key_symbol)
2393           {
2394             case XK_u:
2395             case XK_U:
2396             {
2397               key_symbol=DeleteCommand;
2398               break;
2399             }
2400             default:
2401               break;
2402           }
2403         switch ((int) key_symbol)
2404         {
2405           case XK_BackSpace:
2406           {
2407             /*
2408               Erase one character.
2409             */
2410             if (p == annotate_info->text)
2411               {
2412                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2413                   break;
2414                 else
2415                   {
2416                     /*
2417                       Go to end of the previous line of text.
2418                     */
2419                     annotate_info=annotate_info->previous;
2420                     p=annotate_info->text;
2421                     x=annotate_info->x+annotate_info->width;
2422                     y=annotate_info->y;
2423                     if (annotate_info->width != 0)
2424                       p+=strlen(annotate_info->text);
2425                     break;
2426                   }
2427               }
2428             p--;
2429             x-=XTextWidth(font_info,p,1);
2430             text_event.xexpose.x=x;
2431             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2432             XRefreshWindow(display,&windows->image,&text_event);
2433             break;
2434           }
2435           case XK_bracketleft:
2436           {
2437             key_symbol=XK_Escape;
2438             break;
2439           }
2440           case DeleteCommand:
2441           {
2442             /*
2443               Erase the entire line of text.
2444             */
2445             while (p != annotate_info->text)
2446             {
2447               p--;
2448               x-=XTextWidth(font_info,p,1);
2449               text_event.xexpose.x=x;
2450               XRefreshWindow(display,&windows->image,&text_event);
2451             }
2452             break;
2453           }
2454           case XK_Escape:
2455           case XK_F20:
2456           {
2457             /*
2458               Finished annotating.
2459             */
2460             annotate_info->width=(unsigned int) XTextWidth(font_info,
2461               annotate_info->text,(int) strlen(annotate_info->text));
2462             XRefreshWindow(display,&windows->image,&text_event);
2463             state|=ExitState;
2464             break;
2465           }
2466           default:
2467           {
2468             /*
2469               Draw a single character on the Image window.
2470             */
2471             if ((state & ModifierState) != 0)
2472               break;
2473             if (*command == '\0')
2474               break;
2475             *p=(*command);
2476             if (annotate_info->stencil == ForegroundStencil)
2477               (void) XDrawString(display,windows->image.id,annotate_context,
2478                 x,y,p,1);
2479             else
2480               (void) XDrawImageString(display,windows->image.id,
2481                 annotate_context,x,y,p,1);
2482             x+=XTextWidth(font_info,p,1);
2483             p++;
2484             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2485               break;
2486           }
2487           case XK_Return:
2488           case XK_KP_Enter:
2489           {
2490             /*
2491               Advance to the next line of text.
2492             */
2493             *p='\0';
2494             annotate_info->width=(unsigned int) XTextWidth(font_info,
2495               annotate_info->text,(int) strlen(annotate_info->text));
2496             if (annotate_info->next != (XAnnotateInfo *) NULL)
2497               {
2498                 /*
2499                   Line of text already exists.
2500                 */
2501                 annotate_info=annotate_info->next;
2502                 x=annotate_info->x;
2503                 y=annotate_info->y;
2504                 p=annotate_info->text;
2505                 break;
2506               }
2507             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2508               sizeof(*annotate_info->next));
2509             if (annotate_info->next == (XAnnotateInfo *) NULL)
2510               return(MagickFalse);
2511             *annotate_info->next=(*annotate_info);
2512             annotate_info->next->previous=annotate_info;
2513             annotate_info=annotate_info->next;
2514             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2515               windows->image.width/MagickMax((ssize_t)
2516               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2517             if (annotate_info->text == (char *) NULL)
2518               return(MagickFalse);
2519             annotate_info->y+=annotate_info->height;
2520             if (annotate_info->y > (int) windows->image.height)
2521               annotate_info->y=(int) annotate_info->height;
2522             annotate_info->next=(XAnnotateInfo *) NULL;
2523             x=annotate_info->x;
2524             y=annotate_info->y;
2525             p=annotate_info->text;
2526             break;
2527           }
2528         }
2529         break;
2530       }
2531       case KeyRelease:
2532       {
2533         /*
2534           Respond to a user key release.
2535         */
2536         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2537           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2538         state&=(~ModifierState);
2539         break;
2540       }
2541       case SelectionNotify:
2542       {
2543         Atom
2544           type;
2545 
2546         int
2547           format;
2548 
2549         unsigned char
2550           *data;
2551 
2552         unsigned long
2553           after,
2554           length;
2555 
2556         /*
2557           Obtain response from primary selection.
2558         */
2559         if (event.xselection.property == (Atom) None)
2560           break;
2561         status=XGetWindowProperty(display,event.xselection.requestor,
2562           event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2563           &type,&format,&length,&after,&data);
2564         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2565             (length == 0))
2566           break;
2567         /*
2568           Annotate Image window with primary selection.
2569         */
2570         for (i=0; i < (ssize_t) length; i++)
2571         {
2572           if ((char) data[i] != '\n')
2573             {
2574               /*
2575                 Draw a single character on the Image window.
2576               */
2577               *p=(char) data[i];
2578               (void) XDrawString(display,windows->image.id,annotate_context,
2579                 x,y,p,1);
2580               x+=XTextWidth(font_info,p,1);
2581               p++;
2582               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2583                 continue;
2584             }
2585           /*
2586             Advance to the next line of text.
2587           */
2588           *p='\0';
2589           annotate_info->width=(unsigned int) XTextWidth(font_info,
2590             annotate_info->text,(int) strlen(annotate_info->text));
2591           if (annotate_info->next != (XAnnotateInfo *) NULL)
2592             {
2593               /*
2594                 Line of text already exists.
2595               */
2596               annotate_info=annotate_info->next;
2597               x=annotate_info->x;
2598               y=annotate_info->y;
2599               p=annotate_info->text;
2600               continue;
2601             }
2602           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2603             sizeof(*annotate_info->next));
2604           if (annotate_info->next == (XAnnotateInfo *) NULL)
2605             return(MagickFalse);
2606           *annotate_info->next=(*annotate_info);
2607           annotate_info->next->previous=annotate_info;
2608           annotate_info=annotate_info->next;
2609           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2610             windows->image.width/MagickMax((ssize_t)
2611             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2612           if (annotate_info->text == (char *) NULL)
2613             return(MagickFalse);
2614           annotate_info->y+=annotate_info->height;
2615           if (annotate_info->y > (int) windows->image.height)
2616             annotate_info->y=(int) annotate_info->height;
2617           annotate_info->next=(XAnnotateInfo *) NULL;
2618           x=annotate_info->x;
2619           y=annotate_info->y;
2620           p=annotate_info->text;
2621         }
2622         (void) XFree((void *) data);
2623         break;
2624       }
2625       default:
2626         break;
2627     }
2628   } while ((state & ExitState) == 0);
2629   (void) XFreeCursor(display,cursor);
2630   /*
2631     Annotation is relative to image configuration.
2632   */
2633   width=(unsigned int) image->columns;
2634   height=(unsigned int) image->rows;
2635   x=0;
2636   y=0;
2637   if (windows->image.crop_geometry != (char *) NULL)
2638     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2639   /*
2640     Initialize annotated image.
2641   */
2642   XSetCursorState(display,windows,MagickTrue);
2643   XCheckRefreshWindows(display,windows);
2644   while (annotate_info != (XAnnotateInfo *) NULL)
2645   {
2646     if (annotate_info->width == 0)
2647       {
2648         /*
2649           No text on this line--  go to the next line of text.
2650         */
2651         previous_info=annotate_info->previous;
2652         annotate_info->text=(char *)
2653           RelinquishMagickMemory(annotate_info->text);
2654         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2655         annotate_info=previous_info;
2656         continue;
2657       }
2658     /*
2659       Determine pixel index for box and pen color.
2660     */
2661     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2662     if (windows->pixel_info->colors != 0)
2663       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2664         if (windows->pixel_info->pixels[i] ==
2665             windows->pixel_info->pen_colors[box_id].pixel)
2666           {
2667             windows->pixel_info->box_index=(unsigned short) i;
2668             break;
2669           }
2670     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2671     if (windows->pixel_info->colors != 0)
2672       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2673         if (windows->pixel_info->pixels[i] ==
2674             windows->pixel_info->pen_colors[pen_id].pixel)
2675           {
2676             windows->pixel_info->pen_index=(unsigned short) i;
2677             break;
2678           }
2679     /*
2680       Define the annotate geometry string.
2681     */
2682     annotate_info->x=(int)
2683       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2684     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2685       windows->image.y)/windows->image.ximage->height;
2686     (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2687       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2688       height*annotate_info->height/windows->image.ximage->height,
2689       annotate_info->x+x,annotate_info->y+y);
2690     /*
2691       Annotate image with text.
2692     */
2693     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2694       exception);
2695     if (status == 0)
2696       return(MagickFalse);
2697     /*
2698       Free up memory.
2699     */
2700     previous_info=annotate_info->previous;
2701     annotate_info->text=DestroyString(annotate_info->text);
2702     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2703     annotate_info=previous_info;
2704   }
2705   (void) XSetForeground(display,annotate_context,
2706     windows->pixel_info->foreground_color.pixel);
2707   (void) XSetBackground(display,annotate_context,
2708     windows->pixel_info->background_color.pixel);
2709   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2710   XSetCursorState(display,windows,MagickFalse);
2711   (void) XFreeFont(display,font_info);
2712   /*
2713     Update image configuration.
2714   */
2715   XConfigureImageColormap(display,resource_info,windows,image,exception);
2716   (void) XConfigureImage(display,resource_info,windows,image,exception);
2717   return(MagickTrue);
2718 }
2719 
2720 /*
2721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2722 %                                                                             %
2723 %                                                                             %
2724 %                                                                             %
2725 +   X B a c k g r o u n d I m a g e                                           %
2726 %                                                                             %
2727 %                                                                             %
2728 %                                                                             %
2729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2730 %
2731 %  XBackgroundImage() displays the image in the background of a window.
2732 %
2733 %  The format of the XBackgroundImage method is:
2734 %
2735 %      MagickBooleanType XBackgroundImage(Display *display,
2736 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2737 %        ExceptionInfo *exception)
2738 %
2739 %  A description of each parameter follows:
2740 %
2741 %    o display: Specifies a connection to an X server; returned from
2742 %      XOpenDisplay.
2743 %
2744 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2745 %
2746 %    o windows: Specifies a pointer to a XWindows structure.
2747 %
2748 %    o image: the image.
2749 %
2750 %    o exception: return any errors or warnings in this structure.
2751 %
2752 */
XBackgroundImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2753 static MagickBooleanType XBackgroundImage(Display *display,
2754   XResourceInfo *resource_info,XWindows *windows,Image **image,
2755   ExceptionInfo *exception)
2756 {
2757 #define BackgroundImageTag  "Background/Image"
2758 
2759   int
2760     status;
2761 
2762   static char
2763     window_id[MagickPathExtent] = "root";
2764 
2765   XResourceInfo
2766     background_resources;
2767 
2768   /*
2769     Put image in background.
2770   */
2771   status=XDialogWidget(display,windows,"Background",
2772     "Enter window id (id 0x00 selects window with pointer):",window_id);
2773   if (*window_id == '\0')
2774     return(MagickFalse);
2775   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2776     exception);
2777   XInfoWidget(display,windows,BackgroundImageTag);
2778   XSetCursorState(display,windows,MagickTrue);
2779   XCheckRefreshWindows(display,windows);
2780   background_resources=(*resource_info);
2781   background_resources.window_id=window_id;
2782   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2783   status=XDisplayBackgroundImage(display,&background_resources,*image,
2784     exception);
2785   if (status != MagickFalse)
2786     XClientMessage(display,windows->image.id,windows->im_protocols,
2787       windows->im_retain_colors,CurrentTime);
2788   XSetCursorState(display,windows,MagickFalse);
2789   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2790     exception);
2791   return(MagickTrue);
2792 }
2793 
2794 /*
2795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2796 %                                                                             %
2797 %                                                                             %
2798 %                                                                             %
2799 +   X C h o p I m a g e                                                       %
2800 %                                                                             %
2801 %                                                                             %
2802 %                                                                             %
2803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2804 %
2805 %  XChopImage() chops the X image.
2806 %
2807 %  The format of the XChopImage method is:
2808 %
2809 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2810 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2811 %
2812 %  A description of each parameter follows:
2813 %
2814 %    o display: Specifies a connection to an X server; returned from
2815 %      XOpenDisplay.
2816 %
2817 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2818 %
2819 %    o windows: Specifies a pointer to a XWindows structure.
2820 %
2821 %    o image: the image.
2822 %
2823 %    o exception: return any errors or warnings in this structure.
2824 %
2825 */
XChopImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)2826 static MagickBooleanType XChopImage(Display *display,
2827   XResourceInfo *resource_info,XWindows *windows,Image **image,
2828   ExceptionInfo *exception)
2829 {
2830   const char
2831     *const ChopMenu[] =
2832     {
2833       "Direction",
2834       "Help",
2835       "Dismiss",
2836       (char *) NULL
2837     };
2838 
2839   static ModeType
2840     direction = HorizontalChopCommand;
2841 
2842   static const ModeType
2843     ChopCommands[] =
2844     {
2845       ChopDirectionCommand,
2846       ChopHelpCommand,
2847       ChopDismissCommand
2848     },
2849     DirectionCommands[] =
2850     {
2851       HorizontalChopCommand,
2852       VerticalChopCommand
2853     };
2854 
2855   char
2856     text[MagickPathExtent];
2857 
2858   Image
2859     *chop_image;
2860 
2861   int
2862     id,
2863     x,
2864     y;
2865 
2866   double
2867     scale_factor;
2868 
2869   RectangleInfo
2870     chop_info;
2871 
2872   unsigned int
2873     distance,
2874     height,
2875     width;
2876 
2877   size_t
2878     state;
2879 
2880   XEvent
2881     event;
2882 
2883   XSegment
2884     segment_info;
2885 
2886   /*
2887     Map Command widget.
2888   */
2889   (void) CloneString(&windows->command.name,"Chop");
2890   windows->command.data=1;
2891   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2892   (void) XMapRaised(display,windows->command.id);
2893   XClientMessage(display,windows->image.id,windows->im_protocols,
2894     windows->im_update_widget,CurrentTime);
2895   /*
2896     Track pointer until button 1 is pressed.
2897   */
2898   XQueryPosition(display,windows->image.id,&x,&y);
2899   (void) XSelectInput(display,windows->image.id,
2900     windows->image.attributes.event_mask | PointerMotionMask);
2901   state=DefaultState;
2902   (void) memset(&segment_info,0,sizeof(segment_info));
2903   do
2904   {
2905     if (windows->info.mapped != MagickFalse )
2906       {
2907         /*
2908           Display pointer position.
2909         */
2910         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2911           x+windows->image.x,y+windows->image.y);
2912         XInfoWidget(display,windows,text);
2913       }
2914     /*
2915       Wait for next event.
2916     */
2917     XScreenEvent(display,windows,&event,exception);
2918     if (event.xany.window == windows->command.id)
2919       {
2920         /*
2921           Select a command from the Command widget.
2922         */
2923         id=XCommandWidget(display,windows,ChopMenu,&event);
2924         if (id < 0)
2925           continue;
2926         switch (ChopCommands[id])
2927         {
2928           case ChopDirectionCommand:
2929           {
2930             char
2931               command[MagickPathExtent];
2932 
2933             const char
2934               *const Directions[] =
2935               {
2936                 "horizontal",
2937                 "vertical",
2938                 (char *) NULL,
2939               };
2940 
2941             /*
2942               Select a command from the pop-up menu.
2943             */
2944             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2945             if (id >= 0)
2946               direction=DirectionCommands[id];
2947             break;
2948           }
2949           case ChopHelpCommand:
2950           {
2951             XTextViewHelp(display,resource_info,windows,MagickFalse,
2952               "Help Viewer - Image Chop",ImageChopHelp);
2953             break;
2954           }
2955           case ChopDismissCommand:
2956           {
2957             /*
2958               Prematurely exit.
2959             */
2960             state|=EscapeState;
2961             state|=ExitState;
2962             break;
2963           }
2964           default:
2965             break;
2966         }
2967         continue;
2968       }
2969     switch (event.type)
2970     {
2971       case ButtonPress:
2972       {
2973         if (event.xbutton.button != Button1)
2974           break;
2975         if (event.xbutton.window != windows->image.id)
2976           break;
2977         /*
2978           User has committed to start point of chopping line.
2979         */
2980         segment_info.x1=(short int) event.xbutton.x;
2981         segment_info.x2=(short int) event.xbutton.x;
2982         segment_info.y1=(short int) event.xbutton.y;
2983         segment_info.y2=(short int) event.xbutton.y;
2984         state|=ExitState;
2985         break;
2986       }
2987       case ButtonRelease:
2988         break;
2989       case Expose:
2990         break;
2991       case KeyPress:
2992       {
2993         char
2994           command[MagickPathExtent];
2995 
2996         KeySym
2997           key_symbol;
2998 
2999         if (event.xkey.window != windows->image.id)
3000           break;
3001         /*
3002           Respond to a user key press.
3003         */
3004         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3005           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3006         switch ((int) key_symbol)
3007         {
3008           case XK_Escape:
3009           case XK_F20:
3010           {
3011             /*
3012               Prematurely exit.
3013             */
3014             state|=EscapeState;
3015             state|=ExitState;
3016             break;
3017           }
3018           case XK_F1:
3019           case XK_Help:
3020           {
3021             (void) XSetFunction(display,windows->image.highlight_context,
3022               GXcopy);
3023             XTextViewHelp(display,resource_info,windows,MagickFalse,
3024               "Help Viewer - Image Chop",ImageChopHelp);
3025             (void) XSetFunction(display,windows->image.highlight_context,
3026               GXinvert);
3027             break;
3028           }
3029           default:
3030           {
3031             (void) XBell(display,0);
3032             break;
3033           }
3034         }
3035         break;
3036       }
3037       case MotionNotify:
3038       {
3039         /*
3040           Map and unmap Info widget as text cursor crosses its boundaries.
3041         */
3042         x=event.xmotion.x;
3043         y=event.xmotion.y;
3044         if (windows->info.mapped != MagickFalse )
3045           {
3046             if ((x < (int) (windows->info.x+windows->info.width)) &&
3047                 (y < (int) (windows->info.y+windows->info.height)))
3048               (void) XWithdrawWindow(display,windows->info.id,
3049                 windows->info.screen);
3050           }
3051         else
3052           if ((x > (int) (windows->info.x+windows->info.width)) ||
3053               (y > (int) (windows->info.y+windows->info.height)))
3054             (void) XMapWindow(display,windows->info.id);
3055       }
3056     }
3057   } while ((state & ExitState) == 0);
3058   (void) XSelectInput(display,windows->image.id,
3059     windows->image.attributes.event_mask);
3060   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061   if ((state & EscapeState) != 0)
3062     return(MagickTrue);
3063   /*
3064     Draw line as pointer moves until the mouse button is released.
3065   */
3066   chop_info.width=0;
3067   chop_info.height=0;
3068   chop_info.x=0;
3069   chop_info.y=0;
3070   distance=0;
3071   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3072   state=DefaultState;
3073   do
3074   {
3075     if (distance > 9)
3076       {
3077         /*
3078           Display info and draw chopping line.
3079         */
3080         if (windows->info.mapped == MagickFalse)
3081           (void) XMapWindow(display,windows->info.id);
3082         (void) FormatLocaleString(text,MagickPathExtent,
3083           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3084           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3085         XInfoWidget(display,windows,text);
3086         XHighlightLine(display,windows->image.id,
3087           windows->image.highlight_context,&segment_info);
3088       }
3089     else
3090       if (windows->info.mapped != MagickFalse )
3091         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092     /*
3093       Wait for next event.
3094     */
3095     XScreenEvent(display,windows,&event,exception);
3096     if (distance > 9)
3097       XHighlightLine(display,windows->image.id,
3098         windows->image.highlight_context,&segment_info);
3099     switch (event.type)
3100     {
3101       case ButtonPress:
3102       {
3103         segment_info.x2=(short int) event.xmotion.x;
3104         segment_info.y2=(short int) event.xmotion.y;
3105         break;
3106       }
3107       case ButtonRelease:
3108       {
3109         /*
3110           User has committed to chopping line.
3111         */
3112         segment_info.x2=(short int) event.xbutton.x;
3113         segment_info.y2=(short int) event.xbutton.y;
3114         state|=ExitState;
3115         break;
3116       }
3117       case Expose:
3118         break;
3119       case MotionNotify:
3120       {
3121         segment_info.x2=(short int) event.xmotion.x;
3122         segment_info.y2=(short int) event.xmotion.y;
3123       }
3124       default:
3125         break;
3126     }
3127     /*
3128       Check boundary conditions.
3129     */
3130     if (segment_info.x2 < 0)
3131       segment_info.x2=0;
3132     else
3133       if (segment_info.x2 > windows->image.ximage->width)
3134         segment_info.x2=windows->image.ximage->width;
3135     if (segment_info.y2 < 0)
3136       segment_info.y2=0;
3137     else
3138       if (segment_info.y2 > windows->image.ximage->height)
3139         segment_info.y2=windows->image.ximage->height;
3140     distance=(unsigned int)
3141       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3142        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3143     /*
3144       Compute chopping geometry.
3145     */
3146     if (direction == HorizontalChopCommand)
3147       {
3148         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3149         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3150         chop_info.height=0;
3151         chop_info.y=0;
3152         if (segment_info.x1 > (int) segment_info.x2)
3153           {
3154             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3155             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3156           }
3157       }
3158     else
3159       {
3160         chop_info.width=0;
3161         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3162         chop_info.x=0;
3163         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3164         if (segment_info.y1 > segment_info.y2)
3165           {
3166             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3167             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3168           }
3169       }
3170   } while ((state & ExitState) == 0);
3171   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3172   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3173   if (distance <= 9)
3174     return(MagickTrue);
3175   /*
3176     Image chopping is relative to image configuration.
3177   */
3178   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3179     exception);
3180   XSetCursorState(display,windows,MagickTrue);
3181   XCheckRefreshWindows(display,windows);
3182   windows->image.window_changes.width=windows->image.ximage->width-
3183     (unsigned int) chop_info.width;
3184   windows->image.window_changes.height=windows->image.ximage->height-
3185     (unsigned int) chop_info.height;
3186   width=(unsigned int) (*image)->columns;
3187   height=(unsigned int) (*image)->rows;
3188   x=0;
3189   y=0;
3190   if (windows->image.crop_geometry != (char *) NULL)
3191     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3192   scale_factor=(double) width/windows->image.ximage->width;
3193   chop_info.x+=x;
3194   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3195   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3196   scale_factor=(double) height/windows->image.ximage->height;
3197   chop_info.y+=y;
3198   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3199   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3200   /*
3201     Chop image.
3202   */
3203   chop_image=ChopImage(*image,&chop_info,exception);
3204   XSetCursorState(display,windows,MagickFalse);
3205   if (chop_image == (Image *) NULL)
3206     return(MagickFalse);
3207   *image=DestroyImage(*image);
3208   *image=chop_image;
3209   /*
3210     Update image configuration.
3211   */
3212   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3213   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3214   return(MagickTrue);
3215 }
3216 
3217 /*
3218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219 %                                                                             %
3220 %                                                                             %
3221 %                                                                             %
3222 +   X C o l o r E d i t I m a g e                                             %
3223 %                                                                             %
3224 %                                                                             %
3225 %                                                                             %
3226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227 %
3228 %  XColorEditImage() allows the user to interactively change the color of one
3229 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3230 %
3231 %  The format of the XColorEditImage method is:
3232 %
3233 %      MagickBooleanType XColorEditImage(Display *display,
3234 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3235 %          ExceptionInfo *exception)
3236 %
3237 %  A description of each parameter follows:
3238 %
3239 %    o display: Specifies a connection to an X server;  returned from
3240 %      XOpenDisplay.
3241 %
3242 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3243 %
3244 %    o windows: Specifies a pointer to a XWindows structure.
3245 %
3246 %    o image: the image; returned from ReadImage.
3247 %
3248 %    o exception: return any errors or warnings in this structure.
3249 %
3250 */
XColorEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)3251 static MagickBooleanType XColorEditImage(Display *display,
3252   XResourceInfo *resource_info,XWindows *windows,Image **image,
3253   ExceptionInfo *exception)
3254 {
3255   const char
3256     *const ColorEditMenu[] =
3257     {
3258       "Method",
3259       "Pixel Color",
3260       "Border Color",
3261       "Fuzz",
3262       "Undo",
3263       "Help",
3264       "Dismiss",
3265       (char *) NULL
3266     };
3267 
3268   static const ModeType
3269     ColorEditCommands[] =
3270     {
3271       ColorEditMethodCommand,
3272       ColorEditColorCommand,
3273       ColorEditBorderCommand,
3274       ColorEditFuzzCommand,
3275       ColorEditUndoCommand,
3276       ColorEditHelpCommand,
3277       ColorEditDismissCommand
3278     };
3279 
3280   static PaintMethod
3281     method = PointMethod;
3282 
3283   static unsigned int
3284     pen_id = 0;
3285 
3286   static XColor
3287     border_color = { 0, 0, 0, 0, 0, 0 };
3288 
3289   char
3290     command[MagickPathExtent],
3291     text[MagickPathExtent];
3292 
3293   Cursor
3294     cursor;
3295 
3296   int
3297     entry,
3298     id,
3299     x,
3300     x_offset,
3301     y,
3302     y_offset;
3303 
3304   register Quantum
3305     *q;
3306 
3307   register ssize_t
3308     i;
3309 
3310   unsigned int
3311     height,
3312     width;
3313 
3314   size_t
3315     state;
3316 
3317   XColor
3318     color;
3319 
3320   XEvent
3321     event;
3322 
3323   /*
3324     Map Command widget.
3325   */
3326   (void) CloneString(&windows->command.name,"Color Edit");
3327   windows->command.data=4;
3328   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3329   (void) XMapRaised(display,windows->command.id);
3330   XClientMessage(display,windows->image.id,windows->im_protocols,
3331     windows->im_update_widget,CurrentTime);
3332   /*
3333     Make cursor.
3334   */
3335   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3336     resource_info->background_color,resource_info->foreground_color);
3337   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338   /*
3339     Track pointer until button 1 is pressed.
3340   */
3341   XQueryPosition(display,windows->image.id,&x,&y);
3342   (void) XSelectInput(display,windows->image.id,
3343     windows->image.attributes.event_mask | PointerMotionMask);
3344   state=DefaultState;
3345   do
3346   {
3347     if (windows->info.mapped != MagickFalse )
3348       {
3349         /*
3350           Display pointer position.
3351         */
3352         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3353           x+windows->image.x,y+windows->image.y);
3354         XInfoWidget(display,windows,text);
3355       }
3356     /*
3357       Wait for next event.
3358     */
3359     XScreenEvent(display,windows,&event,exception);
3360     if (event.xany.window == windows->command.id)
3361       {
3362         /*
3363           Select a command from the Command widget.
3364         */
3365         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3366         if (id < 0)
3367           {
3368             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3369             continue;
3370           }
3371         switch (ColorEditCommands[id])
3372         {
3373           case ColorEditMethodCommand:
3374           {
3375             char
3376               **methods;
3377 
3378             /*
3379               Select a method from the pop-up menu.
3380             */
3381             methods=(char **) GetCommandOptions(MagickMethodOptions);
3382             if (methods == (char **) NULL)
3383               break;
3384             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3385               (const char **) methods,command);
3386             if (entry >= 0)
3387               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3388                 MagickFalse,methods[entry]);
3389             methods=DestroyStringList(methods);
3390             break;
3391           }
3392           case ColorEditColorCommand:
3393           {
3394             const char
3395               *ColorMenu[MaxNumberPens];
3396 
3397             int
3398               pen_number;
3399 
3400             /*
3401               Initialize menu selections.
3402             */
3403             for (i=0; i < (int) (MaxNumberPens-2); i++)
3404               ColorMenu[i]=resource_info->pen_colors[i];
3405             ColorMenu[MaxNumberPens-2]="Browser...";
3406             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3407             /*
3408               Select a pen color from the pop-up menu.
3409             */
3410             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3411               (const char **) ColorMenu,command);
3412             if (pen_number < 0)
3413               break;
3414             if (pen_number == (MaxNumberPens-2))
3415               {
3416                 static char
3417                   color_name[MagickPathExtent] = "gray";
3418 
3419                 /*
3420                   Select a pen color from a dialog.
3421                 */
3422                 resource_info->pen_colors[pen_number]=color_name;
3423                 XColorBrowserWidget(display,windows,"Select",color_name);
3424                 if (*color_name == '\0')
3425                   break;
3426               }
3427             /*
3428               Set pen color.
3429             */
3430             (void) XParseColor(display,windows->map_info->colormap,
3431               resource_info->pen_colors[pen_number],&color);
3432             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3433               (unsigned int) MaxColors,&color);
3434             windows->pixel_info->pen_colors[pen_number]=color;
3435             pen_id=(unsigned int) pen_number;
3436             break;
3437           }
3438           case ColorEditBorderCommand:
3439           {
3440             const char
3441               *ColorMenu[MaxNumberPens];
3442 
3443             int
3444               pen_number;
3445 
3446             /*
3447               Initialize menu selections.
3448             */
3449             for (i=0; i < (int) (MaxNumberPens-2); i++)
3450               ColorMenu[i]=resource_info->pen_colors[i];
3451             ColorMenu[MaxNumberPens-2]="Browser...";
3452             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3453             /*
3454               Select a pen color from the pop-up menu.
3455             */
3456             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3457               (const char **) ColorMenu,command);
3458             if (pen_number < 0)
3459               break;
3460             if (pen_number == (MaxNumberPens-2))
3461               {
3462                 static char
3463                   color_name[MagickPathExtent] = "gray";
3464 
3465                 /*
3466                   Select a pen color from a dialog.
3467                 */
3468                 resource_info->pen_colors[pen_number]=color_name;
3469                 XColorBrowserWidget(display,windows,"Select",color_name);
3470                 if (*color_name == '\0')
3471                   break;
3472               }
3473             /*
3474               Set border color.
3475             */
3476             (void) XParseColor(display,windows->map_info->colormap,
3477               resource_info->pen_colors[pen_number],&border_color);
3478             break;
3479           }
3480           case ColorEditFuzzCommand:
3481           {
3482             const char
3483               *const FuzzMenu[] =
3484               {
3485                 "0%",
3486                 "2%",
3487                 "5%",
3488                 "10%",
3489                 "15%",
3490                 "Dialog...",
3491                 (char *) NULL,
3492               };
3493 
3494             static char
3495               fuzz[MagickPathExtent];
3496 
3497             /*
3498               Select a command from the pop-up menu.
3499             */
3500             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3501               command);
3502             if (entry < 0)
3503               break;
3504             if (entry != 5)
3505               {
3506                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3507                   QuantumRange+1.0);
3508                 break;
3509               }
3510             (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3511             (void) XDialogWidget(display,windows,"Ok",
3512               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3513             if (*fuzz == '\0')
3514               break;
3515             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3516             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3517               1.0);
3518             break;
3519           }
3520           case ColorEditUndoCommand:
3521           {
3522             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3523               image,exception);
3524             break;
3525           }
3526           case ColorEditHelpCommand:
3527           default:
3528           {
3529             XTextViewHelp(display,resource_info,windows,MagickFalse,
3530               "Help Viewer - Image Annotation",ImageColorEditHelp);
3531             break;
3532           }
3533           case ColorEditDismissCommand:
3534           {
3535             /*
3536               Prematurely exit.
3537             */
3538             state|=EscapeState;
3539             state|=ExitState;
3540             break;
3541           }
3542         }
3543         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3544         continue;
3545       }
3546     switch (event.type)
3547     {
3548       case ButtonPress:
3549       {
3550         if (event.xbutton.button != Button1)
3551           break;
3552         if ((event.xbutton.window != windows->image.id) &&
3553             (event.xbutton.window != windows->magnify.id))
3554           break;
3555         /*
3556           exit loop.
3557         */
3558         x=event.xbutton.x;
3559         y=event.xbutton.y;
3560         (void) XMagickCommand(display,resource_info,windows,
3561           SaveToUndoBufferCommand,image,exception);
3562         state|=UpdateConfigurationState;
3563         break;
3564       }
3565       case ButtonRelease:
3566       {
3567         if (event.xbutton.button != Button1)
3568           break;
3569         if ((event.xbutton.window != windows->image.id) &&
3570             (event.xbutton.window != windows->magnify.id))
3571           break;
3572         /*
3573           Update colormap information.
3574         */
3575         x=event.xbutton.x;
3576         y=event.xbutton.y;
3577         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3578         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3579         XInfoWidget(display,windows,text);
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         state&=(~UpdateConfigurationState);
3582         break;
3583       }
3584       case Expose:
3585         break;
3586       case KeyPress:
3587       {
3588         KeySym
3589           key_symbol;
3590 
3591         if (event.xkey.window == windows->magnify.id)
3592           {
3593             Window
3594               window;
3595 
3596             window=windows->magnify.id;
3597             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3598           }
3599         if (event.xkey.window != windows->image.id)
3600           break;
3601         /*
3602           Respond to a user key press.
3603         */
3604         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3605           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3606         switch ((int) key_symbol)
3607         {
3608           case XK_Escape:
3609           case XK_F20:
3610           {
3611             /*
3612               Prematurely exit.
3613             */
3614             state|=ExitState;
3615             break;
3616           }
3617           case XK_F1:
3618           case XK_Help:
3619           {
3620             XTextViewHelp(display,resource_info,windows,MagickFalse,
3621               "Help Viewer - Image Annotation",ImageColorEditHelp);
3622             break;
3623           }
3624           default:
3625           {
3626             (void) XBell(display,0);
3627             break;
3628           }
3629         }
3630         break;
3631       }
3632       case MotionNotify:
3633       {
3634         /*
3635           Map and unmap Info widget as cursor crosses its boundaries.
3636         */
3637         x=event.xmotion.x;
3638         y=event.xmotion.y;
3639         if (windows->info.mapped != MagickFalse )
3640           {
3641             if ((x < (int) (windows->info.x+windows->info.width)) &&
3642                 (y < (int) (windows->info.y+windows->info.height)))
3643               (void) XWithdrawWindow(display,windows->info.id,
3644                 windows->info.screen);
3645           }
3646         else
3647           if ((x > (int) (windows->info.x+windows->info.width)) ||
3648               (y > (int) (windows->info.y+windows->info.height)))
3649             (void) XMapWindow(display,windows->info.id);
3650         break;
3651       }
3652       default:
3653         break;
3654     }
3655     if (event.xany.window == windows->magnify.id)
3656       {
3657         x=windows->magnify.x-windows->image.x;
3658         y=windows->magnify.y-windows->image.y;
3659       }
3660     x_offset=x;
3661     y_offset=y;
3662     if ((state & UpdateConfigurationState) != 0)
3663       {
3664         CacheView
3665           *image_view;
3666 
3667         int
3668           x,
3669           y;
3670 
3671         /*
3672           Pixel edit is relative to image configuration.
3673         */
3674         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3675           MagickTrue);
3676         color=windows->pixel_info->pen_colors[pen_id];
3677         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3678         width=(unsigned int) (*image)->columns;
3679         height=(unsigned int) (*image)->rows;
3680         x=0;
3681         y=0;
3682         if (windows->image.crop_geometry != (char *) NULL)
3683           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3684             &width,&height);
3685         x_offset=(int)
3686           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3687         y_offset=(int)
3688           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3689         if ((x_offset < 0) || (y_offset < 0))
3690           continue;
3691         if ((x_offset >= (int) (*image)->columns) ||
3692             (y_offset >= (int) (*image)->rows))
3693           continue;
3694         image_view=AcquireAuthenticCacheView(*image,exception);
3695         switch (method)
3696         {
3697           case PointMethod:
3698           default:
3699           {
3700             /*
3701               Update color information using point algorithm.
3702             */
3703             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3704               return(MagickFalse);
3705             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3706               (ssize_t) y_offset,1,1,exception);
3707             if (q == (Quantum *) NULL)
3708               break;
3709             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3710             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3711             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3712             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3713             break;
3714           }
3715           case ReplaceMethod:
3716           {
3717             PixelInfo
3718               pixel,
3719               target;
3720 
3721             /*
3722               Update color information using replace algorithm.
3723             */
3724             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3725               x_offset,(ssize_t) y_offset,&target,exception);
3726             if ((*image)->storage_class == DirectClass)
3727               {
3728                 for (y=0; y < (int) (*image)->rows; y++)
3729                 {
3730                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3731                     (*image)->columns,1,exception);
3732                   if (q == (Quantum *) NULL)
3733                     break;
3734                   for (x=0; x < (int) (*image)->columns; x++)
3735                   {
3736                     GetPixelInfoPixel(*image,q,&pixel);
3737                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3738                       {
3739                         SetPixelRed(*image,ScaleShortToQuantum(
3740                           color.red),q);
3741                         SetPixelGreen(*image,ScaleShortToQuantum(
3742                           color.green),q);
3743                         SetPixelBlue(*image,ScaleShortToQuantum(
3744                           color.blue),q);
3745                       }
3746                     q+=GetPixelChannels(*image);
3747                   }
3748                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3749                     break;
3750                 }
3751               }
3752             else
3753               {
3754                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3755                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3756                     {
3757                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3758                         color.red);
3759                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3760                         color.green);
3761                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3762                         color.blue);
3763                     }
3764                 (void) SyncImage(*image,exception);
3765               }
3766             break;
3767           }
3768           case FloodfillMethod:
3769           case FillToBorderMethod:
3770           {
3771             DrawInfo
3772               *draw_info;
3773 
3774             PixelInfo
3775               target;
3776 
3777             /*
3778               Update color information using floodfill algorithm.
3779             */
3780             (void) GetOneVirtualPixelInfo(*image,
3781               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3782               y_offset,&target,exception);
3783             if (method == FillToBorderMethod)
3784               {
3785                 target.red=(double)
3786                   ScaleShortToQuantum(border_color.red);
3787                 target.green=(double)
3788                   ScaleShortToQuantum(border_color.green);
3789                 target.blue=(double)
3790                   ScaleShortToQuantum(border_color.blue);
3791               }
3792             draw_info=CloneDrawInfo(resource_info->image_info,
3793               (DrawInfo *) NULL);
3794             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3795               AllCompliance,&draw_info->fill,exception);
3796             (void) FloodfillPaintImage(*image,draw_info,&target,
3797               (ssize_t)x_offset,(ssize_t)y_offset,
3798               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3799             draw_info=DestroyDrawInfo(draw_info);
3800             break;
3801           }
3802           case ResetMethod:
3803           {
3804             /*
3805               Update color information using reset algorithm.
3806             */
3807             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3808               return(MagickFalse);
3809             for (y=0; y < (int) (*image)->rows; y++)
3810             {
3811               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3812                 (*image)->columns,1,exception);
3813               if (q == (Quantum *) NULL)
3814                 break;
3815               for (x=0; x < (int) (*image)->columns; x++)
3816               {
3817                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3818                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3819                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3820                 q+=GetPixelChannels(*image);
3821               }
3822               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3823                 break;
3824             }
3825             break;
3826           }
3827         }
3828         image_view=DestroyCacheView(image_view);
3829         state&=(~UpdateConfigurationState);
3830       }
3831   } while ((state & ExitState) == 0);
3832   (void) XSelectInput(display,windows->image.id,
3833     windows->image.attributes.event_mask);
3834   XSetCursorState(display,windows,MagickFalse);
3835   (void) XFreeCursor(display,cursor);
3836   return(MagickTrue);
3837 }
3838 
3839 /*
3840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3841 %                                                                             %
3842 %                                                                             %
3843 %                                                                             %
3844 +   X C o m p o s i t e I m a g e                                             %
3845 %                                                                             %
3846 %                                                                             %
3847 %                                                                             %
3848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849 %
3850 %  XCompositeImage() requests an image name from the user, reads the image and
3851 %  composites it with the X window image at a location the user chooses with
3852 %  the pointer.
3853 %
3854 %  The format of the XCompositeImage method is:
3855 %
3856 %      MagickBooleanType XCompositeImage(Display *display,
3857 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3858 %        ExceptionInfo *exception)
3859 %
3860 %  A description of each parameter follows:
3861 %
3862 %    o display: Specifies a connection to an X server;  returned from
3863 %      XOpenDisplay.
3864 %
3865 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3866 %
3867 %    o windows: Specifies a pointer to a XWindows structure.
3868 %
3869 %    o image: the image; returned from ReadImage.
3870 %
3871 %    o exception: return any errors or warnings in this structure.
3872 %
3873 */
XCompositeImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)3874 static MagickBooleanType XCompositeImage(Display *display,
3875   XResourceInfo *resource_info,XWindows *windows,Image *image,
3876   ExceptionInfo *exception)
3877 {
3878   const char
3879     *const CompositeMenu[] =
3880     {
3881       "Operators",
3882       "Dissolve",
3883       "Displace",
3884       "Help",
3885       "Dismiss",
3886       (char *) NULL
3887     };
3888 
3889   static char
3890     displacement_geometry[MagickPathExtent] = "30x30",
3891     filename[MagickPathExtent] = "\0";
3892 
3893   static CompositeOperator
3894     compose = CopyCompositeOp;
3895 
3896   static const ModeType
3897     CompositeCommands[] =
3898     {
3899       CompositeOperatorsCommand,
3900       CompositeDissolveCommand,
3901       CompositeDisplaceCommand,
3902       CompositeHelpCommand,
3903       CompositeDismissCommand
3904     };
3905 
3906   char
3907     text[MagickPathExtent];
3908 
3909   Cursor
3910     cursor;
3911 
3912   Image
3913     *composite_image;
3914 
3915   int
3916     entry,
3917     id,
3918     x,
3919     y;
3920 
3921   double
3922     blend,
3923     scale_factor;
3924 
3925   RectangleInfo
3926     highlight_info,
3927     composite_info;
3928 
3929   unsigned int
3930     height,
3931     width;
3932 
3933   size_t
3934     state;
3935 
3936   XEvent
3937     event;
3938 
3939   /*
3940     Request image file name from user.
3941   */
3942   XFileBrowserWidget(display,windows,"Composite",filename);
3943   if (*filename == '\0')
3944     return(MagickTrue);
3945   /*
3946     Read image.
3947   */
3948   XSetCursorState(display,windows,MagickTrue);
3949   XCheckRefreshWindows(display,windows);
3950   (void) CopyMagickString(resource_info->image_info->filename,filename,
3951     MagickPathExtent);
3952   composite_image=ReadImage(resource_info->image_info,exception);
3953   CatchException(exception);
3954   XSetCursorState(display,windows,MagickFalse);
3955   if (composite_image == (Image *) NULL)
3956     return(MagickFalse);
3957   /*
3958     Map Command widget.
3959   */
3960   (void) CloneString(&windows->command.name,"Composite");
3961   windows->command.data=1;
3962   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3963   (void) XMapRaised(display,windows->command.id);
3964   XClientMessage(display,windows->image.id,windows->im_protocols,
3965     windows->im_update_widget,CurrentTime);
3966   /*
3967     Track pointer until button 1 is pressed.
3968   */
3969   XQueryPosition(display,windows->image.id,&x,&y);
3970   (void) XSelectInput(display,windows->image.id,
3971     windows->image.attributes.event_mask | PointerMotionMask);
3972   composite_info.x=(ssize_t) windows->image.x+x;
3973   composite_info.y=(ssize_t) windows->image.y+y;
3974   composite_info.width=0;
3975   composite_info.height=0;
3976   cursor=XCreateFontCursor(display,XC_ul_angle);
3977   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3978   blend=0.0;
3979   state=DefaultState;
3980   do
3981   {
3982     if (windows->info.mapped != MagickFalse )
3983       {
3984         /*
3985           Display pointer position.
3986         */
3987         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3988           (long) composite_info.x,(long) composite_info.y);
3989         XInfoWidget(display,windows,text);
3990       }
3991     highlight_info=composite_info;
3992     highlight_info.x=composite_info.x-windows->image.x;
3993     highlight_info.y=composite_info.y-windows->image.y;
3994     XHighlightRectangle(display,windows->image.id,
3995       windows->image.highlight_context,&highlight_info);
3996     /*
3997       Wait for next event.
3998     */
3999     XScreenEvent(display,windows,&event,exception);
4000     XHighlightRectangle(display,windows->image.id,
4001       windows->image.highlight_context,&highlight_info);
4002     if (event.xany.window == windows->command.id)
4003       {
4004         /*
4005           Select a command from the Command widget.
4006         */
4007         id=XCommandWidget(display,windows,CompositeMenu,&event);
4008         if (id < 0)
4009           continue;
4010         switch (CompositeCommands[id])
4011         {
4012           case CompositeOperatorsCommand:
4013           {
4014             char
4015               command[MagickPathExtent],
4016               **operators;
4017 
4018             /*
4019               Select a command from the pop-up menu.
4020             */
4021             operators=GetCommandOptions(MagickComposeOptions);
4022             if (operators == (char **) NULL)
4023               break;
4024             entry=XMenuWidget(display,windows,CompositeMenu[id],
4025               (const char **) operators,command);
4026             if (entry >= 0)
4027               compose=(CompositeOperator) ParseCommandOption(
4028                 MagickComposeOptions,MagickFalse,operators[entry]);
4029             operators=DestroyStringList(operators);
4030             break;
4031           }
4032           case CompositeDissolveCommand:
4033           {
4034             static char
4035               factor[MagickPathExtent] = "20.0";
4036 
4037             /*
4038               Dissolve the two images a given percent.
4039             */
4040             (void) XSetFunction(display,windows->image.highlight_context,
4041               GXcopy);
4042             (void) XDialogWidget(display,windows,"Dissolve",
4043               "Enter the blend factor (0.0 - 99.9%):",factor);
4044             (void) XSetFunction(display,windows->image.highlight_context,
4045               GXinvert);
4046             if (*factor == '\0')
4047               break;
4048             blend=StringToDouble(factor,(char **) NULL);
4049             compose=DissolveCompositeOp;
4050             break;
4051           }
4052           case CompositeDisplaceCommand:
4053           {
4054             /*
4055               Get horizontal and vertical scale displacement geometry.
4056             */
4057             (void) XSetFunction(display,windows->image.highlight_context,
4058               GXcopy);
4059             (void) XDialogWidget(display,windows,"Displace",
4060               "Enter the horizontal and vertical scale:",displacement_geometry);
4061             (void) XSetFunction(display,windows->image.highlight_context,
4062               GXinvert);
4063             if (*displacement_geometry == '\0')
4064               break;
4065             compose=DisplaceCompositeOp;
4066             break;
4067           }
4068           case CompositeHelpCommand:
4069           {
4070             (void) XSetFunction(display,windows->image.highlight_context,
4071               GXcopy);
4072             XTextViewHelp(display,resource_info,windows,MagickFalse,
4073               "Help Viewer - Image Composite",ImageCompositeHelp);
4074             (void) XSetFunction(display,windows->image.highlight_context,
4075               GXinvert);
4076             break;
4077           }
4078           case CompositeDismissCommand:
4079           {
4080             /*
4081               Prematurely exit.
4082             */
4083             state|=EscapeState;
4084             state|=ExitState;
4085             break;
4086           }
4087           default:
4088             break;
4089         }
4090         continue;
4091       }
4092     switch (event.type)
4093     {
4094       case ButtonPress:
4095       {
4096         if (image->debug != MagickFalse )
4097           (void) LogMagickEvent(X11Event,GetMagickModule(),
4098             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4099             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4100         if (event.xbutton.button != Button1)
4101           break;
4102         if (event.xbutton.window != windows->image.id)
4103           break;
4104         /*
4105           Change cursor.
4106         */
4107         composite_info.width=composite_image->columns;
4108         composite_info.height=composite_image->rows;
4109         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4110         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4111         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4112         break;
4113       }
4114       case ButtonRelease:
4115       {
4116         if (image->debug != MagickFalse )
4117           (void) LogMagickEvent(X11Event,GetMagickModule(),
4118             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4119             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4120         if (event.xbutton.button != Button1)
4121           break;
4122         if (event.xbutton.window != windows->image.id)
4123           break;
4124         if ((composite_info.width != 0) && (composite_info.height != 0))
4125           {
4126             /*
4127               User has selected the location of the composite image.
4128             */
4129             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4130             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4131             state|=ExitState;
4132           }
4133         break;
4134       }
4135       case Expose:
4136         break;
4137       case KeyPress:
4138       {
4139         char
4140           command[MagickPathExtent];
4141 
4142         KeySym
4143           key_symbol;
4144 
4145         int
4146           length;
4147 
4148         if (event.xkey.window != windows->image.id)
4149           break;
4150         /*
4151           Respond to a user key press.
4152         */
4153         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4154           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4155         *(command+length)='\0';
4156         if (image->debug != MagickFalse )
4157           (void) LogMagickEvent(X11Event,GetMagickModule(),
4158             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4159         switch ((int) key_symbol)
4160         {
4161           case XK_Escape:
4162           case XK_F20:
4163           {
4164             /*
4165               Prematurely exit.
4166             */
4167             composite_image=DestroyImage(composite_image);
4168             state|=EscapeState;
4169             state|=ExitState;
4170             break;
4171           }
4172           case XK_F1:
4173           case XK_Help:
4174           {
4175             (void) XSetFunction(display,windows->image.highlight_context,
4176               GXcopy);
4177             XTextViewHelp(display,resource_info,windows,MagickFalse,
4178               "Help Viewer - Image Composite",ImageCompositeHelp);
4179             (void) XSetFunction(display,windows->image.highlight_context,
4180               GXinvert);
4181             break;
4182           }
4183           default:
4184           {
4185             (void) XBell(display,0);
4186             break;
4187           }
4188         }
4189         break;
4190       }
4191       case MotionNotify:
4192       {
4193         /*
4194           Map and unmap Info widget as text cursor crosses its boundaries.
4195         */
4196         x=event.xmotion.x;
4197         y=event.xmotion.y;
4198         if (windows->info.mapped != MagickFalse )
4199           {
4200             if ((x < (int) (windows->info.x+windows->info.width)) &&
4201                 (y < (int) (windows->info.y+windows->info.height)))
4202               (void) XWithdrawWindow(display,windows->info.id,
4203                 windows->info.screen);
4204           }
4205         else
4206           if ((x > (int) (windows->info.x+windows->info.width)) ||
4207               (y > (int) (windows->info.y+windows->info.height)))
4208             (void) XMapWindow(display,windows->info.id);
4209         composite_info.x=(ssize_t) windows->image.x+x;
4210         composite_info.y=(ssize_t) windows->image.y+y;
4211         break;
4212       }
4213       default:
4214       {
4215         if (image->debug != MagickFalse )
4216           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4217             event.type);
4218         break;
4219       }
4220     }
4221   } while ((state & ExitState) == 0);
4222   (void) XSelectInput(display,windows->image.id,
4223     windows->image.attributes.event_mask);
4224   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4225   XSetCursorState(display,windows,MagickFalse);
4226   (void) XFreeCursor(display,cursor);
4227   if ((state & EscapeState) != 0)
4228     return(MagickTrue);
4229   /*
4230     Image compositing is relative to image configuration.
4231   */
4232   XSetCursorState(display,windows,MagickTrue);
4233   XCheckRefreshWindows(display,windows);
4234   width=(unsigned int) image->columns;
4235   height=(unsigned int) image->rows;
4236   x=0;
4237   y=0;
4238   if (windows->image.crop_geometry != (char *) NULL)
4239     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4240   scale_factor=(double) width/windows->image.ximage->width;
4241   composite_info.x+=x;
4242   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4243   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4244   scale_factor=(double) height/windows->image.ximage->height;
4245   composite_info.y+=y;
4246   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4247   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4248   if ((composite_info.width != composite_image->columns) ||
4249       (composite_info.height != composite_image->rows))
4250     {
4251       Image
4252         *resize_image;
4253 
4254       /*
4255         Scale composite image.
4256       */
4257       resize_image=ResizeImage(composite_image,composite_info.width,
4258         composite_info.height,composite_image->filter,exception);
4259       composite_image=DestroyImage(composite_image);
4260       if (resize_image == (Image *) NULL)
4261         {
4262           XSetCursorState(display,windows,MagickFalse);
4263           return(MagickFalse);
4264         }
4265       composite_image=resize_image;
4266     }
4267   if (compose == DisplaceCompositeOp)
4268     (void) SetImageArtifact(composite_image,"compose:args",
4269       displacement_geometry);
4270   if (blend != 0.0)
4271     {
4272       CacheView
4273         *image_view;
4274 
4275       int
4276         y;
4277 
4278       Quantum
4279         opacity;
4280 
4281       register int
4282         x;
4283 
4284       register Quantum
4285         *q;
4286 
4287       /*
4288         Create mattes for blending.
4289       */
4290       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4291       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4292         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4293       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4294         return(MagickFalse);
4295       image->alpha_trait=BlendPixelTrait;
4296       image_view=AcquireAuthenticCacheView(image,exception);
4297       for (y=0; y < (int) image->rows; y++)
4298       {
4299         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4300           exception);
4301         if (q == (Quantum *) NULL)
4302           break;
4303         for (x=0; x < (int) image->columns; x++)
4304         {
4305           SetPixelAlpha(image,opacity,q);
4306           q+=GetPixelChannels(image);
4307         }
4308         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4309           break;
4310       }
4311       image_view=DestroyCacheView(image_view);
4312     }
4313   /*
4314     Composite image with X Image window.
4315   */
4316   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4317     composite_info.x,composite_info.y,exception);
4318   composite_image=DestroyImage(composite_image);
4319   XSetCursorState(display,windows,MagickFalse);
4320   /*
4321     Update image configuration.
4322   */
4323   XConfigureImageColormap(display,resource_info,windows,image,exception);
4324   (void) XConfigureImage(display,resource_info,windows,image,exception);
4325   return(MagickTrue);
4326 }
4327 
4328 /*
4329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4330 %                                                                             %
4331 %                                                                             %
4332 %                                                                             %
4333 +   X C o n f i g u r e I m a g e                                             %
4334 %                                                                             %
4335 %                                                                             %
4336 %                                                                             %
4337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4338 %
4339 %  XConfigureImage() creates a new X image.  It also notifies the window
4340 %  manager of the new image size and configures the transient widows.
4341 %
4342 %  The format of the XConfigureImage method is:
4343 %
4344 %      MagickBooleanType XConfigureImage(Display *display,
4345 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4346 %        ExceptionInfo *exception)
4347 %
4348 %  A description of each parameter follows:
4349 %
4350 %    o display: Specifies a connection to an X server; returned from
4351 %      XOpenDisplay.
4352 %
4353 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4354 %
4355 %    o windows: Specifies a pointer to a XWindows structure.
4356 %
4357 %    o image: the image.
4358 %
4359 %    o exception: return any errors or warnings in this structure.
4360 %
4361 %    o exception: return any errors or warnings in this structure.
4362 %
4363 */
XConfigureImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)4364 static MagickBooleanType XConfigureImage(Display *display,
4365   XResourceInfo *resource_info,XWindows *windows,Image *image,
4366   ExceptionInfo *exception)
4367 {
4368   char
4369     geometry[MagickPathExtent];
4370 
4371   MagickStatusType
4372     status;
4373 
4374   size_t
4375     mask,
4376     height,
4377     width;
4378 
4379   ssize_t
4380     x,
4381     y;
4382 
4383   XSizeHints
4384     *size_hints;
4385 
4386   XWindowChanges
4387     window_changes;
4388 
4389   /*
4390     Dismiss if window dimensions are zero.
4391   */
4392   width=(unsigned int) windows->image.window_changes.width;
4393   height=(unsigned int) windows->image.window_changes.height;
4394   if (image->debug != MagickFalse )
4395     (void) LogMagickEvent(X11Event,GetMagickModule(),
4396       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4397       windows->image.ximage->height,(double) width,(double) height);
4398   if ((width*height) == 0)
4399     return(MagickTrue);
4400   x=0;
4401   y=0;
4402   /*
4403     Resize image to fit Image window dimensions.
4404   */
4405   XSetCursorState(display,windows,MagickTrue);
4406   (void) XFlush(display);
4407   if (((int) width != windows->image.ximage->width) ||
4408       ((int) height != windows->image.ximage->height))
4409     image->taint=MagickTrue;
4410   windows->magnify.x=(int)
4411     width*windows->magnify.x/windows->image.ximage->width;
4412   windows->magnify.y=(int)
4413     height*windows->magnify.y/windows->image.ximage->height;
4414   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4415   windows->image.y=(int)
4416     (height*windows->image.y/windows->image.ximage->height);
4417   status=XMakeImage(display,resource_info,&windows->image,image,
4418     (unsigned int) width,(unsigned int) height,exception);
4419   if (status == MagickFalse)
4420     XNoticeWidget(display,windows,"Unable to configure X image:",
4421       windows->image.name);
4422   /*
4423     Notify window manager of the new configuration.
4424   */
4425   if (resource_info->image_geometry != (char *) NULL)
4426     (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4427       resource_info->image_geometry);
4428   else
4429     (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4430       XDisplayWidth(display,windows->image.screen),
4431       XDisplayHeight(display,windows->image.screen));
4432   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4433   window_changes.width=(int) width;
4434   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4435     window_changes.width=XDisplayWidth(display,windows->image.screen);
4436   window_changes.height=(int) height;
4437   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4438     window_changes.height=XDisplayHeight(display,windows->image.screen);
4439   mask=(size_t) (CWWidth | CWHeight);
4440   if (resource_info->backdrop)
4441     {
4442       mask|=CWX | CWY;
4443       window_changes.x=(int)
4444         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4445       window_changes.y=(int)
4446         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4447     }
4448   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4449     (unsigned int) mask,&window_changes);
4450   (void) XClearWindow(display,windows->image.id);
4451   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4452   /*
4453     Update Magnify window configuration.
4454   */
4455   if (windows->magnify.mapped != MagickFalse )
4456     XMakeMagnifyImage(display,windows,exception);
4457   windows->pan.crop_geometry=windows->image.crop_geometry;
4458   XBestIconSize(display,&windows->pan,image);
4459   while (((windows->pan.width << 1) < MaxIconSize) &&
4460          ((windows->pan.height << 1) < MaxIconSize))
4461   {
4462     windows->pan.width<<=1;
4463     windows->pan.height<<=1;
4464   }
4465   if (windows->pan.geometry != (char *) NULL)
4466     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4467       &windows->pan.width,&windows->pan.height);
4468   window_changes.width=(int) windows->pan.width;
4469   window_changes.height=(int) windows->pan.height;
4470   size_hints=XAllocSizeHints();
4471   if (size_hints != (XSizeHints *) NULL)
4472     {
4473       /*
4474         Set new size hints.
4475       */
4476       size_hints->flags=PSize | PMinSize | PMaxSize;
4477       size_hints->width=window_changes.width;
4478       size_hints->height=window_changes.height;
4479       size_hints->min_width=size_hints->width;
4480       size_hints->min_height=size_hints->height;
4481       size_hints->max_width=size_hints->width;
4482       size_hints->max_height=size_hints->height;
4483       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4484       (void) XFree((void *) size_hints);
4485     }
4486   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4487     (unsigned int) (CWWidth | CWHeight),&window_changes);
4488   /*
4489     Update icon window configuration.
4490   */
4491   windows->icon.crop_geometry=windows->image.crop_geometry;
4492   XBestIconSize(display,&windows->icon,image);
4493   window_changes.width=(int) windows->icon.width;
4494   window_changes.height=(int) windows->icon.height;
4495   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4496     (unsigned int) (CWWidth | CWHeight),&window_changes);
4497   XSetCursorState(display,windows,MagickFalse);
4498   return(status != 0 ? MagickTrue : MagickFalse);
4499 }
4500 
4501 /*
4502 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4503 %                                                                             %
4504 %                                                                             %
4505 %                                                                             %
4506 +   X C r o p I m a g e                                                       %
4507 %                                                                             %
4508 %                                                                             %
4509 %                                                                             %
4510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4511 %
4512 %  XCropImage() allows the user to select a region of the image and crop, copy,
4513 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4514 %  the image with XPasteImage.
4515 %
4516 %  The format of the XCropImage method is:
4517 %
4518 %      MagickBooleanType XCropImage(Display *display,
4519 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4520 %        const ClipboardMode mode,ExceptionInfo *exception)
4521 %
4522 %  A description of each parameter follows:
4523 %
4524 %    o display: Specifies a connection to an X server; returned from
4525 %      XOpenDisplay.
4526 %
4527 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4528 %
4529 %    o windows: Specifies a pointer to a XWindows structure.
4530 %
4531 %    o image: the image; returned from ReadImage.
4532 %
4533 %    o mode: This unsigned value specified whether the image should be
4534 %      cropped, copied, or cut.
4535 %
4536 %    o exception: return any errors or warnings in this structure.
4537 %
4538 */
XCropImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,const ClipboardMode mode,ExceptionInfo * exception)4539 static MagickBooleanType XCropImage(Display *display,
4540   XResourceInfo *resource_info,XWindows *windows,Image *image,
4541   const ClipboardMode mode,ExceptionInfo *exception)
4542 {
4543   const char
4544     *const CropModeMenu[] =
4545     {
4546       "Help",
4547       "Dismiss",
4548       (char *) NULL
4549     },
4550     *RectifyModeMenu[] =
4551     {
4552       "Crop",
4553       "Help",
4554       "Dismiss",
4555       (char *) NULL
4556     };
4557 
4558   static const ModeType
4559     CropCommands[] =
4560     {
4561       CropHelpCommand,
4562       CropDismissCommand
4563     },
4564     RectifyCommands[] =
4565     {
4566       RectifyCopyCommand,
4567       RectifyHelpCommand,
4568       RectifyDismissCommand
4569     };
4570 
4571   CacheView
4572     *image_view;
4573 
4574   char
4575     command[MagickPathExtent],
4576     text[MagickPathExtent];
4577 
4578   Cursor
4579     cursor;
4580 
4581   int
4582     id,
4583     x,
4584     y;
4585 
4586   KeySym
4587     key_symbol;
4588 
4589   Image
4590     *crop_image;
4591 
4592   double
4593     scale_factor;
4594 
4595   RectangleInfo
4596     crop_info,
4597     highlight_info;
4598 
4599   register Quantum
4600     *q;
4601 
4602   unsigned int
4603     height,
4604     width;
4605 
4606   size_t
4607     state;
4608 
4609   XEvent
4610     event;
4611 
4612   /*
4613     Map Command widget.
4614   */
4615   switch (mode)
4616   {
4617     case CopyMode:
4618     {
4619       (void) CloneString(&windows->command.name,"Copy");
4620       break;
4621     }
4622     case CropMode:
4623     {
4624       (void) CloneString(&windows->command.name,"Crop");
4625       break;
4626     }
4627     case CutMode:
4628     {
4629       (void) CloneString(&windows->command.name,"Cut");
4630       break;
4631     }
4632   }
4633   RectifyModeMenu[0]=windows->command.name;
4634   windows->command.data=0;
4635   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4636   (void) XMapRaised(display,windows->command.id);
4637   XClientMessage(display,windows->image.id,windows->im_protocols,
4638     windows->im_update_widget,CurrentTime);
4639   /*
4640     Track pointer until button 1 is pressed.
4641   */
4642   XQueryPosition(display,windows->image.id,&x,&y);
4643   (void) XSelectInput(display,windows->image.id,
4644     windows->image.attributes.event_mask | PointerMotionMask);
4645   crop_info.x=(ssize_t) windows->image.x+x;
4646   crop_info.y=(ssize_t) windows->image.y+y;
4647   crop_info.width=0;
4648   crop_info.height=0;
4649   cursor=XCreateFontCursor(display,XC_fleur);
4650   state=DefaultState;
4651   do
4652   {
4653     if (windows->info.mapped != MagickFalse )
4654       {
4655         /*
4656           Display pointer position.
4657         */
4658         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4659           (long) crop_info.x,(long) crop_info.y);
4660         XInfoWidget(display,windows,text);
4661       }
4662     /*
4663       Wait for next event.
4664     */
4665     XScreenEvent(display,windows,&event,exception);
4666     if (event.xany.window == windows->command.id)
4667       {
4668         /*
4669           Select a command from the Command widget.
4670         */
4671         id=XCommandWidget(display,windows,CropModeMenu,&event);
4672         if (id < 0)
4673           continue;
4674         switch (CropCommands[id])
4675         {
4676           case CropHelpCommand:
4677           {
4678             switch (mode)
4679             {
4680               case CopyMode:
4681               {
4682                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4683                   "Help Viewer - Image Copy",ImageCopyHelp);
4684                 break;
4685               }
4686               case CropMode:
4687               {
4688                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4689                   "Help Viewer - Image Crop",ImageCropHelp);
4690                 break;
4691               }
4692               case CutMode:
4693               {
4694                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4695                   "Help Viewer - Image Cut",ImageCutHelp);
4696                 break;
4697               }
4698             }
4699             break;
4700           }
4701           case CropDismissCommand:
4702           {
4703             /*
4704               Prematurely exit.
4705             */
4706             state|=EscapeState;
4707             state|=ExitState;
4708             break;
4709           }
4710           default:
4711             break;
4712         }
4713         continue;
4714       }
4715     switch (event.type)
4716     {
4717       case ButtonPress:
4718       {
4719         if (event.xbutton.button != Button1)
4720           break;
4721         if (event.xbutton.window != windows->image.id)
4722           break;
4723         /*
4724           Note first corner of cropping rectangle-- exit loop.
4725         */
4726         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4727         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4728         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4729         state|=ExitState;
4730         break;
4731       }
4732       case ButtonRelease:
4733         break;
4734       case Expose:
4735         break;
4736       case KeyPress:
4737       {
4738         if (event.xkey.window != windows->image.id)
4739           break;
4740         /*
4741           Respond to a user key press.
4742         */
4743         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4744           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4745         switch ((int) key_symbol)
4746         {
4747           case XK_Escape:
4748           case XK_F20:
4749           {
4750             /*
4751               Prematurely exit.
4752             */
4753             state|=EscapeState;
4754             state|=ExitState;
4755             break;
4756           }
4757           case XK_F1:
4758           case XK_Help:
4759           {
4760             switch (mode)
4761             {
4762               case CopyMode:
4763               {
4764                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4765                   "Help Viewer - Image Copy",ImageCopyHelp);
4766                 break;
4767               }
4768               case CropMode:
4769               {
4770                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4771                   "Help Viewer - Image Crop",ImageCropHelp);
4772                 break;
4773               }
4774               case CutMode:
4775               {
4776                 XTextViewHelp(display,resource_info,windows,MagickFalse,
4777                   "Help Viewer - Image Cut",ImageCutHelp);
4778                 break;
4779               }
4780             }
4781             break;
4782           }
4783           default:
4784           {
4785             (void) XBell(display,0);
4786             break;
4787           }
4788         }
4789         break;
4790       }
4791       case MotionNotify:
4792       {
4793         if (event.xmotion.window != windows->image.id)
4794           break;
4795         /*
4796           Map and unmap Info widget as text cursor crosses its boundaries.
4797         */
4798         x=event.xmotion.x;
4799         y=event.xmotion.y;
4800         if (windows->info.mapped != MagickFalse )
4801           {
4802             if ((x < (int) (windows->info.x+windows->info.width)) &&
4803                 (y < (int) (windows->info.y+windows->info.height)))
4804               (void) XWithdrawWindow(display,windows->info.id,
4805                 windows->info.screen);
4806           }
4807         else
4808           if ((x > (int) (windows->info.x+windows->info.width)) ||
4809               (y > (int) (windows->info.y+windows->info.height)))
4810             (void) XMapWindow(display,windows->info.id);
4811         crop_info.x=(ssize_t) windows->image.x+x;
4812         crop_info.y=(ssize_t) windows->image.y+y;
4813         break;
4814       }
4815       default:
4816         break;
4817     }
4818   } while ((state & ExitState) == 0);
4819   (void) XSelectInput(display,windows->image.id,
4820     windows->image.attributes.event_mask);
4821   if ((state & EscapeState) != 0)
4822     {
4823       /*
4824         User want to exit without cropping.
4825       */
4826       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4827       (void) XFreeCursor(display,cursor);
4828       return(MagickTrue);
4829     }
4830   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4831   do
4832   {
4833     /*
4834       Size rectangle as pointer moves until the mouse button is released.
4835     */
4836     x=(int) crop_info.x;
4837     y=(int) crop_info.y;
4838     crop_info.width=0;
4839     crop_info.height=0;
4840     state=DefaultState;
4841     do
4842     {
4843       highlight_info=crop_info;
4844       highlight_info.x=crop_info.x-windows->image.x;
4845       highlight_info.y=crop_info.y-windows->image.y;
4846       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4847         {
4848           /*
4849             Display info and draw cropping rectangle.
4850           */
4851           if (windows->info.mapped == MagickFalse)
4852             (void) XMapWindow(display,windows->info.id);
4853           (void) FormatLocaleString(text,MagickPathExtent,
4854             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4855             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4856           XInfoWidget(display,windows,text);
4857           XHighlightRectangle(display,windows->image.id,
4858             windows->image.highlight_context,&highlight_info);
4859         }
4860       else
4861         if (windows->info.mapped != MagickFalse )
4862           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4863       /*
4864         Wait for next event.
4865       */
4866       XScreenEvent(display,windows,&event,exception);
4867       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4868         XHighlightRectangle(display,windows->image.id,
4869           windows->image.highlight_context,&highlight_info);
4870       switch (event.type)
4871       {
4872         case ButtonPress:
4873         {
4874           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4875           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4876           break;
4877         }
4878         case ButtonRelease:
4879         {
4880           /*
4881             User has committed to cropping rectangle.
4882           */
4883           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4884           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4885           XSetCursorState(display,windows,MagickFalse);
4886           state|=ExitState;
4887           windows->command.data=0;
4888           (void) XCommandWidget(display,windows,RectifyModeMenu,
4889             (XEvent *) NULL);
4890           break;
4891         }
4892         case Expose:
4893           break;
4894         case MotionNotify:
4895         {
4896           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4897           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4898         }
4899         default:
4900           break;
4901       }
4902       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4903           ((state & ExitState) != 0))
4904         {
4905           /*
4906             Check boundary conditions.
4907           */
4908           if (crop_info.x < 0)
4909             crop_info.x=0;
4910           else
4911             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4912               crop_info.x=(ssize_t) windows->image.ximage->width;
4913           if ((int) crop_info.x < x)
4914             crop_info.width=(unsigned int) (x-crop_info.x);
4915           else
4916             {
4917               crop_info.width=(unsigned int) (crop_info.x-x);
4918               crop_info.x=(ssize_t) x;
4919             }
4920           if (crop_info.y < 0)
4921             crop_info.y=0;
4922           else
4923             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4924               crop_info.y=(ssize_t) windows->image.ximage->height;
4925           if ((int) crop_info.y < y)
4926             crop_info.height=(unsigned int) (y-crop_info.y);
4927           else
4928             {
4929               crop_info.height=(unsigned int) (crop_info.y-y);
4930               crop_info.y=(ssize_t) y;
4931             }
4932         }
4933     } while ((state & ExitState) == 0);
4934     /*
4935       Wait for user to grab a corner of the rectangle or press return.
4936     */
4937     state=DefaultState;
4938     (void) XMapWindow(display,windows->info.id);
4939     do
4940     {
4941       if (windows->info.mapped != MagickFalse )
4942         {
4943           /*
4944             Display pointer position.
4945           */
4946           (void) FormatLocaleString(text,MagickPathExtent,
4947             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4948             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4949           XInfoWidget(display,windows,text);
4950         }
4951       highlight_info=crop_info;
4952       highlight_info.x=crop_info.x-windows->image.x;
4953       highlight_info.y=crop_info.y-windows->image.y;
4954       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4955         {
4956           state|=EscapeState;
4957           state|=ExitState;
4958           break;
4959         }
4960       XHighlightRectangle(display,windows->image.id,
4961         windows->image.highlight_context,&highlight_info);
4962       XScreenEvent(display,windows,&event,exception);
4963       if (event.xany.window == windows->command.id)
4964         {
4965           /*
4966             Select a command from the Command widget.
4967           */
4968           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4969           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4970           (void) XSetFunction(display,windows->image.highlight_context,
4971             GXinvert);
4972           XHighlightRectangle(display,windows->image.id,
4973             windows->image.highlight_context,&highlight_info);
4974           if (id >= 0)
4975             switch (RectifyCommands[id])
4976             {
4977               case RectifyCopyCommand:
4978               {
4979                 state|=ExitState;
4980                 break;
4981               }
4982               case RectifyHelpCommand:
4983               {
4984                 (void) XSetFunction(display,windows->image.highlight_context,
4985                   GXcopy);
4986                 switch (mode)
4987                 {
4988                   case CopyMode:
4989                   {
4990                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4991                       "Help Viewer - Image Copy",ImageCopyHelp);
4992                     break;
4993                   }
4994                   case CropMode:
4995                   {
4996                     XTextViewHelp(display,resource_info,windows,MagickFalse,
4997                       "Help Viewer - Image Crop",ImageCropHelp);
4998                     break;
4999                   }
5000                   case CutMode:
5001                   {
5002                     XTextViewHelp(display,resource_info,windows,MagickFalse,
5003                       "Help Viewer - Image Cut",ImageCutHelp);
5004                     break;
5005                   }
5006                 }
5007                 (void) XSetFunction(display,windows->image.highlight_context,
5008                   GXinvert);
5009                 break;
5010               }
5011               case RectifyDismissCommand:
5012               {
5013                 /*
5014                   Prematurely exit.
5015                 */
5016                 state|=EscapeState;
5017                 state|=ExitState;
5018                 break;
5019               }
5020               default:
5021                 break;
5022             }
5023           continue;
5024         }
5025       XHighlightRectangle(display,windows->image.id,
5026         windows->image.highlight_context,&highlight_info);
5027       switch (event.type)
5028       {
5029         case ButtonPress:
5030         {
5031           if (event.xbutton.button != Button1)
5032             break;
5033           if (event.xbutton.window != windows->image.id)
5034             break;
5035           x=windows->image.x+event.xbutton.x;
5036           y=windows->image.y+event.xbutton.y;
5037           if ((x < (int) (crop_info.x+RoiDelta)) &&
5038               (x > (int) (crop_info.x-RoiDelta)) &&
5039               (y < (int) (crop_info.y+RoiDelta)) &&
5040               (y > (int) (crop_info.y-RoiDelta)))
5041             {
5042               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5043               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5044               state|=UpdateConfigurationState;
5045               break;
5046             }
5047           if ((x < (int) (crop_info.x+RoiDelta)) &&
5048               (x > (int) (crop_info.x-RoiDelta)) &&
5049               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5050               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5051             {
5052               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5053               state|=UpdateConfigurationState;
5054               break;
5055             }
5056           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5057               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5058               (y < (int) (crop_info.y+RoiDelta)) &&
5059               (y > (int) (crop_info.y-RoiDelta)))
5060             {
5061               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5062               state|=UpdateConfigurationState;
5063               break;
5064             }
5065           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5066               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5067               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5068               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5069             {
5070               state|=UpdateConfigurationState;
5071               break;
5072             }
5073         }
5074         case ButtonRelease:
5075         {
5076           if (event.xbutton.window == windows->pan.id)
5077             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5078                 (highlight_info.y != crop_info.y-windows->image.y))
5079               XHighlightRectangle(display,windows->image.id,
5080                 windows->image.highlight_context,&highlight_info);
5081           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5082             event.xbutton.time);
5083           break;
5084         }
5085         case Expose:
5086         {
5087           if (event.xexpose.window == windows->image.id)
5088             if (event.xexpose.count == 0)
5089               {
5090                 event.xexpose.x=(int) highlight_info.x;
5091                 event.xexpose.y=(int) highlight_info.y;
5092                 event.xexpose.width=(int) highlight_info.width;
5093                 event.xexpose.height=(int) highlight_info.height;
5094                 XRefreshWindow(display,&windows->image,&event);
5095               }
5096           if (event.xexpose.window == windows->info.id)
5097             if (event.xexpose.count == 0)
5098               XInfoWidget(display,windows,text);
5099           break;
5100         }
5101         case KeyPress:
5102         {
5103           if (event.xkey.window != windows->image.id)
5104             break;
5105           /*
5106             Respond to a user key press.
5107           */
5108           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5109             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5110           switch ((int) key_symbol)
5111           {
5112             case XK_Escape:
5113             case XK_F20:
5114               state|=EscapeState;
5115             case XK_Return:
5116             {
5117               state|=ExitState;
5118               break;
5119             }
5120             case XK_Home:
5121             case XK_KP_Home:
5122             {
5123               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5124                 2L);
5125               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5126                 2L);
5127               break;
5128             }
5129             case XK_Left:
5130             case XK_KP_Left:
5131             {
5132               crop_info.x--;
5133               break;
5134             }
5135             case XK_Up:
5136             case XK_KP_Up:
5137             case XK_Next:
5138             {
5139               crop_info.y--;
5140               break;
5141             }
5142             case XK_Right:
5143             case XK_KP_Right:
5144             {
5145               crop_info.x++;
5146               break;
5147             }
5148             case XK_Prior:
5149             case XK_Down:
5150             case XK_KP_Down:
5151             {
5152               crop_info.y++;
5153               break;
5154             }
5155             case XK_F1:
5156             case XK_Help:
5157             {
5158               (void) XSetFunction(display,windows->image.highlight_context,
5159                 GXcopy);
5160               switch (mode)
5161               {
5162                 case CopyMode:
5163                 {
5164                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5165                     "Help Viewer - Image Copy",ImageCopyHelp);
5166                   break;
5167                 }
5168                 case CropMode:
5169                 {
5170                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5171                     "Help Viewer - Image Cropg",ImageCropHelp);
5172                   break;
5173                 }
5174                 case CutMode:
5175                 {
5176                   XTextViewHelp(display,resource_info,windows,MagickFalse,
5177                     "Help Viewer - Image Cutg",ImageCutHelp);
5178                   break;
5179                 }
5180               }
5181               (void) XSetFunction(display,windows->image.highlight_context,
5182                 GXinvert);
5183               break;
5184             }
5185             default:
5186             {
5187               (void) XBell(display,0);
5188               break;
5189             }
5190           }
5191           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5192             event.xkey.time);
5193           break;
5194         }
5195         case KeyRelease:
5196           break;
5197         case MotionNotify:
5198         {
5199           if (event.xmotion.window != windows->image.id)
5200             break;
5201           /*
5202             Map and unmap Info widget as text cursor crosses its boundaries.
5203           */
5204           x=event.xmotion.x;
5205           y=event.xmotion.y;
5206           if (windows->info.mapped != MagickFalse )
5207             {
5208               if ((x < (int) (windows->info.x+windows->info.width)) &&
5209                   (y < (int) (windows->info.y+windows->info.height)))
5210                 (void) XWithdrawWindow(display,windows->info.id,
5211                   windows->info.screen);
5212             }
5213           else
5214             if ((x > (int) (windows->info.x+windows->info.width)) ||
5215                 (y > (int) (windows->info.y+windows->info.height)))
5216               (void) XMapWindow(display,windows->info.id);
5217           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5218           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5219           break;
5220         }
5221         case SelectionRequest:
5222         {
5223           XSelectionEvent
5224             notify;
5225 
5226           XSelectionRequestEvent
5227             *request;
5228 
5229           /*
5230             Set primary selection.
5231           */
5232           (void) FormatLocaleString(text,MagickPathExtent,
5233             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5234             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5235           request=(&(event.xselectionrequest));
5236           (void) XChangeProperty(request->display,request->requestor,
5237             request->property,request->target,8,PropModeReplace,
5238             (unsigned char *) text,(int) strlen(text));
5239           notify.type=SelectionNotify;
5240           notify.display=request->display;
5241           notify.requestor=request->requestor;
5242           notify.selection=request->selection;
5243           notify.target=request->target;
5244           notify.time=request->time;
5245           if (request->property == None)
5246             notify.property=request->target;
5247           else
5248             notify.property=request->property;
5249           (void) XSendEvent(request->display,request->requestor,False,0,
5250             (XEvent *) &notify);
5251         }
5252         default:
5253           break;
5254       }
5255       if ((state & UpdateConfigurationState) != 0)
5256         {
5257           (void) XPutBackEvent(display,&event);
5258           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5259           break;
5260         }
5261     } while ((state & ExitState) == 0);
5262   } while ((state & ExitState) == 0);
5263   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5264   XSetCursorState(display,windows,MagickFalse);
5265   if ((state & EscapeState) != 0)
5266     return(MagickTrue);
5267   if (mode == CropMode)
5268     if (((int) crop_info.width != windows->image.ximage->width) ||
5269         ((int) crop_info.height != windows->image.ximage->height))
5270       {
5271         /*
5272           Reconfigure Image window as defined by cropping rectangle.
5273         */
5274         XSetCropGeometry(display,windows,&crop_info,image);
5275         windows->image.window_changes.width=(int) crop_info.width;
5276         windows->image.window_changes.height=(int) crop_info.height;
5277         (void) XConfigureImage(display,resource_info,windows,image,exception);
5278         return(MagickTrue);
5279       }
5280   /*
5281     Copy image before applying image transforms.
5282   */
5283   XSetCursorState(display,windows,MagickTrue);
5284   XCheckRefreshWindows(display,windows);
5285   width=(unsigned int) image->columns;
5286   height=(unsigned int) image->rows;
5287   x=0;
5288   y=0;
5289   if (windows->image.crop_geometry != (char *) NULL)
5290     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5291   scale_factor=(double) width/windows->image.ximage->width;
5292   crop_info.x+=x;
5293   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5294   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5295   scale_factor=(double) height/windows->image.ximage->height;
5296   crop_info.y+=y;
5297   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5298   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5299   crop_image=CropImage(image,&crop_info,exception);
5300   XSetCursorState(display,windows,MagickFalse);
5301   if (crop_image == (Image *) NULL)
5302     return(MagickFalse);
5303   if (resource_info->copy_image != (Image *) NULL)
5304     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5305   resource_info->copy_image=crop_image;
5306   if (mode == CopyMode)
5307     {
5308       (void) XConfigureImage(display,resource_info,windows,image,exception);
5309       return(MagickTrue);
5310     }
5311   /*
5312     Cut image.
5313   */
5314   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5315     return(MagickFalse);
5316   image->alpha_trait=BlendPixelTrait;
5317   image_view=AcquireAuthenticCacheView(image,exception);
5318   for (y=0; y < (int) crop_info.height; y++)
5319   {
5320     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5321       crop_info.width,1,exception);
5322     if (q == (Quantum *) NULL)
5323       break;
5324     for (x=0; x < (int) crop_info.width; x++)
5325     {
5326       SetPixelAlpha(image,TransparentAlpha,q);
5327       q+=GetPixelChannels(image);
5328     }
5329     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5330       break;
5331   }
5332   image_view=DestroyCacheView(image_view);
5333   /*
5334     Update image configuration.
5335   */
5336   XConfigureImageColormap(display,resource_info,windows,image,exception);
5337   (void) XConfigureImage(display,resource_info,windows,image,exception);
5338   return(MagickTrue);
5339 }
5340 
5341 /*
5342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5343 %                                                                             %
5344 %                                                                             %
5345 %                                                                             %
5346 +   X D r a w I m a g e                                                       %
5347 %                                                                             %
5348 %                                                                             %
5349 %                                                                             %
5350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351 %
5352 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5353 %  the image.
5354 %
5355 %  The format of the XDrawEditImage method is:
5356 %
5357 %      MagickBooleanType XDrawEditImage(Display *display,
5358 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5359 %        ExceptionInfo *exception)
5360 %
5361 %  A description of each parameter follows:
5362 %
5363 %    o display: Specifies a connection to an X server; returned from
5364 %      XOpenDisplay.
5365 %
5366 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5367 %
5368 %    o windows: Specifies a pointer to a XWindows structure.
5369 %
5370 %    o image: the image.
5371 %
5372 %    o exception: return any errors or warnings in this structure.
5373 %
5374 */
XDrawEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)5375 static MagickBooleanType XDrawEditImage(Display *display,
5376   XResourceInfo *resource_info,XWindows *windows,Image **image,
5377   ExceptionInfo *exception)
5378 {
5379   const char
5380     *const DrawMenu[] =
5381     {
5382       "Element",
5383       "Color",
5384       "Stipple",
5385       "Width",
5386       "Undo",
5387       "Help",
5388       "Dismiss",
5389       (char *) NULL
5390     };
5391 
5392   static ElementType
5393     element = PointElement;
5394 
5395   static const ModeType
5396     DrawCommands[] =
5397     {
5398       DrawElementCommand,
5399       DrawColorCommand,
5400       DrawStippleCommand,
5401       DrawWidthCommand,
5402       DrawUndoCommand,
5403       DrawHelpCommand,
5404       DrawDismissCommand
5405     };
5406 
5407   static Pixmap
5408     stipple = (Pixmap) NULL;
5409 
5410   static unsigned int
5411     pen_id = 0,
5412     line_width = 1;
5413 
5414   char
5415     command[MagickPathExtent],
5416     text[MagickPathExtent];
5417 
5418   Cursor
5419     cursor;
5420 
5421   int
5422     entry,
5423     id,
5424     number_coordinates,
5425     x,
5426     y;
5427 
5428   double
5429     degrees;
5430 
5431   MagickStatusType
5432     status;
5433 
5434   RectangleInfo
5435     rectangle_info;
5436 
5437   register int
5438     i;
5439 
5440   unsigned int
5441     distance,
5442     height,
5443     max_coordinates,
5444     width;
5445 
5446   size_t
5447     state;
5448 
5449   Window
5450     root_window;
5451 
5452   XDrawInfo
5453     draw_info;
5454 
5455   XEvent
5456     event;
5457 
5458   XPoint
5459     *coordinate_info;
5460 
5461   XSegment
5462     line_info;
5463 
5464   /*
5465     Allocate polygon info.
5466   */
5467   max_coordinates=2048;
5468   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5469     sizeof(*coordinate_info));
5470   if (coordinate_info == (XPoint *) NULL)
5471     {
5472       (void) ThrowMagickException(exception,GetMagickModule(),
5473         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5474       return(MagickFalse);
5475     }
5476   /*
5477     Map Command widget.
5478   */
5479   (void) CloneString(&windows->command.name,"Draw");
5480   windows->command.data=4;
5481   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5482   (void) XMapRaised(display,windows->command.id);
5483   XClientMessage(display,windows->image.id,windows->im_protocols,
5484     windows->im_update_widget,CurrentTime);
5485   /*
5486     Wait for first button press.
5487   */
5488   root_window=XRootWindow(display,XDefaultScreen(display));
5489   draw_info.stencil=OpaqueStencil;
5490   status=MagickTrue;
5491   cursor=XCreateFontCursor(display,XC_tcross);
5492   for ( ; ; )
5493   {
5494     XQueryPosition(display,windows->image.id,&x,&y);
5495     (void) XSelectInput(display,windows->image.id,
5496       windows->image.attributes.event_mask | PointerMotionMask);
5497     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5498     state=DefaultState;
5499     do
5500     {
5501       if (windows->info.mapped != MagickFalse )
5502         {
5503           /*
5504             Display pointer position.
5505           */
5506           (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5507             x+windows->image.x,y+windows->image.y);
5508           XInfoWidget(display,windows,text);
5509         }
5510       /*
5511         Wait for next event.
5512       */
5513       XScreenEvent(display,windows,&event,exception);
5514       if (event.xany.window == windows->command.id)
5515         {
5516           /*
5517             Select a command from the Command widget.
5518           */
5519           id=XCommandWidget(display,windows,DrawMenu,&event);
5520           if (id < 0)
5521             continue;
5522           switch (DrawCommands[id])
5523           {
5524             case DrawElementCommand:
5525             {
5526               const char
5527                 *const Elements[] =
5528                 {
5529                   "point",
5530                   "line",
5531                   "rectangle",
5532                   "fill rectangle",
5533                   "circle",
5534                   "fill circle",
5535                   "ellipse",
5536                   "fill ellipse",
5537                   "polygon",
5538                   "fill polygon",
5539                   (char *) NULL,
5540                 };
5541 
5542               /*
5543                 Select a command from the pop-up menu.
5544               */
5545               element=(ElementType) (XMenuWidget(display,windows,
5546                 DrawMenu[id],Elements,command)+1);
5547               break;
5548             }
5549             case DrawColorCommand:
5550             {
5551               const char
5552                 *ColorMenu[MaxNumberPens+1];
5553 
5554               int
5555                 pen_number;
5556 
5557               MagickBooleanType
5558                 transparent;
5559 
5560               XColor
5561                 color;
5562 
5563               /*
5564                 Initialize menu selections.
5565               */
5566               for (i=0; i < (int) (MaxNumberPens-2); i++)
5567                 ColorMenu[i]=resource_info->pen_colors[i];
5568               ColorMenu[MaxNumberPens-2]="transparent";
5569               ColorMenu[MaxNumberPens-1]="Browser...";
5570               ColorMenu[MaxNumberPens]=(char *) NULL;
5571               /*
5572                 Select a pen color from the pop-up menu.
5573               */
5574               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5575                 (const char **) ColorMenu,command);
5576               if (pen_number < 0)
5577                 break;
5578               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5579                 MagickFalse;
5580               if (transparent != MagickFalse )
5581                 {
5582                   draw_info.stencil=TransparentStencil;
5583                   break;
5584                 }
5585               if (pen_number == (MaxNumberPens-1))
5586                 {
5587                   static char
5588                     color_name[MagickPathExtent] = "gray";
5589 
5590                   /*
5591                     Select a pen color from a dialog.
5592                   */
5593                   resource_info->pen_colors[pen_number]=color_name;
5594                   XColorBrowserWidget(display,windows,"Select",color_name);
5595                   if (*color_name == '\0')
5596                     break;
5597                 }
5598               /*
5599                 Set pen color.
5600               */
5601               (void) XParseColor(display,windows->map_info->colormap,
5602                 resource_info->pen_colors[pen_number],&color);
5603               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5604                 (unsigned int) MaxColors,&color);
5605               windows->pixel_info->pen_colors[pen_number]=color;
5606               pen_id=(unsigned int) pen_number;
5607               draw_info.stencil=OpaqueStencil;
5608               break;
5609             }
5610             case DrawStippleCommand:
5611             {
5612               const char
5613                 *StipplesMenu[] =
5614                 {
5615                   "Brick",
5616                   "Diagonal",
5617                   "Scales",
5618                   "Vertical",
5619                   "Wavy",
5620                   "Translucent",
5621                   "Opaque",
5622                   (char *) NULL,
5623                   (char *) NULL,
5624                 };
5625 
5626               Image
5627                 *stipple_image;
5628 
5629               ImageInfo
5630                 *image_info;
5631 
5632               int
5633                 status;
5634 
5635               static char
5636                 filename[MagickPathExtent] = "\0";
5637 
5638               /*
5639                 Select a command from the pop-up menu.
5640               */
5641               StipplesMenu[7]="Open...";
5642               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5643                 command);
5644               if (entry < 0)
5645                 break;
5646               if (stipple != (Pixmap) NULL)
5647                 (void) XFreePixmap(display,stipple);
5648               stipple=(Pixmap) NULL;
5649               if (entry != 7)
5650                 {
5651                   switch (entry)
5652                   {
5653                     case 0:
5654                     {
5655                       stipple=XCreateBitmapFromData(display,root_window,
5656                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5657                       break;
5658                     }
5659                     case 1:
5660                     {
5661                       stipple=XCreateBitmapFromData(display,root_window,
5662                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5663                       break;
5664                     }
5665                     case 2:
5666                     {
5667                       stipple=XCreateBitmapFromData(display,root_window,
5668                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5669                       break;
5670                     }
5671                     case 3:
5672                     {
5673                       stipple=XCreateBitmapFromData(display,root_window,
5674                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5675                       break;
5676                     }
5677                     case 4:
5678                     {
5679                       stipple=XCreateBitmapFromData(display,root_window,
5680                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5681                       break;
5682                     }
5683                     case 5:
5684                     {
5685                       stipple=XCreateBitmapFromData(display,root_window,
5686                         (char *) HighlightBitmap,HighlightWidth,
5687                         HighlightHeight);
5688                       break;
5689                     }
5690                     case 6:
5691                     default:
5692                     {
5693                       stipple=XCreateBitmapFromData(display,root_window,
5694                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5695                       break;
5696                     }
5697                   }
5698                   break;
5699                 }
5700               XFileBrowserWidget(display,windows,"Stipple",filename);
5701               if (*filename == '\0')
5702                 break;
5703               /*
5704                 Read image.
5705               */
5706               XSetCursorState(display,windows,MagickTrue);
5707               XCheckRefreshWindows(display,windows);
5708               image_info=AcquireImageInfo();
5709               (void) CopyMagickString(image_info->filename,filename,
5710                 MagickPathExtent);
5711               stipple_image=ReadImage(image_info,exception);
5712               CatchException(exception);
5713               XSetCursorState(display,windows,MagickFalse);
5714               if (stipple_image == (Image *) NULL)
5715                 break;
5716               (void) AcquireUniqueFileResource(filename);
5717               (void) FormatLocaleString(stipple_image->filename,
5718                 MagickPathExtent,"xbm:%s",filename);
5719               (void) WriteImage(image_info,stipple_image,exception);
5720               stipple_image=DestroyImage(stipple_image);
5721               image_info=DestroyImageInfo(image_info);
5722               status=XReadBitmapFile(display,root_window,filename,&width,
5723                 &height,&stipple,&x,&y);
5724               (void) RelinquishUniqueFileResource(filename);
5725               if ((status != BitmapSuccess) != 0)
5726                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5727                   filename);
5728               break;
5729             }
5730             case DrawWidthCommand:
5731             {
5732               const char
5733                 *const WidthsMenu[] =
5734                 {
5735                   "1",
5736                   "2",
5737                   "4",
5738                   "8",
5739                   "16",
5740                   "Dialog...",
5741                   (char *) NULL,
5742                 };
5743 
5744               static char
5745                 width[MagickPathExtent] = "0";
5746 
5747               /*
5748                 Select a command from the pop-up menu.
5749               */
5750               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5751                 command);
5752               if (entry < 0)
5753                 break;
5754               if (entry != 5)
5755                 {
5756                   line_width=(unsigned int) StringToUnsignedLong(
5757                     WidthsMenu[entry]);
5758                   break;
5759                 }
5760               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5761                 width);
5762               if (*width == '\0')
5763                 break;
5764               line_width=(unsigned int) StringToUnsignedLong(width);
5765               break;
5766             }
5767             case DrawUndoCommand:
5768             {
5769               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5770                 image,exception);
5771               break;
5772             }
5773             case DrawHelpCommand:
5774             {
5775               XTextViewHelp(display,resource_info,windows,MagickFalse,
5776                 "Help Viewer - Image Rotation",ImageDrawHelp);
5777               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5778               break;
5779             }
5780             case DrawDismissCommand:
5781             {
5782               /*
5783                 Prematurely exit.
5784               */
5785               state|=EscapeState;
5786               state|=ExitState;
5787               break;
5788             }
5789             default:
5790               break;
5791           }
5792           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5793           continue;
5794         }
5795       switch (event.type)
5796       {
5797         case ButtonPress:
5798         {
5799           if (event.xbutton.button != Button1)
5800             break;
5801           if (event.xbutton.window != windows->image.id)
5802             break;
5803           /*
5804             exit loop.
5805           */
5806           x=event.xbutton.x;
5807           y=event.xbutton.y;
5808           state|=ExitState;
5809           break;
5810         }
5811         case ButtonRelease:
5812           break;
5813         case Expose:
5814           break;
5815         case KeyPress:
5816         {
5817           KeySym
5818             key_symbol;
5819 
5820           if (event.xkey.window != windows->image.id)
5821             break;
5822           /*
5823             Respond to a user key press.
5824           */
5825           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5826             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5827           switch ((int) key_symbol)
5828           {
5829             case XK_Escape:
5830             case XK_F20:
5831             {
5832               /*
5833                 Prematurely exit.
5834               */
5835               state|=EscapeState;
5836               state|=ExitState;
5837               break;
5838             }
5839             case XK_F1:
5840             case XK_Help:
5841             {
5842               XTextViewHelp(display,resource_info,windows,MagickFalse,
5843                 "Help Viewer - Image Rotation",ImageDrawHelp);
5844               break;
5845             }
5846             default:
5847             {
5848               (void) XBell(display,0);
5849               break;
5850             }
5851           }
5852           break;
5853         }
5854         case MotionNotify:
5855         {
5856           /*
5857             Map and unmap Info widget as text cursor crosses its boundaries.
5858           */
5859           x=event.xmotion.x;
5860           y=event.xmotion.y;
5861           if (windows->info.mapped != MagickFalse )
5862             {
5863               if ((x < (int) (windows->info.x+windows->info.width)) &&
5864                   (y < (int) (windows->info.y+windows->info.height)))
5865                 (void) XWithdrawWindow(display,windows->info.id,
5866                   windows->info.screen);
5867             }
5868           else
5869             if ((x > (int) (windows->info.x+windows->info.width)) ||
5870                 (y > (int) (windows->info.y+windows->info.height)))
5871               (void) XMapWindow(display,windows->info.id);
5872           break;
5873         }
5874       }
5875     } while ((state & ExitState) == 0);
5876     (void) XSelectInput(display,windows->image.id,
5877       windows->image.attributes.event_mask);
5878     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5879     if ((state & EscapeState) != 0)
5880       break;
5881     /*
5882       Draw element as pointer moves until the button is released.
5883     */
5884     distance=0;
5885     degrees=0.0;
5886     line_info.x1=x;
5887     line_info.y1=y;
5888     line_info.x2=x;
5889     line_info.y2=y;
5890     rectangle_info.x=(ssize_t) x;
5891     rectangle_info.y=(ssize_t) y;
5892     rectangle_info.width=0;
5893     rectangle_info.height=0;
5894     number_coordinates=1;
5895     coordinate_info->x=x;
5896     coordinate_info->y=y;
5897     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5898     state=DefaultState;
5899     do
5900     {
5901       switch (element)
5902       {
5903         case PointElement:
5904         default:
5905         {
5906           if (number_coordinates > 1)
5907             {
5908               (void) XDrawLines(display,windows->image.id,
5909                 windows->image.highlight_context,coordinate_info,
5910                 number_coordinates,CoordModeOrigin);
5911               (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5912                 coordinate_info[number_coordinates-1].x,
5913                 coordinate_info[number_coordinates-1].y);
5914               XInfoWidget(display,windows,text);
5915             }
5916           break;
5917         }
5918         case LineElement:
5919         {
5920           if (distance > 9)
5921             {
5922               /*
5923                 Display angle of the line.
5924               */
5925               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5926                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5927               (void) FormatLocaleString(text,MagickPathExtent," %g",
5928                 (double) degrees);
5929               XInfoWidget(display,windows,text);
5930               XHighlightLine(display,windows->image.id,
5931                 windows->image.highlight_context,&line_info);
5932             }
5933           else
5934             if (windows->info.mapped != MagickFalse )
5935               (void) XWithdrawWindow(display,windows->info.id,
5936                 windows->info.screen);
5937           break;
5938         }
5939         case RectangleElement:
5940         case FillRectangleElement:
5941         {
5942           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5943             {
5944               /*
5945                 Display info and draw drawing rectangle.
5946               */
5947               (void) FormatLocaleString(text,MagickPathExtent,
5948                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5949                 (double) rectangle_info.height,(double) rectangle_info.x,
5950                 (double) rectangle_info.y);
5951               XInfoWidget(display,windows,text);
5952               XHighlightRectangle(display,windows->image.id,
5953                 windows->image.highlight_context,&rectangle_info);
5954             }
5955           else
5956             if (windows->info.mapped != MagickFalse )
5957               (void) XWithdrawWindow(display,windows->info.id,
5958                 windows->info.screen);
5959           break;
5960         }
5961         case CircleElement:
5962         case FillCircleElement:
5963         case EllipseElement:
5964         case FillEllipseElement:
5965         {
5966           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5967             {
5968               /*
5969                 Display info and draw drawing rectangle.
5970               */
5971               (void) FormatLocaleString(text,MagickPathExtent,
5972                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5973                 (double) rectangle_info.height,(double) rectangle_info.x,
5974                 (double) rectangle_info.y);
5975               XInfoWidget(display,windows,text);
5976               XHighlightEllipse(display,windows->image.id,
5977                 windows->image.highlight_context,&rectangle_info);
5978             }
5979           else
5980             if (windows->info.mapped != MagickFalse )
5981               (void) XWithdrawWindow(display,windows->info.id,
5982                 windows->info.screen);
5983           break;
5984         }
5985         case PolygonElement:
5986         case FillPolygonElement:
5987         {
5988           if (number_coordinates > 1)
5989             (void) XDrawLines(display,windows->image.id,
5990               windows->image.highlight_context,coordinate_info,
5991               number_coordinates,CoordModeOrigin);
5992           if (distance > 9)
5993             {
5994               /*
5995                 Display angle of the line.
5996               */
5997               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5998                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5999               (void) FormatLocaleString(text,MagickPathExtent," %g",
6000                 (double) degrees);
6001               XInfoWidget(display,windows,text);
6002               XHighlightLine(display,windows->image.id,
6003                 windows->image.highlight_context,&line_info);
6004             }
6005           else
6006             if (windows->info.mapped != MagickFalse )
6007               (void) XWithdrawWindow(display,windows->info.id,
6008                 windows->info.screen);
6009           break;
6010         }
6011       }
6012       /*
6013         Wait for next event.
6014       */
6015       XScreenEvent(display,windows,&event,exception);
6016       switch (element)
6017       {
6018         case PointElement:
6019         default:
6020         {
6021           if (number_coordinates > 1)
6022             (void) XDrawLines(display,windows->image.id,
6023               windows->image.highlight_context,coordinate_info,
6024               number_coordinates,CoordModeOrigin);
6025           break;
6026         }
6027         case LineElement:
6028         {
6029           if (distance > 9)
6030             XHighlightLine(display,windows->image.id,
6031               windows->image.highlight_context,&line_info);
6032           break;
6033         }
6034         case RectangleElement:
6035         case FillRectangleElement:
6036         {
6037           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6038             XHighlightRectangle(display,windows->image.id,
6039               windows->image.highlight_context,&rectangle_info);
6040           break;
6041         }
6042         case CircleElement:
6043         case FillCircleElement:
6044         case EllipseElement:
6045         case FillEllipseElement:
6046         {
6047           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6048             XHighlightEllipse(display,windows->image.id,
6049               windows->image.highlight_context,&rectangle_info);
6050           break;
6051         }
6052         case PolygonElement:
6053         case FillPolygonElement:
6054         {
6055           if (number_coordinates > 1)
6056             (void) XDrawLines(display,windows->image.id,
6057               windows->image.highlight_context,coordinate_info,
6058               number_coordinates,CoordModeOrigin);
6059           if (distance > 9)
6060             XHighlightLine(display,windows->image.id,
6061               windows->image.highlight_context,&line_info);
6062           break;
6063         }
6064       }
6065       switch (event.type)
6066       {
6067         case ButtonPress:
6068           break;
6069         case ButtonRelease:
6070         {
6071           /*
6072             User has committed to element.
6073           */
6074           line_info.x2=event.xbutton.x;
6075           line_info.y2=event.xbutton.y;
6076           rectangle_info.x=(ssize_t) event.xbutton.x;
6077           rectangle_info.y=(ssize_t) event.xbutton.y;
6078           coordinate_info[number_coordinates].x=event.xbutton.x;
6079           coordinate_info[number_coordinates].y=event.xbutton.y;
6080           if (((element != PolygonElement) &&
6081                (element != FillPolygonElement)) || (distance <= 9))
6082             {
6083               state|=ExitState;
6084               break;
6085             }
6086           number_coordinates++;
6087           if (number_coordinates < (int) max_coordinates)
6088             {
6089               line_info.x1=event.xbutton.x;
6090               line_info.y1=event.xbutton.y;
6091               break;
6092             }
6093           max_coordinates<<=1;
6094           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6095             max_coordinates,sizeof(*coordinate_info));
6096           if (coordinate_info == (XPoint *) NULL)
6097             (void) ThrowMagickException(exception,GetMagickModule(),
6098               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6099           break;
6100         }
6101         case Expose:
6102           break;
6103         case MotionNotify:
6104         {
6105           if (event.xmotion.window != windows->image.id)
6106             break;
6107           if (element != PointElement)
6108             {
6109               line_info.x2=event.xmotion.x;
6110               line_info.y2=event.xmotion.y;
6111               rectangle_info.x=(ssize_t) event.xmotion.x;
6112               rectangle_info.y=(ssize_t) event.xmotion.y;
6113               break;
6114             }
6115           coordinate_info[number_coordinates].x=event.xbutton.x;
6116           coordinate_info[number_coordinates].y=event.xbutton.y;
6117           number_coordinates++;
6118           if (number_coordinates < (int) max_coordinates)
6119             break;
6120           max_coordinates<<=1;
6121           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6122             max_coordinates,sizeof(*coordinate_info));
6123           if (coordinate_info == (XPoint *) NULL)
6124             (void) ThrowMagickException(exception,GetMagickModule(),
6125               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6126           break;
6127         }
6128         default:
6129           break;
6130       }
6131       /*
6132         Check boundary conditions.
6133       */
6134       if (line_info.x2 < 0)
6135         line_info.x2=0;
6136       else
6137         if (line_info.x2 > (int) windows->image.width)
6138           line_info.x2=(short) windows->image.width;
6139       if (line_info.y2 < 0)
6140         line_info.y2=0;
6141       else
6142         if (line_info.y2 > (int) windows->image.height)
6143           line_info.y2=(short) windows->image.height;
6144       distance=(unsigned int)
6145         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6146          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6147       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6148           ((state & ExitState) != 0))
6149         {
6150           if (rectangle_info.x < 0)
6151             rectangle_info.x=0;
6152           else
6153             if (rectangle_info.x > (ssize_t) windows->image.width)
6154               rectangle_info.x=(ssize_t) windows->image.width;
6155           if ((int) rectangle_info.x < x)
6156             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6157           else
6158             {
6159               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6160               rectangle_info.x=(ssize_t) x;
6161             }
6162           if (rectangle_info.y < 0)
6163             rectangle_info.y=0;
6164           else
6165             if (rectangle_info.y > (ssize_t) windows->image.height)
6166               rectangle_info.y=(ssize_t) windows->image.height;
6167           if ((int) rectangle_info.y < y)
6168             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6169           else
6170             {
6171               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6172               rectangle_info.y=(ssize_t) y;
6173             }
6174         }
6175     } while ((state & ExitState) == 0);
6176     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6177     if ((element == PointElement) || (element == PolygonElement) ||
6178         (element == FillPolygonElement))
6179       {
6180         /*
6181           Determine polygon bounding box.
6182         */
6183         rectangle_info.x=(ssize_t) coordinate_info->x;
6184         rectangle_info.y=(ssize_t) coordinate_info->y;
6185         x=coordinate_info->x;
6186         y=coordinate_info->y;
6187         for (i=1; i < number_coordinates; i++)
6188         {
6189           if (coordinate_info[i].x > x)
6190             x=coordinate_info[i].x;
6191           if (coordinate_info[i].y > y)
6192             y=coordinate_info[i].y;
6193           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6194             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6195           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6196             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6197         }
6198         rectangle_info.width=(size_t) (x-rectangle_info.x);
6199         rectangle_info.height=(size_t) (y-rectangle_info.y);
6200         for (i=0; i < number_coordinates; i++)
6201         {
6202           coordinate_info[i].x-=rectangle_info.x;
6203           coordinate_info[i].y-=rectangle_info.y;
6204         }
6205       }
6206     else
6207       if (distance <= 9)
6208         continue;
6209       else
6210         if ((element == RectangleElement) ||
6211             (element == CircleElement) || (element == EllipseElement))
6212           {
6213             rectangle_info.width--;
6214             rectangle_info.height--;
6215           }
6216     /*
6217       Drawing is relative to image configuration.
6218     */
6219     draw_info.x=(int) rectangle_info.x;
6220     draw_info.y=(int) rectangle_info.y;
6221     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6222       image,exception);
6223     width=(unsigned int) (*image)->columns;
6224     height=(unsigned int) (*image)->rows;
6225     x=0;
6226     y=0;
6227     if (windows->image.crop_geometry != (char *) NULL)
6228       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6229     draw_info.x+=windows->image.x-(line_width/2);
6230     if (draw_info.x < 0)
6231       draw_info.x=0;
6232     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6233     draw_info.y+=windows->image.y-(line_width/2);
6234     if (draw_info.y < 0)
6235       draw_info.y=0;
6236     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6237     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6238     if (draw_info.width > (unsigned int) (*image)->columns)
6239       draw_info.width=(unsigned int) (*image)->columns;
6240     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6241     if (draw_info.height > (unsigned int) (*image)->rows)
6242       draw_info.height=(unsigned int) (*image)->rows;
6243     (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6244       width*draw_info.width/windows->image.ximage->width,
6245       height*draw_info.height/windows->image.ximage->height,
6246       draw_info.x+x,draw_info.y+y);
6247     /*
6248       Initialize drawing attributes.
6249     */
6250     draw_info.degrees=0.0;
6251     draw_info.element=element;
6252     draw_info.stipple=stipple;
6253     draw_info.line_width=line_width;
6254     draw_info.line_info=line_info;
6255     if (line_info.x1 > (int) (line_width/2))
6256       draw_info.line_info.x1=(short) line_width/2;
6257     if (line_info.y1 > (int) (line_width/2))
6258       draw_info.line_info.y1=(short) line_width/2;
6259     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6260     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6261     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6262       {
6263         draw_info.line_info.x2=(-draw_info.line_info.x2);
6264         draw_info.line_info.y2=(-draw_info.line_info.y2);
6265       }
6266     if (draw_info.line_info.x2 < 0)
6267       {
6268         draw_info.line_info.x2=(-draw_info.line_info.x2);
6269         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6270       }
6271     if (draw_info.line_info.y2 < 0)
6272       {
6273         draw_info.line_info.y2=(-draw_info.line_info.y2);
6274         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6275       }
6276     draw_info.rectangle_info=rectangle_info;
6277     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6278       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6279     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6280       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6281     draw_info.number_coordinates=(unsigned int) number_coordinates;
6282     draw_info.coordinate_info=coordinate_info;
6283     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6284     /*
6285       Draw element on image.
6286     */
6287     XSetCursorState(display,windows,MagickTrue);
6288     XCheckRefreshWindows(display,windows);
6289     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6290     XSetCursorState(display,windows,MagickFalse);
6291     /*
6292       Update image colormap and return to image drawing.
6293     */
6294     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6295     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6296   }
6297   XSetCursorState(display,windows,MagickFalse);
6298   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6299   return(status != 0 ? MagickTrue : MagickFalse);
6300 }
6301 
6302 /*
6303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6304 %                                                                             %
6305 %                                                                             %
6306 %                                                                             %
6307 +   X D r a w P a n R e c t a n g l e                                         %
6308 %                                                                             %
6309 %                                                                             %
6310 %                                                                             %
6311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6312 %
6313 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6314 %  displays a zoom image and the rectangle shows which portion of the image is
6315 %  displayed in the Image window.
6316 %
6317 %  The format of the XDrawPanRectangle method is:
6318 %
6319 %      XDrawPanRectangle(Display *display,XWindows *windows)
6320 %
6321 %  A description of each parameter follows:
6322 %
6323 %    o display: Specifies a connection to an X server;  returned from
6324 %      XOpenDisplay.
6325 %
6326 %    o windows: Specifies a pointer to a XWindows structure.
6327 %
6328 */
XDrawPanRectangle(Display * display,XWindows * windows)6329 static void XDrawPanRectangle(Display *display,XWindows *windows)
6330 {
6331   double
6332     scale_factor;
6333 
6334   RectangleInfo
6335     highlight_info;
6336 
6337   /*
6338     Determine dimensions of the panning rectangle.
6339   */
6340   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6341   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6342   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6343   scale_factor=(double)
6344     windows->pan.height/windows->image.ximage->height;
6345   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6346   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6347   /*
6348     Display the panning rectangle.
6349   */
6350   (void) XClearWindow(display,windows->pan.id);
6351   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6352     &highlight_info);
6353 }
6354 
6355 /*
6356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6357 %                                                                             %
6358 %                                                                             %
6359 %                                                                             %
6360 +   X I m a g e C a c h e                                                     %
6361 %                                                                             %
6362 %                                                                             %
6363 %                                                                             %
6364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6365 %
6366 %  XImageCache() handles the creation, manipulation, and destruction of the
6367 %  image cache (undo and redo buffers).
6368 %
6369 %  The format of the XImageCache method is:
6370 %
6371 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6372 %        XWindows *windows,const CommandType command,Image **image,
6373 %        ExceptionInfo *exception)
6374 %
6375 %  A description of each parameter follows:
6376 %
6377 %    o display: Specifies a connection to an X server; returned from
6378 %      XOpenDisplay.
6379 %
6380 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6381 %
6382 %    o windows: Specifies a pointer to a XWindows structure.
6383 %
6384 %    o command: Specifies a command to perform.
6385 %
6386 %    o image: the image;  XImageCache may transform the image and return a new
6387 %      image pointer.
6388 %
6389 %    o exception: return any errors or warnings in this structure.
6390 %
6391 */
XImageCache(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)6392 static void XImageCache(Display *display,XResourceInfo *resource_info,
6393   XWindows *windows,const CommandType command,Image **image,
6394   ExceptionInfo *exception)
6395 {
6396   Image
6397     *cache_image;
6398 
6399   static Image
6400     *redo_image = (Image *) NULL,
6401     *undo_image = (Image *) NULL;
6402 
6403   switch (command)
6404   {
6405     case FreeBuffersCommand:
6406     {
6407       /*
6408         Free memory from the undo and redo cache.
6409       */
6410       while (undo_image != (Image *) NULL)
6411       {
6412         cache_image=undo_image;
6413         undo_image=GetPreviousImageInList(undo_image);
6414         cache_image->list=DestroyImage(cache_image->list);
6415         cache_image=DestroyImage(cache_image);
6416       }
6417       undo_image=NewImageList();
6418       if (redo_image != (Image *) NULL)
6419         redo_image=DestroyImage(redo_image);
6420       redo_image=NewImageList();
6421       return;
6422     }
6423     case UndoCommand:
6424     {
6425       char
6426         image_geometry[MagickPathExtent];
6427 
6428       /*
6429         Undo the last image transformation.
6430       */
6431       if (undo_image == (Image *) NULL)
6432         {
6433           (void) XBell(display,0);
6434           return;
6435         }
6436       cache_image=undo_image;
6437       undo_image=GetPreviousImageInList(undo_image);
6438       windows->image.window_changes.width=(int) cache_image->columns;
6439       windows->image.window_changes.height=(int) cache_image->rows;
6440       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6441         windows->image.ximage->width,windows->image.ximage->height);
6442       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6443         exception);
6444       if (windows->image.crop_geometry != (char *) NULL)
6445         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6446           windows->image.crop_geometry);
6447       windows->image.crop_geometry=cache_image->geometry;
6448       if (redo_image != (Image *) NULL)
6449         redo_image=DestroyImage(redo_image);
6450       redo_image=(*image);
6451       *image=cache_image->list;
6452       cache_image=DestroyImage(cache_image);
6453       if (windows->image.orphan != MagickFalse )
6454         return;
6455       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6456       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6457       return;
6458     }
6459     case CutCommand:
6460     case PasteCommand:
6461     case ApplyCommand:
6462     case HalfSizeCommand:
6463     case OriginalSizeCommand:
6464     case DoubleSizeCommand:
6465     case ResizeCommand:
6466     case TrimCommand:
6467     case CropCommand:
6468     case ChopCommand:
6469     case FlipCommand:
6470     case FlopCommand:
6471     case RotateRightCommand:
6472     case RotateLeftCommand:
6473     case RotateCommand:
6474     case ShearCommand:
6475     case RollCommand:
6476     case NegateCommand:
6477     case ContrastStretchCommand:
6478     case SigmoidalContrastCommand:
6479     case NormalizeCommand:
6480     case EqualizeCommand:
6481     case HueCommand:
6482     case SaturationCommand:
6483     case BrightnessCommand:
6484     case GammaCommand:
6485     case SpiffCommand:
6486     case DullCommand:
6487     case GrayscaleCommand:
6488     case MapCommand:
6489     case QuantizeCommand:
6490     case DespeckleCommand:
6491     case EmbossCommand:
6492     case ReduceNoiseCommand:
6493     case AddNoiseCommand:
6494     case SharpenCommand:
6495     case BlurCommand:
6496     case ThresholdCommand:
6497     case EdgeDetectCommand:
6498     case SpreadCommand:
6499     case ShadeCommand:
6500     case RaiseCommand:
6501     case SegmentCommand:
6502     case SolarizeCommand:
6503     case SepiaToneCommand:
6504     case SwirlCommand:
6505     case ImplodeCommand:
6506     case VignetteCommand:
6507     case WaveCommand:
6508     case OilPaintCommand:
6509     case CharcoalDrawCommand:
6510     case AnnotateCommand:
6511     case AddBorderCommand:
6512     case AddFrameCommand:
6513     case CompositeCommand:
6514     case CommentCommand:
6515     case LaunchCommand:
6516     case RegionofInterestCommand:
6517     case SaveToUndoBufferCommand:
6518     case RedoCommand:
6519     {
6520       Image
6521         *previous_image;
6522 
6523       ssize_t
6524         bytes;
6525 
6526       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6527       if (undo_image != (Image *) NULL)
6528         {
6529           /*
6530             Ensure the undo cache has enough memory available.
6531           */
6532           previous_image=undo_image;
6533           while (previous_image != (Image *) NULL)
6534           {
6535             bytes+=previous_image->list->columns*previous_image->list->rows*
6536               sizeof(PixelInfo);
6537             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6538               {
6539                 previous_image=GetPreviousImageInList(previous_image);
6540                 continue;
6541               }
6542             bytes-=previous_image->list->columns*previous_image->list->rows*
6543               sizeof(PixelInfo);
6544             if (previous_image == undo_image)
6545               undo_image=NewImageList();
6546             else
6547               previous_image->next->previous=NewImageList();
6548             break;
6549           }
6550           while (previous_image != (Image *) NULL)
6551           {
6552             /*
6553               Delete any excess memory from undo cache.
6554             */
6555             cache_image=previous_image;
6556             previous_image=GetPreviousImageInList(previous_image);
6557             cache_image->list=DestroyImage(cache_image->list);
6558             cache_image=DestroyImage(cache_image);
6559           }
6560         }
6561       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6562         break;
6563       /*
6564         Save image before transformations are applied.
6565       */
6566       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6567       if (cache_image == (Image *) NULL)
6568         break;
6569       XSetCursorState(display,windows,MagickTrue);
6570       XCheckRefreshWindows(display,windows);
6571       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6572       XSetCursorState(display,windows,MagickFalse);
6573       if (cache_image->list == (Image *) NULL)
6574         {
6575           cache_image=DestroyImage(cache_image);
6576           break;
6577         }
6578       cache_image->columns=(size_t) windows->image.ximage->width;
6579       cache_image->rows=(size_t) windows->image.ximage->height;
6580       cache_image->geometry=windows->image.crop_geometry;
6581       if (windows->image.crop_geometry != (char *) NULL)
6582         {
6583           cache_image->geometry=AcquireString((char *) NULL);
6584           (void) CopyMagickString(cache_image->geometry,
6585             windows->image.crop_geometry,MagickPathExtent);
6586         }
6587       if (undo_image == (Image *) NULL)
6588         {
6589           undo_image=cache_image;
6590           break;
6591         }
6592       undo_image->next=cache_image;
6593       undo_image->next->previous=undo_image;
6594       undo_image=undo_image->next;
6595       break;
6596     }
6597     default:
6598       break;
6599   }
6600   if (command == RedoCommand)
6601     {
6602       /*
6603         Redo the last image transformation.
6604       */
6605       if (redo_image == (Image *) NULL)
6606         {
6607           (void) XBell(display,0);
6608           return;
6609         }
6610       windows->image.window_changes.width=(int) redo_image->columns;
6611       windows->image.window_changes.height=(int) redo_image->rows;
6612       if (windows->image.crop_geometry != (char *) NULL)
6613         windows->image.crop_geometry=(char *)
6614           RelinquishMagickMemory(windows->image.crop_geometry);
6615       windows->image.crop_geometry=redo_image->geometry;
6616       *image=DestroyImage(*image);
6617       *image=redo_image;
6618       redo_image=NewImageList();
6619       if (windows->image.orphan != MagickFalse )
6620         return;
6621       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6622       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6623       return;
6624     }
6625   if (command != InfoCommand)
6626     return;
6627   /*
6628     Display image info.
6629   */
6630   XSetCursorState(display,windows,MagickTrue);
6631   XCheckRefreshWindows(display,windows);
6632   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6633   XSetCursorState(display,windows,MagickFalse);
6634 }
6635 
6636 /*
6637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6638 %                                                                             %
6639 %                                                                             %
6640 %                                                                             %
6641 +   X I m a g e W i n d o w C o m m a n d                                     %
6642 %                                                                             %
6643 %                                                                             %
6644 %                                                                             %
6645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6646 %
6647 %  XImageWindowCommand() makes a transform to the image or Image window as
6648 %  specified by a user menu button or keyboard command.
6649 %
6650 %  The format of the XImageWindowCommand method is:
6651 %
6652 %      CommandType XImageWindowCommand(Display *display,
6653 %        XResourceInfo *resource_info,XWindows *windows,
6654 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6655 %        ExceptionInfo *exception)
6656 %
6657 %  A description of each parameter follows:
6658 %
6659 %    o nexus:  Method XImageWindowCommand returns an image when the
6660 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6661 %      image is returned.
6662 %
6663 %    o display: Specifies a connection to an X server; returned from
6664 %      XOpenDisplay.
6665 %
6666 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6667 %
6668 %    o windows: Specifies a pointer to a XWindows structure.
6669 %
6670 %    o state: key mask.
6671 %
6672 %    o key_symbol: Specifies a command to perform.
6673 %
6674 %    o image: the image;  XImageWIndowCommand may transform the image and
6675 %      return a new image pointer.
6676 %
6677 %    o exception: return any errors or warnings in this structure.
6678 %
6679 */
XImageWindowCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickStatusType state,KeySym key_symbol,Image ** image,ExceptionInfo * exception)6680 static CommandType XImageWindowCommand(Display *display,
6681   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6682   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6683 {
6684   static char
6685     delta[MagickPathExtent] = "";
6686 
6687   static const char
6688     Digits[] = "01234567890";
6689 
6690   static KeySym
6691     last_symbol = XK_0;
6692 
6693   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6694     {
6695       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6696         {
6697           *delta='\0';
6698           resource_info->quantum=1;
6699         }
6700       last_symbol=key_symbol;
6701       delta[strlen(delta)+1]='\0';
6702       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6703       resource_info->quantum=StringToLong(delta);
6704       return(NullCommand);
6705     }
6706   last_symbol=key_symbol;
6707   if (resource_info->immutable)
6708     {
6709       /*
6710         Virtual image window has a restricted command set.
6711       */
6712       switch (key_symbol)
6713       {
6714         case XK_question:
6715           return(InfoCommand);
6716         case XK_p:
6717         case XK_Print:
6718           return(PrintCommand);
6719         case XK_space:
6720           return(NextCommand);
6721         case XK_q:
6722         case XK_Escape:
6723           return(QuitCommand);
6724         default:
6725           break;
6726       }
6727       return(NullCommand);
6728     }
6729   switch ((int) key_symbol)
6730   {
6731     case XK_o:
6732     {
6733       if ((state & ControlMask) == 0)
6734         break;
6735       return(OpenCommand);
6736     }
6737     case XK_space:
6738       return(NextCommand);
6739     case XK_BackSpace:
6740       return(FormerCommand);
6741     case XK_s:
6742     {
6743       if ((state & Mod1Mask) != 0)
6744         return(SwirlCommand);
6745       if ((state & ControlMask) == 0)
6746         return(ShearCommand);
6747       return(SaveCommand);
6748     }
6749     case XK_p:
6750     case XK_Print:
6751     {
6752       if ((state & Mod1Mask) != 0)
6753         return(OilPaintCommand);
6754       if ((state & Mod4Mask) != 0)
6755         return(ColorCommand);
6756       if ((state & ControlMask) == 0)
6757         return(NullCommand);
6758       return(PrintCommand);
6759     }
6760     case XK_d:
6761     {
6762       if ((state & Mod4Mask) != 0)
6763         return(DrawCommand);
6764       if ((state & ControlMask) == 0)
6765         return(NullCommand);
6766       return(DeleteCommand);
6767     }
6768     case XK_Select:
6769     {
6770       if ((state & ControlMask) == 0)
6771         return(NullCommand);
6772       return(SelectCommand);
6773     }
6774     case XK_n:
6775     {
6776       if ((state & ControlMask) == 0)
6777         return(NullCommand);
6778       return(NewCommand);
6779     }
6780     case XK_q:
6781     case XK_Escape:
6782       return(QuitCommand);
6783     case XK_z:
6784     case XK_Undo:
6785     {
6786       if ((state & ControlMask) == 0)
6787         return(NullCommand);
6788       return(UndoCommand);
6789     }
6790     case XK_r:
6791     case XK_Redo:
6792     {
6793       if ((state & ControlMask) == 0)
6794         return(RollCommand);
6795       return(RedoCommand);
6796     }
6797     case XK_x:
6798     {
6799       if ((state & ControlMask) == 0)
6800         return(NullCommand);
6801       return(CutCommand);
6802     }
6803     case XK_c:
6804     {
6805       if ((state & Mod1Mask) != 0)
6806         return(CharcoalDrawCommand);
6807       if ((state & ControlMask) == 0)
6808         return(CropCommand);
6809       return(CopyCommand);
6810     }
6811     case XK_v:
6812     case XK_Insert:
6813     {
6814       if ((state & Mod4Mask) != 0)
6815         return(CompositeCommand);
6816       if ((state & ControlMask) == 0)
6817         return(FlipCommand);
6818       return(PasteCommand);
6819     }
6820     case XK_less:
6821       return(HalfSizeCommand);
6822     case XK_minus:
6823       return(OriginalSizeCommand);
6824     case XK_greater:
6825       return(DoubleSizeCommand);
6826     case XK_percent:
6827       return(ResizeCommand);
6828     case XK_at:
6829       return(RefreshCommand);
6830     case XK_bracketleft:
6831       return(ChopCommand);
6832     case XK_h:
6833       return(FlopCommand);
6834     case XK_slash:
6835       return(RotateRightCommand);
6836     case XK_backslash:
6837       return(RotateLeftCommand);
6838     case XK_asterisk:
6839       return(RotateCommand);
6840     case XK_t:
6841       return(TrimCommand);
6842     case XK_H:
6843       return(HueCommand);
6844     case XK_S:
6845       return(SaturationCommand);
6846     case XK_L:
6847       return(BrightnessCommand);
6848     case XK_G:
6849       return(GammaCommand);
6850     case XK_C:
6851       return(SpiffCommand);
6852     case XK_Z:
6853       return(DullCommand);
6854     case XK_N:
6855       return(NormalizeCommand);
6856     case XK_equal:
6857       return(EqualizeCommand);
6858     case XK_asciitilde:
6859       return(NegateCommand);
6860     case XK_period:
6861       return(GrayscaleCommand);
6862     case XK_numbersign:
6863       return(QuantizeCommand);
6864     case XK_F2:
6865       return(DespeckleCommand);
6866     case XK_F3:
6867       return(EmbossCommand);
6868     case XK_F4:
6869       return(ReduceNoiseCommand);
6870     case XK_F5:
6871       return(AddNoiseCommand);
6872     case XK_F6:
6873       return(SharpenCommand);
6874     case XK_F7:
6875       return(BlurCommand);
6876     case XK_F8:
6877       return(ThresholdCommand);
6878     case XK_F9:
6879       return(EdgeDetectCommand);
6880     case XK_F10:
6881       return(SpreadCommand);
6882     case XK_F11:
6883       return(ShadeCommand);
6884     case XK_F12:
6885       return(RaiseCommand);
6886     case XK_F13:
6887       return(SegmentCommand);
6888     case XK_i:
6889     {
6890       if ((state & Mod1Mask) == 0)
6891         return(NullCommand);
6892       return(ImplodeCommand);
6893     }
6894     case XK_w:
6895     {
6896       if ((state & Mod1Mask) == 0)
6897         return(NullCommand);
6898       return(WaveCommand);
6899     }
6900     case XK_m:
6901     {
6902       if ((state & Mod4Mask) == 0)
6903         return(NullCommand);
6904       return(MatteCommand);
6905     }
6906     case XK_b:
6907     {
6908       if ((state & Mod4Mask) == 0)
6909         return(NullCommand);
6910       return(AddBorderCommand);
6911     }
6912     case XK_f:
6913     {
6914       if ((state & Mod4Mask) == 0)
6915         return(NullCommand);
6916       return(AddFrameCommand);
6917     }
6918     case XK_exclam:
6919     {
6920       if ((state & Mod4Mask) == 0)
6921         return(NullCommand);
6922       return(CommentCommand);
6923     }
6924     case XK_a:
6925     {
6926       if ((state & Mod1Mask) != 0)
6927         return(ApplyCommand);
6928       if ((state & Mod4Mask) != 0)
6929         return(AnnotateCommand);
6930       if ((state & ControlMask) == 0)
6931         return(NullCommand);
6932       return(RegionofInterestCommand);
6933     }
6934     case XK_question:
6935       return(InfoCommand);
6936     case XK_plus:
6937       return(ZoomCommand);
6938     case XK_P:
6939     {
6940       if ((state & ShiftMask) == 0)
6941         return(NullCommand);
6942       return(ShowPreviewCommand);
6943     }
6944     case XK_Execute:
6945       return(LaunchCommand);
6946     case XK_F1:
6947       return(HelpCommand);
6948     case XK_Find:
6949       return(BrowseDocumentationCommand);
6950     case XK_Menu:
6951     {
6952       (void) XMapRaised(display,windows->command.id);
6953       return(NullCommand);
6954     }
6955     case XK_Next:
6956     case XK_Prior:
6957     case XK_Home:
6958     case XK_KP_Home:
6959     {
6960       XTranslateImage(display,windows,*image,key_symbol);
6961       return(NullCommand);
6962     }
6963     case XK_Up:
6964     case XK_KP_Up:
6965     case XK_Down:
6966     case XK_KP_Down:
6967     case XK_Left:
6968     case XK_KP_Left:
6969     case XK_Right:
6970     case XK_KP_Right:
6971     {
6972       if ((state & Mod1Mask) != 0)
6973         {
6974           RectangleInfo
6975             crop_info;
6976 
6977           /*
6978             Trim one pixel from edge of image.
6979           */
6980           crop_info.x=0;
6981           crop_info.y=0;
6982           crop_info.width=(size_t) windows->image.ximage->width;
6983           crop_info.height=(size_t) windows->image.ximage->height;
6984           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6985             {
6986               if (resource_info->quantum >= (int) crop_info.height)
6987                 resource_info->quantum=(int) crop_info.height-1;
6988               crop_info.height-=resource_info->quantum;
6989             }
6990           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6991             {
6992               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6993                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6994               crop_info.y+=resource_info->quantum;
6995               crop_info.height-=resource_info->quantum;
6996             }
6997           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6998             {
6999               if (resource_info->quantum >= (int) crop_info.width)
7000                 resource_info->quantum=(int) crop_info.width-1;
7001               crop_info.width-=resource_info->quantum;
7002             }
7003           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7004             {
7005               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7006                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7007               crop_info.x+=resource_info->quantum;
7008               crop_info.width-=resource_info->quantum;
7009             }
7010           if ((int) (windows->image.x+windows->image.width) >
7011               (int) crop_info.width)
7012             windows->image.x=(int) (crop_info.width-windows->image.width);
7013           if ((int) (windows->image.y+windows->image.height) >
7014               (int) crop_info.height)
7015             windows->image.y=(int) (crop_info.height-windows->image.height);
7016           XSetCropGeometry(display,windows,&crop_info,*image);
7017           windows->image.window_changes.width=(int) crop_info.width;
7018           windows->image.window_changes.height=(int) crop_info.height;
7019           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7020           (void) XConfigureImage(display,resource_info,windows,*image,
7021             exception);
7022           return(NullCommand);
7023         }
7024       XTranslateImage(display,windows,*image,key_symbol);
7025       return(NullCommand);
7026     }
7027     default:
7028       return(NullCommand);
7029   }
7030   return(NullCommand);
7031 }
7032 
7033 /*
7034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7035 %                                                                             %
7036 %                                                                             %
7037 %                                                                             %
7038 +   X M a g i c k C o m m a n d                                               %
7039 %                                                                             %
7040 %                                                                             %
7041 %                                                                             %
7042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7043 %
7044 %  XMagickCommand() makes a transform to the image or Image window as
7045 %  specified by a user menu button or keyboard command.
7046 %
7047 %  The format of the XMagickCommand method is:
7048 %
7049 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7050 %        XWindows *windows,const CommandType command,Image **image,
7051 %        ExceptionInfo *exception)
7052 %
7053 %  A description of each parameter follows:
7054 %
7055 %    o display: Specifies a connection to an X server; returned from
7056 %      XOpenDisplay.
7057 %
7058 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7059 %
7060 %    o windows: Specifies a pointer to a XWindows structure.
7061 %
7062 %    o command: Specifies a command to perform.
7063 %
7064 %    o image: the image;  XMagickCommand may transform the image and return a
7065 %      new image pointer.
7066 %
7067 %    o exception: return any errors or warnings in this structure.
7068 %
7069 */
XMagickCommand(Display * display,XResourceInfo * resource_info,XWindows * windows,const CommandType command,Image ** image,ExceptionInfo * exception)7070 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7071   XWindows *windows,const CommandType command,Image **image,
7072   ExceptionInfo *exception)
7073 {
7074   char
7075     filename[MagickPathExtent],
7076     geometry[MagickPathExtent],
7077     modulate_factors[MagickPathExtent];
7078 
7079   GeometryInfo
7080     geometry_info;
7081 
7082   Image
7083     *nexus;
7084 
7085   ImageInfo
7086     *image_info;
7087 
7088   int
7089     x,
7090     y;
7091 
7092   MagickStatusType
7093     flags,
7094     status;
7095 
7096   QuantizeInfo
7097     quantize_info;
7098 
7099   RectangleInfo
7100     page_geometry;
7101 
7102   register int
7103     i;
7104 
7105   static char
7106     color[MagickPathExtent] = "gray";
7107 
7108   unsigned int
7109     height,
7110     width;
7111 
7112   /*
7113     Process user command.
7114   */
7115   XCheckRefreshWindows(display,windows);
7116   XImageCache(display,resource_info,windows,command,image,exception);
7117   nexus=NewImageList();
7118   windows->image.window_changes.width=windows->image.ximage->width;
7119   windows->image.window_changes.height=windows->image.ximage->height;
7120   image_info=CloneImageInfo(resource_info->image_info);
7121   SetGeometryInfo(&geometry_info);
7122   GetQuantizeInfo(&quantize_info);
7123   switch (command)
7124   {
7125     case OpenCommand:
7126     {
7127       /*
7128         Load image.
7129       */
7130       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7131       break;
7132     }
7133     case NextCommand:
7134     {
7135       /*
7136         Display next image.
7137       */
7138       for (i=0; i < resource_info->quantum; i++)
7139         XClientMessage(display,windows->image.id,windows->im_protocols,
7140           windows->im_next_image,CurrentTime);
7141       break;
7142     }
7143     case FormerCommand:
7144     {
7145       /*
7146         Display former image.
7147       */
7148       for (i=0; i < resource_info->quantum; i++)
7149         XClientMessage(display,windows->image.id,windows->im_protocols,
7150           windows->im_former_image,CurrentTime);
7151       break;
7152     }
7153     case SelectCommand:
7154     {
7155       int
7156         status;
7157 
7158       /*
7159         Select image.
7160       */
7161       if (*resource_info->home_directory == '\0')
7162         (void) CopyMagickString(resource_info->home_directory,".",
7163           MagickPathExtent);
7164       status=chdir(resource_info->home_directory);
7165       if (status == -1)
7166         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7167           "UnableToOpenFile","%s",resource_info->home_directory);
7168       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7169       break;
7170     }
7171     case SaveCommand:
7172     {
7173       /*
7174         Save image.
7175       */
7176       status=XSaveImage(display,resource_info,windows,*image,exception);
7177       if (status == MagickFalse)
7178         {
7179           char
7180             message[MagickPathExtent];
7181 
7182           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7183             exception->reason != (char *) NULL ? exception->reason : "",
7184             exception->description != (char *) NULL ? exception->description :
7185             "");
7186           XNoticeWidget(display,windows,"Unable to save file:",message);
7187           break;
7188         }
7189       break;
7190     }
7191     case PrintCommand:
7192     {
7193       /*
7194         Print image.
7195       */
7196       status=XPrintImage(display,resource_info,windows,*image,exception);
7197       if (status == MagickFalse)
7198         {
7199           char
7200             message[MagickPathExtent];
7201 
7202           (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7203             exception->reason != (char *) NULL ? exception->reason : "",
7204             exception->description != (char *) NULL ? exception->description :
7205             "");
7206           XNoticeWidget(display,windows,"Unable to print file:",message);
7207           break;
7208         }
7209       break;
7210     }
7211     case DeleteCommand:
7212     {
7213       static char
7214         filename[MagickPathExtent] = "\0";
7215 
7216       /*
7217         Delete image file.
7218       */
7219       XFileBrowserWidget(display,windows,"Delete",filename);
7220       if (*filename == '\0')
7221         break;
7222       status=ShredFile(filename);
7223       if (status != MagickFalse )
7224         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7225       break;
7226     }
7227     case NewCommand:
7228     {
7229       int
7230         status;
7231 
7232       static char
7233         color[MagickPathExtent] = "gray",
7234         geometry[MagickPathExtent] = "640x480";
7235 
7236       static const char
7237         *format = "gradient";
7238 
7239       /*
7240         Query user for canvas geometry.
7241       */
7242       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7243         geometry);
7244       if (*geometry == '\0')
7245         break;
7246       if (status == 0)
7247         format="xc";
7248       XColorBrowserWidget(display,windows,"Select",color);
7249       if (*color == '\0')
7250         break;
7251       /*
7252         Create canvas.
7253       */
7254       (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7255         "%s:%s",format,color);
7256       (void) CloneString(&image_info->size,geometry);
7257       nexus=ReadImage(image_info,exception);
7258       CatchException(exception);
7259       XClientMessage(display,windows->image.id,windows->im_protocols,
7260         windows->im_next_image,CurrentTime);
7261       break;
7262     }
7263     case VisualDirectoryCommand:
7264     {
7265       /*
7266         Visual Image directory.
7267       */
7268       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7269       break;
7270     }
7271     case QuitCommand:
7272     {
7273       /*
7274         exit program.
7275       */
7276       if (resource_info->confirm_exit == MagickFalse)
7277         XClientMessage(display,windows->image.id,windows->im_protocols,
7278           windows->im_exit,CurrentTime);
7279       else
7280         {
7281           int
7282             status;
7283 
7284           /*
7285             Confirm program exit.
7286           */
7287           status=XConfirmWidget(display,windows,"Do you really want to exit",
7288             resource_info->client_name);
7289           if (status > 0)
7290             XClientMessage(display,windows->image.id,windows->im_protocols,
7291               windows->im_exit,CurrentTime);
7292         }
7293       break;
7294     }
7295     case CutCommand:
7296     {
7297       /*
7298         Cut image.
7299       */
7300       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7301       break;
7302     }
7303     case CopyCommand:
7304     {
7305       /*
7306         Copy image.
7307       */
7308       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7309         exception);
7310       break;
7311     }
7312     case PasteCommand:
7313     {
7314       /*
7315         Paste image.
7316       */
7317       status=XPasteImage(display,resource_info,windows,*image,exception);
7318       if (status == MagickFalse)
7319         {
7320           XNoticeWidget(display,windows,"Unable to paste X image",
7321             (*image)->filename);
7322           break;
7323         }
7324       break;
7325     }
7326     case HalfSizeCommand:
7327     {
7328       /*
7329         Half image size.
7330       */
7331       windows->image.window_changes.width=windows->image.ximage->width/2;
7332       windows->image.window_changes.height=windows->image.ximage->height/2;
7333       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7334       break;
7335     }
7336     case OriginalSizeCommand:
7337     {
7338       /*
7339         Original image size.
7340       */
7341       windows->image.window_changes.width=(int) (*image)->columns;
7342       windows->image.window_changes.height=(int) (*image)->rows;
7343       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7344       break;
7345     }
7346     case DoubleSizeCommand:
7347     {
7348       /*
7349         Double the image size.
7350       */
7351       windows->image.window_changes.width=windows->image.ximage->width << 1;
7352       windows->image.window_changes.height=windows->image.ximage->height << 1;
7353       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7354       break;
7355     }
7356     case ResizeCommand:
7357     {
7358       int
7359         status;
7360 
7361       size_t
7362         height,
7363         width;
7364 
7365       ssize_t
7366         x,
7367         y;
7368 
7369       /*
7370         Resize image.
7371       */
7372       width=(size_t) windows->image.ximage->width;
7373       height=(size_t) windows->image.ximage->height;
7374       x=0;
7375       y=0;
7376       (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7377         (double) width,(double) height);
7378       status=XDialogWidget(display,windows,"Resize",
7379         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7380       if (*geometry == '\0')
7381         break;
7382       if (status == 0)
7383         (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7384       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7385       windows->image.window_changes.width=(int) width;
7386       windows->image.window_changes.height=(int) height;
7387       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7388       break;
7389     }
7390     case ApplyCommand:
7391     {
7392       char
7393         image_geometry[MagickPathExtent];
7394 
7395       if ((windows->image.crop_geometry == (char *) NULL) &&
7396           ((int) (*image)->columns == windows->image.ximage->width) &&
7397           ((int) (*image)->rows == windows->image.ximage->height))
7398         break;
7399       /*
7400         Apply size transforms to image.
7401       */
7402       XSetCursorState(display,windows,MagickTrue);
7403       XCheckRefreshWindows(display,windows);
7404       /*
7405         Crop and/or scale displayed image.
7406       */
7407       (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7408         windows->image.ximage->width,windows->image.ximage->height);
7409       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7410         exception);
7411       if (windows->image.crop_geometry != (char *) NULL)
7412         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7413           windows->image.crop_geometry);
7414       windows->image.x=0;
7415       windows->image.y=0;
7416       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7417       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7418       break;
7419     }
7420     case RefreshCommand:
7421     {
7422       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7423       break;
7424     }
7425     case RestoreCommand:
7426     {
7427       /*
7428         Restore Image window to its original size.
7429       */
7430       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7431           (windows->image.height == (unsigned int) (*image)->rows) &&
7432           (windows->image.crop_geometry == (char *) NULL))
7433         {
7434           (void) XBell(display,0);
7435           break;
7436         }
7437       windows->image.window_changes.width=(int) (*image)->columns;
7438       windows->image.window_changes.height=(int) (*image)->rows;
7439       if (windows->image.crop_geometry != (char *) NULL)
7440         {
7441           windows->image.crop_geometry=(char *)
7442             RelinquishMagickMemory(windows->image.crop_geometry);
7443           windows->image.crop_geometry=(char *) NULL;
7444           windows->image.x=0;
7445           windows->image.y=0;
7446         }
7447       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7448       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7449       break;
7450     }
7451     case CropCommand:
7452     {
7453       /*
7454         Crop image.
7455       */
7456       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7457         exception);
7458       break;
7459     }
7460     case ChopCommand:
7461     {
7462       /*
7463         Chop image.
7464       */
7465       status=XChopImage(display,resource_info,windows,image,exception);
7466       if (status == MagickFalse)
7467         {
7468           XNoticeWidget(display,windows,"Unable to cut X image",
7469             (*image)->filename);
7470           break;
7471         }
7472       break;
7473     }
7474     case FlopCommand:
7475     {
7476       Image
7477         *flop_image;
7478 
7479       /*
7480         Flop image scanlines.
7481       */
7482       XSetCursorState(display,windows,MagickTrue);
7483       XCheckRefreshWindows(display,windows);
7484       flop_image=FlopImage(*image,exception);
7485       if (flop_image != (Image *) NULL)
7486         {
7487           *image=DestroyImage(*image);
7488           *image=flop_image;
7489         }
7490       CatchException(exception);
7491       XSetCursorState(display,windows,MagickFalse);
7492       if (windows->image.crop_geometry != (char *) NULL)
7493         {
7494           /*
7495             Flop crop geometry.
7496           */
7497           width=(unsigned int) (*image)->columns;
7498           height=(unsigned int) (*image)->rows;
7499           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7500             &width,&height);
7501           (void) FormatLocaleString(windows->image.crop_geometry,
7502             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7503             (int) width-x,y);
7504         }
7505       if (windows->image.orphan != MagickFalse )
7506         break;
7507       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7508       break;
7509     }
7510     case FlipCommand:
7511     {
7512       Image
7513         *flip_image;
7514 
7515       /*
7516         Flip image scanlines.
7517       */
7518       XSetCursorState(display,windows,MagickTrue);
7519       XCheckRefreshWindows(display,windows);
7520       flip_image=FlipImage(*image,exception);
7521       if (flip_image != (Image *) NULL)
7522         {
7523           *image=DestroyImage(*image);
7524           *image=flip_image;
7525         }
7526       CatchException(exception);
7527       XSetCursorState(display,windows,MagickFalse);
7528       if (windows->image.crop_geometry != (char *) NULL)
7529         {
7530           /*
7531             Flip crop geometry.
7532           */
7533           width=(unsigned int) (*image)->columns;
7534           height=(unsigned int) (*image)->rows;
7535           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7536             &width,&height);
7537           (void) FormatLocaleString(windows->image.crop_geometry,
7538             MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7539             (int) height-y);
7540         }
7541       if (windows->image.orphan != MagickFalse )
7542         break;
7543       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7544       break;
7545     }
7546     case RotateRightCommand:
7547     {
7548       /*
7549         Rotate image 90 degrees clockwise.
7550       */
7551       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7552       if (status == MagickFalse)
7553         {
7554           XNoticeWidget(display,windows,"Unable to rotate X image",
7555             (*image)->filename);
7556           break;
7557         }
7558       break;
7559     }
7560     case RotateLeftCommand:
7561     {
7562       /*
7563         Rotate image 90 degrees counter-clockwise.
7564       */
7565       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7566       if (status == MagickFalse)
7567         {
7568           XNoticeWidget(display,windows,"Unable to rotate X image",
7569             (*image)->filename);
7570           break;
7571         }
7572       break;
7573     }
7574     case RotateCommand:
7575     {
7576       /*
7577         Rotate image.
7578       */
7579       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7580       if (status == MagickFalse)
7581         {
7582           XNoticeWidget(display,windows,"Unable to rotate X image",
7583             (*image)->filename);
7584           break;
7585         }
7586       break;
7587     }
7588     case ShearCommand:
7589     {
7590       Image
7591         *shear_image;
7592 
7593       static char
7594         geometry[MagickPathExtent] = "45.0x45.0";
7595 
7596       /*
7597         Query user for shear color and geometry.
7598       */
7599       XColorBrowserWidget(display,windows,"Select",color);
7600       if (*color == '\0')
7601         break;
7602       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7603         geometry);
7604       if (*geometry == '\0')
7605         break;
7606       /*
7607         Shear image.
7608       */
7609       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7610         exception);
7611       XSetCursorState(display,windows,MagickTrue);
7612       XCheckRefreshWindows(display,windows);
7613       (void) QueryColorCompliance(color,AllCompliance,
7614         &(*image)->background_color,exception);
7615       flags=ParseGeometry(geometry,&geometry_info);
7616       if ((flags & SigmaValue) == 0)
7617         geometry_info.sigma=geometry_info.rho;
7618       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7619         exception);
7620       if (shear_image != (Image *) NULL)
7621         {
7622           *image=DestroyImage(*image);
7623           *image=shear_image;
7624         }
7625       CatchException(exception);
7626       XSetCursorState(display,windows,MagickFalse);
7627       if (windows->image.orphan != MagickFalse )
7628         break;
7629       windows->image.window_changes.width=(int) (*image)->columns;
7630       windows->image.window_changes.height=(int) (*image)->rows;
7631       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7632       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7633       break;
7634     }
7635     case RollCommand:
7636     {
7637       Image
7638         *roll_image;
7639 
7640       static char
7641         geometry[MagickPathExtent] = "+2+2";
7642 
7643       /*
7644         Query user for the roll geometry.
7645       */
7646       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7647         geometry);
7648       if (*geometry == '\0')
7649         break;
7650       /*
7651         Roll image.
7652       */
7653       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7654         exception);
7655       XSetCursorState(display,windows,MagickTrue);
7656       XCheckRefreshWindows(display,windows);
7657       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7658         exception);
7659       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7660         exception);
7661       if (roll_image != (Image *) NULL)
7662         {
7663           *image=DestroyImage(*image);
7664           *image=roll_image;
7665         }
7666       CatchException(exception);
7667       XSetCursorState(display,windows,MagickFalse);
7668       if (windows->image.orphan != MagickFalse )
7669         break;
7670       windows->image.window_changes.width=(int) (*image)->columns;
7671       windows->image.window_changes.height=(int) (*image)->rows;
7672       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7673       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7674       break;
7675     }
7676     case TrimCommand:
7677     {
7678       static char
7679         fuzz[MagickPathExtent];
7680 
7681       /*
7682         Query user for the fuzz factor.
7683       */
7684       (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7685         (*image)->fuzz/(QuantumRange+1.0));
7686       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7687       if (*fuzz == '\0')
7688         break;
7689       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7690       /*
7691         Trim image.
7692       */
7693       status=XTrimImage(display,resource_info,windows,*image,exception);
7694       if (status == MagickFalse)
7695         {
7696           XNoticeWidget(display,windows,"Unable to trim X image",
7697             (*image)->filename);
7698           break;
7699         }
7700       break;
7701     }
7702     case HueCommand:
7703     {
7704       static char
7705         hue_percent[MagickPathExtent] = "110";
7706 
7707       /*
7708         Query user for percent hue change.
7709       */
7710       (void) XDialogWidget(display,windows,"Apply",
7711         "Enter percent change in image hue (0-200):",hue_percent);
7712       if (*hue_percent == '\0')
7713         break;
7714       /*
7715         Vary the image hue.
7716       */
7717       XSetCursorState(display,windows,MagickTrue);
7718       XCheckRefreshWindows(display,windows);
7719       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7720       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7721         MagickPathExtent);
7722       (void) ModulateImage(*image,modulate_factors,exception);
7723       XSetCursorState(display,windows,MagickFalse);
7724       if (windows->image.orphan != MagickFalse )
7725         break;
7726       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7727       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7728       break;
7729     }
7730     case SaturationCommand:
7731     {
7732       static char
7733         saturation_percent[MagickPathExtent] = "110";
7734 
7735       /*
7736         Query user for percent saturation change.
7737       */
7738       (void) XDialogWidget(display,windows,"Apply",
7739         "Enter percent change in color saturation (0-200):",saturation_percent);
7740       if (*saturation_percent == '\0')
7741         break;
7742       /*
7743         Vary color saturation.
7744       */
7745       XSetCursorState(display,windows,MagickTrue);
7746       XCheckRefreshWindows(display,windows);
7747       (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7748       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7749         MagickPathExtent);
7750       (void) ModulateImage(*image,modulate_factors,exception);
7751       XSetCursorState(display,windows,MagickFalse);
7752       if (windows->image.orphan != MagickFalse )
7753         break;
7754       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7755       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7756       break;
7757     }
7758     case BrightnessCommand:
7759     {
7760       static char
7761         brightness_percent[MagickPathExtent] = "110";
7762 
7763       /*
7764         Query user for percent brightness change.
7765       */
7766       (void) XDialogWidget(display,windows,"Apply",
7767         "Enter percent change in color brightness (0-200):",brightness_percent);
7768       if (*brightness_percent == '\0')
7769         break;
7770       /*
7771         Vary the color brightness.
7772       */
7773       XSetCursorState(display,windows,MagickTrue);
7774       XCheckRefreshWindows(display,windows);
7775       (void) CopyMagickString(modulate_factors,brightness_percent,
7776         MagickPathExtent);
7777       (void) ModulateImage(*image,modulate_factors,exception);
7778       XSetCursorState(display,windows,MagickFalse);
7779       if (windows->image.orphan != MagickFalse )
7780         break;
7781       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7782       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7783       break;
7784     }
7785     case GammaCommand:
7786     {
7787       static char
7788         factor[MagickPathExtent] = "1.6";
7789 
7790       /*
7791         Query user for gamma value.
7792       */
7793       (void) XDialogWidget(display,windows,"Gamma",
7794         "Enter gamma value (e.g. 1.2):",factor);
7795       if (*factor == '\0')
7796         break;
7797       /*
7798         Gamma correct image.
7799       */
7800       XSetCursorState(display,windows,MagickTrue);
7801       XCheckRefreshWindows(display,windows);
7802       (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7803       XSetCursorState(display,windows,MagickFalse);
7804       if (windows->image.orphan != MagickFalse )
7805         break;
7806       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7807       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7808       break;
7809     }
7810     case SpiffCommand:
7811     {
7812       /*
7813         Sharpen the image contrast.
7814       */
7815       XSetCursorState(display,windows,MagickTrue);
7816       XCheckRefreshWindows(display,windows);
7817       (void) ContrastImage(*image,MagickTrue,exception);
7818       XSetCursorState(display,windows,MagickFalse);
7819       if (windows->image.orphan != MagickFalse )
7820         break;
7821       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7822       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7823       break;
7824     }
7825     case DullCommand:
7826     {
7827       /*
7828         Dull the image contrast.
7829       */
7830       XSetCursorState(display,windows,MagickTrue);
7831       XCheckRefreshWindows(display,windows);
7832       (void) ContrastImage(*image,MagickFalse,exception);
7833       XSetCursorState(display,windows,MagickFalse);
7834       if (windows->image.orphan != MagickFalse )
7835         break;
7836       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7837       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838       break;
7839     }
7840     case ContrastStretchCommand:
7841     {
7842       double
7843         black_point,
7844         white_point;
7845 
7846       static char
7847         levels[MagickPathExtent] = "1%";
7848 
7849       /*
7850         Query user for gamma value.
7851       */
7852       (void) XDialogWidget(display,windows,"Contrast Stretch",
7853         "Enter black and white points:",levels);
7854       if (*levels == '\0')
7855         break;
7856       /*
7857         Contrast stretch image.
7858       */
7859       XSetCursorState(display,windows,MagickTrue);
7860       XCheckRefreshWindows(display,windows);
7861       flags=ParseGeometry(levels,&geometry_info);
7862       black_point=geometry_info.rho;
7863       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7864       if ((flags & PercentValue) != 0)
7865         {
7866           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7867           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7868         }
7869       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7870       (void) ContrastStretchImage(*image,black_point,white_point,
7871         exception);
7872       XSetCursorState(display,windows,MagickFalse);
7873       if (windows->image.orphan != MagickFalse )
7874         break;
7875       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7876       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7877       break;
7878     }
7879     case SigmoidalContrastCommand:
7880     {
7881       GeometryInfo
7882         geometry_info;
7883 
7884       MagickStatusType
7885         flags;
7886 
7887       static char
7888         levels[MagickPathExtent] = "3x50%";
7889 
7890       /*
7891         Query user for gamma value.
7892       */
7893       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7894         "Enter contrast and midpoint:",levels);
7895       if (*levels == '\0')
7896         break;
7897       /*
7898         Contrast stretch image.
7899       */
7900       XSetCursorState(display,windows,MagickTrue);
7901       XCheckRefreshWindows(display,windows);
7902       flags=ParseGeometry(levels,&geometry_info);
7903       if ((flags & SigmaValue) == 0)
7904         geometry_info.sigma=1.0*QuantumRange/2.0;
7905       if ((flags & PercentValue) != 0)
7906         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7907       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7908         geometry_info.sigma,exception);
7909       XSetCursorState(display,windows,MagickFalse);
7910       if (windows->image.orphan != MagickFalse )
7911         break;
7912       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7913       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7914       break;
7915     }
7916     case NormalizeCommand:
7917     {
7918       /*
7919         Perform histogram normalization on the image.
7920       */
7921       XSetCursorState(display,windows,MagickTrue);
7922       XCheckRefreshWindows(display,windows);
7923       (void) NormalizeImage(*image,exception);
7924       XSetCursorState(display,windows,MagickFalse);
7925       if (windows->image.orphan != MagickFalse )
7926         break;
7927       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7928       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7929       break;
7930     }
7931     case EqualizeCommand:
7932     {
7933       /*
7934         Perform histogram equalization on the image.
7935       */
7936       XSetCursorState(display,windows,MagickTrue);
7937       XCheckRefreshWindows(display,windows);
7938       (void) EqualizeImage(*image,exception);
7939       XSetCursorState(display,windows,MagickFalse);
7940       if (windows->image.orphan != MagickFalse )
7941         break;
7942       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7943       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944       break;
7945     }
7946     case NegateCommand:
7947     {
7948       /*
7949         Negate colors in image.
7950       */
7951       XSetCursorState(display,windows,MagickTrue);
7952       XCheckRefreshWindows(display,windows);
7953       (void) NegateImage(*image,MagickFalse,exception);
7954       XSetCursorState(display,windows,MagickFalse);
7955       if (windows->image.orphan != MagickFalse )
7956         break;
7957       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959       break;
7960     }
7961     case GrayscaleCommand:
7962     {
7963       /*
7964         Convert image to grayscale.
7965       */
7966       XSetCursorState(display,windows,MagickTrue);
7967       XCheckRefreshWindows(display,windows);
7968       (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7969         GrayscaleType : GrayscaleAlphaType,exception);
7970       XSetCursorState(display,windows,MagickFalse);
7971       if (windows->image.orphan != MagickFalse )
7972         break;
7973       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7974       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7975       break;
7976     }
7977     case MapCommand:
7978     {
7979       Image
7980         *affinity_image;
7981 
7982       static char
7983         filename[MagickPathExtent] = "\0";
7984 
7985       /*
7986         Request image file name from user.
7987       */
7988       XFileBrowserWidget(display,windows,"Map",filename);
7989       if (*filename == '\0')
7990         break;
7991       /*
7992         Map image.
7993       */
7994       XSetCursorState(display,windows,MagickTrue);
7995       XCheckRefreshWindows(display,windows);
7996       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
7997       affinity_image=ReadImage(image_info,exception);
7998       if (affinity_image != (Image *) NULL)
7999         {
8000           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8001           affinity_image=DestroyImage(affinity_image);
8002         }
8003       CatchException(exception);
8004       XSetCursorState(display,windows,MagickFalse);
8005       if (windows->image.orphan != MagickFalse )
8006         break;
8007       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8008       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8009       break;
8010     }
8011     case QuantizeCommand:
8012     {
8013       int
8014         status;
8015 
8016       static char
8017         colors[MagickPathExtent] = "256";
8018 
8019       /*
8020         Query user for maximum number of colors.
8021       */
8022       status=XDialogWidget(display,windows,"Quantize",
8023         "Maximum number of colors:",colors);
8024       if (*colors == '\0')
8025         break;
8026       /*
8027         Color reduce the image.
8028       */
8029       XSetCursorState(display,windows,MagickTrue);
8030       XCheckRefreshWindows(display,windows);
8031       quantize_info.number_colors=StringToUnsignedLong(colors);
8032       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8033         NoDitherMethod;
8034       (void) QuantizeImage(&quantize_info,*image,exception);
8035       XSetCursorState(display,windows,MagickFalse);
8036       if (windows->image.orphan != MagickFalse )
8037         break;
8038       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8039       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8040       break;
8041     }
8042     case DespeckleCommand:
8043     {
8044       Image
8045         *despeckle_image;
8046 
8047       /*
8048         Despeckle image.
8049       */
8050       XSetCursorState(display,windows,MagickTrue);
8051       XCheckRefreshWindows(display,windows);
8052       despeckle_image=DespeckleImage(*image,exception);
8053       if (despeckle_image != (Image *) NULL)
8054         {
8055           *image=DestroyImage(*image);
8056           *image=despeckle_image;
8057         }
8058       CatchException(exception);
8059       XSetCursorState(display,windows,MagickFalse);
8060       if (windows->image.orphan != MagickFalse )
8061         break;
8062       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8063       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8064       break;
8065     }
8066     case EmbossCommand:
8067     {
8068       Image
8069         *emboss_image;
8070 
8071       static char
8072         radius[MagickPathExtent] = "0.0x1.0";
8073 
8074       /*
8075         Query user for emboss radius.
8076       */
8077       (void) XDialogWidget(display,windows,"Emboss",
8078         "Enter the emboss radius and standard deviation:",radius);
8079       if (*radius == '\0')
8080         break;
8081       /*
8082         Reduce noise in the image.
8083       */
8084       XSetCursorState(display,windows,MagickTrue);
8085       XCheckRefreshWindows(display,windows);
8086       flags=ParseGeometry(radius,&geometry_info);
8087       if ((flags & SigmaValue) == 0)
8088         geometry_info.sigma=1.0;
8089       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8090         exception);
8091       if (emboss_image != (Image *) NULL)
8092         {
8093           *image=DestroyImage(*image);
8094           *image=emboss_image;
8095         }
8096       CatchException(exception);
8097       XSetCursorState(display,windows,MagickFalse);
8098       if (windows->image.orphan != MagickFalse )
8099         break;
8100       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8101       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8102       break;
8103     }
8104     case ReduceNoiseCommand:
8105     {
8106       Image
8107         *noise_image;
8108 
8109       static char
8110         radius[MagickPathExtent] = "0";
8111 
8112       /*
8113         Query user for noise radius.
8114       */
8115       (void) XDialogWidget(display,windows,"Reduce Noise",
8116         "Enter the noise radius:",radius);
8117       if (*radius == '\0')
8118         break;
8119       /*
8120         Reduce noise in the image.
8121       */
8122       XSetCursorState(display,windows,MagickTrue);
8123       XCheckRefreshWindows(display,windows);
8124       flags=ParseGeometry(radius,&geometry_info);
8125       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8126         geometry_info.rho,(size_t) geometry_info.rho,exception);
8127       if (noise_image != (Image *) NULL)
8128         {
8129           *image=DestroyImage(*image);
8130           *image=noise_image;
8131         }
8132       CatchException(exception);
8133       XSetCursorState(display,windows,MagickFalse);
8134       if (windows->image.orphan != MagickFalse )
8135         break;
8136       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8137       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8138       break;
8139     }
8140     case AddNoiseCommand:
8141     {
8142       char
8143         **noises;
8144 
8145       Image
8146         *noise_image;
8147 
8148       static char
8149         noise_type[MagickPathExtent] = "Gaussian";
8150 
8151       /*
8152         Add noise to the image.
8153       */
8154       noises=GetCommandOptions(MagickNoiseOptions);
8155       if (noises == (char **) NULL)
8156         break;
8157       XListBrowserWidget(display,windows,&windows->widget,
8158         (const char **) noises,"Add Noise",
8159         "Select a type of noise to add to your image:",noise_type);
8160       noises=DestroyStringList(noises);
8161       if (*noise_type == '\0')
8162         break;
8163       XSetCursorState(display,windows,MagickTrue);
8164       XCheckRefreshWindows(display,windows);
8165       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8166         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8167       if (noise_image != (Image *) NULL)
8168         {
8169           *image=DestroyImage(*image);
8170           *image=noise_image;
8171         }
8172       CatchException(exception);
8173       XSetCursorState(display,windows,MagickFalse);
8174       if (windows->image.orphan != MagickFalse )
8175         break;
8176       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8177       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8178       break;
8179     }
8180     case SharpenCommand:
8181     {
8182       Image
8183         *sharp_image;
8184 
8185       static char
8186         radius[MagickPathExtent] = "0.0x1.0";
8187 
8188       /*
8189         Query user for sharpen radius.
8190       */
8191       (void) XDialogWidget(display,windows,"Sharpen",
8192         "Enter the sharpen radius and standard deviation:",radius);
8193       if (*radius == '\0')
8194         break;
8195       /*
8196         Sharpen image scanlines.
8197       */
8198       XSetCursorState(display,windows,MagickTrue);
8199       XCheckRefreshWindows(display,windows);
8200       flags=ParseGeometry(radius,&geometry_info);
8201       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8202         exception);
8203       if (sharp_image != (Image *) NULL)
8204         {
8205           *image=DestroyImage(*image);
8206           *image=sharp_image;
8207         }
8208       CatchException(exception);
8209       XSetCursorState(display,windows,MagickFalse);
8210       if (windows->image.orphan != MagickFalse )
8211         break;
8212       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8213       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8214       break;
8215     }
8216     case BlurCommand:
8217     {
8218       Image
8219         *blur_image;
8220 
8221       static char
8222         radius[MagickPathExtent] = "0.0x1.0";
8223 
8224       /*
8225         Query user for blur radius.
8226       */
8227       (void) XDialogWidget(display,windows,"Blur",
8228         "Enter the blur radius and standard deviation:",radius);
8229       if (*radius == '\0')
8230         break;
8231       /*
8232         Blur an image.
8233       */
8234       XSetCursorState(display,windows,MagickTrue);
8235       XCheckRefreshWindows(display,windows);
8236       flags=ParseGeometry(radius,&geometry_info);
8237       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8238         exception);
8239       if (blur_image != (Image *) NULL)
8240         {
8241           *image=DestroyImage(*image);
8242           *image=blur_image;
8243         }
8244       CatchException(exception);
8245       XSetCursorState(display,windows,MagickFalse);
8246       if (windows->image.orphan != MagickFalse )
8247         break;
8248       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8249       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8250       break;
8251     }
8252     case ThresholdCommand:
8253     {
8254       double
8255         threshold;
8256 
8257       static char
8258         factor[MagickPathExtent] = "128";
8259 
8260       /*
8261         Query user for threshold value.
8262       */
8263       (void) XDialogWidget(display,windows,"Threshold",
8264         "Enter threshold value:",factor);
8265       if (*factor == '\0')
8266         break;
8267       /*
8268         Gamma correct image.
8269       */
8270       XSetCursorState(display,windows,MagickTrue);
8271       XCheckRefreshWindows(display,windows);
8272       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8273       (void) BilevelImage(*image,threshold,exception);
8274       XSetCursorState(display,windows,MagickFalse);
8275       if (windows->image.orphan != MagickFalse )
8276         break;
8277       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8278       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8279       break;
8280     }
8281     case EdgeDetectCommand:
8282     {
8283       Image
8284         *edge_image;
8285 
8286       static char
8287         radius[MagickPathExtent] = "0";
8288 
8289       /*
8290         Query user for edge factor.
8291       */
8292       (void) XDialogWidget(display,windows,"Detect Edges",
8293         "Enter the edge detect radius:",radius);
8294       if (*radius == '\0')
8295         break;
8296       /*
8297         Detect edge in image.
8298       */
8299       XSetCursorState(display,windows,MagickTrue);
8300       XCheckRefreshWindows(display,windows);
8301       flags=ParseGeometry(radius,&geometry_info);
8302       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8303       if (edge_image != (Image *) NULL)
8304         {
8305           *image=DestroyImage(*image);
8306           *image=edge_image;
8307         }
8308       CatchException(exception);
8309       XSetCursorState(display,windows,MagickFalse);
8310       if (windows->image.orphan != MagickFalse )
8311         break;
8312       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8313       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8314       break;
8315     }
8316     case SpreadCommand:
8317     {
8318       Image
8319         *spread_image;
8320 
8321       static char
8322         amount[MagickPathExtent] = "2";
8323 
8324       /*
8325         Query user for spread amount.
8326       */
8327       (void) XDialogWidget(display,windows,"Spread",
8328         "Enter the displacement amount:",amount);
8329       if (*amount == '\0')
8330         break;
8331       /*
8332         Displace image pixels by a random amount.
8333       */
8334       XSetCursorState(display,windows,MagickTrue);
8335       XCheckRefreshWindows(display,windows);
8336       flags=ParseGeometry(amount,&geometry_info);
8337       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8338       if (spread_image != (Image *) NULL)
8339         {
8340           *image=DestroyImage(*image);
8341           *image=spread_image;
8342         }
8343       CatchException(exception);
8344       XSetCursorState(display,windows,MagickFalse);
8345       if (windows->image.orphan != MagickFalse )
8346         break;
8347       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8348       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8349       break;
8350     }
8351     case ShadeCommand:
8352     {
8353       Image
8354         *shade_image;
8355 
8356       int
8357         status;
8358 
8359       static char
8360         geometry[MagickPathExtent] = "30x30";
8361 
8362       /*
8363         Query user for the shade geometry.
8364       */
8365       status=XDialogWidget(display,windows,"Shade",
8366         "Enter the azimuth and elevation of the light source:",geometry);
8367       if (*geometry == '\0')
8368         break;
8369       /*
8370         Shade image pixels.
8371       */
8372       XSetCursorState(display,windows,MagickTrue);
8373       XCheckRefreshWindows(display,windows);
8374       flags=ParseGeometry(geometry,&geometry_info);
8375       if ((flags & SigmaValue) == 0)
8376         geometry_info.sigma=1.0;
8377       shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8378         geometry_info.rho,geometry_info.sigma,exception);
8379       if (shade_image != (Image *) NULL)
8380         {
8381           *image=DestroyImage(*image);
8382           *image=shade_image;
8383         }
8384       CatchException(exception);
8385       XSetCursorState(display,windows,MagickFalse);
8386       if (windows->image.orphan != MagickFalse )
8387         break;
8388       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8389       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8390       break;
8391     }
8392     case RaiseCommand:
8393     {
8394       static char
8395         bevel_width[MagickPathExtent] = "10";
8396 
8397       /*
8398         Query user for bevel width.
8399       */
8400       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8401       if (*bevel_width == '\0')
8402         break;
8403       /*
8404         Raise an image.
8405       */
8406       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8407         exception);
8408       XSetCursorState(display,windows,MagickTrue);
8409       XCheckRefreshWindows(display,windows);
8410       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8411         exception);
8412       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8413       XSetCursorState(display,windows,MagickFalse);
8414       if (windows->image.orphan != MagickFalse )
8415         break;
8416       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8417       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8418       break;
8419     }
8420     case SegmentCommand:
8421     {
8422       static char
8423         threshold[MagickPathExtent] = "1.0x1.5";
8424 
8425       /*
8426         Query user for smoothing threshold.
8427       */
8428       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8429         threshold);
8430       if (*threshold == '\0')
8431         break;
8432       /*
8433         Segment an image.
8434       */
8435       XSetCursorState(display,windows,MagickTrue);
8436       XCheckRefreshWindows(display,windows);
8437       flags=ParseGeometry(threshold,&geometry_info);
8438       if ((flags & SigmaValue) == 0)
8439         geometry_info.sigma=1.0;
8440       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8441         geometry_info.sigma,exception);
8442       XSetCursorState(display,windows,MagickFalse);
8443       if (windows->image.orphan != MagickFalse )
8444         break;
8445       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8446       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8447       break;
8448     }
8449     case SepiaToneCommand:
8450     {
8451       double
8452         threshold;
8453 
8454       Image
8455         *sepia_image;
8456 
8457       static char
8458         factor[MagickPathExtent] = "80%";
8459 
8460       /*
8461         Query user for sepia-tone factor.
8462       */
8463       (void) XDialogWidget(display,windows,"Sepia Tone",
8464         "Enter the sepia tone factor (0 - 99.9%):",factor);
8465       if (*factor == '\0')
8466         break;
8467       /*
8468         Sepia tone image pixels.
8469       */
8470       XSetCursorState(display,windows,MagickTrue);
8471       XCheckRefreshWindows(display,windows);
8472       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8473       sepia_image=SepiaToneImage(*image,threshold,exception);
8474       if (sepia_image != (Image *) NULL)
8475         {
8476           *image=DestroyImage(*image);
8477           *image=sepia_image;
8478         }
8479       CatchException(exception);
8480       XSetCursorState(display,windows,MagickFalse);
8481       if (windows->image.orphan != MagickFalse )
8482         break;
8483       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8484       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8485       break;
8486     }
8487     case SolarizeCommand:
8488     {
8489       double
8490         threshold;
8491 
8492       static char
8493         factor[MagickPathExtent] = "60%";
8494 
8495       /*
8496         Query user for solarize factor.
8497       */
8498       (void) XDialogWidget(display,windows,"Solarize",
8499         "Enter the solarize factor (0 - 99.9%):",factor);
8500       if (*factor == '\0')
8501         break;
8502       /*
8503         Solarize image pixels.
8504       */
8505       XSetCursorState(display,windows,MagickTrue);
8506       XCheckRefreshWindows(display,windows);
8507       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8508       (void) SolarizeImage(*image,threshold,exception);
8509       XSetCursorState(display,windows,MagickFalse);
8510       if (windows->image.orphan != MagickFalse )
8511         break;
8512       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8513       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8514       break;
8515     }
8516     case SwirlCommand:
8517     {
8518       Image
8519         *swirl_image;
8520 
8521       static char
8522         degrees[MagickPathExtent] = "60";
8523 
8524       /*
8525         Query user for swirl angle.
8526       */
8527       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8528         degrees);
8529       if (*degrees == '\0')
8530         break;
8531       /*
8532         Swirl image pixels about the center.
8533       */
8534       XSetCursorState(display,windows,MagickTrue);
8535       XCheckRefreshWindows(display,windows);
8536       flags=ParseGeometry(degrees,&geometry_info);
8537       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8538         exception);
8539       if (swirl_image != (Image *) NULL)
8540         {
8541           *image=DestroyImage(*image);
8542           *image=swirl_image;
8543         }
8544       CatchException(exception);
8545       XSetCursorState(display,windows,MagickFalse);
8546       if (windows->image.orphan != MagickFalse )
8547         break;
8548       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8549       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8550       break;
8551     }
8552     case ImplodeCommand:
8553     {
8554       Image
8555         *implode_image;
8556 
8557       static char
8558         factor[MagickPathExtent] = "0.3";
8559 
8560       /*
8561         Query user for implode factor.
8562       */
8563       (void) XDialogWidget(display,windows,"Implode",
8564         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8565       if (*factor == '\0')
8566         break;
8567       /*
8568         Implode image pixels about the center.
8569       */
8570       XSetCursorState(display,windows,MagickTrue);
8571       XCheckRefreshWindows(display,windows);
8572       flags=ParseGeometry(factor,&geometry_info);
8573       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8574         exception);
8575       if (implode_image != (Image *) NULL)
8576         {
8577           *image=DestroyImage(*image);
8578           *image=implode_image;
8579         }
8580       CatchException(exception);
8581       XSetCursorState(display,windows,MagickFalse);
8582       if (windows->image.orphan != MagickFalse )
8583         break;
8584       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8585       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8586       break;
8587     }
8588     case VignetteCommand:
8589     {
8590       Image
8591         *vignette_image;
8592 
8593       static char
8594         geometry[MagickPathExtent] = "0x20";
8595 
8596       /*
8597         Query user for the vignette geometry.
8598       */
8599       (void) XDialogWidget(display,windows,"Vignette",
8600         "Enter the radius, sigma, and x and y offsets:",geometry);
8601       if (*geometry == '\0')
8602         break;
8603       /*
8604         Soften the edges of the image in vignette style
8605       */
8606       XSetCursorState(display,windows,MagickTrue);
8607       XCheckRefreshWindows(display,windows);
8608       flags=ParseGeometry(geometry,&geometry_info);
8609       if ((flags & SigmaValue) == 0)
8610         geometry_info.sigma=1.0;
8611       if ((flags & XiValue) == 0)
8612         geometry_info.xi=0.1*(*image)->columns;
8613       if ((flags & PsiValue) == 0)
8614         geometry_info.psi=0.1*(*image)->rows;
8615       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8616         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8617         exception);
8618       if (vignette_image != (Image *) NULL)
8619         {
8620           *image=DestroyImage(*image);
8621           *image=vignette_image;
8622         }
8623       CatchException(exception);
8624       XSetCursorState(display,windows,MagickFalse);
8625       if (windows->image.orphan != MagickFalse )
8626         break;
8627       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8628       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8629       break;
8630     }
8631     case WaveCommand:
8632     {
8633       Image
8634         *wave_image;
8635 
8636       static char
8637         geometry[MagickPathExtent] = "25x150";
8638 
8639       /*
8640         Query user for the wave geometry.
8641       */
8642       (void) XDialogWidget(display,windows,"Wave",
8643         "Enter the amplitude and length of the wave:",geometry);
8644       if (*geometry == '\0')
8645         break;
8646       /*
8647         Alter an image along a sine wave.
8648       */
8649       XSetCursorState(display,windows,MagickTrue);
8650       XCheckRefreshWindows(display,windows);
8651       flags=ParseGeometry(geometry,&geometry_info);
8652       if ((flags & SigmaValue) == 0)
8653         geometry_info.sigma=1.0;
8654       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8655         (*image)->interpolate,exception);
8656       if (wave_image != (Image *) NULL)
8657         {
8658           *image=DestroyImage(*image);
8659           *image=wave_image;
8660         }
8661       CatchException(exception);
8662       XSetCursorState(display,windows,MagickFalse);
8663       if (windows->image.orphan != MagickFalse )
8664         break;
8665       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8666       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8667       break;
8668     }
8669     case OilPaintCommand:
8670     {
8671       Image
8672         *paint_image;
8673 
8674       static char
8675         radius[MagickPathExtent] = "0";
8676 
8677       /*
8678         Query user for circular neighborhood radius.
8679       */
8680       (void) XDialogWidget(display,windows,"Oil Paint",
8681         "Enter the mask radius:",radius);
8682       if (*radius == '\0')
8683         break;
8684       /*
8685         OilPaint image scanlines.
8686       */
8687       XSetCursorState(display,windows,MagickTrue);
8688       XCheckRefreshWindows(display,windows);
8689       flags=ParseGeometry(radius,&geometry_info);
8690       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8691         exception);
8692       if (paint_image != (Image *) NULL)
8693         {
8694           *image=DestroyImage(*image);
8695           *image=paint_image;
8696         }
8697       CatchException(exception);
8698       XSetCursorState(display,windows,MagickFalse);
8699       if (windows->image.orphan != MagickFalse )
8700         break;
8701       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8702       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8703       break;
8704     }
8705     case CharcoalDrawCommand:
8706     {
8707       Image
8708         *charcoal_image;
8709 
8710       static char
8711         radius[MagickPathExtent] = "0x1";
8712 
8713       /*
8714         Query user for charcoal radius.
8715       */
8716       (void) XDialogWidget(display,windows,"Charcoal Draw",
8717         "Enter the charcoal radius and sigma:",radius);
8718       if (*radius == '\0')
8719         break;
8720       /*
8721         Charcoal the image.
8722       */
8723       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8724         exception);
8725       XSetCursorState(display,windows,MagickTrue);
8726       XCheckRefreshWindows(display,windows);
8727       flags=ParseGeometry(radius,&geometry_info);
8728       if ((flags & SigmaValue) == 0)
8729         geometry_info.sigma=geometry_info.rho;
8730       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8731         exception);
8732       if (charcoal_image != (Image *) NULL)
8733         {
8734           *image=DestroyImage(*image);
8735           *image=charcoal_image;
8736         }
8737       CatchException(exception);
8738       XSetCursorState(display,windows,MagickFalse);
8739       if (windows->image.orphan != MagickFalse )
8740         break;
8741       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8742       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8743       break;
8744     }
8745     case AnnotateCommand:
8746     {
8747       /*
8748         Annotate the image with text.
8749       */
8750       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8751       if (status == MagickFalse)
8752         {
8753           XNoticeWidget(display,windows,"Unable to annotate X image",
8754             (*image)->filename);
8755           break;
8756         }
8757       break;
8758     }
8759     case DrawCommand:
8760     {
8761       /*
8762         Draw image.
8763       */
8764       status=XDrawEditImage(display,resource_info,windows,image,exception);
8765       if (status == MagickFalse)
8766         {
8767           XNoticeWidget(display,windows,"Unable to draw on the X image",
8768             (*image)->filename);
8769           break;
8770         }
8771       break;
8772     }
8773     case ColorCommand:
8774     {
8775       /*
8776         Color edit.
8777       */
8778       status=XColorEditImage(display,resource_info,windows,image,exception);
8779       if (status == MagickFalse)
8780         {
8781           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8782             (*image)->filename);
8783           break;
8784         }
8785       break;
8786     }
8787     case MatteCommand:
8788     {
8789       /*
8790         Matte edit.
8791       */
8792       status=XMatteEditImage(display,resource_info,windows,image,exception);
8793       if (status == MagickFalse)
8794         {
8795           XNoticeWidget(display,windows,"Unable to matte edit X image",
8796             (*image)->filename);
8797           break;
8798         }
8799       break;
8800     }
8801     case CompositeCommand:
8802     {
8803       /*
8804         Composite image.
8805       */
8806       status=XCompositeImage(display,resource_info,windows,*image,
8807         exception);
8808       if (status == MagickFalse)
8809         {
8810           XNoticeWidget(display,windows,"Unable to composite X image",
8811             (*image)->filename);
8812           break;
8813         }
8814       break;
8815     }
8816     case AddBorderCommand:
8817     {
8818       Image
8819         *border_image;
8820 
8821       static char
8822         geometry[MagickPathExtent] = "6x6";
8823 
8824       /*
8825         Query user for border color and geometry.
8826       */
8827       XColorBrowserWidget(display,windows,"Select",color);
8828       if (*color == '\0')
8829         break;
8830       (void) XDialogWidget(display,windows,"Add Border",
8831         "Enter border geometry:",geometry);
8832       if (*geometry == '\0')
8833         break;
8834       /*
8835         Add a border to the image.
8836       */
8837       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8838         exception);
8839       XSetCursorState(display,windows,MagickTrue);
8840       XCheckRefreshWindows(display,windows);
8841       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8842         exception);
8843       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8844         exception);
8845       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8846         exception);
8847       if (border_image != (Image *) NULL)
8848         {
8849           *image=DestroyImage(*image);
8850           *image=border_image;
8851         }
8852       CatchException(exception);
8853       XSetCursorState(display,windows,MagickFalse);
8854       if (windows->image.orphan != MagickFalse )
8855         break;
8856       windows->image.window_changes.width=(int) (*image)->columns;
8857       windows->image.window_changes.height=(int) (*image)->rows;
8858       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8859       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8860       break;
8861     }
8862     case AddFrameCommand:
8863     {
8864       FrameInfo
8865         frame_info;
8866 
8867       Image
8868         *frame_image;
8869 
8870       static char
8871         geometry[MagickPathExtent] = "6x6";
8872 
8873       /*
8874         Query user for frame color and geometry.
8875       */
8876       XColorBrowserWidget(display,windows,"Select",color);
8877       if (*color == '\0')
8878         break;
8879       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8880         geometry);
8881       if (*geometry == '\0')
8882         break;
8883       /*
8884         Surround image with an ornamental border.
8885       */
8886       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8887         exception);
8888       XSetCursorState(display,windows,MagickTrue);
8889       XCheckRefreshWindows(display,windows);
8890       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8891         exception);
8892       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8893         exception);
8894       frame_info.width=page_geometry.width;
8895       frame_info.height=page_geometry.height;
8896       frame_info.outer_bevel=page_geometry.x;
8897       frame_info.inner_bevel=page_geometry.y;
8898       frame_info.x=(ssize_t) frame_info.width;
8899       frame_info.y=(ssize_t) frame_info.height;
8900       frame_info.width=(*image)->columns+2*frame_info.width;
8901       frame_info.height=(*image)->rows+2*frame_info.height;
8902       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8903       if (frame_image != (Image *) NULL)
8904         {
8905           *image=DestroyImage(*image);
8906           *image=frame_image;
8907         }
8908       CatchException(exception);
8909       XSetCursorState(display,windows,MagickFalse);
8910       if (windows->image.orphan != MagickFalse )
8911         break;
8912       windows->image.window_changes.width=(int) (*image)->columns;
8913       windows->image.window_changes.height=(int) (*image)->rows;
8914       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8915       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8916       break;
8917     }
8918     case CommentCommand:
8919     {
8920       const char
8921         *value;
8922 
8923       FILE
8924         *file;
8925 
8926       int
8927         unique_file;
8928 
8929       /*
8930         Edit image comment.
8931       */
8932       unique_file=AcquireUniqueFileResource(image_info->filename);
8933       if (unique_file == -1)
8934         XNoticeWidget(display,windows,"Unable to edit image comment",
8935           image_info->filename);
8936       value=GetImageProperty(*image,"comment",exception);
8937       if (value == (char *) NULL)
8938         unique_file=close(unique_file)-1;
8939       else
8940         {
8941           register const char
8942             *p;
8943 
8944           file=fdopen(unique_file,"w");
8945           if (file == (FILE *) NULL)
8946             {
8947               XNoticeWidget(display,windows,"Unable to edit image comment",
8948                 image_info->filename);
8949               break;
8950             }
8951           for (p=value; *p != '\0'; p++)
8952             (void) fputc((int) *p,file);
8953           (void) fputc('\n',file);
8954           (void) fclose(file);
8955         }
8956       XSetCursorState(display,windows,MagickTrue);
8957       XCheckRefreshWindows(display,windows);
8958       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8959         exception);
8960       if (status == MagickFalse)
8961         XNoticeWidget(display,windows,"Unable to edit image comment",
8962           (char *) NULL);
8963       else
8964         {
8965           char
8966             *comment;
8967 
8968           comment=FileToString(image_info->filename,~0UL,exception);
8969           if (comment != (char *) NULL)
8970             {
8971               (void) SetImageProperty(*image,"comment",comment,exception);
8972               (*image)->taint=MagickTrue;
8973             }
8974         }
8975       (void) RelinquishUniqueFileResource(image_info->filename);
8976       XSetCursorState(display,windows,MagickFalse);
8977       break;
8978     }
8979     case LaunchCommand:
8980     {
8981       /*
8982         Launch program.
8983       */
8984       XSetCursorState(display,windows,MagickTrue);
8985       XCheckRefreshWindows(display,windows);
8986       (void) AcquireUniqueFilename(filename);
8987       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
8988         filename);
8989       status=WriteImage(image_info,*image,exception);
8990       if (status == MagickFalse)
8991         XNoticeWidget(display,windows,"Unable to launch image editor",
8992           (char *) NULL);
8993       else
8994         {
8995           nexus=ReadImage(resource_info->image_info,exception);
8996           CatchException(exception);
8997           XClientMessage(display,windows->image.id,windows->im_protocols,
8998             windows->im_next_image,CurrentTime);
8999         }
9000       (void) RelinquishUniqueFileResource(filename);
9001       XSetCursorState(display,windows,MagickFalse);
9002       break;
9003     }
9004     case RegionofInterestCommand:
9005     {
9006       /*
9007         Apply an image processing technique to a region of interest.
9008       */
9009       (void) XROIImage(display,resource_info,windows,image,exception);
9010       break;
9011     }
9012     case InfoCommand:
9013       break;
9014     case ZoomCommand:
9015     {
9016       /*
9017         Zoom image.
9018       */
9019       if (windows->magnify.mapped != MagickFalse )
9020         (void) XRaiseWindow(display,windows->magnify.id);
9021       else
9022         {
9023           /*
9024             Make magnify image.
9025           */
9026           XSetCursorState(display,windows,MagickTrue);
9027           (void) XMapRaised(display,windows->magnify.id);
9028           XSetCursorState(display,windows,MagickFalse);
9029         }
9030       break;
9031     }
9032     case ShowPreviewCommand:
9033     {
9034       char
9035         **previews,
9036         value[MagickPathExtent];
9037 
9038       Image
9039         *preview_image;
9040 
9041       PreviewType
9042         preview;
9043 
9044       static char
9045         preview_type[MagickPathExtent] = "Gamma";
9046 
9047       /*
9048         Select preview type from menu.
9049       */
9050       previews=GetCommandOptions(MagickPreviewOptions);
9051       if (previews == (char **) NULL)
9052         break;
9053       XListBrowserWidget(display,windows,&windows->widget,
9054         (const char **) previews,"Preview",
9055         "Select an enhancement, effect, or F/X:",preview_type);
9056       previews=DestroyStringList(previews);
9057       if (*preview_type == '\0')
9058         break;
9059       /*
9060         Show image preview.
9061       */
9062       XSetCursorState(display,windows,MagickTrue);
9063       XCheckRefreshWindows(display,windows);
9064       preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9065         MagickFalse,preview_type);
9066       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9067         windows->image.id);
9068       (void) SetImageProperty(*image,"group",value,exception);
9069       (void) DeleteImageProperty(*image,"label");
9070       (void) SetImageProperty(*image,"label","Preview",exception);
9071       preview_image=PreviewImage(*image,preview,exception);
9072       if (preview_image == (Image *) NULL)
9073         break;
9074       (void) AcquireUniqueFilename(filename);
9075       (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9076         "show:%s",filename);
9077       status=WriteImage(image_info,preview_image,exception);
9078       (void) RelinquishUniqueFileResource(filename);
9079       preview_image=DestroyImage(preview_image);
9080       if (status == MagickFalse)
9081         XNoticeWidget(display,windows,"Unable to show image preview",
9082           (*image)->filename);
9083       XDelay(display,1500);
9084       XSetCursorState(display,windows,MagickFalse);
9085       break;
9086     }
9087     case ShowHistogramCommand:
9088     {
9089       char
9090         value[MagickPathExtent];
9091 
9092       Image
9093         *histogram_image;
9094 
9095       /*
9096         Show image histogram.
9097       */
9098       XSetCursorState(display,windows,MagickTrue);
9099       XCheckRefreshWindows(display,windows);
9100       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9101         windows->image.id);
9102       (void) SetImageProperty(*image,"group",value,exception);
9103       (void) DeleteImageProperty(*image,"label");
9104       (void) SetImageProperty(*image,"label","Histogram",exception);
9105       (void) AcquireUniqueFilename(filename);
9106       (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9107         "histogram:%s",filename);
9108       status=WriteImage(image_info,*image,exception);
9109       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9110       histogram_image=ReadImage(image_info,exception);
9111       (void) RelinquishUniqueFileResource(filename);
9112       if (histogram_image == (Image *) NULL)
9113         break;
9114       (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9115         "show:%s",filename);
9116       status=WriteImage(image_info,histogram_image,exception);
9117       histogram_image=DestroyImage(histogram_image);
9118       if (status == MagickFalse)
9119         XNoticeWidget(display,windows,"Unable to show histogram",
9120           (*image)->filename);
9121       XDelay(display,1500);
9122       XSetCursorState(display,windows,MagickFalse);
9123       break;
9124     }
9125     case ShowMatteCommand:
9126     {
9127       char
9128         value[MagickPathExtent];
9129 
9130       Image
9131         *matte_image;
9132 
9133       if ((*image)->alpha_trait == UndefinedPixelTrait)
9134         {
9135           XNoticeWidget(display,windows,
9136             "Image does not have any matte information",(*image)->filename);
9137           break;
9138         }
9139       /*
9140         Show image matte.
9141       */
9142       XSetCursorState(display,windows,MagickTrue);
9143       XCheckRefreshWindows(display,windows);
9144       (void) FormatLocaleString(value,MaxTextExtent,"%.20g",(double)
9145         windows->image.id);
9146       (void) SetImageProperty(*image,"group",value,exception);
9147       (void) DeleteImageProperty(*image,"label");
9148       (void) SetImageProperty(*image,"label","Matte",exception);
9149       (void) AcquireUniqueFilename(filename);
9150       (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9151         filename);
9152       status=WriteImage(image_info,*image,exception);
9153       (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9154       matte_image=ReadImage(image_info,exception);
9155       (void) RelinquishUniqueFileResource(filename);
9156       if (matte_image == (Image *) NULL)
9157         break;
9158       (void) FormatLocaleString(matte_image->filename,MagickPathExtent,"show:%s",
9159         filename);
9160       status=WriteImage(image_info,matte_image,exception);
9161       matte_image=DestroyImage(matte_image);
9162       if (status == MagickFalse)
9163         XNoticeWidget(display,windows,"Unable to show matte",
9164           (*image)->filename);
9165       XDelay(display,1500);
9166       XSetCursorState(display,windows,MagickFalse);
9167       break;
9168     }
9169     case BackgroundCommand:
9170     {
9171       /*
9172         Background image.
9173       */
9174       status=XBackgroundImage(display,resource_info,windows,image,exception);
9175       if (status == MagickFalse)
9176         break;
9177       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9178       if (nexus != (Image *) NULL)
9179         XClientMessage(display,windows->image.id,windows->im_protocols,
9180           windows->im_next_image,CurrentTime);
9181       break;
9182     }
9183     case SlideShowCommand:
9184     {
9185       static char
9186         delay[MagickPathExtent] = "5";
9187 
9188       /*
9189         Display next image after pausing.
9190       */
9191       (void) XDialogWidget(display,windows,"Slide Show",
9192         "Pause how many 1/100ths of a second between images:",delay);
9193       if (*delay == '\0')
9194         break;
9195       resource_info->delay=StringToUnsignedLong(delay);
9196       XClientMessage(display,windows->image.id,windows->im_protocols,
9197         windows->im_next_image,CurrentTime);
9198       break;
9199     }
9200     case PreferencesCommand:
9201     {
9202       /*
9203         Set user preferences.
9204       */
9205       status=XPreferencesWidget(display,resource_info,windows);
9206       if (status == MagickFalse)
9207         break;
9208       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209       if (nexus != (Image *) NULL)
9210         XClientMessage(display,windows->image.id,windows->im_protocols,
9211           windows->im_next_image,CurrentTime);
9212       break;
9213     }
9214     case HelpCommand:
9215     {
9216       /*
9217         User requested help.
9218       */
9219       XTextViewHelp(display,resource_info,windows,MagickFalse,
9220         "Help Viewer - Display",DisplayHelp);
9221       break;
9222     }
9223     case BrowseDocumentationCommand:
9224     {
9225       Atom
9226         mozilla_atom;
9227 
9228       Window
9229         mozilla_window,
9230         root_window;
9231 
9232       /*
9233         Browse the ImageMagick documentation.
9234       */
9235       root_window=XRootWindow(display,XDefaultScreen(display));
9236       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9237       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9238       if (mozilla_window != (Window) NULL)
9239         {
9240           char
9241             command[MagickPathExtent],
9242             *url;
9243 
9244           /*
9245             Display documentation using Netscape remote control.
9246           */
9247           url=GetMagickHomeURL();
9248           (void) FormatLocaleString(command,MagickPathExtent,
9249             "openurl(%s,new-tab)",url);
9250           url=DestroyString(url);
9251           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9252           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9253             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9254           XSetCursorState(display,windows,MagickFalse);
9255           break;
9256         }
9257       XSetCursorState(display,windows,MagickTrue);
9258       XCheckRefreshWindows(display,windows);
9259       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9260         exception);
9261       if (status == MagickFalse)
9262         XNoticeWidget(display,windows,"Unable to browse documentation",
9263           (char *) NULL);
9264       XDelay(display,1500);
9265       XSetCursorState(display,windows,MagickFalse);
9266       break;
9267     }
9268     case VersionCommand:
9269     {
9270       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9271         GetMagickCopyright());
9272       break;
9273     }
9274     case SaveToUndoBufferCommand:
9275       break;
9276     default:
9277     {
9278       (void) XBell(display,0);
9279       break;
9280     }
9281   }
9282   image_info=DestroyImageInfo(image_info);
9283   return(nexus);
9284 }
9285 
9286 /*
9287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9288 %                                                                             %
9289 %                                                                             %
9290 %                                                                             %
9291 +   X M a g n i f y I m a g e                                                 %
9292 %                                                                             %
9293 %                                                                             %
9294 %                                                                             %
9295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9296 %
9297 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9298 %  The magnified portion is displayed in a separate window.
9299 %
9300 %  The format of the XMagnifyImage method is:
9301 %
9302 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9303 %        ExceptionInfo *exception)
9304 %
9305 %  A description of each parameter follows:
9306 %
9307 %    o display: Specifies a connection to an X server;  returned from
9308 %      XOpenDisplay.
9309 %
9310 %    o windows: Specifies a pointer to a XWindows structure.
9311 %
9312 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9313 %      the entire image is refreshed.
9314 %
9315 %    o exception: return any errors or warnings in this structure.
9316 %
9317 */
XMagnifyImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)9318 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9319   ExceptionInfo *exception)
9320 {
9321   char
9322     text[MagickPathExtent];
9323 
9324   register int
9325     x,
9326     y;
9327 
9328   size_t
9329     state;
9330 
9331   /*
9332     Update magnified image until the mouse button is released.
9333   */
9334   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9335   state=DefaultState;
9336   x=event->xbutton.x;
9337   y=event->xbutton.y;
9338   windows->magnify.x=(int) windows->image.x+x;
9339   windows->magnify.y=(int) windows->image.y+y;
9340   do
9341   {
9342     /*
9343       Map and unmap Info widget as text cursor crosses its boundaries.
9344     */
9345     if (windows->info.mapped != MagickFalse )
9346       {
9347         if ((x < (int) (windows->info.x+windows->info.width)) &&
9348             (y < (int) (windows->info.y+windows->info.height)))
9349           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9350       }
9351     else
9352       if ((x > (int) (windows->info.x+windows->info.width)) ||
9353           (y > (int) (windows->info.y+windows->info.height)))
9354         (void) XMapWindow(display,windows->info.id);
9355     if (windows->info.mapped != MagickFalse )
9356       {
9357         /*
9358           Display pointer position.
9359         */
9360         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9361           windows->magnify.x,windows->magnify.y);
9362         XInfoWidget(display,windows,text);
9363       }
9364     /*
9365       Wait for next event.
9366     */
9367     XScreenEvent(display,windows,event,exception);
9368     switch (event->type)
9369     {
9370       case ButtonPress:
9371         break;
9372       case ButtonRelease:
9373       {
9374         /*
9375           User has finished magnifying image.
9376         */
9377         x=event->xbutton.x;
9378         y=event->xbutton.y;
9379         state|=ExitState;
9380         break;
9381       }
9382       case Expose:
9383         break;
9384       case MotionNotify:
9385       {
9386         x=event->xmotion.x;
9387         y=event->xmotion.y;
9388         break;
9389       }
9390       default:
9391         break;
9392     }
9393     /*
9394       Check boundary conditions.
9395     */
9396     if (x < 0)
9397       x=0;
9398     else
9399       if (x >= (int) windows->image.width)
9400         x=(int) windows->image.width-1;
9401     if (y < 0)
9402       y=0;
9403     else
9404      if (y >= (int) windows->image.height)
9405        y=(int) windows->image.height-1;
9406   } while ((state & ExitState) == 0);
9407   /*
9408     Display magnified image.
9409   */
9410   XSetCursorState(display,windows,MagickFalse);
9411 }
9412 
9413 /*
9414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9415 %                                                                             %
9416 %                                                                             %
9417 %                                                                             %
9418 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9419 %                                                                             %
9420 %                                                                             %
9421 %                                                                             %
9422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9423 %
9424 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9425 %  pixel as specified by the key symbol.
9426 %
9427 %  The format of the XMagnifyWindowCommand method is:
9428 %
9429 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9430 %        const MagickStatusType state,const KeySym key_symbol,
9431 %        ExceptionInfo *exception)
9432 %
9433 %  A description of each parameter follows:
9434 %
9435 %    o display: Specifies a connection to an X server; returned from
9436 %      XOpenDisplay.
9437 %
9438 %    o windows: Specifies a pointer to a XWindows structure.
9439 %
9440 %    o state: key mask.
9441 %
9442 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9443 %      to trim.
9444 %
9445 %    o exception: return any errors or warnings in this structure.
9446 %
9447 */
XMagnifyWindowCommand(Display * display,XWindows * windows,const MagickStatusType state,const KeySym key_symbol,ExceptionInfo * exception)9448 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9449   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9450 {
9451   unsigned int
9452     quantum;
9453 
9454   /*
9455     User specified a magnify factor or position.
9456   */
9457   quantum=1;
9458   if ((state & Mod1Mask) != 0)
9459     quantum=10;
9460   switch ((int) key_symbol)
9461   {
9462     case QuitCommand:
9463     {
9464       (void) XWithdrawWindow(display,windows->magnify.id,
9465         windows->magnify.screen);
9466       break;
9467     }
9468     case XK_Home:
9469     case XK_KP_Home:
9470     {
9471       windows->magnify.x=(int) windows->image.width/2;
9472       windows->magnify.y=(int) windows->image.height/2;
9473       break;
9474     }
9475     case XK_Left:
9476     case XK_KP_Left:
9477     {
9478       if (windows->magnify.x > 0)
9479         windows->magnify.x-=quantum;
9480       break;
9481     }
9482     case XK_Up:
9483     case XK_KP_Up:
9484     {
9485       if (windows->magnify.y > 0)
9486         windows->magnify.y-=quantum;
9487       break;
9488     }
9489     case XK_Right:
9490     case XK_KP_Right:
9491     {
9492       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9493         windows->magnify.x+=quantum;
9494       break;
9495     }
9496     case XK_Down:
9497     case XK_KP_Down:
9498     {
9499       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9500         windows->magnify.y+=quantum;
9501       break;
9502     }
9503     case XK_0:
9504     case XK_1:
9505     case XK_2:
9506     case XK_3:
9507     case XK_4:
9508     case XK_5:
9509     case XK_6:
9510     case XK_7:
9511     case XK_8:
9512     case XK_9:
9513     {
9514       windows->magnify.data=(key_symbol-XK_0);
9515       break;
9516     }
9517     case XK_KP_0:
9518     case XK_KP_1:
9519     case XK_KP_2:
9520     case XK_KP_3:
9521     case XK_KP_4:
9522     case XK_KP_5:
9523     case XK_KP_6:
9524     case XK_KP_7:
9525     case XK_KP_8:
9526     case XK_KP_9:
9527     {
9528       windows->magnify.data=(key_symbol-XK_KP_0);
9529       break;
9530     }
9531     default:
9532       break;
9533   }
9534   XMakeMagnifyImage(display,windows,exception);
9535 }
9536 
9537 /*
9538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9539 %                                                                             %
9540 %                                                                             %
9541 %                                                                             %
9542 +   X M a k e P a n I m a g e                                                 %
9543 %                                                                             %
9544 %                                                                             %
9545 %                                                                             %
9546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9547 %
9548 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9549 %  icon window.
9550 %
9551 %  The format of the XMakePanImage method is:
9552 %
9553 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9554 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9555 %
9556 %  A description of each parameter follows:
9557 %
9558 %    o display: Specifies a connection to an X server;  returned from
9559 %      XOpenDisplay.
9560 %
9561 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9562 %
9563 %    o windows: Specifies a pointer to a XWindows structure.
9564 %
9565 %    o image: the image.
9566 %
9567 %    o exception: return any errors or warnings in this structure.
9568 %
9569 */
XMakePanImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)9570 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9571   XWindows *windows,Image *image,ExceptionInfo *exception)
9572 {
9573   MagickStatusType
9574     status;
9575 
9576   /*
9577     Create and display image for panning icon.
9578   */
9579   XSetCursorState(display,windows,MagickTrue);
9580   XCheckRefreshWindows(display,windows);
9581   windows->pan.x=(int) windows->image.x;
9582   windows->pan.y=(int) windows->image.y;
9583   status=XMakeImage(display,resource_info,&windows->pan,image,
9584     windows->pan.width,windows->pan.height,exception);
9585   if (status == MagickFalse)
9586     ThrowXWindowException(ResourceLimitError,
9587      "MemoryAllocationFailed",image->filename);
9588   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9589     windows->pan.pixmap);
9590   (void) XClearWindow(display,windows->pan.id);
9591   XDrawPanRectangle(display,windows);
9592   XSetCursorState(display,windows,MagickFalse);
9593 }
9594 
9595 /*
9596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9597 %                                                                             %
9598 %                                                                             %
9599 %                                                                             %
9600 +   X M a t t a E d i t I m a g e                                             %
9601 %                                                                             %
9602 %                                                                             %
9603 %                                                                             %
9604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9605 %
9606 %  XMatteEditImage() allows the user to interactively change the Matte channel
9607 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9608 %  before the matte information is stored.
9609 %
9610 %  The format of the XMatteEditImage method is:
9611 %
9612 %      MagickBooleanType XMatteEditImage(Display *display,
9613 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9614 %        ExceptionInfo *exception)
9615 %
9616 %  A description of each parameter follows:
9617 %
9618 %    o display: Specifies a connection to an X server;  returned from
9619 %      XOpenDisplay.
9620 %
9621 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9622 %
9623 %    o windows: Specifies a pointer to a XWindows structure.
9624 %
9625 %    o image: the image; returned from ReadImage.
9626 %
9627 %    o exception: return any errors or warnings in this structure.
9628 %
9629 */
XMatteEditImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)9630 static MagickBooleanType XMatteEditImage(Display *display,
9631   XResourceInfo *resource_info,XWindows *windows,Image **image,
9632   ExceptionInfo *exception)
9633 {
9634   const char
9635     *const MatteEditMenu[] =
9636     {
9637       "Method",
9638       "Border Color",
9639       "Fuzz",
9640       "Matte Value",
9641       "Undo",
9642       "Help",
9643       "Dismiss",
9644       (char *) NULL
9645     };
9646 
9647   static char
9648     matte[MagickPathExtent] = "0";
9649 
9650   static const ModeType
9651     MatteEditCommands[] =
9652     {
9653       MatteEditMethod,
9654       MatteEditBorderCommand,
9655       MatteEditFuzzCommand,
9656       MatteEditValueCommand,
9657       MatteEditUndoCommand,
9658       MatteEditHelpCommand,
9659       MatteEditDismissCommand
9660     };
9661 
9662   static PaintMethod
9663     method = PointMethod;
9664 
9665   static XColor
9666     border_color = { 0, 0, 0, 0, 0, 0 };
9667 
9668   char
9669     command[MagickPathExtent],
9670     text[MagickPathExtent];
9671 
9672   Cursor
9673     cursor;
9674 
9675   int
9676     entry,
9677     id,
9678     x,
9679     x_offset,
9680     y,
9681     y_offset;
9682 
9683   register int
9684     i;
9685 
9686   register Quantum
9687     *q;
9688 
9689   unsigned int
9690     height,
9691     width;
9692 
9693   size_t
9694     state;
9695 
9696   XEvent
9697     event;
9698 
9699   /*
9700     Map Command widget.
9701   */
9702   (void) CloneString(&windows->command.name,"Matte Edit");
9703   windows->command.data=4;
9704   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9705   (void) XMapRaised(display,windows->command.id);
9706   XClientMessage(display,windows->image.id,windows->im_protocols,
9707     windows->im_update_widget,CurrentTime);
9708   /*
9709     Make cursor.
9710   */
9711   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9712     resource_info->background_color,resource_info->foreground_color);
9713   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9714   /*
9715     Track pointer until button 1 is pressed.
9716   */
9717   XQueryPosition(display,windows->image.id,&x,&y);
9718   (void) XSelectInput(display,windows->image.id,
9719     windows->image.attributes.event_mask | PointerMotionMask);
9720   state=DefaultState;
9721   do
9722   {
9723     if (windows->info.mapped != MagickFalse )
9724       {
9725         /*
9726           Display pointer position.
9727         */
9728         (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9729           x+windows->image.x,y+windows->image.y);
9730         XInfoWidget(display,windows,text);
9731       }
9732     /*
9733       Wait for next event.
9734     */
9735     XScreenEvent(display,windows,&event,exception);
9736     if (event.xany.window == windows->command.id)
9737       {
9738         /*
9739           Select a command from the Command widget.
9740         */
9741         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9742         if (id < 0)
9743           {
9744             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745             continue;
9746           }
9747         switch (MatteEditCommands[id])
9748         {
9749           case MatteEditMethod:
9750           {
9751             char
9752               **methods;
9753 
9754             /*
9755               Select a method from the pop-up menu.
9756             */
9757             methods=GetCommandOptions(MagickMethodOptions);
9758             if (methods == (char **) NULL)
9759               break;
9760             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9761               (const char **) methods,command);
9762             if (entry >= 0)
9763               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9764                 MagickFalse,methods[entry]);
9765             methods=DestroyStringList(methods);
9766             break;
9767           }
9768           case MatteEditBorderCommand:
9769           {
9770             const char
9771               *ColorMenu[MaxNumberPens];
9772 
9773             int
9774               pen_number;
9775 
9776             /*
9777               Initialize menu selections.
9778             */
9779             for (i=0; i < (int) (MaxNumberPens-2); i++)
9780               ColorMenu[i]=resource_info->pen_colors[i];
9781             ColorMenu[MaxNumberPens-2]="Browser...";
9782             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9783             /*
9784               Select a pen color from the pop-up menu.
9785             */
9786             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9787               (const char **) ColorMenu,command);
9788             if (pen_number < 0)
9789               break;
9790             if (pen_number == (MaxNumberPens-2))
9791               {
9792                 static char
9793                   color_name[MagickPathExtent] = "gray";
9794 
9795                 /*
9796                   Select a pen color from a dialog.
9797                 */
9798                 resource_info->pen_colors[pen_number]=color_name;
9799                 XColorBrowserWidget(display,windows,"Select",color_name);
9800                 if (*color_name == '\0')
9801                   break;
9802               }
9803             /*
9804               Set border color.
9805             */
9806             (void) XParseColor(display,windows->map_info->colormap,
9807               resource_info->pen_colors[pen_number],&border_color);
9808             break;
9809           }
9810           case MatteEditFuzzCommand:
9811           {
9812             const char
9813               *const FuzzMenu[] =
9814               {
9815                 "0%",
9816                 "2%",
9817                 "5%",
9818                 "10%",
9819                 "15%",
9820                 "Dialog...",
9821                 (char *) NULL,
9822               };
9823 
9824             static char
9825               fuzz[MagickPathExtent];
9826 
9827             /*
9828               Select a command from the pop-up menu.
9829             */
9830             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9831               command);
9832             if (entry < 0)
9833               break;
9834             if (entry != 5)
9835               {
9836                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9837                   QuantumRange+1.0);
9838                 break;
9839               }
9840             (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9841             (void) XDialogWidget(display,windows,"Ok",
9842               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9843             if (*fuzz == '\0')
9844               break;
9845             (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9846             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9847               1.0);
9848             break;
9849           }
9850           case MatteEditValueCommand:
9851           {
9852             const char
9853               *const MatteMenu[] =
9854               {
9855                 "Opaque",
9856                 "Transparent",
9857                 "Dialog...",
9858                 (char *) NULL,
9859               };
9860 
9861             static char
9862               message[MagickPathExtent];
9863 
9864             /*
9865               Select a command from the pop-up menu.
9866             */
9867             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9868               command);
9869             if (entry < 0)
9870               break;
9871             if (entry != 2)
9872               {
9873                 (void) FormatLocaleString(matte,MagickPathExtent,QuantumFormat,
9874                   OpaqueAlpha);
9875                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9876                   (void) FormatLocaleString(matte,MagickPathExtent,
9877                     QuantumFormat,(Quantum) TransparentAlpha);
9878                 break;
9879               }
9880             (void) FormatLocaleString(message,MagickPathExtent,
9881               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9882               QuantumRange);
9883             (void) XDialogWidget(display,windows,"Matte",message,matte);
9884             if (*matte == '\0')
9885               break;
9886             break;
9887           }
9888           case MatteEditUndoCommand:
9889           {
9890             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9891               image,exception);
9892             break;
9893           }
9894           case MatteEditHelpCommand:
9895           {
9896             XTextViewHelp(display,resource_info,windows,MagickFalse,
9897               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9898             break;
9899           }
9900           case MatteEditDismissCommand:
9901           {
9902             /*
9903               Prematurely exit.
9904             */
9905             state|=EscapeState;
9906             state|=ExitState;
9907             break;
9908           }
9909           default:
9910             break;
9911         }
9912         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9913         continue;
9914       }
9915     switch (event.type)
9916     {
9917       case ButtonPress:
9918       {
9919         if (event.xbutton.button != Button1)
9920           break;
9921         if ((event.xbutton.window != windows->image.id) &&
9922             (event.xbutton.window != windows->magnify.id))
9923           break;
9924         /*
9925           Update matte data.
9926         */
9927         x=event.xbutton.x;
9928         y=event.xbutton.y;
9929         (void) XMagickCommand(display,resource_info,windows,
9930           SaveToUndoBufferCommand,image,exception);
9931         state|=UpdateConfigurationState;
9932         break;
9933       }
9934       case ButtonRelease:
9935       {
9936         if (event.xbutton.button != Button1)
9937           break;
9938         if ((event.xbutton.window != windows->image.id) &&
9939             (event.xbutton.window != windows->magnify.id))
9940           break;
9941         /*
9942           Update colormap information.
9943         */
9944         x=event.xbutton.x;
9945         y=event.xbutton.y;
9946         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9947         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9948         XInfoWidget(display,windows,text);
9949         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9950         state&=(~UpdateConfigurationState);
9951         break;
9952       }
9953       case Expose:
9954         break;
9955       case KeyPress:
9956       {
9957         char
9958           command[MagickPathExtent];
9959 
9960         KeySym
9961           key_symbol;
9962 
9963         if (event.xkey.window == windows->magnify.id)
9964           {
9965             Window
9966               window;
9967 
9968             window=windows->magnify.id;
9969             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9970           }
9971         if (event.xkey.window != windows->image.id)
9972           break;
9973         /*
9974           Respond to a user key press.
9975         */
9976         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9977           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9978         switch ((int) key_symbol)
9979         {
9980           case XK_Escape:
9981           case XK_F20:
9982           {
9983             /*
9984               Prematurely exit.
9985             */
9986             state|=ExitState;
9987             break;
9988           }
9989           case XK_F1:
9990           case XK_Help:
9991           {
9992             XTextViewHelp(display,resource_info,windows,MagickFalse,
9993               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9994             break;
9995           }
9996           default:
9997           {
9998             (void) XBell(display,0);
9999             break;
10000           }
10001         }
10002         break;
10003       }
10004       case MotionNotify:
10005       {
10006         /*
10007           Map and unmap Info widget as cursor crosses its boundaries.
10008         */
10009         x=event.xmotion.x;
10010         y=event.xmotion.y;
10011         if (windows->info.mapped != MagickFalse )
10012           {
10013             if ((x < (int) (windows->info.x+windows->info.width)) &&
10014                 (y < (int) (windows->info.y+windows->info.height)))
10015               (void) XWithdrawWindow(display,windows->info.id,
10016                 windows->info.screen);
10017           }
10018         else
10019           if ((x > (int) (windows->info.x+windows->info.width)) ||
10020               (y > (int) (windows->info.y+windows->info.height)))
10021             (void) XMapWindow(display,windows->info.id);
10022         break;
10023       }
10024       default:
10025         break;
10026     }
10027     if (event.xany.window == windows->magnify.id)
10028       {
10029         x=windows->magnify.x-windows->image.x;
10030         y=windows->magnify.y-windows->image.y;
10031       }
10032     x_offset=x;
10033     y_offset=y;
10034     if ((state & UpdateConfigurationState) != 0)
10035       {
10036         CacheView
10037           *image_view;
10038 
10039         int
10040           x,
10041           y;
10042 
10043         /*
10044           Matte edit is relative to image configuration.
10045         */
10046         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10047           MagickTrue);
10048         XPutPixel(windows->image.ximage,x_offset,y_offset,
10049           windows->pixel_info->background_color.pixel);
10050         width=(unsigned int) (*image)->columns;
10051         height=(unsigned int) (*image)->rows;
10052         x=0;
10053         y=0;
10054         if (windows->image.crop_geometry != (char *) NULL)
10055           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10056             &height);
10057         x_offset=(int) (width*(windows->image.x+x_offset)/
10058           windows->image.ximage->width+x);
10059         y_offset=(int) (height*(windows->image.y+y_offset)/
10060           windows->image.ximage->height+y);
10061         if ((x_offset < 0) || (y_offset < 0))
10062           continue;
10063         if ((x_offset >= (int) (*image)->columns) ||
10064             (y_offset >= (int) (*image)->rows))
10065           continue;
10066         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10067           return(MagickFalse);
10068         if ((*image)->alpha_trait == UndefinedPixelTrait)
10069           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10070         image_view=AcquireAuthenticCacheView(*image,exception);
10071         switch (method)
10072         {
10073           case PointMethod:
10074           default:
10075           {
10076             /*
10077               Update matte information using point algorithm.
10078             */
10079             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10080               (ssize_t) y_offset,1,1,exception);
10081             if (q == (Quantum *) NULL)
10082               break;
10083             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10084             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10085             break;
10086           }
10087           case ReplaceMethod:
10088           {
10089             PixelInfo
10090               pixel,
10091               target;
10092 
10093             /*
10094               Update matte information using replace algorithm.
10095             */
10096             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10097               x_offset,(ssize_t) y_offset,&target,exception);
10098             for (y=0; y < (int) (*image)->rows; y++)
10099             {
10100               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10101                 (*image)->columns,1,exception);
10102               if (q == (Quantum *) NULL)
10103                 break;
10104               for (x=0; x < (int) (*image)->columns; x++)
10105               {
10106                 GetPixelInfoPixel(*image,q,&pixel);
10107                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10108                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10109                 q+=GetPixelChannels(*image);
10110               }
10111               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10112                 break;
10113             }
10114             break;
10115           }
10116           case FloodfillMethod:
10117           case FillToBorderMethod:
10118           {
10119             ChannelType
10120               channel_mask;
10121 
10122             DrawInfo
10123               *draw_info;
10124 
10125             PixelInfo
10126               target;
10127 
10128             /*
10129               Update matte information using floodfill algorithm.
10130             */
10131             (void) GetOneVirtualPixelInfo(*image,
10132               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10133               y_offset,&target,exception);
10134             if (method == FillToBorderMethod)
10135               {
10136                 target.red=(double) ScaleShortToQuantum(
10137                   border_color.red);
10138                 target.green=(double) ScaleShortToQuantum(
10139                   border_color.green);
10140                 target.blue=(double) ScaleShortToQuantum(
10141                   border_color.blue);
10142               }
10143             draw_info=CloneDrawInfo(resource_info->image_info,
10144               (DrawInfo *) NULL);
10145             draw_info->fill.alpha=(double) ClampToQuantum(
10146               StringToDouble(matte,(char **) NULL));
10147             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10148             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10149               x_offset,(ssize_t) y_offset,
10150               method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10151             (void) SetPixelChannelMask(*image,channel_mask);
10152             draw_info=DestroyDrawInfo(draw_info);
10153             break;
10154           }
10155           case ResetMethod:
10156           {
10157             /*
10158               Update matte information using reset algorithm.
10159             */
10160             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10161               return(MagickFalse);
10162             for (y=0; y < (int) (*image)->rows; y++)
10163             {
10164               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10165                 (*image)->columns,1,exception);
10166               if (q == (Quantum *) NULL)
10167                 break;
10168               for (x=0; x < (int) (*image)->columns; x++)
10169               {
10170                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10171                 q+=GetPixelChannels(*image);
10172               }
10173               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10174                 break;
10175             }
10176             if (StringToLong(matte) == (long) OpaqueAlpha)
10177               (*image)->alpha_trait=UndefinedPixelTrait;
10178             break;
10179           }
10180         }
10181         image_view=DestroyCacheView(image_view);
10182         state&=(~UpdateConfigurationState);
10183       }
10184   } while ((state & ExitState) == 0);
10185   (void) XSelectInput(display,windows->image.id,
10186     windows->image.attributes.event_mask);
10187   XSetCursorState(display,windows,MagickFalse);
10188   (void) XFreeCursor(display,cursor);
10189   return(MagickTrue);
10190 }
10191 
10192 /*
10193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10194 %                                                                             %
10195 %                                                                             %
10196 %                                                                             %
10197 +   X O p e n I m a g e                                                       %
10198 %                                                                             %
10199 %                                                                             %
10200 %                                                                             %
10201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10202 %
10203 %  XOpenImage() loads an image from a file.
10204 %
10205 %  The format of the XOpenImage method is:
10206 %
10207 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10208 %       XWindows *windows,const unsigned int command)
10209 %
10210 %  A description of each parameter follows:
10211 %
10212 %    o display: Specifies a connection to an X server; returned from
10213 %      XOpenDisplay.
10214 %
10215 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10216 %
10217 %    o windows: Specifies a pointer to a XWindows structure.
10218 %
10219 %    o command: A value other than zero indicates that the file is selected
10220 %      from the command line argument list.
10221 %
10222 */
XOpenImage(Display * display,XResourceInfo * resource_info,XWindows * windows,const MagickBooleanType command)10223 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10224   XWindows *windows,const MagickBooleanType command)
10225 {
10226   const MagickInfo
10227     *magick_info;
10228 
10229   ExceptionInfo
10230     *exception;
10231 
10232   Image
10233     *nexus;
10234 
10235   ImageInfo
10236     *image_info;
10237 
10238   static char
10239     filename[MagickPathExtent] = "\0";
10240 
10241   /*
10242     Request file name from user.
10243   */
10244   if (command == MagickFalse)
10245     XFileBrowserWidget(display,windows,"Open",filename);
10246   else
10247     {
10248       char
10249         **filelist,
10250         **files;
10251 
10252       int
10253         count,
10254         status;
10255 
10256       register int
10257         i,
10258         j;
10259 
10260       /*
10261         Select next image from the command line.
10262       */
10263       status=XGetCommand(display,windows->image.id,&files,&count);
10264       if (status == 0)
10265         {
10266           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10267           return((Image *) NULL);
10268         }
10269       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10270       if (filelist == (char **) NULL)
10271         {
10272           ThrowXWindowException(ResourceLimitError,
10273             "MemoryAllocationFailed","...");
10274           (void) XFreeStringList(files);
10275           return((Image *) NULL);
10276         }
10277       j=0;
10278       for (i=1; i < count; i++)
10279         if (*files[i] != '-')
10280           filelist[j++]=files[i];
10281       filelist[j]=(char *) NULL;
10282       XListBrowserWidget(display,windows,&windows->widget,
10283         (const char **) filelist,"Load","Select Image to Load:",filename);
10284       filelist=(char **) RelinquishMagickMemory(filelist);
10285       (void) XFreeStringList(files);
10286     }
10287   if (*filename == '\0')
10288     return((Image *) NULL);
10289   image_info=CloneImageInfo(resource_info->image_info);
10290   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10291     (void *) NULL);
10292   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10293   exception=AcquireExceptionInfo();
10294   (void) SetImageInfo(image_info,0,exception);
10295   if (LocaleCompare(image_info->magick,"X") == 0)
10296     {
10297       char
10298         seconds[MagickPathExtent];
10299 
10300       /*
10301         User may want to delay the X server screen grab.
10302       */
10303       (void) CopyMagickString(seconds,"0",MagickPathExtent);
10304       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10305         seconds);
10306       if (*seconds == '\0')
10307         return((Image *) NULL);
10308       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10309     }
10310   magick_info=GetMagickInfo(image_info->magick,exception);
10311   if ((magick_info != (const MagickInfo *) NULL) &&
10312       GetMagickRawSupport(magick_info) == MagickTrue)
10313     {
10314       char
10315         geometry[MagickPathExtent];
10316 
10317       /*
10318         Request image size from the user.
10319       */
10320       (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10321       if (image_info->size != (char *) NULL)
10322         (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10323       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10324         geometry);
10325       (void) CloneString(&image_info->size,geometry);
10326     }
10327   /*
10328     Load the image.
10329   */
10330   XSetCursorState(display,windows,MagickTrue);
10331   XCheckRefreshWindows(display,windows);
10332   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10333   nexus=ReadImage(image_info,exception);
10334   CatchException(exception);
10335   XSetCursorState(display,windows,MagickFalse);
10336   if (nexus != (Image *) NULL)
10337     XClientMessage(display,windows->image.id,windows->im_protocols,
10338       windows->im_next_image,CurrentTime);
10339   else
10340     {
10341       char
10342         *text,
10343         **textlist;
10344 
10345       /*
10346         Unknown image format.
10347       */
10348       text=FileToString(filename,~0UL,exception);
10349       if (text == (char *) NULL)
10350         return((Image *) NULL);
10351       textlist=StringToList(text);
10352       if (textlist != (char **) NULL)
10353         {
10354           char
10355             title[MagickPathExtent];
10356 
10357           register int
10358             i;
10359 
10360           (void) FormatLocaleString(title,MagickPathExtent,
10361             "Unknown format: %s",filename);
10362           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10363             (const char **) textlist);
10364           for (i=0; textlist[i] != (char *) NULL; i++)
10365             textlist[i]=DestroyString(textlist[i]);
10366           textlist=(char **) RelinquishMagickMemory(textlist);
10367         }
10368       text=DestroyString(text);
10369     }
10370   exception=DestroyExceptionInfo(exception);
10371   image_info=DestroyImageInfo(image_info);
10372   return(nexus);
10373 }
10374 
10375 /*
10376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10377 %                                                                             %
10378 %                                                                             %
10379 %                                                                             %
10380 +   X P a n I m a g e                                                         %
10381 %                                                                             %
10382 %                                                                             %
10383 %                                                                             %
10384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10385 %
10386 %  XPanImage() pans the image until the mouse button is released.
10387 %
10388 %  The format of the XPanImage method is:
10389 %
10390 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10391 %        ExceptionInfo *exception)
10392 %
10393 %  A description of each parameter follows:
10394 %
10395 %    o display: Specifies a connection to an X server;  returned from
10396 %      XOpenDisplay.
10397 %
10398 %    o windows: Specifies a pointer to a XWindows structure.
10399 %
10400 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10401 %      the entire image is refreshed.
10402 %
10403 %    o exception: return any errors or warnings in this structure.
10404 %
10405 */
XPanImage(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)10406 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10407   ExceptionInfo *exception)
10408 {
10409   char
10410     text[MagickPathExtent];
10411 
10412   Cursor
10413     cursor;
10414 
10415   double
10416     x_factor,
10417     y_factor;
10418 
10419   RectangleInfo
10420     pan_info;
10421 
10422   size_t
10423     state;
10424 
10425   /*
10426     Define cursor.
10427   */
10428   if ((windows->image.ximage->width > (int) windows->image.width) &&
10429       (windows->image.ximage->height > (int) windows->image.height))
10430     cursor=XCreateFontCursor(display,XC_fleur);
10431   else
10432     if (windows->image.ximage->width > (int) windows->image.width)
10433       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10434     else
10435       if (windows->image.ximage->height > (int) windows->image.height)
10436         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10437       else
10438         cursor=XCreateFontCursor(display,XC_arrow);
10439   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10440   /*
10441     Pan image as pointer moves until the mouse button is released.
10442   */
10443   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10444   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10445   pan_info.width=windows->pan.width*windows->image.width/
10446     windows->image.ximage->width;
10447   pan_info.height=windows->pan.height*windows->image.height/
10448     windows->image.ximage->height;
10449   pan_info.x=0;
10450   pan_info.y=0;
10451   state=UpdateConfigurationState;
10452   do
10453   {
10454     switch (event->type)
10455     {
10456       case ButtonPress:
10457       {
10458         /*
10459           User choose an initial pan location.
10460         */
10461         pan_info.x=(ssize_t) event->xbutton.x;
10462         pan_info.y=(ssize_t) event->xbutton.y;
10463         state|=UpdateConfigurationState;
10464         break;
10465       }
10466       case ButtonRelease:
10467       {
10468         /*
10469           User has finished panning the image.
10470         */
10471         pan_info.x=(ssize_t) event->xbutton.x;
10472         pan_info.y=(ssize_t) event->xbutton.y;
10473         state|=UpdateConfigurationState | ExitState;
10474         break;
10475       }
10476       case MotionNotify:
10477       {
10478         pan_info.x=(ssize_t) event->xmotion.x;
10479         pan_info.y=(ssize_t) event->xmotion.y;
10480         state|=UpdateConfigurationState;
10481       }
10482       default:
10483         break;
10484     }
10485     if ((state & UpdateConfigurationState) != 0)
10486       {
10487         /*
10488           Check boundary conditions.
10489         */
10490         if (pan_info.x < (ssize_t) (pan_info.width/2))
10491           pan_info.x=0;
10492         else
10493           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10494         if (pan_info.x < 0)
10495           pan_info.x=0;
10496         else
10497           if ((int) (pan_info.x+windows->image.width) >
10498               windows->image.ximage->width)
10499             pan_info.x=(ssize_t)
10500               (windows->image.ximage->width-windows->image.width);
10501         if (pan_info.y < (ssize_t) (pan_info.height/2))
10502           pan_info.y=0;
10503         else
10504           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10505         if (pan_info.y < 0)
10506           pan_info.y=0;
10507         else
10508           if ((int) (pan_info.y+windows->image.height) >
10509               windows->image.ximage->height)
10510             pan_info.y=(ssize_t)
10511               (windows->image.ximage->height-windows->image.height);
10512         if ((windows->image.x != (int) pan_info.x) ||
10513             (windows->image.y != (int) pan_info.y))
10514           {
10515             /*
10516               Display image pan offset.
10517             */
10518             windows->image.x=(int) pan_info.x;
10519             windows->image.y=(int) pan_info.y;
10520             (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10521               windows->image.width,windows->image.height,windows->image.x,
10522               windows->image.y);
10523             XInfoWidget(display,windows,text);
10524             /*
10525               Refresh Image window.
10526             */
10527             XDrawPanRectangle(display,windows);
10528             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10529           }
10530         state&=(~UpdateConfigurationState);
10531       }
10532     /*
10533       Wait for next event.
10534     */
10535     if ((state & ExitState) == 0)
10536       XScreenEvent(display,windows,event,exception);
10537   } while ((state & ExitState) == 0);
10538   /*
10539     Restore cursor.
10540   */
10541   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10542   (void) XFreeCursor(display,cursor);
10543   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10544 }
10545 
10546 /*
10547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10548 %                                                                             %
10549 %                                                                             %
10550 %                                                                             %
10551 +   X P a s t e I m a g e                                                     %
10552 %                                                                             %
10553 %                                                                             %
10554 %                                                                             %
10555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10556 %
10557 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10558 %  window image at a location the user chooses with the pointer.
10559 %
10560 %  The format of the XPasteImage method is:
10561 %
10562 %      MagickBooleanType XPasteImage(Display *display,
10563 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10564 %        ExceptionInfo *exception)
10565 %
10566 %  A description of each parameter follows:
10567 %
10568 %    o display: Specifies a connection to an X server;  returned from
10569 %      XOpenDisplay.
10570 %
10571 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10572 %
10573 %    o windows: Specifies a pointer to a XWindows structure.
10574 %
10575 %    o image: the image; returned from ReadImage.
10576 %
10577 %    o exception: return any errors or warnings in this structure.
10578 %
10579 */
XPasteImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10580 static MagickBooleanType XPasteImage(Display *display,
10581   XResourceInfo *resource_info,XWindows *windows,Image *image,
10582   ExceptionInfo *exception)
10583 {
10584   const char
10585     *const PasteMenu[] =
10586     {
10587       "Operator",
10588       "Help",
10589       "Dismiss",
10590       (char *) NULL
10591     };
10592 
10593   static const ModeType
10594     PasteCommands[] =
10595     {
10596       PasteOperatorsCommand,
10597       PasteHelpCommand,
10598       PasteDismissCommand
10599     };
10600 
10601   static CompositeOperator
10602     compose = CopyCompositeOp;
10603 
10604   char
10605     text[MagickPathExtent];
10606 
10607   Cursor
10608     cursor;
10609 
10610   Image
10611     *paste_image;
10612 
10613   int
10614     entry,
10615     id,
10616     x,
10617     y;
10618 
10619   double
10620     scale_factor;
10621 
10622   RectangleInfo
10623     highlight_info,
10624     paste_info;
10625 
10626   unsigned int
10627     height,
10628     width;
10629 
10630   size_t
10631     state;
10632 
10633   XEvent
10634     event;
10635 
10636   /*
10637     Copy image.
10638   */
10639   if (resource_info->copy_image == (Image *) NULL)
10640     return(MagickFalse);
10641   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10642   if (paste_image == (Image *) NULL)
10643     return(MagickFalse);
10644   /*
10645     Map Command widget.
10646   */
10647   (void) CloneString(&windows->command.name,"Paste");
10648   windows->command.data=1;
10649   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10650   (void) XMapRaised(display,windows->command.id);
10651   XClientMessage(display,windows->image.id,windows->im_protocols,
10652     windows->im_update_widget,CurrentTime);
10653   /*
10654     Track pointer until button 1 is pressed.
10655   */
10656   XSetCursorState(display,windows,MagickFalse);
10657   XQueryPosition(display,windows->image.id,&x,&y);
10658   (void) XSelectInput(display,windows->image.id,
10659     windows->image.attributes.event_mask | PointerMotionMask);
10660   paste_info.x=(ssize_t) windows->image.x+x;
10661   paste_info.y=(ssize_t) windows->image.y+y;
10662   paste_info.width=0;
10663   paste_info.height=0;
10664   cursor=XCreateFontCursor(display,XC_ul_angle);
10665   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10666   state=DefaultState;
10667   do
10668   {
10669     if (windows->info.mapped != MagickFalse )
10670       {
10671         /*
10672           Display pointer position.
10673         */
10674         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10675           (long) paste_info.x,(long) paste_info.y);
10676         XInfoWidget(display,windows,text);
10677       }
10678     highlight_info=paste_info;
10679     highlight_info.x=paste_info.x-windows->image.x;
10680     highlight_info.y=paste_info.y-windows->image.y;
10681     XHighlightRectangle(display,windows->image.id,
10682       windows->image.highlight_context,&highlight_info);
10683     /*
10684       Wait for next event.
10685     */
10686     XScreenEvent(display,windows,&event,exception);
10687     XHighlightRectangle(display,windows->image.id,
10688       windows->image.highlight_context,&highlight_info);
10689     if (event.xany.window == windows->command.id)
10690       {
10691         /*
10692           Select a command from the Command widget.
10693         */
10694         id=XCommandWidget(display,windows,PasteMenu,&event);
10695         if (id < 0)
10696           continue;
10697         switch (PasteCommands[id])
10698         {
10699           case PasteOperatorsCommand:
10700           {
10701             char
10702               command[MagickPathExtent],
10703               **operators;
10704 
10705             /*
10706               Select a command from the pop-up menu.
10707             */
10708             operators=GetCommandOptions(MagickComposeOptions);
10709             if (operators == (char **) NULL)
10710               break;
10711             entry=XMenuWidget(display,windows,PasteMenu[id],
10712               (const char **) operators,command);
10713             if (entry >= 0)
10714               compose=(CompositeOperator) ParseCommandOption(
10715                 MagickComposeOptions,MagickFalse,operators[entry]);
10716             operators=DestroyStringList(operators);
10717             break;
10718           }
10719           case PasteHelpCommand:
10720           {
10721             XTextViewHelp(display,resource_info,windows,MagickFalse,
10722               "Help Viewer - Image Composite",ImagePasteHelp);
10723             break;
10724           }
10725           case PasteDismissCommand:
10726           {
10727             /*
10728               Prematurely exit.
10729             */
10730             state|=EscapeState;
10731             state|=ExitState;
10732             break;
10733           }
10734           default:
10735             break;
10736         }
10737         continue;
10738       }
10739     switch (event.type)
10740     {
10741       case ButtonPress:
10742       {
10743         if (image->debug != MagickFalse )
10744           (void) LogMagickEvent(X11Event,GetMagickModule(),
10745             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10746             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10747         if (event.xbutton.button != Button1)
10748           break;
10749         if (event.xbutton.window != windows->image.id)
10750           break;
10751         /*
10752           Paste rectangle is relative to image configuration.
10753         */
10754         width=(unsigned int) image->columns;
10755         height=(unsigned int) image->rows;
10756         x=0;
10757         y=0;
10758         if (windows->image.crop_geometry != (char *) NULL)
10759           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10760             &width,&height);
10761         scale_factor=(double) windows->image.ximage->width/width;
10762         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10763         scale_factor=(double) windows->image.ximage->height/height;
10764         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10765         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10766         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10767         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10768         break;
10769       }
10770       case ButtonRelease:
10771       {
10772         if (image->debug != MagickFalse )
10773           (void) LogMagickEvent(X11Event,GetMagickModule(),
10774             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10775             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776         if (event.xbutton.button != Button1)
10777           break;
10778         if (event.xbutton.window != windows->image.id)
10779           break;
10780         if ((paste_info.width != 0) && (paste_info.height != 0))
10781           {
10782             /*
10783               User has selected the location of the paste image.
10784             */
10785             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10786             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10787             state|=ExitState;
10788           }
10789         break;
10790       }
10791       case Expose:
10792         break;
10793       case KeyPress:
10794       {
10795         char
10796           command[MagickPathExtent];
10797 
10798         KeySym
10799           key_symbol;
10800 
10801         int
10802           length;
10803 
10804         if (event.xkey.window != windows->image.id)
10805           break;
10806         /*
10807           Respond to a user key press.
10808         */
10809         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10810           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10811         *(command+length)='\0';
10812         if (image->debug != MagickFalse )
10813           (void) LogMagickEvent(X11Event,GetMagickModule(),
10814             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10815         switch ((int) key_symbol)
10816         {
10817           case XK_Escape:
10818           case XK_F20:
10819           {
10820             /*
10821               Prematurely exit.
10822             */
10823             paste_image=DestroyImage(paste_image);
10824             state|=EscapeState;
10825             state|=ExitState;
10826             break;
10827           }
10828           case XK_F1:
10829           case XK_Help:
10830           {
10831             (void) XSetFunction(display,windows->image.highlight_context,
10832               GXcopy);
10833             XTextViewHelp(display,resource_info,windows,MagickFalse,
10834               "Help Viewer - Image Composite",ImagePasteHelp);
10835             (void) XSetFunction(display,windows->image.highlight_context,
10836               GXinvert);
10837             break;
10838           }
10839           default:
10840           {
10841             (void) XBell(display,0);
10842             break;
10843           }
10844         }
10845         break;
10846       }
10847       case MotionNotify:
10848       {
10849         /*
10850           Map and unmap Info widget as text cursor crosses its boundaries.
10851         */
10852         x=event.xmotion.x;
10853         y=event.xmotion.y;
10854         if (windows->info.mapped != MagickFalse )
10855           {
10856             if ((x < (int) (windows->info.x+windows->info.width)) &&
10857                 (y < (int) (windows->info.y+windows->info.height)))
10858               (void) XWithdrawWindow(display,windows->info.id,
10859                 windows->info.screen);
10860           }
10861         else
10862           if ((x > (int) (windows->info.x+windows->info.width)) ||
10863               (y > (int) (windows->info.y+windows->info.height)))
10864             (void) XMapWindow(display,windows->info.id);
10865         paste_info.x=(ssize_t) windows->image.x+x;
10866         paste_info.y=(ssize_t) windows->image.y+y;
10867         break;
10868       }
10869       default:
10870       {
10871         if (image->debug != MagickFalse )
10872           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10873             event.type);
10874         break;
10875       }
10876     }
10877   } while ((state & ExitState) == 0);
10878   (void) XSelectInput(display,windows->image.id,
10879     windows->image.attributes.event_mask);
10880   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10881   XSetCursorState(display,windows,MagickFalse);
10882   (void) XFreeCursor(display,cursor);
10883   if ((state & EscapeState) != 0)
10884     return(MagickTrue);
10885   /*
10886     Image pasting is relative to image configuration.
10887   */
10888   XSetCursorState(display,windows,MagickTrue);
10889   XCheckRefreshWindows(display,windows);
10890   width=(unsigned int) image->columns;
10891   height=(unsigned int) image->rows;
10892   x=0;
10893   y=0;
10894   if (windows->image.crop_geometry != (char *) NULL)
10895     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10896   scale_factor=(double) width/windows->image.ximage->width;
10897   paste_info.x+=x;
10898   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10899   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10900   scale_factor=(double) height/windows->image.ximage->height;
10901   paste_info.y+=y;
10902   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10903   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10904   /*
10905     Paste image with X Image window.
10906   */
10907   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10908     paste_info.y,exception);
10909   paste_image=DestroyImage(paste_image);
10910   XSetCursorState(display,windows,MagickFalse);
10911   /*
10912     Update image colormap.
10913   */
10914   XConfigureImageColormap(display,resource_info,windows,image,exception);
10915   (void) XConfigureImage(display,resource_info,windows,image,exception);
10916   return(MagickTrue);
10917 }
10918 
10919 /*
10920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10921 %                                                                             %
10922 %                                                                             %
10923 %                                                                             %
10924 +   X P r i n t I m a g e                                                     %
10925 %                                                                             %
10926 %                                                                             %
10927 %                                                                             %
10928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10929 %
10930 %  XPrintImage() prints an image to a Postscript printer.
10931 %
10932 %  The format of the XPrintImage method is:
10933 %
10934 %      MagickBooleanType XPrintImage(Display *display,
10935 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10936 %        ExceptionInfo *exception)
10937 %
10938 %  A description of each parameter follows:
10939 %
10940 %    o display: Specifies a connection to an X server; returned from
10941 %      XOpenDisplay.
10942 %
10943 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10944 %
10945 %    o windows: Specifies a pointer to a XWindows structure.
10946 %
10947 %    o image: the image.
10948 %
10949 %    o exception: return any errors or warnings in this structure.
10950 %
10951 */
XPrintImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)10952 static MagickBooleanType XPrintImage(Display *display,
10953   XResourceInfo *resource_info,XWindows *windows,Image *image,
10954   ExceptionInfo *exception)
10955 {
10956   char
10957     filename[MagickPathExtent],
10958     geometry[MagickPathExtent];
10959 
10960   const char
10961     *const PageSizes[] =
10962     {
10963       "Letter",
10964       "Tabloid",
10965       "Ledger",
10966       "Legal",
10967       "Statement",
10968       "Executive",
10969       "A3",
10970       "A4",
10971       "A5",
10972       "B4",
10973       "B5",
10974       "Folio",
10975       "Quarto",
10976       "10x14",
10977       (char *) NULL
10978     };
10979 
10980   Image
10981     *print_image;
10982 
10983   ImageInfo
10984     *image_info;
10985 
10986   MagickStatusType
10987     status;
10988 
10989   /*
10990     Request Postscript page geometry from user.
10991   */
10992   image_info=CloneImageInfo(resource_info->image_info);
10993   (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
10994   if (image_info->page != (char *) NULL)
10995     (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
10996   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10997     "Select Postscript Page Geometry:",geometry);
10998   if (*geometry == '\0')
10999     return(MagickTrue);
11000   image_info->page=GetPageGeometry(geometry);
11001   /*
11002     Apply image transforms.
11003   */
11004   XSetCursorState(display,windows,MagickTrue);
11005   XCheckRefreshWindows(display,windows);
11006   print_image=CloneImage(image,0,0,MagickTrue,exception);
11007   if (print_image == (Image *) NULL)
11008     return(MagickFalse);
11009   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11010     windows->image.ximage->width,windows->image.ximage->height);
11011   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11012     exception);
11013   /*
11014     Print image.
11015   */
11016   (void) AcquireUniqueFilename(filename);
11017   (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11018     filename);
11019   status=WriteImage(image_info,print_image,exception);
11020   (void) RelinquishUniqueFileResource(filename);
11021   print_image=DestroyImage(print_image);
11022   image_info=DestroyImageInfo(image_info);
11023   XSetCursorState(display,windows,MagickFalse);
11024   return(status != 0 ? MagickTrue : MagickFalse);
11025 }
11026 
11027 /*
11028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11029 %                                                                             %
11030 %                                                                             %
11031 %                                                                             %
11032 +   X R O I I m a g e                                                         %
11033 %                                                                             %
11034 %                                                                             %
11035 %                                                                             %
11036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037 %
11038 %  XROIImage() applies an image processing technique to a region of interest.
11039 %
11040 %  The format of the XROIImage method is:
11041 %
11042 %      MagickBooleanType XROIImage(Display *display,
11043 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11044 %        ExceptionInfo *exception)
11045 %
11046 %  A description of each parameter follows:
11047 %
11048 %    o display: Specifies a connection to an X server; returned from
11049 %      XOpenDisplay.
11050 %
11051 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11052 %
11053 %    o windows: Specifies a pointer to a XWindows structure.
11054 %
11055 %    o image: the image; returned from ReadImage.
11056 %
11057 %    o exception: return any errors or warnings in this structure.
11058 %
11059 */
XROIImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image ** image,ExceptionInfo * exception)11060 static MagickBooleanType XROIImage(Display *display,
11061   XResourceInfo *resource_info,XWindows *windows,Image **image,
11062   ExceptionInfo *exception)
11063 {
11064 #define ApplyMenus  7
11065 
11066   const char
11067     *const ROIMenu[] =
11068     {
11069       "Help",
11070       "Dismiss",
11071       (char *) NULL
11072     },
11073     *const ApplyMenu[] =
11074     {
11075       "File",
11076       "Edit",
11077       "Transform",
11078       "Enhance",
11079       "Effects",
11080       "F/X",
11081       "Miscellany",
11082       "Help",
11083       "Dismiss",
11084       (char *) NULL
11085     },
11086     *const FileMenu[] =
11087     {
11088       "Save...",
11089       "Print...",
11090       (char *) NULL
11091     },
11092     *const EditMenu[] =
11093     {
11094       "Undo",
11095       "Redo",
11096       (char *) NULL
11097     },
11098     *const TransformMenu[] =
11099     {
11100       "Flop",
11101       "Flip",
11102       "Rotate Right",
11103       "Rotate Left",
11104       (char *) NULL
11105     },
11106     *const EnhanceMenu[] =
11107     {
11108       "Hue...",
11109       "Saturation...",
11110       "Brightness...",
11111       "Gamma...",
11112       "Spiff",
11113       "Dull",
11114       "Contrast Stretch...",
11115       "Sigmoidal Contrast...",
11116       "Normalize",
11117       "Equalize",
11118       "Negate",
11119       "Grayscale",
11120       "Map...",
11121       "Quantize...",
11122       (char *) NULL
11123     },
11124     *const EffectsMenu[] =
11125     {
11126       "Despeckle",
11127       "Emboss",
11128       "Reduce Noise",
11129       "Add Noise",
11130       "Sharpen...",
11131       "Blur...",
11132       "Threshold...",
11133       "Edge Detect...",
11134       "Spread...",
11135       "Shade...",
11136       "Raise...",
11137       "Segment...",
11138       (char *) NULL
11139     },
11140     *const FXMenu[] =
11141     {
11142       "Solarize...",
11143       "Sepia Tone...",
11144       "Swirl...",
11145       "Implode...",
11146       "Vignette...",
11147       "Wave...",
11148       "Oil Paint...",
11149       "Charcoal Draw...",
11150       (char *) NULL
11151     },
11152     *const MiscellanyMenu[] =
11153     {
11154       "Image Info",
11155       "Zoom Image",
11156       "Show Preview...",
11157       "Show Histogram",
11158       "Show Matte",
11159       (char *) NULL
11160     };
11161 
11162   const char
11163     *const *Menus[ApplyMenus] =
11164     {
11165       FileMenu,
11166       EditMenu,
11167       TransformMenu,
11168       EnhanceMenu,
11169       EffectsMenu,
11170       FXMenu,
11171       MiscellanyMenu
11172     };
11173 
11174   static const CommandType
11175     ApplyCommands[] =
11176     {
11177       NullCommand,
11178       NullCommand,
11179       NullCommand,
11180       NullCommand,
11181       NullCommand,
11182       NullCommand,
11183       NullCommand,
11184       HelpCommand,
11185       QuitCommand
11186     },
11187     FileCommands[] =
11188     {
11189       SaveCommand,
11190       PrintCommand
11191     },
11192     EditCommands[] =
11193     {
11194       UndoCommand,
11195       RedoCommand
11196     },
11197     TransformCommands[] =
11198     {
11199       FlopCommand,
11200       FlipCommand,
11201       RotateRightCommand,
11202       RotateLeftCommand
11203     },
11204     EnhanceCommands[] =
11205     {
11206       HueCommand,
11207       SaturationCommand,
11208       BrightnessCommand,
11209       GammaCommand,
11210       SpiffCommand,
11211       DullCommand,
11212       ContrastStretchCommand,
11213       SigmoidalContrastCommand,
11214       NormalizeCommand,
11215       EqualizeCommand,
11216       NegateCommand,
11217       GrayscaleCommand,
11218       MapCommand,
11219       QuantizeCommand
11220     },
11221     EffectsCommands[] =
11222     {
11223       DespeckleCommand,
11224       EmbossCommand,
11225       ReduceNoiseCommand,
11226       AddNoiseCommand,
11227       SharpenCommand,
11228       BlurCommand,
11229       EdgeDetectCommand,
11230       SpreadCommand,
11231       ShadeCommand,
11232       RaiseCommand,
11233       SegmentCommand
11234     },
11235     FXCommands[] =
11236     {
11237       SolarizeCommand,
11238       SepiaToneCommand,
11239       SwirlCommand,
11240       ImplodeCommand,
11241       VignetteCommand,
11242       WaveCommand,
11243       OilPaintCommand,
11244       CharcoalDrawCommand
11245     },
11246     MiscellanyCommands[] =
11247     {
11248       InfoCommand,
11249       ZoomCommand,
11250       ShowPreviewCommand,
11251       ShowHistogramCommand,
11252       ShowMatteCommand
11253     },
11254     ROICommands[] =
11255     {
11256       ROIHelpCommand,
11257       ROIDismissCommand
11258     };
11259 
11260   static const CommandType
11261     *Commands[ApplyMenus] =
11262     {
11263       FileCommands,
11264       EditCommands,
11265       TransformCommands,
11266       EnhanceCommands,
11267       EffectsCommands,
11268       FXCommands,
11269       MiscellanyCommands
11270     };
11271 
11272   char
11273     command[MagickPathExtent],
11274     text[MagickPathExtent];
11275 
11276   CommandType
11277     command_type;
11278 
11279   Cursor
11280     cursor;
11281 
11282   Image
11283     *roi_image;
11284 
11285   int
11286     entry,
11287     id,
11288     x,
11289     y;
11290 
11291   double
11292     scale_factor;
11293 
11294   MagickProgressMonitor
11295     progress_monitor;
11296 
11297   RectangleInfo
11298     crop_info,
11299     highlight_info,
11300     roi_info;
11301 
11302   unsigned int
11303     height,
11304     width;
11305 
11306   size_t
11307     state;
11308 
11309   XEvent
11310     event;
11311 
11312   /*
11313     Map Command widget.
11314   */
11315   (void) CloneString(&windows->command.name,"ROI");
11316   windows->command.data=0;
11317   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11318   (void) XMapRaised(display,windows->command.id);
11319   XClientMessage(display,windows->image.id,windows->im_protocols,
11320     windows->im_update_widget,CurrentTime);
11321   /*
11322     Track pointer until button 1 is pressed.
11323   */
11324   XQueryPosition(display,windows->image.id,&x,&y);
11325   (void) XSelectInput(display,windows->image.id,
11326     windows->image.attributes.event_mask | PointerMotionMask);
11327   roi_info.x=(ssize_t) windows->image.x+x;
11328   roi_info.y=(ssize_t) windows->image.y+y;
11329   roi_info.width=0;
11330   roi_info.height=0;
11331   cursor=XCreateFontCursor(display,XC_fleur);
11332   state=DefaultState;
11333   do
11334   {
11335     if (windows->info.mapped != MagickFalse )
11336       {
11337         /*
11338           Display pointer position.
11339         */
11340         (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11341           (long) roi_info.x,(long) roi_info.y);
11342         XInfoWidget(display,windows,text);
11343       }
11344     /*
11345       Wait for next event.
11346     */
11347     XScreenEvent(display,windows,&event,exception);
11348     if (event.xany.window == windows->command.id)
11349       {
11350         /*
11351           Select a command from the Command widget.
11352         */
11353         id=XCommandWidget(display,windows,ROIMenu,&event);
11354         if (id < 0)
11355           continue;
11356         switch (ROICommands[id])
11357         {
11358           case ROIHelpCommand:
11359           {
11360             XTextViewHelp(display,resource_info,windows,MagickFalse,
11361               "Help Viewer - Region of Interest",ImageROIHelp);
11362             break;
11363           }
11364           case ROIDismissCommand:
11365           {
11366             /*
11367               Prematurely exit.
11368             */
11369             state|=EscapeState;
11370             state|=ExitState;
11371             break;
11372           }
11373           default:
11374             break;
11375         }
11376         continue;
11377       }
11378     switch (event.type)
11379     {
11380       case ButtonPress:
11381       {
11382         if (event.xbutton.button != Button1)
11383           break;
11384         if (event.xbutton.window != windows->image.id)
11385           break;
11386         /*
11387           Note first corner of region of interest rectangle-- exit loop.
11388         */
11389         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11390         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11391         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11392         state|=ExitState;
11393         break;
11394       }
11395       case ButtonRelease:
11396         break;
11397       case Expose:
11398         break;
11399       case KeyPress:
11400       {
11401         KeySym
11402           key_symbol;
11403 
11404         if (event.xkey.window != windows->image.id)
11405           break;
11406         /*
11407           Respond to a user key press.
11408         */
11409         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11410           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11411         switch ((int) key_symbol)
11412         {
11413           case XK_Escape:
11414           case XK_F20:
11415           {
11416             /*
11417               Prematurely exit.
11418             */
11419             state|=EscapeState;
11420             state|=ExitState;
11421             break;
11422           }
11423           case XK_F1:
11424           case XK_Help:
11425           {
11426             XTextViewHelp(display,resource_info,windows,MagickFalse,
11427               "Help Viewer - Region of Interest",ImageROIHelp);
11428             break;
11429           }
11430           default:
11431           {
11432             (void) XBell(display,0);
11433             break;
11434           }
11435         }
11436         break;
11437       }
11438       case MotionNotify:
11439       {
11440         /*
11441           Map and unmap Info widget as text cursor crosses its boundaries.
11442         */
11443         x=event.xmotion.x;
11444         y=event.xmotion.y;
11445         if (windows->info.mapped != MagickFalse )
11446           {
11447             if ((x < (int) (windows->info.x+windows->info.width)) &&
11448                 (y < (int) (windows->info.y+windows->info.height)))
11449               (void) XWithdrawWindow(display,windows->info.id,
11450                 windows->info.screen);
11451           }
11452         else
11453           if ((x > (int) (windows->info.x+windows->info.width)) ||
11454               (y > (int) (windows->info.y+windows->info.height)))
11455             (void) XMapWindow(display,windows->info.id);
11456         roi_info.x=(ssize_t) windows->image.x+x;
11457         roi_info.y=(ssize_t) windows->image.y+y;
11458         break;
11459       }
11460       default:
11461         break;
11462     }
11463   } while ((state & ExitState) == 0);
11464   (void) XSelectInput(display,windows->image.id,
11465     windows->image.attributes.event_mask);
11466   if ((state & EscapeState) != 0)
11467     {
11468       /*
11469         User want to exit without region of interest.
11470       */
11471       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11472       (void) XFreeCursor(display,cursor);
11473       return(MagickTrue);
11474     }
11475   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11476   do
11477   {
11478     /*
11479       Size rectangle as pointer moves until the mouse button is released.
11480     */
11481     x=(int) roi_info.x;
11482     y=(int) roi_info.y;
11483     roi_info.width=0;
11484     roi_info.height=0;
11485     state=DefaultState;
11486     do
11487     {
11488       highlight_info=roi_info;
11489       highlight_info.x=roi_info.x-windows->image.x;
11490       highlight_info.y=roi_info.y-windows->image.y;
11491       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11492         {
11493           /*
11494             Display info and draw region of interest rectangle.
11495           */
11496           if (windows->info.mapped == MagickFalse)
11497             (void) XMapWindow(display,windows->info.id);
11498           (void) FormatLocaleString(text,MagickPathExtent,
11499             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11500             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11501           XInfoWidget(display,windows,text);
11502           XHighlightRectangle(display,windows->image.id,
11503             windows->image.highlight_context,&highlight_info);
11504         }
11505       else
11506         if (windows->info.mapped != MagickFalse )
11507           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11508       /*
11509         Wait for next event.
11510       */
11511       XScreenEvent(display,windows,&event,exception);
11512       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11513         XHighlightRectangle(display,windows->image.id,
11514           windows->image.highlight_context,&highlight_info);
11515       switch (event.type)
11516       {
11517         case ButtonPress:
11518         {
11519           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11520           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11521           break;
11522         }
11523         case ButtonRelease:
11524         {
11525           /*
11526             User has committed to region of interest rectangle.
11527           */
11528           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530           XSetCursorState(display,windows,MagickFalse);
11531           state|=ExitState;
11532           if (LocaleCompare(windows->command.name,"Apply") == 0)
11533             break;
11534           (void) CloneString(&windows->command.name,"Apply");
11535           windows->command.data=ApplyMenus;
11536           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11537           break;
11538         }
11539         case Expose:
11540           break;
11541         case MotionNotify:
11542         {
11543           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11544           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11545         }
11546         default:
11547           break;
11548       }
11549       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11550           ((state & ExitState) != 0))
11551         {
11552           /*
11553             Check boundary conditions.
11554           */
11555           if (roi_info.x < 0)
11556             roi_info.x=0;
11557           else
11558             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11559               roi_info.x=(ssize_t) windows->image.ximage->width;
11560           if ((int) roi_info.x < x)
11561             roi_info.width=(unsigned int) (x-roi_info.x);
11562           else
11563             {
11564               roi_info.width=(unsigned int) (roi_info.x-x);
11565               roi_info.x=(ssize_t) x;
11566             }
11567           if (roi_info.y < 0)
11568             roi_info.y=0;
11569           else
11570             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11571               roi_info.y=(ssize_t) windows->image.ximage->height;
11572           if ((int) roi_info.y < y)
11573             roi_info.height=(unsigned int) (y-roi_info.y);
11574           else
11575             {
11576               roi_info.height=(unsigned int) (roi_info.y-y);
11577               roi_info.y=(ssize_t) y;
11578             }
11579         }
11580     } while ((state & ExitState) == 0);
11581     /*
11582       Wait for user to grab a corner of the rectangle or press return.
11583     */
11584     state=DefaultState;
11585     command_type=NullCommand;
11586     crop_info.x=0;
11587     crop_info.y=0;
11588     (void) XMapWindow(display,windows->info.id);
11589     do
11590     {
11591       if (windows->info.mapped != MagickFalse )
11592         {
11593           /*
11594             Display pointer position.
11595           */
11596           (void) FormatLocaleString(text,MagickPathExtent,
11597             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11598             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11599           XInfoWidget(display,windows,text);
11600         }
11601       highlight_info=roi_info;
11602       highlight_info.x=roi_info.x-windows->image.x;
11603       highlight_info.y=roi_info.y-windows->image.y;
11604       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11605         {
11606           state|=EscapeState;
11607           state|=ExitState;
11608           break;
11609         }
11610       if ((state & UpdateRegionState) != 0)
11611         {
11612           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11613           switch (command_type)
11614           {
11615             case UndoCommand:
11616             case RedoCommand:
11617             {
11618               (void) XMagickCommand(display,resource_info,windows,command_type,
11619                 image,exception);
11620               break;
11621             }
11622             default:
11623             {
11624               /*
11625                 Region of interest is relative to image configuration.
11626               */
11627               progress_monitor=SetImageProgressMonitor(*image,
11628                 (MagickProgressMonitor) NULL,(*image)->client_data);
11629               crop_info=roi_info;
11630               width=(unsigned int) (*image)->columns;
11631               height=(unsigned int) (*image)->rows;
11632               x=0;
11633               y=0;
11634               if (windows->image.crop_geometry != (char *) NULL)
11635                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11636                   &width,&height);
11637               scale_factor=(double) width/windows->image.ximage->width;
11638               crop_info.x+=x;
11639               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11640               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11641               scale_factor=(double)
11642                 height/windows->image.ximage->height;
11643               crop_info.y+=y;
11644               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11645               crop_info.height=(unsigned int)
11646                 (scale_factor*crop_info.height+0.5);
11647               roi_image=CropImage(*image,&crop_info,exception);
11648               (void) SetImageProgressMonitor(*image,progress_monitor,
11649                 (*image)->client_data);
11650               if (roi_image == (Image *) NULL)
11651                 continue;
11652               /*
11653                 Apply image processing technique to the region of interest.
11654               */
11655               windows->image.orphan=MagickTrue;
11656               (void) XMagickCommand(display,resource_info,windows,command_type,
11657                 &roi_image,exception);
11658               progress_monitor=SetImageProgressMonitor(*image,
11659                 (MagickProgressMonitor) NULL,(*image)->client_data);
11660               (void) XMagickCommand(display,resource_info,windows,
11661                 SaveToUndoBufferCommand,image,exception);
11662               windows->image.orphan=MagickFalse;
11663               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11664                 MagickTrue,crop_info.x,crop_info.y,exception);
11665               roi_image=DestroyImage(roi_image);
11666               (void) SetImageProgressMonitor(*image,progress_monitor,
11667                 (*image)->client_data);
11668               break;
11669             }
11670           }
11671           if (command_type != InfoCommand)
11672             {
11673               XConfigureImageColormap(display,resource_info,windows,*image,
11674                 exception);
11675               (void) XConfigureImage(display,resource_info,windows,*image,
11676                 exception);
11677             }
11678           XCheckRefreshWindows(display,windows);
11679           XInfoWidget(display,windows,text);
11680           (void) XSetFunction(display,windows->image.highlight_context,
11681             GXinvert);
11682           state&=(~UpdateRegionState);
11683         }
11684       XHighlightRectangle(display,windows->image.id,
11685         windows->image.highlight_context,&highlight_info);
11686       XScreenEvent(display,windows,&event,exception);
11687       if (event.xany.window == windows->command.id)
11688         {
11689           /*
11690             Select a command from the Command widget.
11691           */
11692           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11693           command_type=NullCommand;
11694           id=XCommandWidget(display,windows,ApplyMenu,&event);
11695           if (id >= 0)
11696             {
11697               (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11698               command_type=ApplyCommands[id];
11699               if (id < ApplyMenus)
11700                 {
11701                   /*
11702                     Select a command from a pop-up menu.
11703                   */
11704                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11705                     (const char **) Menus[id],command);
11706                   if (entry >= 0)
11707                     {
11708                       (void) CopyMagickString(command,Menus[id][entry],
11709                         MagickPathExtent);
11710                       command_type=Commands[id][entry];
11711                     }
11712                 }
11713             }
11714           (void) XSetFunction(display,windows->image.highlight_context,
11715             GXinvert);
11716           XHighlightRectangle(display,windows->image.id,
11717             windows->image.highlight_context,&highlight_info);
11718           if (command_type == HelpCommand)
11719             {
11720               (void) XSetFunction(display,windows->image.highlight_context,
11721                 GXcopy);
11722               XTextViewHelp(display,resource_info,windows,MagickFalse,
11723                 "Help Viewer - Region of Interest",ImageROIHelp);
11724               (void) XSetFunction(display,windows->image.highlight_context,
11725                 GXinvert);
11726               continue;
11727             }
11728           if (command_type == QuitCommand)
11729             {
11730               /*
11731                 exit.
11732               */
11733               state|=EscapeState;
11734               state|=ExitState;
11735               continue;
11736             }
11737           if (command_type != NullCommand)
11738             state|=UpdateRegionState;
11739           continue;
11740         }
11741       XHighlightRectangle(display,windows->image.id,
11742         windows->image.highlight_context,&highlight_info);
11743       switch (event.type)
11744       {
11745         case ButtonPress:
11746         {
11747           x=windows->image.x;
11748           y=windows->image.y;
11749           if (event.xbutton.button != Button1)
11750             break;
11751           if (event.xbutton.window != windows->image.id)
11752             break;
11753           x=windows->image.x+event.xbutton.x;
11754           y=windows->image.y+event.xbutton.y;
11755           if ((x < (int) (roi_info.x+RoiDelta)) &&
11756               (x > (int) (roi_info.x-RoiDelta)) &&
11757               (y < (int) (roi_info.y+RoiDelta)) &&
11758               (y > (int) (roi_info.y-RoiDelta)))
11759             {
11760               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11761               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11762               state|=UpdateConfigurationState;
11763               break;
11764             }
11765           if ((x < (int) (roi_info.x+RoiDelta)) &&
11766               (x > (int) (roi_info.x-RoiDelta)) &&
11767               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11768               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11769             {
11770               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11771               state|=UpdateConfigurationState;
11772               break;
11773             }
11774           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11775               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11776               (y < (int) (roi_info.y+RoiDelta)) &&
11777               (y > (int) (roi_info.y-RoiDelta)))
11778             {
11779               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11780               state|=UpdateConfigurationState;
11781               break;
11782             }
11783           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11784               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11785               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11786               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11787             {
11788               state|=UpdateConfigurationState;
11789               break;
11790             }
11791         }
11792         case ButtonRelease:
11793         {
11794           if (event.xbutton.window == windows->pan.id)
11795             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11796                 (highlight_info.y != crop_info.y-windows->image.y))
11797               XHighlightRectangle(display,windows->image.id,
11798                 windows->image.highlight_context,&highlight_info);
11799           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11800             event.xbutton.time);
11801           break;
11802         }
11803         case Expose:
11804         {
11805           if (event.xexpose.window == windows->image.id)
11806             if (event.xexpose.count == 0)
11807               {
11808                 event.xexpose.x=(int) highlight_info.x;
11809                 event.xexpose.y=(int) highlight_info.y;
11810                 event.xexpose.width=(int) highlight_info.width;
11811                 event.xexpose.height=(int) highlight_info.height;
11812                 XRefreshWindow(display,&windows->image,&event);
11813               }
11814           if (event.xexpose.window == windows->info.id)
11815             if (event.xexpose.count == 0)
11816               XInfoWidget(display,windows,text);
11817           break;
11818         }
11819         case KeyPress:
11820         {
11821           KeySym
11822             key_symbol;
11823 
11824           if (event.xkey.window != windows->image.id)
11825             break;
11826           /*
11827             Respond to a user key press.
11828           */
11829           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11830             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11831           switch ((int) key_symbol)
11832           {
11833             case XK_Shift_L:
11834             case XK_Shift_R:
11835               break;
11836             case XK_Escape:
11837             case XK_F20:
11838               state|=EscapeState;
11839             case XK_Return:
11840             {
11841               state|=ExitState;
11842               break;
11843             }
11844             case XK_Home:
11845             case XK_KP_Home:
11846             {
11847               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11848               roi_info.y=(ssize_t) (windows->image.height/2L-
11849                 roi_info.height/2L);
11850               break;
11851             }
11852             case XK_Left:
11853             case XK_KP_Left:
11854             {
11855               roi_info.x--;
11856               break;
11857             }
11858             case XK_Up:
11859             case XK_KP_Up:
11860             case XK_Next:
11861             {
11862               roi_info.y--;
11863               break;
11864             }
11865             case XK_Right:
11866             case XK_KP_Right:
11867             {
11868               roi_info.x++;
11869               break;
11870             }
11871             case XK_Prior:
11872             case XK_Down:
11873             case XK_KP_Down:
11874             {
11875               roi_info.y++;
11876               break;
11877             }
11878             case XK_F1:
11879             case XK_Help:
11880             {
11881               (void) XSetFunction(display,windows->image.highlight_context,
11882                 GXcopy);
11883               XTextViewHelp(display,resource_info,windows,MagickFalse,
11884                 "Help Viewer - Region of Interest",ImageROIHelp);
11885               (void) XSetFunction(display,windows->image.highlight_context,
11886                 GXinvert);
11887               break;
11888             }
11889             default:
11890             {
11891               command_type=XImageWindowCommand(display,resource_info,windows,
11892                 event.xkey.state,key_symbol,image,exception);
11893               if (command_type != NullCommand)
11894                 state|=UpdateRegionState;
11895               break;
11896             }
11897           }
11898           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11899             event.xkey.time);
11900           break;
11901         }
11902         case KeyRelease:
11903           break;
11904         case MotionNotify:
11905         {
11906           if (event.xbutton.window != windows->image.id)
11907             break;
11908           /*
11909             Map and unmap Info widget as text cursor crosses its boundaries.
11910           */
11911           x=event.xmotion.x;
11912           y=event.xmotion.y;
11913           if (windows->info.mapped != MagickFalse )
11914             {
11915               if ((x < (int) (windows->info.x+windows->info.width)) &&
11916                   (y < (int) (windows->info.y+windows->info.height)))
11917                 (void) XWithdrawWindow(display,windows->info.id,
11918                   windows->info.screen);
11919             }
11920           else
11921             if ((x > (int) (windows->info.x+windows->info.width)) ||
11922                 (y > (int) (windows->info.y+windows->info.height)))
11923               (void) XMapWindow(display,windows->info.id);
11924           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11925           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11926           break;
11927         }
11928         case SelectionRequest:
11929         {
11930           XSelectionEvent
11931             notify;
11932 
11933           XSelectionRequestEvent
11934             *request;
11935 
11936           /*
11937             Set primary selection.
11938           */
11939           (void) FormatLocaleString(text,MagickPathExtent,
11940             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11941             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11942           request=(&(event.xselectionrequest));
11943           (void) XChangeProperty(request->display,request->requestor,
11944             request->property,request->target,8,PropModeReplace,
11945             (unsigned char *) text,(int) strlen(text));
11946           notify.type=SelectionNotify;
11947           notify.display=request->display;
11948           notify.requestor=request->requestor;
11949           notify.selection=request->selection;
11950           notify.target=request->target;
11951           notify.time=request->time;
11952           if (request->property == None)
11953             notify.property=request->target;
11954           else
11955             notify.property=request->property;
11956           (void) XSendEvent(request->display,request->requestor,False,0,
11957             (XEvent *) &notify);
11958         }
11959         default:
11960           break;
11961       }
11962       if ((state & UpdateConfigurationState) != 0)
11963         {
11964           (void) XPutBackEvent(display,&event);
11965           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11966           break;
11967         }
11968     } while ((state & ExitState) == 0);
11969   } while ((state & ExitState) == 0);
11970   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11971   XSetCursorState(display,windows,MagickFalse);
11972   if ((state & EscapeState) != 0)
11973     return(MagickTrue);
11974   return(MagickTrue);
11975 }
11976 
11977 /*
11978 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11979 %                                                                             %
11980 %                                                                             %
11981 %                                                                             %
11982 +   X R o t a t e I m a g e                                                   %
11983 %                                                                             %
11984 %                                                                             %
11985 %                                                                             %
11986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11987 %
11988 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11989 %  rotation angle is computed from the slope of a line drawn by the user.
11990 %
11991 %  The format of the XRotateImage method is:
11992 %
11993 %      MagickBooleanType XRotateImage(Display *display,
11994 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11995 %        Image **image,ExceptionInfo *exception)
11996 %
11997 %  A description of each parameter follows:
11998 %
11999 %    o display: Specifies a connection to an X server; returned from
12000 %      XOpenDisplay.
12001 %
12002 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12003 %
12004 %    o windows: Specifies a pointer to a XWindows structure.
12005 %
12006 %    o degrees: Specifies the number of degrees to rotate the image.
12007 %
12008 %    o image: the image.
12009 %
12010 %    o exception: return any errors or warnings in this structure.
12011 %
12012 */
XRotateImage(Display * display,XResourceInfo * resource_info,XWindows * windows,double degrees,Image ** image,ExceptionInfo * exception)12013 static MagickBooleanType XRotateImage(Display *display,
12014   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12015   ExceptionInfo *exception)
12016 {
12017   const char
12018     *const RotateMenu[] =
12019     {
12020       "Pixel Color",
12021       "Direction",
12022       "Help",
12023       "Dismiss",
12024       (char *) NULL
12025     };
12026 
12027   static ModeType
12028     direction = HorizontalRotateCommand;
12029 
12030   static const ModeType
12031     DirectionCommands[] =
12032     {
12033       HorizontalRotateCommand,
12034       VerticalRotateCommand
12035     },
12036     RotateCommands[] =
12037     {
12038       RotateColorCommand,
12039       RotateDirectionCommand,
12040       RotateHelpCommand,
12041       RotateDismissCommand
12042     };
12043 
12044   static unsigned int
12045     pen_id = 0;
12046 
12047   char
12048     command[MagickPathExtent],
12049     text[MagickPathExtent];
12050 
12051   Image
12052     *rotate_image;
12053 
12054   int
12055     id,
12056     x,
12057     y;
12058 
12059   double
12060     normalized_degrees;
12061 
12062   register int
12063     i;
12064 
12065   unsigned int
12066     height,
12067     rotations,
12068     width;
12069 
12070   if (degrees == 0.0)
12071     {
12072       unsigned int
12073         distance;
12074 
12075       size_t
12076         state;
12077 
12078       XEvent
12079         event;
12080 
12081       XSegment
12082         rotate_info;
12083 
12084       /*
12085         Map Command widget.
12086       */
12087       (void) CloneString(&windows->command.name,"Rotate");
12088       windows->command.data=2;
12089       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12090       (void) XMapRaised(display,windows->command.id);
12091       XClientMessage(display,windows->image.id,windows->im_protocols,
12092         windows->im_update_widget,CurrentTime);
12093       /*
12094         Wait for first button press.
12095       */
12096       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12097       XQueryPosition(display,windows->image.id,&x,&y);
12098       rotate_info.x1=x;
12099       rotate_info.y1=y;
12100       rotate_info.x2=x;
12101       rotate_info.y2=y;
12102       state=DefaultState;
12103       do
12104       {
12105         XHighlightLine(display,windows->image.id,
12106           windows->image.highlight_context,&rotate_info);
12107         /*
12108           Wait for next event.
12109         */
12110         XScreenEvent(display,windows,&event,exception);
12111         XHighlightLine(display,windows->image.id,
12112           windows->image.highlight_context,&rotate_info);
12113         if (event.xany.window == windows->command.id)
12114           {
12115             /*
12116               Select a command from the Command widget.
12117             */
12118             id=XCommandWidget(display,windows,RotateMenu,&event);
12119             if (id < 0)
12120               continue;
12121             (void) XSetFunction(display,windows->image.highlight_context,
12122               GXcopy);
12123             switch (RotateCommands[id])
12124             {
12125               case RotateColorCommand:
12126               {
12127                 const char
12128                   *ColorMenu[MaxNumberPens];
12129 
12130                 int
12131                   pen_number;
12132 
12133                 XColor
12134                   color;
12135 
12136                 /*
12137                   Initialize menu selections.
12138                 */
12139                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12140                   ColorMenu[i]=resource_info->pen_colors[i];
12141                 ColorMenu[MaxNumberPens-2]="Browser...";
12142                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12143                 /*
12144                   Select a pen color from the pop-up menu.
12145                 */
12146                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12147                   (const char **) ColorMenu,command);
12148                 if (pen_number < 0)
12149                   break;
12150                 if (pen_number == (MaxNumberPens-2))
12151                   {
12152                     static char
12153                       color_name[MagickPathExtent] = "gray";
12154 
12155                     /*
12156                       Select a pen color from a dialog.
12157                     */
12158                     resource_info->pen_colors[pen_number]=color_name;
12159                     XColorBrowserWidget(display,windows,"Select",color_name);
12160                     if (*color_name == '\0')
12161                       break;
12162                   }
12163                 /*
12164                   Set pen color.
12165                 */
12166                 (void) XParseColor(display,windows->map_info->colormap,
12167                   resource_info->pen_colors[pen_number],&color);
12168                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12169                   (unsigned int) MaxColors,&color);
12170                 windows->pixel_info->pen_colors[pen_number]=color;
12171                 pen_id=(unsigned int) pen_number;
12172                 break;
12173               }
12174               case RotateDirectionCommand:
12175               {
12176                 const char
12177                   *Directions[] =
12178                   {
12179                     "horizontal",
12180                     "vertical",
12181                     (char *) NULL,
12182                   };
12183 
12184                 /*
12185                   Select a command from the pop-up menu.
12186                 */
12187                 id=XMenuWidget(display,windows,RotateMenu[id],
12188                   Directions,command);
12189                 if (id >= 0)
12190                   direction=DirectionCommands[id];
12191                 break;
12192               }
12193               case RotateHelpCommand:
12194               {
12195                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12196                   "Help Viewer - Image Rotation",ImageRotateHelp);
12197                 break;
12198               }
12199               case RotateDismissCommand:
12200               {
12201                 /*
12202                   Prematurely exit.
12203                 */
12204                 state|=EscapeState;
12205                 state|=ExitState;
12206                 break;
12207               }
12208               default:
12209                 break;
12210             }
12211             (void) XSetFunction(display,windows->image.highlight_context,
12212               GXinvert);
12213             continue;
12214           }
12215         switch (event.type)
12216         {
12217           case ButtonPress:
12218           {
12219             if (event.xbutton.button != Button1)
12220               break;
12221             if (event.xbutton.window != windows->image.id)
12222               break;
12223             /*
12224               exit loop.
12225             */
12226             (void) XSetFunction(display,windows->image.highlight_context,
12227               GXcopy);
12228             rotate_info.x1=event.xbutton.x;
12229             rotate_info.y1=event.xbutton.y;
12230             state|=ExitState;
12231             break;
12232           }
12233           case ButtonRelease:
12234             break;
12235           case Expose:
12236             break;
12237           case KeyPress:
12238           {
12239             char
12240               command[MagickPathExtent];
12241 
12242             KeySym
12243               key_symbol;
12244 
12245             if (event.xkey.window != windows->image.id)
12246               break;
12247             /*
12248               Respond to a user key press.
12249             */
12250             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12251               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12252             switch ((int) key_symbol)
12253             {
12254               case XK_Escape:
12255               case XK_F20:
12256               {
12257                 /*
12258                   Prematurely exit.
12259                 */
12260                 state|=EscapeState;
12261                 state|=ExitState;
12262                 break;
12263               }
12264               case XK_F1:
12265               case XK_Help:
12266               {
12267                 (void) XSetFunction(display,windows->image.highlight_context,
12268                   GXcopy);
12269                 XTextViewHelp(display,resource_info,windows,MagickFalse,
12270                   "Help Viewer - Image Rotation",ImageRotateHelp);
12271                 (void) XSetFunction(display,windows->image.highlight_context,
12272                   GXinvert);
12273                 break;
12274               }
12275               default:
12276               {
12277                 (void) XBell(display,0);
12278                 break;
12279               }
12280             }
12281             break;
12282           }
12283           case MotionNotify:
12284           {
12285             rotate_info.x1=event.xmotion.x;
12286             rotate_info.y1=event.xmotion.y;
12287           }
12288         }
12289         rotate_info.x2=rotate_info.x1;
12290         rotate_info.y2=rotate_info.y1;
12291         if (direction == HorizontalRotateCommand)
12292           rotate_info.x2+=32;
12293         else
12294           rotate_info.y2-=32;
12295       } while ((state & ExitState) == 0);
12296       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12297       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12298       if ((state & EscapeState) != 0)
12299         return(MagickTrue);
12300       /*
12301         Draw line as pointer moves until the mouse button is released.
12302       */
12303       distance=0;
12304       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12305       state=DefaultState;
12306       do
12307       {
12308         if (distance > 9)
12309           {
12310             /*
12311               Display info and draw rotation line.
12312             */
12313             if (windows->info.mapped == MagickFalse)
12314               (void) XMapWindow(display,windows->info.id);
12315             (void) FormatLocaleString(text,MagickPathExtent," %g",
12316               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12317             XInfoWidget(display,windows,text);
12318             XHighlightLine(display,windows->image.id,
12319               windows->image.highlight_context,&rotate_info);
12320           }
12321         else
12322           if (windows->info.mapped != MagickFalse )
12323             (void) XWithdrawWindow(display,windows->info.id,
12324               windows->info.screen);
12325         /*
12326           Wait for next event.
12327         */
12328         XScreenEvent(display,windows,&event,exception);
12329         if (distance > 9)
12330           XHighlightLine(display,windows->image.id,
12331             windows->image.highlight_context,&rotate_info);
12332         switch (event.type)
12333         {
12334           case ButtonPress:
12335             break;
12336           case ButtonRelease:
12337           {
12338             /*
12339               User has committed to rotation line.
12340             */
12341             rotate_info.x2=event.xbutton.x;
12342             rotate_info.y2=event.xbutton.y;
12343             state|=ExitState;
12344             break;
12345           }
12346           case Expose:
12347             break;
12348           case MotionNotify:
12349           {
12350             rotate_info.x2=event.xmotion.x;
12351             rotate_info.y2=event.xmotion.y;
12352           }
12353           default:
12354             break;
12355         }
12356         /*
12357           Check boundary conditions.
12358         */
12359         if (rotate_info.x2 < 0)
12360           rotate_info.x2=0;
12361         else
12362           if (rotate_info.x2 > (int) windows->image.width)
12363             rotate_info.x2=(short) windows->image.width;
12364         if (rotate_info.y2 < 0)
12365           rotate_info.y2=0;
12366         else
12367           if (rotate_info.y2 > (int) windows->image.height)
12368             rotate_info.y2=(short) windows->image.height;
12369         /*
12370           Compute rotation angle from the slope of the line.
12371         */
12372         degrees=0.0;
12373         distance=(unsigned int)
12374           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12375           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12376         if (distance > 9)
12377           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12378             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12379       } while ((state & ExitState) == 0);
12380       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12381       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12382       if (distance <= 9)
12383         return(MagickTrue);
12384     }
12385   if (direction == VerticalRotateCommand)
12386     degrees-=90.0;
12387   if (degrees == 0.0)
12388     return(MagickTrue);
12389   /*
12390     Rotate image.
12391   */
12392   normalized_degrees=degrees;
12393   while (normalized_degrees < -45.0)
12394     normalized_degrees+=360.0;
12395   for (rotations=0; normalized_degrees > 45.0; rotations++)
12396     normalized_degrees-=90.0;
12397   if (normalized_degrees != 0.0)
12398     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12399       exception);
12400   XSetCursorState(display,windows,MagickTrue);
12401   XCheckRefreshWindows(display,windows);
12402   (*image)->background_color.red=(double) ScaleShortToQuantum(
12403     windows->pixel_info->pen_colors[pen_id].red);
12404   (*image)->background_color.green=(double) ScaleShortToQuantum(
12405     windows->pixel_info->pen_colors[pen_id].green);
12406   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12407     windows->pixel_info->pen_colors[pen_id].blue);
12408   rotate_image=RotateImage(*image,degrees,exception);
12409   XSetCursorState(display,windows,MagickFalse);
12410   if (rotate_image == (Image *) NULL)
12411     return(MagickFalse);
12412   *image=DestroyImage(*image);
12413   *image=rotate_image;
12414   if (windows->image.crop_geometry != (char *) NULL)
12415     {
12416       /*
12417         Rotate crop geometry.
12418       */
12419       width=(unsigned int) (*image)->columns;
12420       height=(unsigned int) (*image)->rows;
12421       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12422       switch (rotations % 4)
12423       {
12424         default:
12425         case 0:
12426           break;
12427         case 1:
12428         {
12429           /*
12430             Rotate 90 degrees.
12431           */
12432           (void) FormatLocaleString(windows->image.crop_geometry,
12433             MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12434             (int) height-y,x);
12435           break;
12436         }
12437         case 2:
12438         {
12439           /*
12440             Rotate 180 degrees.
12441           */
12442           (void) FormatLocaleString(windows->image.crop_geometry,
12443             MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12444             height-y);
12445           break;
12446         }
12447         case 3:
12448         {
12449           /*
12450             Rotate 270 degrees.
12451           */
12452           (void) FormatLocaleString(windows->image.crop_geometry,
12453             MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12454             (int) width-x);
12455           break;
12456         }
12457       }
12458     }
12459   if (windows->image.orphan != MagickFalse )
12460     return(MagickTrue);
12461   if (normalized_degrees != 0.0)
12462     {
12463       /*
12464         Update image colormap.
12465       */
12466       windows->image.window_changes.width=(int) (*image)->columns;
12467       windows->image.window_changes.height=(int) (*image)->rows;
12468       if (windows->image.crop_geometry != (char *) NULL)
12469         {
12470           /*
12471             Obtain dimensions of image from crop geometry.
12472           */
12473           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12474             &width,&height);
12475           windows->image.window_changes.width=(int) width;
12476           windows->image.window_changes.height=(int) height;
12477         }
12478       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12479     }
12480   else
12481     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12482       {
12483         windows->image.window_changes.width=windows->image.ximage->height;
12484         windows->image.window_changes.height=windows->image.ximage->width;
12485       }
12486   /*
12487     Update image configuration.
12488   */
12489   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12490   return(MagickTrue);
12491 }
12492 
12493 /*
12494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12495 %                                                                             %
12496 %                                                                             %
12497 %                                                                             %
12498 +   X S a v e I m a g e                                                       %
12499 %                                                                             %
12500 %                                                                             %
12501 %                                                                             %
12502 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12503 %
12504 %  XSaveImage() saves an image to a file.
12505 %
12506 %  The format of the XSaveImage method is:
12507 %
12508 %      MagickBooleanType XSaveImage(Display *display,
12509 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12510 %        ExceptionInfo *exception)
12511 %
12512 %  A description of each parameter follows:
12513 %
12514 %    o display: Specifies a connection to an X server; returned from
12515 %      XOpenDisplay.
12516 %
12517 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12518 %
12519 %    o windows: Specifies a pointer to a XWindows structure.
12520 %
12521 %    o image: the image.
12522 %
12523 %    o exception: return any errors or warnings in this structure.
12524 %
12525 */
XSaveImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)12526 static MagickBooleanType XSaveImage(Display *display,
12527   XResourceInfo *resource_info,XWindows *windows,Image *image,
12528   ExceptionInfo *exception)
12529 {
12530   char
12531     filename[MagickPathExtent],
12532     geometry[MagickPathExtent];
12533 
12534   Image
12535     *save_image;
12536 
12537   ImageInfo
12538     *image_info;
12539 
12540   MagickStatusType
12541     status;
12542 
12543   /*
12544     Request file name from user.
12545   */
12546   if (resource_info->write_filename != (char *) NULL)
12547     (void) CopyMagickString(filename,resource_info->write_filename,
12548       MagickPathExtent);
12549   else
12550     {
12551       char
12552         path[MagickPathExtent];
12553 
12554       int
12555         status;
12556 
12557       GetPathComponent(image->filename,HeadPath,path);
12558       GetPathComponent(image->filename,TailPath,filename);
12559       if (*path != '\0')
12560         {
12561           status=chdir(path);
12562           if (status == -1)
12563             (void) ThrowMagickException(exception,GetMagickModule(),
12564               FileOpenError,"UnableToOpenFile","%s",path);
12565         }
12566     }
12567   XFileBrowserWidget(display,windows,"Save",filename);
12568   if (*filename == '\0')
12569     return(MagickTrue);
12570   if (IsPathAccessible(filename) != MagickFalse )
12571     {
12572       int
12573         status;
12574 
12575       /*
12576         File exists-- seek user's permission before overwriting.
12577       */
12578       status=XConfirmWidget(display,windows,"Overwrite",filename);
12579       if (status <= 0)
12580         return(MagickTrue);
12581     }
12582   image_info=CloneImageInfo(resource_info->image_info);
12583   (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12584   (void) SetImageInfo(image_info,1,exception);
12585   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12586       (LocaleCompare(image_info->magick,"JPG") == 0))
12587     {
12588       char
12589         quality[MagickPathExtent];
12590 
12591       int
12592         status;
12593 
12594       /*
12595         Request JPEG quality from user.
12596       */
12597       (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12598         image->quality);
12599       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12600         quality);
12601       if (*quality == '\0')
12602         return(MagickTrue);
12603       image->quality=StringToUnsignedLong(quality);
12604       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12605     }
12606   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12607       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12608       (LocaleCompare(image_info->magick,"PS") == 0) ||
12609       (LocaleCompare(image_info->magick,"PS2") == 0))
12610     {
12611       char
12612         geometry[MagickPathExtent];
12613 
12614       const char
12615         *const PageSizes[] =
12616         {
12617           "Letter",
12618           "Tabloid",
12619           "Ledger",
12620           "Legal",
12621           "Statement",
12622           "Executive",
12623           "A3",
12624           "A4",
12625           "A5",
12626           "B4",
12627           "B5",
12628           "Folio",
12629           "Quarto",
12630           "10x14",
12631           (char *) NULL
12632         };
12633 
12634       /*
12635         Request page geometry from user.
12636       */
12637       (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12638       if (LocaleCompare(image_info->magick,"PDF") == 0)
12639         (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12640       if (image_info->page != (char *) NULL)
12641         (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12642       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12643         "Select page geometry:",geometry);
12644       if (*geometry != '\0')
12645         image_info->page=GetPageGeometry(geometry);
12646     }
12647   /*
12648     Apply image transforms.
12649   */
12650   XSetCursorState(display,windows,MagickTrue);
12651   XCheckRefreshWindows(display,windows);
12652   save_image=CloneImage(image,0,0,MagickTrue,exception);
12653   if (save_image == (Image *) NULL)
12654     return(MagickFalse);
12655   (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12656     windows->image.ximage->width,windows->image.ximage->height);
12657   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12658     exception);
12659   /*
12660     Write image.
12661   */
12662   (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12663   status=WriteImage(image_info,save_image,exception);
12664   if (status != MagickFalse )
12665     image->taint=MagickFalse;
12666   save_image=DestroyImage(save_image);
12667   image_info=DestroyImageInfo(image_info);
12668   XSetCursorState(display,windows,MagickFalse);
12669   return(status != 0 ? MagickTrue : MagickFalse);
12670 }
12671 
12672 /*
12673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12674 %                                                                             %
12675 %                                                                             %
12676 %                                                                             %
12677 +   X S c r e e n E v e n t                                                   %
12678 %                                                                             %
12679 %                                                                             %
12680 %                                                                             %
12681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12682 %
12683 %  XScreenEvent() handles global events associated with the Pan and Magnify
12684 %  windows.
12685 %
12686 %  The format of the XScreenEvent function is:
12687 %
12688 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12689 %        ExceptionInfo *exception)
12690 %
12691 %  A description of each parameter follows:
12692 %
12693 %    o display: Specifies a pointer to the Display structure;  returned from
12694 %      XOpenDisplay.
12695 %
12696 %    o windows: Specifies a pointer to a XWindows structure.
12697 %
12698 %    o event: Specifies a pointer to a X11 XEvent structure.
12699 %
12700 %    o exception: return any errors or warnings in this structure.
12701 %
12702 */
12703 
12704 #if defined(__cplusplus) || defined(c_plusplus)
12705 extern "C" {
12706 #endif
12707 
XPredicate(Display * magick_unused (display),XEvent * event,char * data)12708 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12709 {
12710   register XWindows
12711     *windows;
12712 
12713   windows=(XWindows *) data;
12714   if ((event->type == ClientMessage) &&
12715       (event->xclient.window == windows->image.id))
12716     return(MagickFalse);
12717   return(MagickTrue);
12718 }
12719 
12720 #if defined(__cplusplus) || defined(c_plusplus)
12721 }
12722 #endif
12723 
XScreenEvent(Display * display,XWindows * windows,XEvent * event,ExceptionInfo * exception)12724 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12725   ExceptionInfo *exception)
12726 {
12727   register int
12728     x,
12729     y;
12730 
12731   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12732   if (event->xany.window == windows->command.id)
12733     return;
12734   switch (event->type)
12735   {
12736     case ButtonPress:
12737     case ButtonRelease:
12738     {
12739       if ((event->xbutton.button == Button3) &&
12740           (event->xbutton.state & Mod1Mask))
12741         {
12742           /*
12743             Convert Alt-Button3 to Button2.
12744           */
12745           event->xbutton.button=Button2;
12746           event->xbutton.state&=(~Mod1Mask);
12747         }
12748       if (event->xbutton.window == windows->backdrop.id)
12749         {
12750           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12751             event->xbutton.time);
12752           break;
12753         }
12754       if (event->xbutton.window == windows->pan.id)
12755         {
12756           XPanImage(display,windows,event,exception);
12757           break;
12758         }
12759       if (event->xbutton.window == windows->image.id)
12760         if (event->xbutton.button == Button2)
12761           {
12762             /*
12763               Update magnified image.
12764             */
12765             x=event->xbutton.x;
12766             y=event->xbutton.y;
12767             if (x < 0)
12768               x=0;
12769             else
12770               if (x >= (int) windows->image.width)
12771                 x=(int) (windows->image.width-1);
12772             windows->magnify.x=(int) windows->image.x+x;
12773             if (y < 0)
12774               y=0;
12775             else
12776              if (y >= (int) windows->image.height)
12777                y=(int) (windows->image.height-1);
12778             windows->magnify.y=windows->image.y+y;
12779             if (windows->magnify.mapped == MagickFalse)
12780               (void) XMapRaised(display,windows->magnify.id);
12781             XMakeMagnifyImage(display,windows,exception);
12782             if (event->type == ButtonRelease)
12783               (void) XWithdrawWindow(display,windows->info.id,
12784                 windows->info.screen);
12785             break;
12786           }
12787       break;
12788     }
12789     case ClientMessage:
12790     {
12791       /*
12792         If client window delete message, exit.
12793       */
12794       if (event->xclient.message_type != windows->wm_protocols)
12795         break;
12796       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12797         break;
12798       if (event->xclient.window == windows->magnify.id)
12799         {
12800           (void) XWithdrawWindow(display,windows->magnify.id,
12801             windows->magnify.screen);
12802           break;
12803         }
12804       break;
12805     }
12806     case ConfigureNotify:
12807     {
12808       if (event->xconfigure.window == windows->magnify.id)
12809         {
12810           unsigned int
12811             magnify;
12812 
12813           /*
12814             Magnify window has a new configuration.
12815           */
12816           windows->magnify.width=(unsigned int) event->xconfigure.width;
12817           windows->magnify.height=(unsigned int) event->xconfigure.height;
12818           if (windows->magnify.mapped == MagickFalse)
12819             break;
12820           magnify=1;
12821           while ((int) magnify <= event->xconfigure.width)
12822             magnify<<=1;
12823           while ((int) magnify <= event->xconfigure.height)
12824             magnify<<=1;
12825           magnify>>=1;
12826           if (((int) magnify != event->xconfigure.width) ||
12827               ((int) magnify != event->xconfigure.height))
12828             {
12829               XWindowChanges
12830                 window_changes;
12831 
12832               window_changes.width=(int) magnify;
12833               window_changes.height=(int) magnify;
12834               (void) XReconfigureWMWindow(display,windows->magnify.id,
12835                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12836                 &window_changes);
12837               break;
12838             }
12839           XMakeMagnifyImage(display,windows,exception);
12840           break;
12841         }
12842       break;
12843     }
12844     case Expose:
12845     {
12846       if (event->xexpose.window == windows->image.id)
12847         {
12848           XRefreshWindow(display,&windows->image,event);
12849           break;
12850         }
12851       if (event->xexpose.window == windows->pan.id)
12852         if (event->xexpose.count == 0)
12853           {
12854             XDrawPanRectangle(display,windows);
12855             break;
12856           }
12857       if (event->xexpose.window == windows->magnify.id)
12858         if (event->xexpose.count == 0)
12859           {
12860             XMakeMagnifyImage(display,windows,exception);
12861             break;
12862           }
12863       break;
12864     }
12865     case KeyPress:
12866     {
12867       char
12868         command[MagickPathExtent];
12869 
12870       KeySym
12871         key_symbol;
12872 
12873       if (event->xkey.window != windows->magnify.id)
12874         break;
12875       /*
12876         Respond to a user key press.
12877       */
12878       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12879         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12880       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12881         exception);
12882       break;
12883     }
12884     case MapNotify:
12885     {
12886       if (event->xmap.window == windows->magnify.id)
12887         {
12888           windows->magnify.mapped=MagickTrue;
12889           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12890           break;
12891         }
12892       if (event->xmap.window == windows->info.id)
12893         {
12894           windows->info.mapped=MagickTrue;
12895           break;
12896         }
12897       break;
12898     }
12899     case MotionNotify:
12900     {
12901       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12902       if (event->xmotion.window == windows->image.id)
12903         if (windows->magnify.mapped != MagickFalse )
12904           {
12905             /*
12906               Update magnified image.
12907             */
12908             x=event->xmotion.x;
12909             y=event->xmotion.y;
12910             if (x < 0)
12911               x=0;
12912             else
12913               if (x >= (int) windows->image.width)
12914                 x=(int) (windows->image.width-1);
12915             windows->magnify.x=(int) windows->image.x+x;
12916             if (y < 0)
12917               y=0;
12918             else
12919              if (y >= (int) windows->image.height)
12920                y=(int) (windows->image.height-1);
12921             windows->magnify.y=windows->image.y+y;
12922             XMakeMagnifyImage(display,windows,exception);
12923           }
12924       break;
12925     }
12926     case UnmapNotify:
12927     {
12928       if (event->xunmap.window == windows->magnify.id)
12929         {
12930           windows->magnify.mapped=MagickFalse;
12931           break;
12932         }
12933       if (event->xunmap.window == windows->info.id)
12934         {
12935           windows->info.mapped=MagickFalse;
12936           break;
12937         }
12938       break;
12939     }
12940     default:
12941       break;
12942   }
12943 }
12944 
12945 /*
12946 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12947 %                                                                             %
12948 %                                                                             %
12949 %                                                                             %
12950 +   X S e t C r o p G e o m e t r y                                           %
12951 %                                                                             %
12952 %                                                                             %
12953 %                                                                             %
12954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12955 %
12956 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12957 %  and translates it to a cropping geometry relative to the image.
12958 %
12959 %  The format of the XSetCropGeometry method is:
12960 %
12961 %      void XSetCropGeometry(Display *display,XWindows *windows,
12962 %        RectangleInfo *crop_info,Image *image)
12963 %
12964 %  A description of each parameter follows:
12965 %
12966 %    o display: Specifies a connection to an X server; returned from
12967 %      XOpenDisplay.
12968 %
12969 %    o windows: Specifies a pointer to a XWindows structure.
12970 %
12971 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12972 %      Image window to crop.
12973 %
12974 %    o image: the image.
12975 %
12976 */
XSetCropGeometry(Display * display,XWindows * windows,RectangleInfo * crop_info,Image * image)12977 static void XSetCropGeometry(Display *display,XWindows *windows,
12978   RectangleInfo *crop_info,Image *image)
12979 {
12980   char
12981     text[MagickPathExtent];
12982 
12983   int
12984     x,
12985     y;
12986 
12987   double
12988     scale_factor;
12989 
12990   unsigned int
12991     height,
12992     width;
12993 
12994   if (windows->info.mapped != MagickFalse )
12995     {
12996       /*
12997         Display info on cropping rectangle.
12998       */
12999       (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13000         (double) crop_info->width,(double) crop_info->height,(double)
13001         crop_info->x,(double) crop_info->y);
13002       XInfoWidget(display,windows,text);
13003     }
13004   /*
13005     Cropping geometry is relative to any previous crop geometry.
13006   */
13007   x=0;
13008   y=0;
13009   width=(unsigned int) image->columns;
13010   height=(unsigned int) image->rows;
13011   if (windows->image.crop_geometry != (char *) NULL)
13012     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13013   else
13014     windows->image.crop_geometry=AcquireString((char *) NULL);
13015   /*
13016     Define the crop geometry string from the cropping rectangle.
13017   */
13018   scale_factor=(double) width/windows->image.ximage->width;
13019   if (crop_info->x > 0)
13020     x+=(int) (scale_factor*crop_info->x+0.5);
13021   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13022   if (width == 0)
13023     width=1;
13024   scale_factor=(double) height/windows->image.ximage->height;
13025   if (crop_info->y > 0)
13026     y+=(int) (scale_factor*crop_info->y+0.5);
13027   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13028   if (height == 0)
13029     height=1;
13030   (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13031     "%ux%u%+d%+d",width,height,x,y);
13032 }
13033 
13034 /*
13035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13036 %                                                                             %
13037 %                                                                             %
13038 %                                                                             %
13039 +   X T i l e I m a g e                                                       %
13040 %                                                                             %
13041 %                                                                             %
13042 %                                                                             %
13043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13044 %
13045 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13046 %  The load or delete command is chosen from a menu.
13047 %
13048 %  The format of the XTileImage method is:
13049 %
13050 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13051 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13052 %
13053 %  A description of each parameter follows:
13054 %
13055 %    o tile_image:  XTileImage reads or deletes the tile image
13056 %      and returns it.  A null image is returned if an error occurs.
13057 %
13058 %    o display: Specifies a connection to an X server;  returned from
13059 %      XOpenDisplay.
13060 %
13061 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13062 %
13063 %    o windows: Specifies a pointer to a XWindows structure.
13064 %
13065 %    o image: the image; returned from ReadImage.
13066 %
13067 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13068 %      the entire image is refreshed.
13069 %
13070 %    o exception: return any errors or warnings in this structure.
13071 %
13072 */
XTileImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,XEvent * event,ExceptionInfo * exception)13073 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13074   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13075 {
13076   const char
13077     *const VerbMenu[] =
13078     {
13079       "Load",
13080       "Next",
13081       "Former",
13082       "Delete",
13083       "Update",
13084       (char *) NULL,
13085     };
13086 
13087   static const ModeType
13088     TileCommands[] =
13089     {
13090       TileLoadCommand,
13091       TileNextCommand,
13092       TileFormerCommand,
13093       TileDeleteCommand,
13094       TileUpdateCommand
13095     };
13096 
13097   char
13098     command[MagickPathExtent],
13099     filename[MagickPathExtent];
13100 
13101   Image
13102     *tile_image;
13103 
13104   int
13105     id,
13106     status,
13107     tile,
13108     x,
13109     y;
13110 
13111   double
13112     scale_factor;
13113 
13114   register char
13115     *p,
13116     *q;
13117 
13118   register int
13119     i;
13120 
13121   unsigned int
13122     height,
13123     width;
13124 
13125   /*
13126     Tile image is relative to montage image configuration.
13127   */
13128   x=0;
13129   y=0;
13130   width=(unsigned int) image->columns;
13131   height=(unsigned int) image->rows;
13132   if (windows->image.crop_geometry != (char *) NULL)
13133     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13134   scale_factor=(double) width/windows->image.ximage->width;
13135   event->xbutton.x+=windows->image.x;
13136   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13137   scale_factor=(double) height/windows->image.ximage->height;
13138   event->xbutton.y+=windows->image.y;
13139   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13140   /*
13141     Determine size and location of each tile in the visual image directory.
13142   */
13143   width=(unsigned int) image->columns;
13144   height=(unsigned int) image->rows;
13145   x=0;
13146   y=0;
13147   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13148   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13149     (event->xbutton.x-x)/width;
13150   if (tile < 0)
13151     {
13152       /*
13153         Button press is outside any tile.
13154       */
13155       (void) XBell(display,0);
13156       return((Image *) NULL);
13157     }
13158   /*
13159     Determine file name from the tile directory.
13160   */
13161   p=image->directory;
13162   for (i=tile; (i != 0) && (*p != '\0'); )
13163   {
13164     if (*p == '\xff')
13165       i--;
13166     p++;
13167   }
13168   if (*p == '\0')
13169     {
13170       /*
13171         Button press is outside any tile.
13172       */
13173       (void) XBell(display,0);
13174       return((Image *) NULL);
13175     }
13176   /*
13177     Select a command from the pop-up menu.
13178   */
13179   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13180   if (id < 0)
13181     return((Image *) NULL);
13182   q=p;
13183   while ((*q != '\xff') && (*q != '\0'))
13184     q++;
13185   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13186   /*
13187     Perform command for the selected tile.
13188   */
13189   XSetCursorState(display,windows,MagickTrue);
13190   XCheckRefreshWindows(display,windows);
13191   tile_image=NewImageList();
13192   switch (TileCommands[id])
13193   {
13194     case TileLoadCommand:
13195     {
13196       /*
13197         Load tile image.
13198       */
13199       XCheckRefreshWindows(display,windows);
13200       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13201         MagickPathExtent);
13202       (void) CopyMagickString(resource_info->image_info->filename,filename,
13203         MagickPathExtent);
13204       tile_image=ReadImage(resource_info->image_info,exception);
13205       CatchException(exception);
13206       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13207       break;
13208     }
13209     case TileNextCommand:
13210     {
13211       /*
13212         Display next image.
13213       */
13214       XClientMessage(display,windows->image.id,windows->im_protocols,
13215         windows->im_next_image,CurrentTime);
13216       break;
13217     }
13218     case TileFormerCommand:
13219     {
13220       /*
13221         Display former image.
13222       */
13223       XClientMessage(display,windows->image.id,windows->im_protocols,
13224         windows->im_former_image,CurrentTime);
13225       break;
13226     }
13227     case TileDeleteCommand:
13228     {
13229       /*
13230         Delete tile image.
13231       */
13232       if (IsPathAccessible(filename) == MagickFalse)
13233         {
13234           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13235           break;
13236         }
13237       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13238       if (status <= 0)
13239         break;
13240       status=ShredFile(filename);
13241       if (status != MagickFalse )
13242         {
13243           XNoticeWidget(display,windows,"Unable to delete image file:",
13244             filename);
13245           break;
13246         }
13247     }
13248     case TileUpdateCommand:
13249     {
13250       int
13251         x_offset,
13252         y_offset;
13253 
13254       PixelInfo
13255         pixel;
13256 
13257       register int
13258         j;
13259 
13260       register Quantum
13261         *s;
13262 
13263       /*
13264         Ensure all the images exist.
13265       */
13266       tile=0;
13267       GetPixelInfo(image,&pixel);
13268       for (p=image->directory; *p != '\0'; p++)
13269       {
13270         CacheView
13271           *image_view;
13272 
13273         q=p;
13274         while ((*q != '\xff') && (*q != '\0'))
13275           q++;
13276         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13277         p=q;
13278         if (IsPathAccessible(filename) != MagickFalse )
13279           {
13280             tile++;
13281             continue;
13282           }
13283         /*
13284           Overwrite tile with background color.
13285         */
13286         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13287         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13288         image_view=AcquireAuthenticCacheView(image,exception);
13289         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13290         for (i=0; i < (int) height; i++)
13291         {
13292           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13293             y_offset+i,width,1,exception);
13294           if (s == (Quantum *) NULL)
13295             break;
13296           for (j=0; j < (int) width; j++)
13297           {
13298             SetPixelViaPixelInfo(image,&pixel,s);
13299             s+=GetPixelChannels(image);
13300           }
13301           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13302             break;
13303         }
13304         image_view=DestroyCacheView(image_view);
13305         tile++;
13306       }
13307       windows->image.window_changes.width=(int) image->columns;
13308       windows->image.window_changes.height=(int) image->rows;
13309       XConfigureImageColormap(display,resource_info,windows,image,exception);
13310       (void) XConfigureImage(display,resource_info,windows,image,exception);
13311       break;
13312     }
13313     default:
13314       break;
13315   }
13316   XSetCursorState(display,windows,MagickFalse);
13317   return(tile_image);
13318 }
13319 
13320 /*
13321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13322 %                                                                             %
13323 %                                                                             %
13324 %                                                                             %
13325 +   X T r a n s l a t e I m a g e                                             %
13326 %                                                                             %
13327 %                                                                             %
13328 %                                                                             %
13329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13330 %
13331 %  XTranslateImage() translates the image within an Image window by one pixel
13332 %  as specified by the key symbol.  If the image has a montage string the
13333 %  translation is respect to the width and height contained within the string.
13334 %
13335 %  The format of the XTranslateImage method is:
13336 %
13337 %      void XTranslateImage(Display *display,XWindows *windows,
13338 %        Image *image,const KeySym key_symbol)
13339 %
13340 %  A description of each parameter follows:
13341 %
13342 %    o display: Specifies a connection to an X server; returned from
13343 %      XOpenDisplay.
13344 %
13345 %    o windows: Specifies a pointer to a XWindows structure.
13346 %
13347 %    o image: the image.
13348 %
13349 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13350 %      to trim.
13351 %
13352 */
XTranslateImage(Display * display,XWindows * windows,Image * image,const KeySym key_symbol)13353 static void XTranslateImage(Display *display,XWindows *windows,
13354   Image *image,const KeySym key_symbol)
13355 {
13356   char
13357     text[MagickPathExtent];
13358 
13359   int
13360     x,
13361     y;
13362 
13363   unsigned int
13364     x_offset,
13365     y_offset;
13366 
13367   /*
13368     User specified a pan position offset.
13369   */
13370   x_offset=windows->image.width;
13371   y_offset=windows->image.height;
13372   if (image->montage != (char *) NULL)
13373     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13374   switch ((int) key_symbol)
13375   {
13376     case XK_Home:
13377     case XK_KP_Home:
13378     {
13379       windows->image.x=(int) windows->image.width/2;
13380       windows->image.y=(int) windows->image.height/2;
13381       break;
13382     }
13383     case XK_Left:
13384     case XK_KP_Left:
13385     {
13386       windows->image.x-=x_offset;
13387       break;
13388     }
13389     case XK_Next:
13390     case XK_Up:
13391     case XK_KP_Up:
13392     {
13393       windows->image.y-=y_offset;
13394       break;
13395     }
13396     case XK_Right:
13397     case XK_KP_Right:
13398     {
13399       windows->image.x+=x_offset;
13400       break;
13401     }
13402     case XK_Prior:
13403     case XK_Down:
13404     case XK_KP_Down:
13405     {
13406       windows->image.y+=y_offset;
13407       break;
13408     }
13409     default:
13410       return;
13411   }
13412   /*
13413     Check boundary conditions.
13414   */
13415   if (windows->image.x < 0)
13416     windows->image.x=0;
13417   else
13418     if ((int) (windows->image.x+windows->image.width) >
13419         windows->image.ximage->width)
13420       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13421   if (windows->image.y < 0)
13422     windows->image.y=0;
13423   else
13424     if ((int) (windows->image.y+windows->image.height) >
13425         windows->image.ximage->height)
13426       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13427   /*
13428     Refresh Image window.
13429   */
13430   (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13431     windows->image.width,windows->image.height,windows->image.x,
13432     windows->image.y);
13433   XInfoWidget(display,windows,text);
13434   XCheckRefreshWindows(display,windows);
13435   XDrawPanRectangle(display,windows);
13436   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13437   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13438 }
13439 
13440 /*
13441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13442 %                                                                             %
13443 %                                                                             %
13444 %                                                                             %
13445 +   X T r i m I m a g e                                                       %
13446 %                                                                             %
13447 %                                                                             %
13448 %                                                                             %
13449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13450 %
13451 %  XTrimImage() trims the edges from the Image window.
13452 %
13453 %  The format of the XTrimImage method is:
13454 %
13455 %      MagickBooleanType XTrimImage(Display *display,
13456 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13457 %        ExceptionInfo *exception)
13458 %
13459 %  A description of each parameter follows:
13460 %
13461 %    o display: Specifies a connection to an X server; returned from
13462 %      XOpenDisplay.
13463 %
13464 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13465 %
13466 %    o windows: Specifies a pointer to a XWindows structure.
13467 %
13468 %    o image: the image.
13469 %
13470 %    o exception: return any errors or warnings in this structure.
13471 %
13472 */
XTrimImage(Display * display,XResourceInfo * resource_info,XWindows * windows,Image * image,ExceptionInfo * exception)13473 static MagickBooleanType XTrimImage(Display *display,
13474   XResourceInfo *resource_info,XWindows *windows,Image *image,
13475   ExceptionInfo *exception)
13476 {
13477   RectangleInfo
13478     trim_info;
13479 
13480   register int
13481     x,
13482     y;
13483 
13484   size_t
13485     background,
13486     pixel;
13487 
13488   /*
13489     Trim edges from image.
13490   */
13491   XSetCursorState(display,windows,MagickTrue);
13492   XCheckRefreshWindows(display,windows);
13493   /*
13494     Crop the left edge.
13495   */
13496   background=XGetPixel(windows->image.ximage,0,0);
13497   trim_info.width=(size_t) windows->image.ximage->width;
13498   for (x=0; x < windows->image.ximage->width; x++)
13499   {
13500     for (y=0; y < windows->image.ximage->height; y++)
13501     {
13502       pixel=XGetPixel(windows->image.ximage,x,y);
13503       if (pixel != background)
13504         break;
13505     }
13506     if (y < windows->image.ximage->height)
13507       break;
13508   }
13509   trim_info.x=(ssize_t) x;
13510   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13511     {
13512       XSetCursorState(display,windows,MagickFalse);
13513       return(MagickFalse);
13514     }
13515   /*
13516     Crop the right edge.
13517   */
13518   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13519   for (x=windows->image.ximage->width-1; x != 0; x--)
13520   {
13521     for (y=0; y < windows->image.ximage->height; y++)
13522     {
13523       pixel=XGetPixel(windows->image.ximage,x,y);
13524       if (pixel != background)
13525         break;
13526     }
13527     if (y < windows->image.ximage->height)
13528       break;
13529   }
13530   trim_info.width=(size_t) (x-trim_info.x+1);
13531   /*
13532     Crop the top edge.
13533   */
13534   background=XGetPixel(windows->image.ximage,0,0);
13535   trim_info.height=(size_t) windows->image.ximage->height;
13536   for (y=0; y < windows->image.ximage->height; y++)
13537   {
13538     for (x=0; x < windows->image.ximage->width; x++)
13539     {
13540       pixel=XGetPixel(windows->image.ximage,x,y);
13541       if (pixel != background)
13542         break;
13543     }
13544     if (x < windows->image.ximage->width)
13545       break;
13546   }
13547   trim_info.y=(ssize_t) y;
13548   /*
13549     Crop the bottom edge.
13550   */
13551   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13552   for (y=windows->image.ximage->height-1; y != 0; y--)
13553   {
13554     for (x=0; x < windows->image.ximage->width; x++)
13555     {
13556       pixel=XGetPixel(windows->image.ximage,x,y);
13557       if (pixel != background)
13558         break;
13559     }
13560     if (x < windows->image.ximage->width)
13561       break;
13562   }
13563   trim_info.height=(size_t) y-trim_info.y+1;
13564   if (((unsigned int) trim_info.width != windows->image.width) ||
13565       ((unsigned int) trim_info.height != windows->image.height))
13566     {
13567       /*
13568         Reconfigure Image window as defined by the trimming rectangle.
13569       */
13570       XSetCropGeometry(display,windows,&trim_info,image);
13571       windows->image.window_changes.width=(int) trim_info.width;
13572       windows->image.window_changes.height=(int) trim_info.height;
13573       (void) XConfigureImage(display,resource_info,windows,image,exception);
13574     }
13575   XSetCursorState(display,windows,MagickFalse);
13576   return(MagickTrue);
13577 }
13578 
13579 /*
13580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13581 %                                                                             %
13582 %                                                                             %
13583 %                                                                             %
13584 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13585 %                                                                             %
13586 %                                                                             %
13587 %                                                                             %
13588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13589 %
13590 %  XVisualDirectoryImage() creates a Visual Image Directory.
13591 %
13592 %  The format of the XVisualDirectoryImage method is:
13593 %
13594 %      Image *XVisualDirectoryImage(Display *display,
13595 %        XResourceInfo *resource_info,XWindows *windows,
13596 %        ExceptionInfo *exception)
13597 %
13598 %  A description of each parameter follows:
13599 %
13600 %    o display: Specifies a connection to an X server; returned from
13601 %      XOpenDisplay.
13602 %
13603 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13604 %
13605 %    o windows: Specifies a pointer to a XWindows structure.
13606 %
13607 %    o exception: return any errors or warnings in this structure.
13608 %
13609 */
XVisualDirectoryImage(Display * display,XResourceInfo * resource_info,XWindows * windows,ExceptionInfo * exception)13610 static Image *XVisualDirectoryImage(Display *display,
13611   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13612 {
13613 #define TileImageTag  "Scale/Image"
13614 #define XClientName  "montage"
13615 
13616   char
13617     **filelist;
13618 
13619   Image
13620     *images,
13621     *montage_image,
13622     *next_image,
13623     *thumbnail_image;
13624 
13625   ImageInfo
13626     *read_info;
13627 
13628   int
13629     number_files;
13630 
13631   MagickBooleanType
13632     backdrop;
13633 
13634   MagickStatusType
13635     status;
13636 
13637   MontageInfo
13638     *montage_info;
13639 
13640   RectangleInfo
13641     geometry;
13642 
13643   register int
13644     i;
13645 
13646   static char
13647     filename[MagickPathExtent] = "\0",
13648     filenames[MagickPathExtent] = "*";
13649 
13650   XResourceInfo
13651     background_resources;
13652 
13653   /*
13654     Request file name from user.
13655   */
13656   XFileBrowserWidget(display,windows,"Directory",filenames);
13657   if (*filenames == '\0')
13658     return((Image *) NULL);
13659   /*
13660     Expand the filenames.
13661   */
13662   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13663   if (filelist == (char **) NULL)
13664     {
13665       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13666         filenames);
13667       return((Image *) NULL);
13668     }
13669   number_files=1;
13670   filelist[0]=filenames;
13671   status=ExpandFilenames(&number_files,&filelist);
13672   if ((status == MagickFalse) || (number_files == 0))
13673     {
13674       if (number_files == 0)
13675         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13676       else
13677         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13678           filenames);
13679       return((Image *) NULL);
13680     }
13681   /*
13682     Set image background resources.
13683   */
13684   background_resources=(*resource_info);
13685   background_resources.window_id=AcquireString("");
13686   (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13687     "0x%lx",windows->image.id);
13688   background_resources.backdrop=MagickTrue;
13689   /*
13690     Read each image and convert them to a tile.
13691   */
13692   backdrop=((windows->visual_info->klass == TrueColor) ||
13693     (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13694   read_info=CloneImageInfo(resource_info->image_info);
13695   (void) SetImageOption(read_info,"jpeg:size","120x120");
13696   (void) CloneString(&read_info->size,DefaultTileGeometry);
13697   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13698     (void *) NULL);
13699   images=NewImageList();
13700   XSetCursorState(display,windows,MagickTrue);
13701   XCheckRefreshWindows(display,windows);
13702   for (i=0; i < (int) number_files; i++)
13703   {
13704     (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13705     filelist[i]=DestroyString(filelist[i]);
13706     *read_info->magick='\0';
13707     next_image=ReadImage(read_info,exception);
13708     CatchException(exception);
13709     if (next_image != (Image *) NULL)
13710       {
13711         (void) DeleteImageProperty(next_image,"label");
13712         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13713           read_info,next_image,DefaultTileLabel,exception),exception);
13714         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13715           exception);
13716         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13717           geometry.height,exception);
13718         if (thumbnail_image != (Image *) NULL)
13719           {
13720             next_image=DestroyImage(next_image);
13721             next_image=thumbnail_image;
13722           }
13723         if (backdrop)
13724           {
13725             (void) XDisplayBackgroundImage(display,&background_resources,
13726               next_image,exception);
13727             XSetCursorState(display,windows,MagickTrue);
13728           }
13729         AppendImageToList(&images,next_image);
13730         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13731           {
13732             MagickBooleanType
13733               proceed;
13734 
13735             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13736               (MagickSizeType) number_files);
13737             if (proceed == MagickFalse)
13738               break;
13739           }
13740       }
13741   }
13742   filelist=(char **) RelinquishMagickMemory(filelist);
13743   if (images == (Image *) NULL)
13744     {
13745       read_info=DestroyImageInfo(read_info);
13746       XSetCursorState(display,windows,MagickFalse);
13747       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13748       return((Image *) NULL);
13749     }
13750   /*
13751     Create the Visual Image Directory.
13752   */
13753   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13754   montage_info->pointsize=10;
13755   if (resource_info->font != (char *) NULL)
13756     (void) CloneString(&montage_info->font,resource_info->font);
13757   (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13758   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13759     images),exception);
13760   images=DestroyImageList(images);
13761   montage_info=DestroyMontageInfo(montage_info);
13762   read_info=DestroyImageInfo(read_info);
13763   XSetCursorState(display,windows,MagickFalse);
13764   if (montage_image == (Image *) NULL)
13765     return(montage_image);
13766   XClientMessage(display,windows->image.id,windows->im_protocols,
13767     windows->im_next_image,CurrentTime);
13768   return(montage_image);
13769 }
13770 
13771 /*
13772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13773 %                                                                             %
13774 %                                                                             %
13775 %                                                                             %
13776 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13777 %                                                                             %
13778 %                                                                             %
13779 %                                                                             %
13780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13781 %
13782 %  XDisplayBackgroundImage() displays an image in the background of a window.
13783 %
13784 %  The format of the XDisplayBackgroundImage method is:
13785 %
13786 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13787 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13788 %
13789 %  A description of each parameter follows:
13790 %
13791 %    o display: Specifies a connection to an X server;  returned from
13792 %      XOpenDisplay.
13793 %
13794 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13795 %
13796 %    o image: the image.
13797 %
13798 %    o exception: return any errors or warnings in this structure.
13799 %
13800 */
XDisplayBackgroundImage(Display * display,XResourceInfo * resource_info,Image * image,ExceptionInfo * exception)13801 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13802   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13803 {
13804   char
13805     geometry[MagickPathExtent],
13806     visual_type[MagickPathExtent];
13807 
13808   int
13809     height,
13810     status,
13811     width;
13812 
13813   RectangleInfo
13814     geometry_info;
13815 
13816   static XPixelInfo
13817     pixel;
13818 
13819   static XStandardColormap
13820     *map_info;
13821 
13822   static XVisualInfo
13823     *visual_info = (XVisualInfo *) NULL;
13824 
13825   static XWindowInfo
13826     window_info;
13827 
13828   size_t
13829     delay;
13830 
13831   Window
13832     root_window;
13833 
13834   XGCValues
13835     context_values;
13836 
13837   XResourceInfo
13838     resources;
13839 
13840   XWindowAttributes
13841     window_attributes;
13842 
13843   /*
13844     Determine target window.
13845   */
13846   assert(image != (Image *) NULL);
13847   assert(image->signature == MagickCoreSignature);
13848   if (image->debug != MagickFalse )
13849     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13850   resources=(*resource_info);
13851   window_info.id=(Window) NULL;
13852   root_window=XRootWindow(display,XDefaultScreen(display));
13853   if (LocaleCompare(resources.window_id,"root") == 0)
13854     window_info.id=root_window;
13855   else
13856     {
13857       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13858         window_info.id=XWindowByID(display,root_window,
13859           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13860       if (window_info.id == (Window) NULL)
13861         window_info.id=XWindowByName(display,root_window,resources.window_id);
13862     }
13863   if (window_info.id == (Window) NULL)
13864     {
13865       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13866         resources.window_id);
13867       return(MagickFalse);
13868     }
13869   /*
13870     Determine window visual id.
13871   */
13872   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13873   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13874   (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13875   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13876   if (status != 0)
13877     (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13878       XVisualIDFromVisual(window_attributes.visual));
13879   if (visual_info == (XVisualInfo *) NULL)
13880     {
13881       /*
13882         Allocate standard colormap.
13883       */
13884       map_info=XAllocStandardColormap();
13885       if (map_info == (XStandardColormap *) NULL)
13886         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13887           image->filename);
13888       map_info->colormap=(Colormap) NULL;
13889       pixel.pixels=(unsigned long *) NULL;
13890       /*
13891         Initialize visual info.
13892       */
13893       resources.map_type=(char *) NULL;
13894       resources.visual_type=visual_type;
13895       visual_info=XBestVisualInfo(display,map_info,&resources);
13896       if (visual_info == (XVisualInfo *) NULL)
13897         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13898           resources.visual_type);
13899       /*
13900         Initialize window info.
13901       */
13902       window_info.ximage=(XImage *) NULL;
13903       window_info.matte_image=(XImage *) NULL;
13904       window_info.pixmap=(Pixmap) NULL;
13905       window_info.matte_pixmap=(Pixmap) NULL;
13906     }
13907   /*
13908     Free previous root colors.
13909   */
13910   if (window_info.id == root_window)
13911     (void) XDestroyWindowColors(display,root_window);
13912   /*
13913     Initialize Standard Colormap.
13914   */
13915   resources.colormap=SharedColormap;
13916   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13917     exception);
13918   /*
13919     Graphic context superclass.
13920   */
13921   context_values.background=pixel.background_color.pixel;
13922   context_values.foreground=pixel.foreground_color.pixel;
13923   pixel.annotate_context=XCreateGC(display,window_info.id,
13924     (size_t) (GCBackground | GCForeground),&context_values);
13925   if (pixel.annotate_context == (GC) NULL)
13926     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13927       image->filename);
13928   /*
13929     Initialize Image window attributes.
13930   */
13931   window_info.name=AcquireString("\0");
13932   window_info.icon_name=AcquireString("\0");
13933   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13934     &resources,&window_info);
13935   /*
13936     Create the X image.
13937   */
13938   window_info.width=(unsigned int) image->columns;
13939   window_info.height=(unsigned int) image->rows;
13940   if ((image->columns != window_info.width) ||
13941       (image->rows != window_info.height))
13942     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13943       image->filename);
13944   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13945     window_attributes.width,window_attributes.height);
13946   geometry_info.width=window_info.width;
13947   geometry_info.height=window_info.height;
13948   geometry_info.x=(ssize_t) window_info.x;
13949   geometry_info.y=(ssize_t) window_info.y;
13950   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13951     &geometry_info.width,&geometry_info.height);
13952   window_info.width=(unsigned int) geometry_info.width;
13953   window_info.height=(unsigned int) geometry_info.height;
13954   window_info.x=(int) geometry_info.x;
13955   window_info.y=(int) geometry_info.y;
13956   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13957     window_info.height,exception);
13958   if (status == MagickFalse)
13959     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13960       image->filename);
13961   window_info.x=0;
13962   window_info.y=0;
13963   if (image->debug != MagickFalse )
13964     {
13965       (void) LogMagickEvent(X11Event,GetMagickModule(),
13966         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13967         (double) image->columns,(double) image->rows);
13968       if (image->colors != 0)
13969         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13970           image->colors);
13971       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13972     }
13973   /*
13974     Adjust image dimensions as specified by backdrop or geometry options.
13975   */
13976   width=(int) window_info.width;
13977   height=(int) window_info.height;
13978   if (resources.backdrop != MagickFalse )
13979     {
13980       /*
13981         Center image on window.
13982       */
13983       window_info.x=(window_attributes.width/2)-
13984         (window_info.ximage->width/2);
13985       window_info.y=(window_attributes.height/2)-
13986         (window_info.ximage->height/2);
13987       width=window_attributes.width;
13988       height=window_attributes.height;
13989     }
13990   if ((resources.image_geometry != (char *) NULL) &&
13991       (*resources.image_geometry != '\0'))
13992     {
13993       char
13994         default_geometry[MagickPathExtent];
13995 
13996       int
13997         flags,
13998         gravity;
13999 
14000       XSizeHints
14001         *size_hints;
14002 
14003       /*
14004         User specified geometry.
14005       */
14006       size_hints=XAllocSizeHints();
14007       if (size_hints == (XSizeHints *) NULL)
14008         ThrowXWindowFatalException(ResourceLimitFatalError,
14009           "MemoryAllocationFailed",image->filename);
14010       size_hints->flags=0L;
14011       (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14012         width,height);
14013       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14014         default_geometry,window_info.border_width,size_hints,&window_info.x,
14015         &window_info.y,&width,&height,&gravity);
14016       if (flags & (XValue | YValue))
14017         {
14018           width=window_attributes.width;
14019           height=window_attributes.height;
14020         }
14021       (void) XFree((void *) size_hints);
14022     }
14023   /*
14024     Create the X pixmap.
14025   */
14026   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14027     (unsigned int) height,window_info.depth);
14028   if (window_info.pixmap == (Pixmap) NULL)
14029     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14030       image->filename);
14031   /*
14032     Display pixmap on the window.
14033   */
14034   if (((unsigned int) width > window_info.width) ||
14035       ((unsigned int) height > window_info.height))
14036     (void) XFillRectangle(display,window_info.pixmap,
14037       window_info.annotate_context,0,0,(unsigned int) width,
14038       (unsigned int) height);
14039   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14040     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14041     window_info.width,(unsigned int) window_info.height);
14042   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14043   (void) XClearWindow(display,window_info.id);
14044   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14045   XDelay(display,delay == 0UL ? 10UL : delay);
14046   (void) XSync(display,MagickFalse);
14047   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14048 }
14049 
14050 /*
14051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14052 %                                                                             %
14053 %                                                                             %
14054 %                                                                             %
14055 +   X D i s p l a y I m a g e                                                 %
14056 %                                                                             %
14057 %                                                                             %
14058 %                                                                             %
14059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14060 %
14061 %  XDisplayImage() displays an image via X11.  A new image is created and
14062 %  returned if the user interactively transforms the displayed image.
14063 %
14064 %  The format of the XDisplayImage method is:
14065 %
14066 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14067 %        char **argv,int argc,Image **image,size_t *state,
14068 %        ExceptionInfo *exception)
14069 %
14070 %  A description of each parameter follows:
14071 %
14072 %    o nexus:  Method XDisplayImage returns an image when the
14073 %      user chooses 'Open Image' from the command menu or picks a tile
14074 %      from the image directory.  Otherwise a null image is returned.
14075 %
14076 %    o display: Specifies a connection to an X server;  returned from
14077 %      XOpenDisplay.
14078 %
14079 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14080 %
14081 %    o argv: Specifies the application's argument list.
14082 %
14083 %    o argc: Specifies the number of arguments.
14084 %
14085 %    o image: Specifies an address to an address of an Image structure;
14086 %
14087 %    o exception: return any errors or warnings in this structure.
14088 %
14089 */
XDisplayImage(Display * display,XResourceInfo * resource_info,char ** argv,int argc,Image ** image,size_t * state,ExceptionInfo * exception)14090 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14091   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14092 {
14093 #define MagnifySize  256  /* must be a power of 2 */
14094 #define MagickMenus  10
14095 #define MagickTitle  "Commands"
14096 
14097   const char
14098     *const CommandMenu[] =
14099     {
14100       "File",
14101       "Edit",
14102       "View",
14103       "Transform",
14104       "Enhance",
14105       "Effects",
14106       "F/X",
14107       "Image Edit",
14108       "Miscellany",
14109       "Help",
14110       (char *) NULL
14111     },
14112     *const FileMenu[] =
14113     {
14114       "Open...",
14115       "Next",
14116       "Former",
14117       "Select...",
14118       "Save...",
14119       "Print...",
14120       "Delete...",
14121       "New...",
14122       "Visual Directory...",
14123       "Quit",
14124       (char *) NULL
14125     },
14126     *const EditMenu[] =
14127     {
14128       "Undo",
14129       "Redo",
14130       "Cut",
14131       "Copy",
14132       "Paste",
14133       (char *) NULL
14134     },
14135     *const ViewMenu[] =
14136     {
14137       "Half Size",
14138       "Original Size",
14139       "Double Size",
14140       "Resize...",
14141       "Apply",
14142       "Refresh",
14143       "Restore",
14144       (char *) NULL
14145     },
14146     *const TransformMenu[] =
14147     {
14148       "Crop",
14149       "Chop",
14150       "Flop",
14151       "Flip",
14152       "Rotate Right",
14153       "Rotate Left",
14154       "Rotate...",
14155       "Shear...",
14156       "Roll...",
14157       "Trim Edges",
14158       (char *) NULL
14159     },
14160     *const EnhanceMenu[] =
14161     {
14162       "Hue...",
14163       "Saturation...",
14164       "Brightness...",
14165       "Gamma...",
14166       "Spiff",
14167       "Dull",
14168       "Contrast Stretch...",
14169       "Sigmoidal Contrast...",
14170       "Normalize",
14171       "Equalize",
14172       "Negate",
14173       "Grayscale",
14174       "Map...",
14175       "Quantize...",
14176       (char *) NULL
14177     },
14178     *const EffectsMenu[] =
14179     {
14180       "Despeckle",
14181       "Emboss",
14182       "Reduce Noise",
14183       "Add Noise...",
14184       "Sharpen...",
14185       "Blur...",
14186       "Threshold...",
14187       "Edge Detect...",
14188       "Spread...",
14189       "Shade...",
14190       "Raise...",
14191       "Segment...",
14192       (char *) NULL
14193     },
14194     *const FXMenu[] =
14195     {
14196       "Solarize...",
14197       "Sepia Tone...",
14198       "Swirl...",
14199       "Implode...",
14200       "Vignette...",
14201       "Wave...",
14202       "Oil Paint...",
14203       "Charcoal Draw...",
14204       (char *) NULL
14205     },
14206     *const ImageEditMenu[] =
14207     {
14208       "Annotate...",
14209       "Draw...",
14210       "Color...",
14211       "Matte...",
14212       "Composite...",
14213       "Add Border...",
14214       "Add Frame...",
14215       "Comment...",
14216       "Launch...",
14217       "Region of Interest...",
14218       (char *) NULL
14219     },
14220     *const MiscellanyMenu[] =
14221     {
14222       "Image Info",
14223       "Zoom Image",
14224       "Show Preview...",
14225       "Show Histogram",
14226       "Show Matte",
14227       "Background...",
14228       "Slide Show...",
14229       "Preferences...",
14230       (char *) NULL
14231     },
14232     *const HelpMenu[] =
14233     {
14234       "Overview",
14235       "Browse Documentation",
14236       "About Display",
14237       (char *) NULL
14238     },
14239     *const ShortCutsMenu[] =
14240     {
14241       "Next",
14242       "Former",
14243       "Open...",
14244       "Save...",
14245       "Print...",
14246       "Undo",
14247       "Restore",
14248       "Image Info",
14249       "Quit",
14250       (char *) NULL
14251     },
14252     *const VirtualMenu[] =
14253     {
14254       "Image Info",
14255       "Print",
14256       "Next",
14257       "Quit",
14258       (char *) NULL
14259     };
14260 
14261   const char
14262     *const *Menus[MagickMenus] =
14263     {
14264       FileMenu,
14265       EditMenu,
14266       ViewMenu,
14267       TransformMenu,
14268       EnhanceMenu,
14269       EffectsMenu,
14270       FXMenu,
14271       ImageEditMenu,
14272       MiscellanyMenu,
14273       HelpMenu
14274     };
14275 
14276   static CommandType
14277     CommandMenus[] =
14278     {
14279       NullCommand,
14280       NullCommand,
14281       NullCommand,
14282       NullCommand,
14283       NullCommand,
14284       NullCommand,
14285       NullCommand,
14286       NullCommand,
14287       NullCommand,
14288       NullCommand,
14289     },
14290     FileCommands[] =
14291     {
14292       OpenCommand,
14293       NextCommand,
14294       FormerCommand,
14295       SelectCommand,
14296       SaveCommand,
14297       PrintCommand,
14298       DeleteCommand,
14299       NewCommand,
14300       VisualDirectoryCommand,
14301       QuitCommand
14302     },
14303     EditCommands[] =
14304     {
14305       UndoCommand,
14306       RedoCommand,
14307       CutCommand,
14308       CopyCommand,
14309       PasteCommand
14310     },
14311     ViewCommands[] =
14312     {
14313       HalfSizeCommand,
14314       OriginalSizeCommand,
14315       DoubleSizeCommand,
14316       ResizeCommand,
14317       ApplyCommand,
14318       RefreshCommand,
14319       RestoreCommand
14320     },
14321     TransformCommands[] =
14322     {
14323       CropCommand,
14324       ChopCommand,
14325       FlopCommand,
14326       FlipCommand,
14327       RotateRightCommand,
14328       RotateLeftCommand,
14329       RotateCommand,
14330       ShearCommand,
14331       RollCommand,
14332       TrimCommand
14333     },
14334     EnhanceCommands[] =
14335     {
14336       HueCommand,
14337       SaturationCommand,
14338       BrightnessCommand,
14339       GammaCommand,
14340       SpiffCommand,
14341       DullCommand,
14342       ContrastStretchCommand,
14343       SigmoidalContrastCommand,
14344       NormalizeCommand,
14345       EqualizeCommand,
14346       NegateCommand,
14347       GrayscaleCommand,
14348       MapCommand,
14349       QuantizeCommand
14350     },
14351     EffectsCommands[] =
14352     {
14353       DespeckleCommand,
14354       EmbossCommand,
14355       ReduceNoiseCommand,
14356       AddNoiseCommand,
14357       SharpenCommand,
14358       BlurCommand,
14359       ThresholdCommand,
14360       EdgeDetectCommand,
14361       SpreadCommand,
14362       ShadeCommand,
14363       RaiseCommand,
14364       SegmentCommand
14365     },
14366     FXCommands[] =
14367     {
14368       SolarizeCommand,
14369       SepiaToneCommand,
14370       SwirlCommand,
14371       ImplodeCommand,
14372       VignetteCommand,
14373       WaveCommand,
14374       OilPaintCommand,
14375       CharcoalDrawCommand
14376     },
14377     ImageEditCommands[] =
14378     {
14379       AnnotateCommand,
14380       DrawCommand,
14381       ColorCommand,
14382       MatteCommand,
14383       CompositeCommand,
14384       AddBorderCommand,
14385       AddFrameCommand,
14386       CommentCommand,
14387       LaunchCommand,
14388       RegionofInterestCommand
14389     },
14390     MiscellanyCommands[] =
14391     {
14392       InfoCommand,
14393       ZoomCommand,
14394       ShowPreviewCommand,
14395       ShowHistogramCommand,
14396       ShowMatteCommand,
14397       BackgroundCommand,
14398       SlideShowCommand,
14399       PreferencesCommand
14400     },
14401     HelpCommands[] =
14402     {
14403       HelpCommand,
14404       BrowseDocumentationCommand,
14405       VersionCommand
14406     },
14407     ShortCutsCommands[] =
14408     {
14409       NextCommand,
14410       FormerCommand,
14411       OpenCommand,
14412       SaveCommand,
14413       PrintCommand,
14414       UndoCommand,
14415       RestoreCommand,
14416       InfoCommand,
14417       QuitCommand
14418     },
14419     VirtualCommands[] =
14420     {
14421       InfoCommand,
14422       PrintCommand,
14423       NextCommand,
14424       QuitCommand
14425     };
14426 
14427   static CommandType
14428     *Commands[MagickMenus] =
14429     {
14430       FileCommands,
14431       EditCommands,
14432       ViewCommands,
14433       TransformCommands,
14434       EnhanceCommands,
14435       EffectsCommands,
14436       FXCommands,
14437       ImageEditCommands,
14438       MiscellanyCommands,
14439       HelpCommands
14440     };
14441 
14442   char
14443     command[MagickPathExtent],
14444     *directory,
14445     geometry[MagickPathExtent],
14446     resource_name[MagickPathExtent];
14447 
14448   CommandType
14449     command_type;
14450 
14451   Image
14452     *display_image,
14453     *nexus;
14454 
14455   int
14456     entry,
14457     id;
14458 
14459   KeySym
14460     key_symbol;
14461 
14462   MagickStatusType
14463     context_mask,
14464     status;
14465 
14466   RectangleInfo
14467     geometry_info;
14468 
14469   register int
14470     i;
14471 
14472   static char
14473     working_directory[MagickPathExtent];
14474 
14475   static XPoint
14476     vid_info;
14477 
14478   static XWindowInfo
14479     *magick_windows[MaxXWindows];
14480 
14481   static unsigned int
14482     number_windows;
14483 
14484   struct stat
14485     attributes;
14486 
14487   time_t
14488     timer,
14489     timestamp,
14490     update_time;
14491 
14492   unsigned int
14493     height,
14494     width;
14495 
14496   size_t
14497     delay;
14498 
14499   WarningHandler
14500     warning_handler;
14501 
14502   Window
14503     root_window;
14504 
14505   XClassHint
14506     *class_hints;
14507 
14508   XEvent
14509     event;
14510 
14511   XFontStruct
14512     *font_info;
14513 
14514   XGCValues
14515     context_values;
14516 
14517   XPixelInfo
14518     *icon_pixel,
14519     *pixel;
14520 
14521   XResourceInfo
14522     *icon_resources;
14523 
14524   XStandardColormap
14525     *icon_map,
14526     *map_info;
14527 
14528   XVisualInfo
14529     *icon_visual,
14530     *visual_info;
14531 
14532   XWindowChanges
14533     window_changes;
14534 
14535   XWindows
14536     *windows;
14537 
14538   XWMHints
14539     *manager_hints;
14540 
14541   assert(image != (Image **) NULL);
14542   assert((*image)->signature == MagickCoreSignature);
14543   if ((*image)->debug != MagickFalse )
14544     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14545   display_image=(*image);
14546   warning_handler=(WarningHandler) NULL;
14547   windows=XSetWindows((XWindows *) ~0);
14548   if (windows != (XWindows *) NULL)
14549     {
14550       int
14551         status;
14552 
14553       if (*working_directory == '\0')
14554         (void) CopyMagickString(working_directory,".",MagickPathExtent);
14555       status=chdir(working_directory);
14556       if (status == -1)
14557         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14558           "UnableToOpenFile","%s",working_directory);
14559       warning_handler=resource_info->display_warnings ?
14560         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14561       warning_handler=resource_info->display_warnings ?
14562         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14563     }
14564   else
14565     {
14566       /*
14567         Allocate windows structure.
14568       */
14569       resource_info->colors=display_image->colors;
14570       windows=XSetWindows(XInitializeWindows(display,resource_info));
14571       if (windows == (XWindows *) NULL)
14572         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14573           (*image)->filename);
14574       /*
14575         Initialize window id's.
14576       */
14577       number_windows=0;
14578       magick_windows[number_windows++]=(&windows->icon);
14579       magick_windows[number_windows++]=(&windows->backdrop);
14580       magick_windows[number_windows++]=(&windows->image);
14581       magick_windows[number_windows++]=(&windows->info);
14582       magick_windows[number_windows++]=(&windows->command);
14583       magick_windows[number_windows++]=(&windows->widget);
14584       magick_windows[number_windows++]=(&windows->popup);
14585       magick_windows[number_windows++]=(&windows->magnify);
14586       magick_windows[number_windows++]=(&windows->pan);
14587       for (i=0; i < (int) number_windows; i++)
14588         magick_windows[i]->id=(Window) NULL;
14589       vid_info.x=0;
14590       vid_info.y=0;
14591     }
14592   /*
14593     Initialize font info.
14594   */
14595   if (windows->font_info != (XFontStruct *) NULL)
14596     (void) XFreeFont(display,windows->font_info);
14597   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14598   if (windows->font_info == (XFontStruct *) NULL)
14599     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14600       resource_info->font);
14601   /*
14602     Initialize Standard Colormap.
14603   */
14604   map_info=windows->map_info;
14605   icon_map=windows->icon_map;
14606   visual_info=windows->visual_info;
14607   icon_visual=windows->icon_visual;
14608   pixel=windows->pixel_info;
14609   icon_pixel=windows->icon_pixel;
14610   font_info=windows->font_info;
14611   icon_resources=windows->icon_resources;
14612   class_hints=windows->class_hints;
14613   manager_hints=windows->manager_hints;
14614   root_window=XRootWindow(display,visual_info->screen);
14615   nexus=NewImageList();
14616   if (display_image->debug != MagickFalse )
14617     {
14618       (void) LogMagickEvent(X11Event,GetMagickModule(),
14619         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14620         (double) display_image->scene,(double) display_image->columns,
14621         (double) display_image->rows);
14622       if (display_image->colors != 0)
14623         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14624           display_image->colors);
14625       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14626         display_image->magick);
14627     }
14628   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14629     map_info,pixel,exception);
14630   display_image->taint=MagickFalse;
14631   /*
14632     Initialize graphic context.
14633   */
14634   windows->context.id=(Window) NULL;
14635   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14636     resource_info,&windows->context);
14637   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14638   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14639   class_hints->res_class[0]=(char) LocaleUppercase((int)
14640     class_hints->res_class[0]);
14641   manager_hints->flags=InputHint | StateHint;
14642   manager_hints->input=MagickFalse;
14643   manager_hints->initial_state=WithdrawnState;
14644   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14645     &windows->context);
14646   if (display_image->debug != MagickFalse )
14647     (void) LogMagickEvent(X11Event,GetMagickModule(),
14648       "Window id: 0x%lx (context)",windows->context.id);
14649   context_values.background=pixel->background_color.pixel;
14650   context_values.font=font_info->fid;
14651   context_values.foreground=pixel->foreground_color.pixel;
14652   context_values.graphics_exposures=MagickFalse;
14653   context_mask=(MagickStatusType)
14654     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14655   if (pixel->annotate_context != (GC) NULL)
14656     (void) XFreeGC(display,pixel->annotate_context);
14657   pixel->annotate_context=XCreateGC(display,windows->context.id,
14658     context_mask,&context_values);
14659   if (pixel->annotate_context == (GC) NULL)
14660     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14661       display_image->filename);
14662   context_values.background=pixel->depth_color.pixel;
14663   if (pixel->widget_context != (GC) NULL)
14664     (void) XFreeGC(display,pixel->widget_context);
14665   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14666     &context_values);
14667   if (pixel->widget_context == (GC) NULL)
14668     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14669       display_image->filename);
14670   context_values.background=pixel->foreground_color.pixel;
14671   context_values.foreground=pixel->background_color.pixel;
14672   context_values.plane_mask=context_values.background ^
14673     context_values.foreground;
14674   if (pixel->highlight_context != (GC) NULL)
14675     (void) XFreeGC(display,pixel->highlight_context);
14676   pixel->highlight_context=XCreateGC(display,windows->context.id,
14677     (size_t) (context_mask | GCPlaneMask),&context_values);
14678   if (pixel->highlight_context == (GC) NULL)
14679     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14680       display_image->filename);
14681   (void) XDestroyWindow(display,windows->context.id);
14682   /*
14683     Initialize icon window.
14684   */
14685   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14686     icon_resources,&windows->icon);
14687   windows->icon.geometry=resource_info->icon_geometry;
14688   XBestIconSize(display,&windows->icon,display_image);
14689   windows->icon.attributes.colormap=XDefaultColormap(display,
14690     icon_visual->screen);
14691   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14692   manager_hints->flags=InputHint | StateHint;
14693   manager_hints->input=MagickFalse;
14694   manager_hints->initial_state=IconicState;
14695   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14696     &windows->icon);
14697   if (display_image->debug != MagickFalse )
14698     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14699       windows->icon.id);
14700   /*
14701     Initialize graphic context for icon window.
14702   */
14703   if (icon_pixel->annotate_context != (GC) NULL)
14704     (void) XFreeGC(display,icon_pixel->annotate_context);
14705   context_values.background=icon_pixel->background_color.pixel;
14706   context_values.foreground=icon_pixel->foreground_color.pixel;
14707   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14708     (size_t) (GCBackground | GCForeground),&context_values);
14709   if (icon_pixel->annotate_context == (GC) NULL)
14710     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14711       display_image->filename);
14712   windows->icon.annotate_context=icon_pixel->annotate_context;
14713   /*
14714     Initialize Image window.
14715   */
14716   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14717     &windows->image);
14718   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14719   if (resource_info->use_shared_memory == MagickFalse)
14720     windows->image.shared_memory=MagickFalse;
14721   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14722     {
14723       char
14724         *title;
14725 
14726       title=InterpretImageProperties(resource_info->image_info,display_image,
14727         resource_info->title,exception);
14728       (void) CloneString(&windows->image.name,title);
14729       (void) CloneString(&windows->image.icon_name,title);
14730       title=DestroyString(title);
14731     }
14732   else
14733     {
14734       char
14735         filename[MagickPathExtent],
14736         window_name[MagickPathExtent];
14737 
14738       /*
14739         Window name is the base of the filename.
14740       */
14741       GetPathComponent(display_image->magick_filename,TailPath,filename);
14742       if (display_image->scene == 0)
14743         (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14744           MagickPackageName,filename);
14745       else
14746         (void) FormatLocaleString(window_name,MagickPathExtent,
14747           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14748           (double) display_image->scene,(double) GetImageListLength(
14749           display_image));
14750       (void) CloneString(&windows->image.name,window_name);
14751       (void) CloneString(&windows->image.icon_name,filename);
14752     }
14753   if (resource_info->immutable)
14754     windows->image.immutable=MagickTrue;
14755   windows->image.use_pixmap=resource_info->use_pixmap;
14756   windows->image.geometry=resource_info->image_geometry;
14757   (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14758     XDisplayWidth(display,visual_info->screen),
14759     XDisplayHeight(display,visual_info->screen));
14760   geometry_info.width=display_image->columns;
14761   geometry_info.height=display_image->rows;
14762   geometry_info.x=0;
14763   geometry_info.y=0;
14764   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14765     &geometry_info.width,&geometry_info.height);
14766   windows->image.width=(unsigned int) geometry_info.width;
14767   windows->image.height=(unsigned int) geometry_info.height;
14768   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14769     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14770     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14771     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14772   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14773     resource_info,&windows->backdrop);
14774   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14775     {
14776       /*
14777         Initialize backdrop window.
14778       */
14779       windows->backdrop.x=0;
14780       windows->backdrop.y=0;
14781       (void) CloneString(&windows->backdrop.name,"Backdrop");
14782       windows->backdrop.flags=(size_t) (USSize | USPosition);
14783       windows->backdrop.width=(unsigned int)
14784         XDisplayWidth(display,visual_info->screen);
14785       windows->backdrop.height=(unsigned int)
14786         XDisplayHeight(display,visual_info->screen);
14787       windows->backdrop.border_width=0;
14788       windows->backdrop.immutable=MagickTrue;
14789       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14790         ButtonReleaseMask;
14791       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14792         StructureNotifyMask;
14793       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14794       manager_hints->icon_window=windows->icon.id;
14795       manager_hints->input=MagickTrue;
14796       manager_hints->initial_state=resource_info->iconic ? IconicState :
14797         NormalState;
14798       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14799         &windows->backdrop);
14800       if (display_image->debug != MagickFalse )
14801         (void) LogMagickEvent(X11Event,GetMagickModule(),
14802           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14803       (void) XMapWindow(display,windows->backdrop.id);
14804       (void) XClearWindow(display,windows->backdrop.id);
14805       if (windows->image.id != (Window) NULL)
14806         {
14807           (void) XDestroyWindow(display,windows->image.id);
14808           windows->image.id=(Window) NULL;
14809         }
14810       /*
14811         Position image in the center the backdrop.
14812       */
14813       windows->image.flags|=USPosition;
14814       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14815         (windows->image.width/2);
14816       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14817         (windows->image.height/2);
14818     }
14819   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14820   manager_hints->icon_window=windows->icon.id;
14821   manager_hints->input=MagickTrue;
14822   manager_hints->initial_state=resource_info->iconic ? IconicState :
14823     NormalState;
14824   if (windows->group_leader.id != (Window) NULL)
14825     {
14826       /*
14827         Follow the leader.
14828       */
14829       manager_hints->flags|=WindowGroupHint;
14830       manager_hints->window_group=windows->group_leader.id;
14831       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14832       if (display_image->debug != MagickFalse )
14833         (void) LogMagickEvent(X11Event,GetMagickModule(),
14834           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14835     }
14836   XMakeWindow(display,
14837     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14838     argv,argc,class_hints,manager_hints,&windows->image);
14839   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14840     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14841   if (windows->group_leader.id != (Window) NULL)
14842     (void) XSetTransientForHint(display,windows->image.id,
14843       windows->group_leader.id);
14844   if (display_image->debug != MagickFalse )
14845     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14846       windows->image.id);
14847   /*
14848     Initialize Info widget.
14849   */
14850   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14851     &windows->info);
14852   (void) CloneString(&windows->info.name,"Info");
14853   (void) CloneString(&windows->info.icon_name,"Info");
14854   windows->info.border_width=1;
14855   windows->info.x=2;
14856   windows->info.y=2;
14857   windows->info.flags|=PPosition;
14858   windows->info.attributes.win_gravity=UnmapGravity;
14859   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14860     StructureNotifyMask;
14861   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14862   manager_hints->input=MagickFalse;
14863   manager_hints->initial_state=NormalState;
14864   manager_hints->window_group=windows->image.id;
14865   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14866     &windows->info);
14867   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14868     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14869   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14870     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14871   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14872   if (windows->image.mapped != MagickFalse )
14873     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14874   if (display_image->debug != MagickFalse )
14875     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14876       windows->info.id);
14877   /*
14878     Initialize Command widget.
14879   */
14880   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14881     resource_info,&windows->command);
14882   windows->command.data=MagickMenus;
14883   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14884   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14885     resource_info->client_name);
14886   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14887     resource_name,"geometry",(char *) NULL);
14888   (void) CloneString(&windows->command.name,MagickTitle);
14889   windows->command.border_width=0;
14890   windows->command.flags|=PPosition;
14891   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14892     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14893     OwnerGrabButtonMask | StructureNotifyMask;
14894   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14895   manager_hints->input=MagickTrue;
14896   manager_hints->initial_state=NormalState;
14897   manager_hints->window_group=windows->image.id;
14898   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14899     &windows->command);
14900   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14901     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14902     HighlightHeight);
14903   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14904     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14905   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14906   if (windows->command.mapped != MagickFalse )
14907     (void) XMapRaised(display,windows->command.id);
14908   if (display_image->debug != MagickFalse )
14909     (void) LogMagickEvent(X11Event,GetMagickModule(),
14910       "Window id: 0x%lx (command)",windows->command.id);
14911   /*
14912     Initialize Widget window.
14913   */
14914   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14915     resource_info,&windows->widget);
14916   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14917     resource_info->client_name);
14918   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14919     resource_name,"geometry",(char *) NULL);
14920   windows->widget.border_width=0;
14921   windows->widget.flags|=PPosition;
14922   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14923     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14924     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14925     StructureNotifyMask;
14926   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14927   manager_hints->input=MagickTrue;
14928   manager_hints->initial_state=NormalState;
14929   manager_hints->window_group=windows->image.id;
14930   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14931     &windows->widget);
14932   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14933     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14934   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14935     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14936   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14937   if (display_image->debug != MagickFalse )
14938     (void) LogMagickEvent(X11Event,GetMagickModule(),
14939       "Window id: 0x%lx (widget)",windows->widget.id);
14940   /*
14941     Initialize popup window.
14942   */
14943   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14944     resource_info,&windows->popup);
14945   windows->popup.border_width=0;
14946   windows->popup.flags|=PPosition;
14947   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14948     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14949     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14950   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14951   manager_hints->input=MagickTrue;
14952   manager_hints->initial_state=NormalState;
14953   manager_hints->window_group=windows->image.id;
14954   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14955     &windows->popup);
14956   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14957     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14958   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14959     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14960   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14961   if (display_image->debug != MagickFalse )
14962     (void) LogMagickEvent(X11Event,GetMagickModule(),
14963       "Window id: 0x%lx (pop up)",windows->popup.id);
14964   /*
14965     Initialize Magnify window and cursor.
14966   */
14967   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14968     resource_info,&windows->magnify);
14969   if (resource_info->use_shared_memory == MagickFalse)
14970     windows->magnify.shared_memory=MagickFalse;
14971   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14972     resource_info->client_name);
14973   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14974     resource_name,"geometry",(char *) NULL);
14975   (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14976     "Magnify %uX",resource_info->magnify);
14977   if (windows->magnify.cursor != (Cursor) NULL)
14978     (void) XFreeCursor(display,windows->magnify.cursor);
14979   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14980     map_info->colormap,resource_info->background_color,
14981     resource_info->foreground_color);
14982   if (windows->magnify.cursor == (Cursor) NULL)
14983     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14984       display_image->filename);
14985   windows->magnify.width=MagnifySize;
14986   windows->magnify.height=MagnifySize;
14987   windows->magnify.flags|=PPosition;
14988   windows->magnify.min_width=MagnifySize;
14989   windows->magnify.min_height=MagnifySize;
14990   windows->magnify.width_inc=MagnifySize;
14991   windows->magnify.height_inc=MagnifySize;
14992   windows->magnify.data=resource_info->magnify;
14993   windows->magnify.attributes.cursor=windows->magnify.cursor;
14994   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14995     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14996     StructureNotifyMask;
14997   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14998   manager_hints->input=MagickTrue;
14999   manager_hints->initial_state=NormalState;
15000   manager_hints->window_group=windows->image.id;
15001   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15002     &windows->magnify);
15003   if (display_image->debug != MagickFalse )
15004     (void) LogMagickEvent(X11Event,GetMagickModule(),
15005       "Window id: 0x%lx (magnify)",windows->magnify.id);
15006   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15007   /*
15008     Initialize panning window.
15009   */
15010   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15011     resource_info,&windows->pan);
15012   (void) CloneString(&windows->pan.name,"Pan Icon");
15013   windows->pan.width=windows->icon.width;
15014   windows->pan.height=windows->icon.height;
15015   (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15016     resource_info->client_name);
15017   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15018     resource_name,"geometry",(char *) NULL);
15019   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15020     &windows->pan.width,&windows->pan.height);
15021   windows->pan.flags|=PPosition;
15022   windows->pan.immutable=MagickTrue;
15023   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15024     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15025     StructureNotifyMask;
15026   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15027   manager_hints->input=MagickFalse;
15028   manager_hints->initial_state=NormalState;
15029   manager_hints->window_group=windows->image.id;
15030   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15031     &windows->pan);
15032   if (display_image->debug != MagickFalse )
15033     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15034       windows->pan.id);
15035   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15036   if (windows->info.mapped != MagickFalse )
15037     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15038   if ((windows->image.mapped == MagickFalse) ||
15039       (windows->backdrop.id != (Window) NULL))
15040     (void) XMapWindow(display,windows->image.id);
15041   /*
15042     Set our progress monitor and warning handlers.
15043   */
15044   if (warning_handler == (WarningHandler) NULL)
15045     {
15046       warning_handler=resource_info->display_warnings ?
15047         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15048       warning_handler=resource_info->display_warnings ?
15049         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15050     }
15051   /*
15052     Initialize Image and Magnify X images.
15053   */
15054   windows->image.x=0;
15055   windows->image.y=0;
15056   windows->magnify.shape=MagickFalse;
15057   width=(unsigned int) display_image->columns;
15058   height=(unsigned int) display_image->rows;
15059   if ((display_image->columns != width) || (display_image->rows != height))
15060     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15061       display_image->filename);
15062   status=XMakeImage(display,resource_info,&windows->image,display_image,
15063     width,height,exception);
15064   if (status == MagickFalse)
15065     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15066       display_image->filename);
15067   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15068     windows->magnify.width,windows->magnify.height,exception);
15069   if (status == MagickFalse)
15070     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15071       display_image->filename);
15072   if (windows->magnify.mapped != MagickFalse )
15073     (void) XMapRaised(display,windows->magnify.id);
15074   if (windows->pan.mapped != MagickFalse )
15075     (void) XMapRaised(display,windows->pan.id);
15076   windows->image.window_changes.width=(int) display_image->columns;
15077   windows->image.window_changes.height=(int) display_image->rows;
15078   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15079   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15080   (void) XSync(display,MagickFalse);
15081   /*
15082     Respond to events.
15083   */
15084   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15085   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15086   update_time=0;
15087   if (resource_info->update != MagickFalse )
15088     {
15089       MagickBooleanType
15090         status;
15091 
15092       /*
15093         Determine when file data was last modified.
15094       */
15095       status=GetPathAttributes(display_image->filename,&attributes);
15096       if (status != MagickFalse )
15097         update_time=attributes.st_mtime;
15098     }
15099   *state&=(~FormerImageState);
15100   *state&=(~MontageImageState);
15101   *state&=(~NextImageState);
15102   do
15103   {
15104     /*
15105       Handle a window event.
15106     */
15107     if (windows->image.mapped != MagickFalse )
15108       if ((display_image->delay != 0) || (resource_info->update != 0))
15109         {
15110           if (timer < GetMagickTime())
15111             {
15112               if (resource_info->update == MagickFalse)
15113                 *state|=NextImageState | ExitState;
15114               else
15115                 {
15116                   MagickBooleanType
15117                     status;
15118 
15119                   /*
15120                     Determine if image file was modified.
15121                   */
15122                   status=GetPathAttributes(display_image->filename,&attributes);
15123                   if (status != MagickFalse )
15124                     if (update_time != attributes.st_mtime)
15125                       {
15126                         /*
15127                           Redisplay image.
15128                         */
15129                         (void) FormatLocaleString(
15130                           resource_info->image_info->filename,MagickPathExtent,
15131                           "%s:%s",display_image->magick,
15132                           display_image->filename);
15133                         nexus=ReadImage(resource_info->image_info,exception);
15134                         if (nexus != (Image *) NULL)
15135                           *state|=NextImageState | ExitState;
15136                       }
15137                   delay=display_image->delay/MagickMax(
15138                     display_image->ticks_per_second,1L);
15139                   timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15140                 }
15141             }
15142           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15143             {
15144               /*
15145                 Do not block if delay > 0.
15146               */
15147               XDelay(display,SuspendTime << 2);
15148               continue;
15149             }
15150         }
15151     timestamp=GetMagickTime();
15152     (void) XNextEvent(display,&event);
15153     if ((windows->image.stasis == MagickFalse) ||
15154         (windows->magnify.stasis == MagickFalse))
15155       {
15156         if ((GetMagickTime()-timestamp) > 0)
15157           {
15158             windows->image.stasis=MagickTrue;
15159             windows->magnify.stasis=MagickTrue;
15160           }
15161       }
15162     if (event.xany.window == windows->command.id)
15163       {
15164         /*
15165           Select a command from the Command widget.
15166         */
15167         id=XCommandWidget(display,windows,CommandMenu,&event);
15168         if (id < 0)
15169           continue;
15170         (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15171         command_type=CommandMenus[id];
15172         if (id < MagickMenus)
15173           {
15174             /*
15175               Select a command from a pop-up menu.
15176             */
15177             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15178               command);
15179             if (entry < 0)
15180               continue;
15181             (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15182             command_type=Commands[id][entry];
15183           }
15184         if (command_type != NullCommand)
15185           nexus=XMagickCommand(display,resource_info,windows,command_type,
15186             &display_image,exception);
15187         continue;
15188       }
15189     switch (event.type)
15190     {
15191       case ButtonPress:
15192       {
15193         if (display_image->debug != MagickFalse )
15194           (void) LogMagickEvent(X11Event,GetMagickModule(),
15195             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15196             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15197         if ((event.xbutton.button == Button3) &&
15198             (event.xbutton.state & Mod1Mask))
15199           {
15200             /*
15201               Convert Alt-Button3 to Button2.
15202             */
15203             event.xbutton.button=Button2;
15204             event.xbutton.state&=(~Mod1Mask);
15205           }
15206         if (event.xbutton.window == windows->backdrop.id)
15207           {
15208             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15209               event.xbutton.time);
15210             break;
15211           }
15212         if (event.xbutton.window == windows->image.id)
15213           {
15214             switch (event.xbutton.button)
15215             {
15216               case Button1:
15217               {
15218                 if (resource_info->immutable)
15219                   {
15220                     /*
15221                       Select a command from the Virtual menu.
15222                     */
15223                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15224                       command);
15225                     if (entry >= 0)
15226                       nexus=XMagickCommand(display,resource_info,windows,
15227                         VirtualCommands[entry],&display_image,exception);
15228                     break;
15229                   }
15230                 /*
15231                   Map/unmap Command widget.
15232                 */
15233                 if (windows->command.mapped != MagickFalse )
15234                   (void) XWithdrawWindow(display,windows->command.id,
15235                     windows->command.screen);
15236                 else
15237                   {
15238                     (void) XCommandWidget(display,windows,CommandMenu,
15239                       (XEvent *) NULL);
15240                     (void) XMapRaised(display,windows->command.id);
15241                   }
15242                 break;
15243               }
15244               case Button2:
15245               {
15246                 /*
15247                   User pressed the image magnify button.
15248                 */
15249                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15250                   &display_image,exception);
15251                 XMagnifyImage(display,windows,&event,exception);
15252                 break;
15253               }
15254               case Button3:
15255               {
15256                 if (resource_info->immutable)
15257                   {
15258                     /*
15259                       Select a command from the Virtual menu.
15260                     */
15261                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15262                       command);
15263                     if (entry >= 0)
15264                       nexus=XMagickCommand(display,resource_info,windows,
15265                         VirtualCommands[entry],&display_image,exception);
15266                     break;
15267                   }
15268                 if (display_image->montage != (char *) NULL)
15269                   {
15270                     /*
15271                       Open or delete a tile from a visual image directory.
15272                     */
15273                     nexus=XTileImage(display,resource_info,windows,
15274                       display_image,&event,exception);
15275                     if (nexus != (Image *) NULL)
15276                       *state|=MontageImageState | NextImageState | ExitState;
15277                     vid_info.x=(short int) windows->image.x;
15278                     vid_info.y=(short int) windows->image.y;
15279                     break;
15280                   }
15281                 /*
15282                   Select a command from the Short Cuts menu.
15283                 */
15284                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15285                   command);
15286                 if (entry >= 0)
15287                   nexus=XMagickCommand(display,resource_info,windows,
15288                     ShortCutsCommands[entry],&display_image,exception);
15289                 break;
15290               }
15291               case Button4:
15292               {
15293                 /*
15294                   Wheel up.
15295                 */
15296                 XTranslateImage(display,windows,*image,XK_Up);
15297                 break;
15298               }
15299               case Button5:
15300               {
15301                 /*
15302                   Wheel down.
15303                 */
15304                 XTranslateImage(display,windows,*image,XK_Down);
15305                 break;
15306               }
15307               default:
15308                 break;
15309             }
15310             break;
15311           }
15312         if (event.xbutton.window == windows->magnify.id)
15313           {
15314             const char
15315               *const MagnifyMenu[] =
15316               {
15317                 "2",
15318                 "4",
15319                 "5",
15320                 "6",
15321                 "7",
15322                 "8",
15323                 "9",
15324                 "3",
15325                 (char *) NULL,
15326               };
15327 
15328             int
15329               factor;
15330 
15331             static KeySym
15332               MagnifyCommands[] =
15333               {
15334                 XK_2,
15335                 XK_4,
15336                 XK_5,
15337                 XK_6,
15338                 XK_7,
15339                 XK_8,
15340                 XK_9,
15341                 XK_3
15342               };
15343 
15344             /*
15345               Select a magnify factor from the pop-up menu.
15346             */
15347             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15348             if (factor >= 0)
15349               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15350                 exception);
15351             break;
15352           }
15353         if (event.xbutton.window == windows->pan.id)
15354           {
15355             switch (event.xbutton.button)
15356             {
15357               case Button4:
15358               {
15359                 /*
15360                   Wheel up.
15361                 */
15362                 XTranslateImage(display,windows,*image,XK_Up);
15363                 break;
15364               }
15365               case Button5:
15366               {
15367                 /*
15368                   Wheel down.
15369                 */
15370                 XTranslateImage(display,windows,*image,XK_Down);
15371                 break;
15372               }
15373               default:
15374               {
15375                 XPanImage(display,windows,&event,exception);
15376                 break;
15377               }
15378             }
15379             break;
15380           }
15381         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15382           1L);
15383         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15384         break;
15385       }
15386       case ButtonRelease:
15387       {
15388         if (display_image->debug != MagickFalse )
15389           (void) LogMagickEvent(X11Event,GetMagickModule(),
15390             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15391             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15392         break;
15393       }
15394       case ClientMessage:
15395       {
15396         if (display_image->debug != MagickFalse )
15397           (void) LogMagickEvent(X11Event,GetMagickModule(),
15398             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15399             event.xclient.message_type,event.xclient.format,(unsigned long)
15400             event.xclient.data.l[0]);
15401         if (event.xclient.message_type == windows->im_protocols)
15402           {
15403             if (*event.xclient.data.l == (long) windows->im_update_widget)
15404               {
15405                 (void) CloneString(&windows->command.name,MagickTitle);
15406                 windows->command.data=MagickMenus;
15407                 (void) XCommandWidget(display,windows,CommandMenu,
15408                   (XEvent *) NULL);
15409                 break;
15410               }
15411             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15412               {
15413                 /*
15414                   Update graphic context and window colormap.
15415                 */
15416                 for (i=0; i < (int) number_windows; i++)
15417                 {
15418                   if (magick_windows[i]->id == windows->icon.id)
15419                     continue;
15420                   context_values.background=pixel->background_color.pixel;
15421                   context_values.foreground=pixel->foreground_color.pixel;
15422                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15423                     context_mask,&context_values);
15424                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15425                     context_mask,&context_values);
15426                   context_values.background=pixel->foreground_color.pixel;
15427                   context_values.foreground=pixel->background_color.pixel;
15428                   context_values.plane_mask=context_values.background ^
15429                     context_values.foreground;
15430                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15431                     (size_t) (context_mask | GCPlaneMask),
15432                     &context_values);
15433                   magick_windows[i]->attributes.background_pixel=
15434                     pixel->background_color.pixel;
15435                   magick_windows[i]->attributes.border_pixel=
15436                     pixel->border_color.pixel;
15437                   magick_windows[i]->attributes.colormap=map_info->colormap;
15438                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15439                     (unsigned long) magick_windows[i]->mask,
15440                     &magick_windows[i]->attributes);
15441                 }
15442                 if (windows->pan.mapped != MagickFalse )
15443                   {
15444                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15445                       windows->pan.pixmap);
15446                     (void) XClearWindow(display,windows->pan.id);
15447                     XDrawPanRectangle(display,windows);
15448                   }
15449                 if (windows->backdrop.id != (Window) NULL)
15450                   (void) XInstallColormap(display,map_info->colormap);
15451                 break;
15452               }
15453             if (*event.xclient.data.l == (long) windows->im_former_image)
15454               {
15455                 *state|=FormerImageState | ExitState;
15456                 break;
15457               }
15458             if (*event.xclient.data.l == (long) windows->im_next_image)
15459               {
15460                 *state|=NextImageState | ExitState;
15461                 break;
15462               }
15463             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15464               {
15465                 *state|=RetainColorsState;
15466                 break;
15467               }
15468             if (*event.xclient.data.l == (long) windows->im_exit)
15469               {
15470                 *state|=ExitState;
15471                 break;
15472               }
15473             break;
15474           }
15475         if (event.xclient.message_type == windows->dnd_protocols)
15476           {
15477             Atom
15478               selection,
15479               type;
15480 
15481             int
15482               format,
15483               status;
15484 
15485             unsigned char
15486               *data;
15487 
15488             unsigned long
15489               after,
15490               length;
15491 
15492             /*
15493               Display image named by the Drag-and-Drop selection.
15494             */
15495             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15496               break;
15497             selection=XInternAtom(display,"DndSelection",MagickFalse);
15498             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15499               MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15500               &length,&after,&data);
15501             if ((status != Success) || (length == 0))
15502               break;
15503             if (*event.xclient.data.l == 2)
15504               {
15505                 /*
15506                   Offix DND.
15507                 */
15508                 (void) CopyMagickString(resource_info->image_info->filename,
15509                   (char *) data,MagickPathExtent);
15510               }
15511             else
15512               {
15513                 /*
15514                   XDND.
15515                 */
15516                 if (strncmp((char *) data, "file:", 5) != 0)
15517                   {
15518                     (void) XFree((void *) data);
15519                     break;
15520                   }
15521                 (void) CopyMagickString(resource_info->image_info->filename,
15522                   ((char *) data)+5,MagickPathExtent);
15523               }
15524             nexus=ReadImage(resource_info->image_info,exception);
15525             CatchException(exception);
15526             if (nexus != (Image *) NULL)
15527               *state|=NextImageState | ExitState;
15528             (void) XFree((void *) data);
15529             break;
15530           }
15531         /*
15532           If client window delete message, exit.
15533         */
15534         if (event.xclient.message_type != windows->wm_protocols)
15535           break;
15536         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15537           break;
15538         (void) XWithdrawWindow(display,event.xclient.window,
15539           visual_info->screen);
15540         if (event.xclient.window == windows->image.id)
15541           {
15542             *state|=ExitState;
15543             break;
15544           }
15545         if (event.xclient.window == windows->pan.id)
15546           {
15547             /*
15548               Restore original image size when pan window is deleted.
15549             */
15550             windows->image.window_changes.width=windows->image.ximage->width;
15551             windows->image.window_changes.height=windows->image.ximage->height;
15552             (void) XConfigureImage(display,resource_info,windows,
15553               display_image,exception);
15554           }
15555         break;
15556       }
15557       case ConfigureNotify:
15558       {
15559         if (display_image->debug != MagickFalse )
15560           (void) LogMagickEvent(X11Event,GetMagickModule(),
15561             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15562             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15563             event.xconfigure.y,event.xconfigure.send_event);
15564         if (event.xconfigure.window == windows->image.id)
15565           {
15566             /*
15567               Image window has a new configuration.
15568             */
15569             if (event.xconfigure.send_event != 0)
15570               {
15571                 XWindowChanges
15572                   window_changes;
15573 
15574                 /*
15575                   Position the transient windows relative of the Image window.
15576                 */
15577                 if (windows->command.geometry == (char *) NULL)
15578                   if (windows->command.mapped == MagickFalse)
15579                     {
15580                       windows->command.x=event.xconfigure.x-
15581                         windows->command.width-25;
15582                       windows->command.y=event.xconfigure.y;
15583                       XConstrainWindowPosition(display,&windows->command);
15584                       window_changes.x=windows->command.x;
15585                       window_changes.y=windows->command.y;
15586                       (void) XReconfigureWMWindow(display,windows->command.id,
15587                         windows->command.screen,(unsigned int) (CWX | CWY),
15588                         &window_changes);
15589                     }
15590                 if (windows->widget.geometry == (char *) NULL)
15591                   if (windows->widget.mapped == MagickFalse)
15592                     {
15593                       windows->widget.x=event.xconfigure.x+
15594                         event.xconfigure.width/10;
15595                       windows->widget.y=event.xconfigure.y+
15596                         event.xconfigure.height/10;
15597                       XConstrainWindowPosition(display,&windows->widget);
15598                       window_changes.x=windows->widget.x;
15599                       window_changes.y=windows->widget.y;
15600                       (void) XReconfigureWMWindow(display,windows->widget.id,
15601                         windows->widget.screen,(unsigned int) (CWX | CWY),
15602                         &window_changes);
15603                     }
15604                 if (windows->magnify.geometry == (char *) NULL)
15605                   if (windows->magnify.mapped == MagickFalse)
15606                     {
15607                       windows->magnify.x=event.xconfigure.x+
15608                         event.xconfigure.width+25;
15609                       windows->magnify.y=event.xconfigure.y;
15610                       XConstrainWindowPosition(display,&windows->magnify);
15611                       window_changes.x=windows->magnify.x;
15612                       window_changes.y=windows->magnify.y;
15613                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15614                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15615                         &window_changes);
15616                     }
15617                 if (windows->pan.geometry == (char *) NULL)
15618                   if (windows->pan.mapped == MagickFalse)
15619                     {
15620                       windows->pan.x=event.xconfigure.x+
15621                         event.xconfigure.width+25;
15622                       windows->pan.y=event.xconfigure.y+
15623                         windows->magnify.height+50;
15624                       XConstrainWindowPosition(display,&windows->pan);
15625                       window_changes.x=windows->pan.x;
15626                       window_changes.y=windows->pan.y;
15627                       (void) XReconfigureWMWindow(display,windows->pan.id,
15628                         windows->pan.screen,(unsigned int) (CWX | CWY),
15629                         &window_changes);
15630                     }
15631               }
15632             if ((event.xconfigure.width == (int) windows->image.width) &&
15633                 (event.xconfigure.height == (int) windows->image.height))
15634               break;
15635             windows->image.width=(unsigned int) event.xconfigure.width;
15636             windows->image.height=(unsigned int) event.xconfigure.height;
15637             windows->image.x=0;
15638             windows->image.y=0;
15639             if (display_image->montage != (char *) NULL)
15640               {
15641                 windows->image.x=vid_info.x;
15642                 windows->image.y=vid_info.y;
15643               }
15644             if (windows->image.mapped != MagickFalse &&
15645                 windows->image.stasis != MagickFalse )
15646               {
15647                 /*
15648                   Update image window configuration.
15649                 */
15650                 windows->image.window_changes.width=event.xconfigure.width;
15651                 windows->image.window_changes.height=event.xconfigure.height;
15652                 (void) XConfigureImage(display,resource_info,windows,
15653                   display_image,exception);
15654               }
15655             /*
15656               Update pan window configuration.
15657             */
15658             if ((event.xconfigure.width < windows->image.ximage->width) ||
15659                 (event.xconfigure.height < windows->image.ximage->height))
15660               {
15661                 (void) XMapRaised(display,windows->pan.id);
15662                 XDrawPanRectangle(display,windows);
15663               }
15664             else
15665               if (windows->pan.mapped != MagickFalse )
15666                 (void) XWithdrawWindow(display,windows->pan.id,
15667                   windows->pan.screen);
15668             break;
15669           }
15670         if (event.xconfigure.window == windows->magnify.id)
15671           {
15672             unsigned int
15673               magnify;
15674 
15675             /*
15676               Magnify window has a new configuration.
15677             */
15678             windows->magnify.width=(unsigned int) event.xconfigure.width;
15679             windows->magnify.height=(unsigned int) event.xconfigure.height;
15680             if (windows->magnify.mapped == MagickFalse)
15681               break;
15682             magnify=1;
15683             while ((int) magnify <= event.xconfigure.width)
15684               magnify<<=1;
15685             while ((int) magnify <= event.xconfigure.height)
15686               magnify<<=1;
15687             magnify>>=1;
15688             if (((int) magnify != event.xconfigure.width) ||
15689                 ((int) magnify != event.xconfigure.height))
15690               {
15691                 window_changes.width=(int) magnify;
15692                 window_changes.height=(int) magnify;
15693                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15694                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15695                   &window_changes);
15696                 break;
15697               }
15698             if (windows->magnify.mapped != MagickFalse &&
15699                 windows->magnify.stasis != MagickFalse )
15700               {
15701                 status=XMakeImage(display,resource_info,&windows->magnify,
15702                   display_image,windows->magnify.width,windows->magnify.height,
15703                   exception);
15704                 XMakeMagnifyImage(display,windows,exception);
15705               }
15706             break;
15707           }
15708         if (windows->magnify.mapped != MagickFalse &&
15709             (event.xconfigure.window == windows->pan.id))
15710           {
15711             /*
15712               Pan icon window has a new configuration.
15713             */
15714             if (event.xconfigure.send_event != 0)
15715               {
15716                 windows->pan.x=event.xconfigure.x;
15717                 windows->pan.y=event.xconfigure.y;
15718               }
15719             windows->pan.width=(unsigned int) event.xconfigure.width;
15720             windows->pan.height=(unsigned int) event.xconfigure.height;
15721             break;
15722           }
15723         if (event.xconfigure.window == windows->icon.id)
15724           {
15725             /*
15726               Icon window has a new configuration.
15727             */
15728             windows->icon.width=(unsigned int) event.xconfigure.width;
15729             windows->icon.height=(unsigned int) event.xconfigure.height;
15730             break;
15731           }
15732         break;
15733       }
15734       case DestroyNotify:
15735       {
15736         /*
15737           Group leader has exited.
15738         */
15739         if (display_image->debug != MagickFalse )
15740           (void) LogMagickEvent(X11Event,GetMagickModule(),
15741             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15742         if (event.xdestroywindow.window == windows->group_leader.id)
15743           {
15744             *state|=ExitState;
15745             break;
15746           }
15747         break;
15748       }
15749       case EnterNotify:
15750       {
15751         /*
15752           Selectively install colormap.
15753         */
15754         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15755           if (event.xcrossing.mode != NotifyUngrab)
15756             XInstallColormap(display,map_info->colormap);
15757         break;
15758       }
15759       case Expose:
15760       {
15761         if (display_image->debug != MagickFalse )
15762           (void) LogMagickEvent(X11Event,GetMagickModule(),
15763             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15764             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15765             event.xexpose.y);
15766         /*
15767           Refresh windows that are now exposed.
15768         */
15769         if ((event.xexpose.window == windows->image.id) &&
15770             windows->image.mapped != MagickFalse )
15771           {
15772             XRefreshWindow(display,&windows->image,&event);
15773             delay=display_image->delay/MagickMax(
15774               display_image->ticks_per_second,1L);
15775             timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15776             break;
15777           }
15778         if ((event.xexpose.window == windows->magnify.id) &&
15779             windows->magnify.mapped != MagickFalse)
15780           {
15781             XMakeMagnifyImage(display,windows,exception);
15782             break;
15783           }
15784         if (event.xexpose.window == windows->pan.id)
15785           {
15786             XDrawPanRectangle(display,windows);
15787             break;
15788           }
15789         if (event.xexpose.window == windows->icon.id)
15790           {
15791             XRefreshWindow(display,&windows->icon,&event);
15792             break;
15793           }
15794         break;
15795       }
15796       case KeyPress:
15797       {
15798         int
15799           length;
15800 
15801         /*
15802           Respond to a user key press.
15803         */
15804         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15805           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15806         *(command+length)='\0';
15807         if (display_image->debug != MagickFalse )
15808           (void) LogMagickEvent(X11Event,GetMagickModule(),
15809             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15810             key_symbol,command);
15811         if (event.xkey.window == windows->image.id)
15812           {
15813             command_type=XImageWindowCommand(display,resource_info,windows,
15814               event.xkey.state,key_symbol,&display_image,exception);
15815             if (command_type != NullCommand)
15816               nexus=XMagickCommand(display,resource_info,windows,command_type,
15817                 &display_image,exception);
15818           }
15819         if (event.xkey.window == windows->magnify.id)
15820           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15821             exception);
15822         if (event.xkey.window == windows->pan.id)
15823           {
15824             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15825               (void) XWithdrawWindow(display,windows->pan.id,
15826                 windows->pan.screen);
15827             else
15828               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15829                 XTextViewHelp(display,resource_info,windows,MagickFalse,
15830                   "Help Viewer - Image Pan",ImagePanHelp);
15831               else
15832                 XTranslateImage(display,windows,*image,key_symbol);
15833           }
15834         delay=display_image->delay/MagickMax(
15835           display_image->ticks_per_second,1L);
15836         timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15837         break;
15838       }
15839       case KeyRelease:
15840       {
15841         /*
15842           Respond to a user key release.
15843         */
15844         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15845           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15846         if (display_image->debug != MagickFalse )
15847           (void) LogMagickEvent(X11Event,GetMagickModule(),
15848             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15849         break;
15850       }
15851       case LeaveNotify:
15852       {
15853         /*
15854           Selectively uninstall colormap.
15855         */
15856         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15857           if (event.xcrossing.mode != NotifyUngrab)
15858             XUninstallColormap(display,map_info->colormap);
15859         break;
15860       }
15861       case MapNotify:
15862       {
15863         if (display_image->debug != MagickFalse )
15864           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15865             event.xmap.window);
15866         if (event.xmap.window == windows->backdrop.id)
15867           {
15868             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15869               CurrentTime);
15870             windows->backdrop.mapped=MagickTrue;
15871             break;
15872           }
15873         if (event.xmap.window == windows->image.id)
15874           {
15875             if (windows->backdrop.id != (Window) NULL)
15876               (void) XInstallColormap(display,map_info->colormap);
15877             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15878               {
15879                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15880                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15881               }
15882             if (((int) windows->image.width < windows->image.ximage->width) ||
15883                 ((int) windows->image.height < windows->image.ximage->height))
15884               (void) XMapRaised(display,windows->pan.id);
15885             windows->image.mapped=MagickTrue;
15886             break;
15887           }
15888         if (event.xmap.window == windows->magnify.id)
15889           {
15890             XMakeMagnifyImage(display,windows,exception);
15891             windows->magnify.mapped=MagickTrue;
15892             (void) XWithdrawWindow(display,windows->info.id,
15893               windows->info.screen);
15894             break;
15895           }
15896         if (event.xmap.window == windows->pan.id)
15897           {
15898             XMakePanImage(display,resource_info,windows,display_image,
15899               exception);
15900             windows->pan.mapped=MagickTrue;
15901             break;
15902           }
15903         if (event.xmap.window == windows->info.id)
15904           {
15905             windows->info.mapped=MagickTrue;
15906             break;
15907           }
15908         if (event.xmap.window == windows->icon.id)
15909           {
15910             MagickBooleanType
15911               taint;
15912 
15913             /*
15914               Create an icon image.
15915             */
15916             taint=display_image->taint;
15917             XMakeStandardColormap(display,icon_visual,icon_resources,
15918               display_image,icon_map,icon_pixel,exception);
15919             (void) XMakeImage(display,icon_resources,&windows->icon,
15920               display_image,windows->icon.width,windows->icon.height,
15921               exception);
15922             display_image->taint=taint;
15923             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15924               windows->icon.pixmap);
15925             (void) XClearWindow(display,windows->icon.id);
15926             (void) XWithdrawWindow(display,windows->info.id,
15927               windows->info.screen);
15928             windows->icon.mapped=MagickTrue;
15929             break;
15930           }
15931         if (event.xmap.window == windows->command.id)
15932           {
15933             windows->command.mapped=MagickTrue;
15934             break;
15935           }
15936         if (event.xmap.window == windows->popup.id)
15937           {
15938             windows->popup.mapped=MagickTrue;
15939             break;
15940           }
15941         if (event.xmap.window == windows->widget.id)
15942           {
15943             windows->widget.mapped=MagickTrue;
15944             break;
15945           }
15946         break;
15947       }
15948       case MappingNotify:
15949       {
15950         (void) XRefreshKeyboardMapping(&event.xmapping);
15951         break;
15952       }
15953       case NoExpose:
15954         break;
15955       case PropertyNotify:
15956       {
15957         Atom
15958           type;
15959 
15960         int
15961           format,
15962           status;
15963 
15964         unsigned char
15965           *data;
15966 
15967         unsigned long
15968           after,
15969           length;
15970 
15971         if (display_image->debug != MagickFalse )
15972           (void) LogMagickEvent(X11Event,GetMagickModule(),
15973             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15974             event.xproperty.atom,event.xproperty.state);
15975         if (event.xproperty.atom != windows->im_remote_command)
15976           break;
15977         /*
15978           Display image named by the remote command protocol.
15979         */
15980         status=XGetWindowProperty(display,event.xproperty.window,
15981           event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15982           AnyPropertyType,&type,&format,&length,&after,&data);
15983         if ((status != Success) || (length == 0))
15984           break;
15985         if (LocaleCompare((char *) data,"-quit") == 0)
15986           {
15987             XClientMessage(display,windows->image.id,windows->im_protocols,
15988               windows->im_exit,CurrentTime);
15989             (void) XFree((void *) data);
15990             break;
15991           }
15992         (void) CopyMagickString(resource_info->image_info->filename,
15993           (char *) data,MagickPathExtent);
15994         (void) XFree((void *) data);
15995         nexus=ReadImage(resource_info->image_info,exception);
15996         CatchException(exception);
15997         if (nexus != (Image *) NULL)
15998           *state|=NextImageState | ExitState;
15999         break;
16000       }
16001       case ReparentNotify:
16002       {
16003         if (display_image->debug != MagickFalse )
16004           (void) LogMagickEvent(X11Event,GetMagickModule(),
16005             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16006             event.xreparent.window);
16007         break;
16008       }
16009       case UnmapNotify:
16010       {
16011         if (display_image->debug != MagickFalse )
16012           (void) LogMagickEvent(X11Event,GetMagickModule(),
16013             "Unmap Notify: 0x%lx",event.xunmap.window);
16014         if (event.xunmap.window == windows->backdrop.id)
16015           {
16016             windows->backdrop.mapped=MagickFalse;
16017             break;
16018           }
16019         if (event.xunmap.window == windows->image.id)
16020           {
16021             windows->image.mapped=MagickFalse;
16022             break;
16023           }
16024         if (event.xunmap.window == windows->magnify.id)
16025           {
16026             windows->magnify.mapped=MagickFalse;
16027             break;
16028           }
16029         if (event.xunmap.window == windows->pan.id)
16030           {
16031             windows->pan.mapped=MagickFalse;
16032             break;
16033           }
16034         if (event.xunmap.window == windows->info.id)
16035           {
16036             windows->info.mapped=MagickFalse;
16037             break;
16038           }
16039         if (event.xunmap.window == windows->icon.id)
16040           {
16041             if (map_info->colormap == icon_map->colormap)
16042               XConfigureImageColormap(display,resource_info,windows,
16043                 display_image,exception);
16044             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16045               icon_pixel);
16046             windows->icon.mapped=MagickFalse;
16047             break;
16048           }
16049         if (event.xunmap.window == windows->command.id)
16050           {
16051             windows->command.mapped=MagickFalse;
16052             break;
16053           }
16054         if (event.xunmap.window == windows->popup.id)
16055           {
16056             if (windows->backdrop.id != (Window) NULL)
16057               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16058                 CurrentTime);
16059             windows->popup.mapped=MagickFalse;
16060             break;
16061           }
16062         if (event.xunmap.window == windows->widget.id)
16063           {
16064             if (windows->backdrop.id != (Window) NULL)
16065               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16066                 CurrentTime);
16067             windows->widget.mapped=MagickFalse;
16068             break;
16069           }
16070         break;
16071       }
16072       default:
16073       {
16074         if (display_image->debug != MagickFalse )
16075           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16076             event.type);
16077         break;
16078       }
16079     }
16080   } while (!(*state & ExitState));
16081   if ((*state & ExitState) == 0)
16082     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16083       &display_image,exception);
16084   else
16085     if (resource_info->confirm_edit != MagickFalse )
16086       {
16087         /*
16088           Query user if image has changed.
16089         */
16090         if ((resource_info->immutable == MagickFalse) &&
16091             display_image->taint != MagickFalse)
16092           {
16093             int
16094               status;
16095 
16096             status=XConfirmWidget(display,windows,"Your image changed.",
16097               "Do you want to save it");
16098             if (status == 0)
16099               *state&=(~ExitState);
16100             else
16101               if (status > 0)
16102                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16103                   &display_image,exception);
16104           }
16105       }
16106   if ((windows->visual_info->klass == GrayScale) ||
16107       (windows->visual_info->klass == PseudoColor) ||
16108       (windows->visual_info->klass == DirectColor))
16109     {
16110       /*
16111         Withdraw pan and Magnify window.
16112       */
16113       if (windows->info.mapped != MagickFalse )
16114         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16115       if (windows->magnify.mapped != MagickFalse )
16116         (void) XWithdrawWindow(display,windows->magnify.id,
16117           windows->magnify.screen);
16118       if (windows->command.mapped != MagickFalse )
16119         (void) XWithdrawWindow(display,windows->command.id,
16120           windows->command.screen);
16121     }
16122   if (windows->pan.mapped != MagickFalse )
16123     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16124   if (resource_info->backdrop == MagickFalse)
16125     if (windows->backdrop.mapped)
16126       {
16127         (void) XWithdrawWindow(display,windows->backdrop.id,
16128           windows->backdrop.screen);
16129         (void) XDestroyWindow(display,windows->backdrop.id);
16130         windows->backdrop.id=(Window) NULL;
16131         (void) XWithdrawWindow(display,windows->image.id,
16132           windows->image.screen);
16133         (void) XDestroyWindow(display,windows->image.id);
16134         windows->image.id=(Window) NULL;
16135       }
16136   XSetCursorState(display,windows,MagickTrue);
16137   XCheckRefreshWindows(display,windows);
16138   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16139     *state&=(~ExitState);
16140   if (*state & ExitState)
16141     {
16142       /*
16143         Free Standard Colormap.
16144       */
16145       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16146       if (resource_info->map_type == (char *) NULL)
16147         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16148       /*
16149         Free X resources.
16150       */
16151       if (resource_info->copy_image != (Image *) NULL)
16152         {
16153           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16154           resource_info->copy_image=NewImageList();
16155         }
16156       DestroyXResources();
16157     }
16158   (void) XSync(display,MagickFalse);
16159   /*
16160     Restore our progress monitor and warning handlers.
16161   */
16162   (void) SetErrorHandler(warning_handler);
16163   (void) SetWarningHandler(warning_handler);
16164   /*
16165     Change to home directory.
16166   */
16167   directory=getcwd(working_directory,MagickPathExtent);
16168   (void) directory;
16169   {
16170     int
16171       status;
16172 
16173     if (*resource_info->home_directory == '\0')
16174       (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16175     status=chdir(resource_info->home_directory);
16176     if (status == -1)
16177       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16178         "UnableToOpenFile","%s",resource_info->home_directory);
16179   }
16180   *image=display_image;
16181   return(nexus);
16182 }
16183 #else
16184 
16185 /*
16186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16187 %                                                                             %
16188 %                                                                             %
16189 %                                                                             %
16190 +   D i s p l a y I m a g e s                                                 %
16191 %                                                                             %
16192 %                                                                             %
16193 %                                                                             %
16194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16195 %
16196 %  DisplayImages() displays an image sequence to any X window screen.  It
16197 %  returns a value other than 0 if successful.  Check the exception member
16198 %  of image to determine the reason for any failure.
16199 %
16200 %  The format of the DisplayImages method is:
16201 %
16202 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16203 %        Image *images,ExceptionInfo *exception)
16204 %
16205 %  A description of each parameter follows:
16206 %
16207 %    o image_info: the image info.
16208 %
16209 %    o image: the image.
16210 %
16211 %    o exception: return any errors or warnings in this structure.
16212 %
16213 */
DisplayImages(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)16214 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16215   Image *image,ExceptionInfo *exception)
16216 {
16217   assert(image_info != (const ImageInfo *) NULL);
16218   assert(image_info->signature == MagickCoreSignature);
16219   assert(image != (Image *) NULL);
16220   assert(image->signature == MagickCoreSignature);
16221   (void) image_info;
16222   if (image->debug != MagickFalse )
16223     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16224   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16225     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16226   return(MagickFalse);
16227 }
16228 
16229 /*
16230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16231 %                                                                             %
16232 %                                                                             %
16233 %                                                                             %
16234 +   R e m o t e D i s p l a y C o m m a n d                                   %
16235 %                                                                             %
16236 %                                                                             %
16237 %                                                                             %
16238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16239 %
16240 %  RemoteDisplayCommand() encourages a remote display program to display the
16241 %  specified image filename.
16242 %
16243 %  The format of the RemoteDisplayCommand method is:
16244 %
16245 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16246 %        const char *window,const char *filename,ExceptionInfo *exception)
16247 %
16248 %  A description of each parameter follows:
16249 %
16250 %    o image_info: the image info.
16251 %
16252 %    o window: Specifies the name or id of an X window.
16253 %
16254 %    o filename: the name of the image filename to display.
16255 %
16256 %    o exception: return any errors or warnings in this structure.
16257 %
16258 */
RemoteDisplayCommand(const ImageInfo * image_info,const char * window,const char * filename,ExceptionInfo * exception)16259 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16260   const char *window,const char *filename,ExceptionInfo *exception)
16261 {
16262   assert(image_info != (const ImageInfo *) NULL);
16263   assert(image_info->signature == MagickCoreSignature);
16264   assert(filename != (char *) NULL);
16265   (void) window;
16266   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16267   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16268     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16269   return(MagickFalse);
16270 }
16271 #endif
16272