MagickCore  7.1.1-43
Convert, Edit, Or Compose Bitmap Images
display.c
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 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/locale-private.h"
70 #include "MagickCore/log.h"
71 #include "MagickCore/magick.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/monitor.h"
74 #include "MagickCore/monitor-private.h"
75 #include "MagickCore/montage.h"
76 #include "MagickCore/nt-base-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/paint.h"
79 #include "MagickCore/pixel.h"
80 #include "MagickCore/pixel-accessor.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/resize.h"
85 #include "MagickCore/resource_.h"
86 #include "MagickCore/shear.h"
87 #include "MagickCore/segment.h"
88 #include "MagickCore/statistic.h"
89 #include "MagickCore/string_.h"
90 #include "MagickCore/string-private.h"
91 #include "MagickCore/timer-private.h"
92 #include "MagickCore/transform.h"
93 #include "MagickCore/transform-private.h"
94 #include "MagickCore/threshold.h"
95 #include "MagickCore/utility.h"
96 #include "MagickCore/utility-private.h"
97 #include "MagickCore/version.h"
98 #include "MagickCore/visual-effects.h"
99 #include "MagickCore/widget.h"
100 #include "MagickCore/widget-private.h"
101 #include "MagickCore/xwindow.h"
102 #include "MagickCore/xwindow-private.h"
103 
104 #if defined(MAGICKCORE_X11_DELEGATE)
105 /*
106  Define declarations.
107 */
108 #define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
109 
110 /*
111  Constant declarations.
112 */
113 static const unsigned char
114  HighlightBitmap[8] =
115  {
116  0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
117  },
118  OpaqueBitmap[8] =
119  {
120  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
121  },
122  ShadowBitmap[8] =
123  {
124  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
125  };
126 
127 /*
128  Help widget declarations.
129 */
130 static const char
131  ImageAnnotateHelp[] =
132  {
133  "In annotate mode, the Command widget has these options:\n"
134  "\n"
135  " Font Name\n"
136  " fixed\n"
137  " variable\n"
138  " 5x8\n"
139  " 6x10\n"
140  " 7x13bold\n"
141  " 8x13bold\n"
142  " 9x15bold\n"
143  " 10x20\n"
144  " 12x24\n"
145  " Browser...\n"
146  " Font Color\n"
147  " black\n"
148  " blue\n"
149  " cyan\n"
150  " green\n"
151  " gray\n"
152  " red\n"
153  " magenta\n"
154  " yellow\n"
155  " white\n"
156  " transparent\n"
157  " Browser...\n"
158  " Font Color\n"
159  " black\n"
160  " blue\n"
161  " cyan\n"
162  " green\n"
163  " gray\n"
164  " red\n"
165  " magenta\n"
166  " yellow\n"
167  " white\n"
168  " transparent\n"
169  " Browser...\n"
170  " Rotate Text\n"
171  " -90\n"
172  " -45\n"
173  " -30\n"
174  " 0\n"
175  " 30\n"
176  " 45\n"
177  " 90\n"
178  " 180\n"
179  " Dialog...\n"
180  " Help\n"
181  " Dismiss\n"
182  "\n"
183  "Choose a font name from the Font Name sub-menu. Additional\n"
184  "font names can be specified with the font browser. You can\n"
185  "change the menu names by setting the X resources font1\n"
186  "through font9.\n"
187  "\n"
188  "Choose a font color from the Font Color sub-menu.\n"
189  "Additional font colors can be specified with the color\n"
190  "browser. You can change the menu colors by setting the X\n"
191  "resources pen1 through pen9.\n"
192  "\n"
193  "If you select the color browser and press Grab, you can\n"
194  "choose the font color by moving the pointer to the desired\n"
195  "color on the screen and press any button.\n"
196  "\n"
197  "If you choose to rotate the text, choose Rotate Text from the\n"
198  "menu and select an angle. Typically you will only want to\n"
199  "rotate one line of text at a time. Depending on the angle you\n"
200  "choose, subsequent lines may end up overwriting each other.\n"
201  "\n"
202  "Choosing a font and its color is optional. The default font\n"
203  "is fixed and the default color is black. However, you must\n"
204  "choose a location to begin entering text and press button 1.\n"
205  "An underscore character will appear at the location of the\n"
206  "pointer. The cursor changes to a pencil to indicate you are\n"
207  "in text mode. To exit immediately, press Dismiss.\n"
208  "\n"
209  "In text mode, any key presses will display the character at\n"
210  "the location of the underscore and advance the underscore\n"
211  "cursor. Enter your text and once completed press Apply to\n"
212  "finish your image annotation. To correct errors press BACK\n"
213  "SPACE. To delete an entire line of text, press DELETE. Any\n"
214  "text that exceeds the boundaries of the image window is\n"
215  "automagically continued onto the next line.\n"
216  "\n"
217  "The actual color you request for the font is saved in the\n"
218  "image. However, the color that appears in your image window\n"
219  "may be different. For example, on a monochrome screen the\n"
220  "text will appear black or white even if you choose the color\n"
221  "red as the font color. However, the image saved to a file\n"
222  "with -write is written with red lettering. To assure the\n"
223  "correct color text in the final image, any PseudoClass image\n"
224  "is promoted to DirectClass (see miff(5)). To force a\n"
225  "PseudoClass image to remain PseudoClass, use -colors.\n"
226  },
227  ImageChopHelp[] =
228  {
229  "In chop mode, the Command widget has these options:\n"
230  "\n"
231  " Direction\n"
232  " horizontal\n"
233  " vertical\n"
234  " Help\n"
235  " Dismiss\n"
236  "\n"
237  "If the you choose the horizontal direction (this the\n"
238  "default), the area of the image between the two horizontal\n"
239  "endpoints of the chop line is removed. Otherwise, the area\n"
240  "of the image between the two vertical endpoints of the chop\n"
241  "line is removed.\n"
242  "\n"
243  "Select a location within the image window to begin your chop,\n"
244  "press and hold any button. Next, move the pointer to\n"
245  "another location in the image. As you move a line will\n"
246  "connect the initial location and the pointer. When you\n"
247  "release the button, the area within the image to chop is\n"
248  "determined by which direction you choose from the Command\n"
249  "widget.\n"
250  "\n"
251  "To cancel the image chopping, move the pointer back to the\n"
252  "starting point of the line and release the button.\n"
253  },
254  ImageColorEditHelp[] =
255  {
256  "In color edit mode, the Command widget has these options:\n"
257  "\n"
258  " Method\n"
259  " point\n"
260  " replace\n"
261  " floodfill\n"
262  " filltoborder\n"
263  " reset\n"
264  " Pixel Color\n"
265  " black\n"
266  " blue\n"
267  " cyan\n"
268  " green\n"
269  " gray\n"
270  " red\n"
271  " magenta\n"
272  " yellow\n"
273  " white\n"
274  " Browser...\n"
275  " Border Color\n"
276  " black\n"
277  " blue\n"
278  " cyan\n"
279  " green\n"
280  " gray\n"
281  " red\n"
282  " magenta\n"
283  " yellow\n"
284  " white\n"
285  " Browser...\n"
286  " Fuzz\n"
287  " 0%\n"
288  " 2%\n"
289  " 5%\n"
290  " 10%\n"
291  " 15%\n"
292  " Dialog...\n"
293  " Undo\n"
294  " Help\n"
295  " Dismiss\n"
296  "\n"
297  "Choose a color editing method from the Method sub-menu\n"
298  "of the Command widget. The point method recolors any pixel\n"
299  "selected with the pointer until the button is released. The\n"
300  "replace method recolors any pixel that matches the color of\n"
301  "the pixel you select with a button press. Floodfill recolors\n"
302  "any pixel that matches the color of the pixel you select with\n"
303  "a button press and is a neighbor. Whereas filltoborder recolors\n"
304  "any neighbor pixel that is not the border color. Finally reset\n"
305  "changes the entire image to the designated color.\n"
306  "\n"
307  "Next, choose a pixel color from the Pixel Color sub-menu.\n"
308  "Additional pixel colors can be specified with the color\n"
309  "browser. You can change the menu colors by setting the X\n"
310  "resources pen1 through pen9.\n"
311  "\n"
312  "Now press button 1 to select a pixel within the image window\n"
313  "to change its color. Additional pixels may be recolored as\n"
314  "prescribed by the method you choose.\n"
315  "\n"
316  "If the Magnify widget is mapped, it can be helpful in positioning\n"
317  "your pointer within the image (refer to button 2).\n"
318  "\n"
319  "The actual color you request for the pixels is saved in the\n"
320  "image. However, the color that appears in your image window\n"
321  "may be different. For example, on a monochrome screen the\n"
322  "pixel will appear black or white even if you choose the\n"
323  "color red as the pixel color. However, the image saved to a\n"
324  "file with -write is written with red pixels. To assure the\n"
325  "correct color text in the final image, any PseudoClass image\n"
326  "is promoted to DirectClass (see miff(5)). To force a\n"
327  "PseudoClass image to remain PseudoClass, use -colors.\n"
328  },
329  ImageCompositeHelp[] =
330  {
331  "First a widget window is displayed requesting you to enter an\n"
332  "image name. Press Composite, Grab or type a file name.\n"
333  "Press Cancel if you choose not to create a composite image.\n"
334  "When you choose Grab, move the pointer to the desired window\n"
335  "and press any button.\n"
336  "\n"
337  "If the Composite image does not have any matte information,\n"
338  "you are informed and the file browser is displayed again.\n"
339  "Enter the name of a mask image. The image is typically\n"
340  "grayscale and the same size as the composite image. If the\n"
341  "image is not grayscale, it is converted to grayscale and the\n"
342  "resulting intensities are used as matte information.\n"
343  "\n"
344  "A small window appears showing the location of the cursor in\n"
345  "the image window. You are now in composite mode. To exit\n"
346  "immediately, press Dismiss. In composite mode, the Command\n"
347  "widget has these options:\n"
348  "\n"
349  " Operators\n"
350  " Over\n"
351  " In\n"
352  " Out\n"
353  " Atop\n"
354  " Xor\n"
355  " Plus\n"
356  " Minus\n"
357  " Add\n"
358  " Subtract\n"
359  " Difference\n"
360  " Multiply\n"
361  " Bumpmap\n"
362  " Copy\n"
363  " CopyRed\n"
364  " CopyGreen\n"
365  " CopyBlue\n"
366  " CopyOpacity\n"
367  " Clear\n"
368  " Dissolve\n"
369  " Displace\n"
370  " Help\n"
371  " Dismiss\n"
372  "\n"
373  "Choose a composite operation from the Operators sub-menu of\n"
374  "the Command widget. How each operator behaves is described\n"
375  "below. Image window is the image currently displayed on\n"
376  "your X server and image is the image obtained with the File\n"
377  "Browser widget.\n"
378  "\n"
379  "Over The result is the union of the two image shapes,\n"
380  " with image obscuring image window in the region of\n"
381  " overlap.\n"
382  "\n"
383  "In The result is simply image cut by the shape of\n"
384  " image window. None of the image data of image\n"
385  " window is in the result.\n"
386  "\n"
387  "Out The resulting image is image with the shape of\n"
388  " image window cut out.\n"
389  "\n"
390  "Atop The result is the same shape as the image window,\n"
391  " with image obscuring image window where the image\n"
392  " shapes overlap. Note this differs from over\n"
393  " because the portion of image outside image window's\n"
394  " shape does not appear in the result.\n"
395  "\n"
396  "Xor The result is the image data from both image and\n"
397  " image window that is outside the overlap region.\n"
398  " The overlap region is blank.\n"
399  "\n"
400  "Plus The result is just the sum of the image data.\n"
401  " Output values are cropped to QuantumRange (no overflow).\n"
402  "\n"
403  "Minus The result of image - image window, with underflow\n"
404  " cropped to zero.\n"
405  "\n"
406  "Add The result of image + image window, with overflow\n"
407  " wrapping around (mod 256).\n"
408  "\n"
409  "Subtract The result of image - image window, with underflow\n"
410  " wrapping around (mod 256). The add and subtract\n"
411  " operators can be used to perform reversible\n"
412  " transformations.\n"
413  "\n"
414  "Difference\n"
415  " The result of abs(image - image window). This\n"
416  " useful for comparing two very similar images.\n"
417  "\n"
418  "Multiply\n"
419  " The result of image * image window. This\n"
420  " useful for the creation of drop-shadows.\n"
421  "\n"
422  "Bumpmap The result of surface normals from image * image\n"
423  " window.\n"
424  "\n"
425  "Copy The resulting image is image window replaced with\n"
426  " image. Here the matte information is ignored.\n"
427  "\n"
428  "CopyRed The red layer of the image window is replace with\n"
429  " the red layer of the image. The other layers are\n"
430  " untouched.\n"
431  "\n"
432  "CopyGreen\n"
433  " The green layer of the image window is replace with\n"
434  " the green layer of the image. The other layers are\n"
435  " untouched.\n"
436  "\n"
437  "CopyBlue The blue layer of the image window is replace with\n"
438  " the blue layer of the image. The other layers are\n"
439  " untouched.\n"
440  "\n"
441  "CopyOpacity\n"
442  " The matte layer of the image window is replace with\n"
443  " the matte layer of the image. The other layers are\n"
444  " untouched.\n"
445  "\n"
446  "The image compositor requires a matte, or alpha channel in\n"
447  "the image for some operations. This extra channel usually\n"
448  "defines a mask which represents a sort of a cookie-cutter\n"
449  "for the image. This the case when matte is opaque (full\n"
450  "coverage) for pixels inside the shape, zero outside, and\n"
451  "between 0 and QuantumRange on the boundary. If image does not\n"
452  "have a matte channel, it is initialized with 0 for any pixel\n"
453  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
454  "\n"
455  "If you choose Dissolve, the composite operator becomes Over. The\n"
456  "image matte channel percent transparency is initialized to factor.\n"
457  "The image window is initialized to (100-factor). Where factor is the\n"
458  "value you specify in the Dialog widget.\n"
459  "\n"
460  "Displace shifts the image pixels as defined by a displacement\n"
461  "map. With this option, image is used as a displacement map.\n"
462  "Black, within the displacement map, is a maximum positive\n"
463  "displacement. White is a maximum negative displacement and\n"
464  "middle gray is neutral. The displacement is scaled to determine\n"
465  "the pixel shift. By default, the displacement applies in both the\n"
466  "horizontal and vertical directions. However, if you specify a mask,\n"
467  "image is the horizontal X displacement and mask the vertical Y\n"
468  "displacement.\n"
469  "\n"
470  "Note that matte information for image window is not retained\n"
471  "for colormapped X server visuals (e.g. StaticColor,\n"
472  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
473  "behavior may require a TrueColor or DirectColor visual or a\n"
474  "Standard Colormap.\n"
475  "\n"
476  "Choosing a composite operator is optional. The default\n"
477  "operator is replace. However, you must choose a location to\n"
478  "composite your image and press button 1. Press and hold the\n"
479  "button before releasing and an outline of the image will\n"
480  "appear to help you identify your location.\n"
481  "\n"
482  "The actual colors of the composite image is saved. However,\n"
483  "the color that appears in image window may be different.\n"
484  "For example, on a monochrome screen image window will appear\n"
485  "black or white even though your composited image may have\n"
486  "many colors. If the image is saved to a file it is written\n"
487  "with the correct colors. To assure the correct colors are\n"
488  "saved in the final image, any PseudoClass image is promoted\n"
489  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
490  "to remain PseudoClass, use -colors.\n"
491  },
492  ImageCutHelp[] =
493  {
494  "In cut mode, the Command widget has these options:\n"
495  "\n"
496  " Help\n"
497  " Dismiss\n"
498  "\n"
499  "To define a cut region, press button 1 and drag. The\n"
500  "cut region is defined by a highlighted rectangle that\n"
501  "expands or contracts as it follows the pointer. Once you\n"
502  "are satisfied with the cut region, release the button.\n"
503  "You are now in rectify mode. In rectify mode, the Command\n"
504  "widget has these options:\n"
505  "\n"
506  " Cut\n"
507  " Help\n"
508  " Dismiss\n"
509  "\n"
510  "You can make adjustments by moving the pointer to one of the\n"
511  "cut rectangle corners, pressing a button, and dragging.\n"
512  "Finally, press Cut to commit your copy region. To\n"
513  "exit without cutting the image, press Dismiss.\n"
514  },
515  ImageCopyHelp[] =
516  {
517  "In copy mode, the Command widget has these options:\n"
518  "\n"
519  " Help\n"
520  " Dismiss\n"
521  "\n"
522  "To define a copy region, press button 1 and drag. The\n"
523  "copy region is defined by a highlighted rectangle that\n"
524  "expands or contracts as it follows the pointer. Once you\n"
525  "are satisfied with the copy region, release the button.\n"
526  "You are now in rectify mode. In rectify mode, the Command\n"
527  "widget has these options:\n"
528  "\n"
529  " Copy\n"
530  " Help\n"
531  " Dismiss\n"
532  "\n"
533  "You can make adjustments by moving the pointer to one of the\n"
534  "copy rectangle corners, pressing a button, and dragging.\n"
535  "Finally, press Copy to commit your copy region. To\n"
536  "exit without copying the image, press Dismiss.\n"
537  },
538  ImageCropHelp[] =
539  {
540  "In crop mode, the Command widget has these options:\n"
541  "\n"
542  " Help\n"
543  " Dismiss\n"
544  "\n"
545  "To define a cropping region, press button 1 and drag. The\n"
546  "cropping region is defined by a highlighted rectangle that\n"
547  "expands or contracts as it follows the pointer. Once you\n"
548  "are satisfied with the cropping region, release the button.\n"
549  "You are now in rectify mode. In rectify mode, the Command\n"
550  "widget has these options:\n"
551  "\n"
552  " Crop\n"
553  " Help\n"
554  " Dismiss\n"
555  "\n"
556  "You can make adjustments by moving the pointer to one of the\n"
557  "cropping rectangle corners, pressing a button, and dragging.\n"
558  "Finally, press Crop to commit your cropping region. To\n"
559  "exit without cropping the image, press Dismiss.\n"
560  },
561  ImageDrawHelp[] =
562  {
563  "The cursor changes to a crosshair to indicate you are in\n"
564  "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
565  "the Command widget has these options:\n"
566  "\n"
567  " Element\n"
568  " point\n"
569  " line\n"
570  " rectangle\n"
571  " fill rectangle\n"
572  " circle\n"
573  " fill circle\n"
574  " ellipse\n"
575  " fill ellipse\n"
576  " polygon\n"
577  " fill polygon\n"
578  " Color\n"
579  " black\n"
580  " blue\n"
581  " cyan\n"
582  " green\n"
583  " gray\n"
584  " red\n"
585  " magenta\n"
586  " yellow\n"
587  " white\n"
588  " transparent\n"
589  " Browser...\n"
590  " Stipple\n"
591  " Brick\n"
592  " Diagonal\n"
593  " Scales\n"
594  " Vertical\n"
595  " Wavy\n"
596  " Translucent\n"
597  " Opaque\n"
598  " Open...\n"
599  " Width\n"
600  " 1\n"
601  " 2\n"
602  " 4\n"
603  " 8\n"
604  " 16\n"
605  " Dialog...\n"
606  " Undo\n"
607  " Help\n"
608  " Dismiss\n"
609  "\n"
610  "Choose a drawing primitive from the Element sub-menu.\n"
611  "\n"
612  "Choose a color from the Color sub-menu. Additional\n"
613  "colors can be specified with the color browser.\n"
614  "\n"
615  "If you choose the color browser and press Grab, you can\n"
616  "select the color by moving the pointer to the desired\n"
617  "color on the screen and press any button. The transparent\n"
618  "color updates the image matte channel and is useful for\n"
619  "image compositing.\n"
620  "\n"
621  "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
622  "Additional stipples can be specified with the file browser.\n"
623  "Stipples obtained from the file browser must be on disk in the\n"
624  "X11 bitmap format.\n"
625  "\n"
626  "Choose a width, if appropriate, from the Width sub-menu. To\n"
627  "choose a specific width select the Dialog widget.\n"
628  "\n"
629  "Choose a point in the Image window and press button 1 and\n"
630  "hold. Next, move the pointer to another location in the\n"
631  "image. As you move, a line connects the initial location and\n"
632  "the pointer. When you release the button, the image is\n"
633  "updated with the primitive you just drew. For polygons, the\n"
634  "image is updated when you press and release the button without\n"
635  "moving the pointer.\n"
636  "\n"
637  "To cancel image drawing, move the pointer back to the\n"
638  "starting point of the line and release the button.\n"
639  },
640  DisplayHelp[] =
641  {
642  "BUTTONS\n"
643  " The effects of each button press is described below. Three\n"
644  " buttons are required. If you have a two button mouse,\n"
645  " button 1 and 3 are returned. Press ALT and button 3 to\n"
646  " simulate button 2.\n"
647  "\n"
648  " 1 Press this button to map or unmap the Command widget.\n"
649  "\n"
650  " 2 Press and drag to define a region of the image to\n"
651  " magnify.\n"
652  "\n"
653  " 3 Press and drag to choose from a select set of commands.\n"
654  " This button behaves differently if the image being\n"
655  " displayed is a visual image directory. Here, choose a\n"
656  " particular tile of the directory and press this button and\n"
657  " drag to select a command from a pop-up menu. Choose from\n"
658  " these menu items:\n"
659  "\n"
660  " Open\n"
661  " Next\n"
662  " Former\n"
663  " Delete\n"
664  " Update\n"
665  "\n"
666  " If you choose Open, the image represented by the tile is\n"
667  " displayed. To return to the visual image directory, choose\n"
668  " Next from the Command widget. Next and Former moves to the\n"
669  " next or former image respectively. Choose Delete to delete\n"
670  " a particular image tile. Finally, choose Update to\n"
671  " synchronize all the image tiles with their respective\n"
672  " images.\n"
673  "\n"
674  "COMMAND WIDGET\n"
675  " The Command widget lists a number of sub-menus and commands.\n"
676  " They are\n"
677  "\n"
678  " File\n"
679  " Open...\n"
680  " Next\n"
681  " Former\n"
682  " Select...\n"
683  " Save...\n"
684  " Print...\n"
685  " Delete...\n"
686  " New...\n"
687  " Visual Directory...\n"
688  " Quit\n"
689  " Edit\n"
690  " Undo\n"
691  " Redo\n"
692  " Cut\n"
693  " Copy\n"
694  " Paste\n"
695  " View\n"
696  " Half Size\n"
697  " Original Size\n"
698  " Double Size\n"
699  " Resize...\n"
700  " Apply\n"
701  " Refresh\n"
702  " Restore\n"
703  " Transform\n"
704  " Crop\n"
705  " Chop\n"
706  " Flop\n"
707  " Flip\n"
708  " Rotate Right\n"
709  " Rotate Left\n"
710  " Rotate...\n"
711  " Shear...\n"
712  " Roll...\n"
713  " Trim Edges\n"
714  " Enhance\n"
715  " Brightness...\n"
716  " Saturation...\n"
717  " Hue...\n"
718  " Gamma...\n"
719  " Sharpen...\n"
720  " Dull\n"
721  " Contrast Stretch...\n"
722  " Sigmoidal Contrast...\n"
723  " Normalize\n"
724  " Equalize\n"
725  " Negate\n"
726  " Grayscale\n"
727  " Map...\n"
728  " Quantize...\n"
729  " Effects\n"
730  " Despeckle\n"
731  " Emboss\n"
732  " Reduce Noise\n"
733  " Add Noise\n"
734  " Sharpen...\n"
735  " Blur...\n"
736  " Threshold...\n"
737  " Edge Detect...\n"
738  " Spread...\n"
739  " Shade...\n"
740  " Painting...\n"
741  " Segment...\n"
742  " F/X\n"
743  " Solarize...\n"
744  " Sepia Tone...\n"
745  " Swirl...\n"
746  " Implode...\n"
747  " Vignette...\n"
748  " Wave...\n"
749  " Oil Painting...\n"
750  " Charcoal Drawing...\n"
751  " Image Edit\n"
752  " Annotate...\n"
753  " Draw...\n"
754  " Color...\n"
755  " Matte...\n"
756  " Composite...\n"
757  " Add Border...\n"
758  " Add Frame...\n"
759  " Comment...\n"
760  " Launch...\n"
761  " Region of Interest...\n"
762  " Miscellany\n"
763  " Image Info\n"
764  " Zoom Image\n"
765  " Show Preview...\n"
766  " Show Histogram\n"
767  " Show Matte\n"
768  " Background...\n"
769  " Slide Show\n"
770  " Preferences...\n"
771  " Help\n"
772  " Overview\n"
773  " Browse Documentation\n"
774  " About Display\n"
775  "\n"
776  " Menu items with a indented triangle have a sub-menu. They\n"
777  " are represented above as the indented items. To access a\n"
778  " sub-menu item, move the pointer to the appropriate menu and\n"
779  " press a button and drag. When you find the desired sub-menu\n"
780  " item, release the button and the command is executed. Move\n"
781  " the pointer away from the sub-menu if you decide not to\n"
782  " execute a particular command.\n"
783  "\n"
784  "KEYBOARD ACCELERATORS\n"
785  " Accelerators are one or two key presses that effect a\n"
786  " particular command. The keyboard accelerators that\n"
787  " display(1) understands is:\n"
788  "\n"
789  " Ctl+O Press to open an image from a file.\n"
790  "\n"
791  " space Press to display the next image.\n"
792  "\n"
793  " If the image is a multi-paged document such as a Postscript\n"
794  " document, you can skip ahead several pages by preceding\n"
795  " this command with a number. For example to display the\n"
796  " third page beyond the current page, press 3<space>.\n"
797  "\n"
798  " backspace Press to display the former image.\n"
799  "\n"
800  " If the image is a multi-paged document such as a Postscript\n"
801  " document, you can skip behind several pages by preceding\n"
802  " this command with a number. For example to display the\n"
803  " third page preceding the current page, press 3<backspace>.\n"
804  "\n"
805  " Ctl+S Press to write the image to a file.\n"
806  "\n"
807  " Ctl+P Press to print the image to a Postscript printer.\n"
808  "\n"
809  " Ctl+D Press to delete an image file.\n"
810  "\n"
811  " Ctl+N Press to create a blank canvas.\n"
812  "\n"
813  " Ctl+Q Press to discard all images and exit program.\n"
814  "\n"
815  " Ctl+Z Press to undo last image transformation.\n"
816  "\n"
817  " Ctl+R Press to redo last image transformation.\n"
818  "\n"
819  " Ctl+X Press to cut a region of the image.\n"
820  "\n"
821  " Ctl+C Press to copy a region of the image.\n"
822  "\n"
823  " Ctl+V Press to paste a region to the image.\n"
824  "\n"
825  " < Press to half the image size.\n"
826  "\n"
827  " - Press to return to the original image size.\n"
828  "\n"
829  " > Press to double the image size.\n"
830  "\n"
831  " % Press to resize the image to a width and height you\n"
832  " specify.\n"
833  "\n"
834  "Cmd-A Press to make any image transformations permanent."
835  "\n"
836  " By default, any image size transformations are applied\n"
837  " to the original image to create the image displayed on\n"
838  " the X server. However, the transformations are not\n"
839  " permanent (i.e. the original image does not change\n"
840  " size only the X image does). For example, if you\n"
841  " press > the X image will appear to double in size,\n"
842  " but the original image will in fact remain the same size.\n"
843  " To force the original image to double in size, press >\n"
844  " followed by Cmd-A.\n"
845  "\n"
846  " @ Press to refresh the image window.\n"
847  "\n"
848  " C Press to cut out a rectangular region of the image.\n"
849  "\n"
850  " [ Press to chop the image.\n"
851  "\n"
852  " H Press to flop image in the horizontal direction.\n"
853  "\n"
854  " V Press to flip image in the vertical direction.\n"
855  "\n"
856  " / Press to rotate the image 90 degrees clockwise.\n"
857  "\n"
858  " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
859  "\n"
860  " * Press to rotate the image the number of degrees you\n"
861  " specify.\n"
862  "\n"
863  " S Press to shear the image the number of degrees you\n"
864  " specify.\n"
865  "\n"
866  " R Press to roll the image.\n"
867  "\n"
868  " T Press to trim the image edges.\n"
869  "\n"
870  " Shft-H Press to vary the image hue.\n"
871  "\n"
872  " Shft-S Press to vary the color saturation.\n"
873  "\n"
874  " Shft-L Press to vary the color brightness.\n"
875  "\n"
876  " Shft-G Press to gamma correct the image.\n"
877  "\n"
878  " Shft-C Press to sharpen the image contrast.\n"
879  "\n"
880  " Shft-Z Press to dull the image contrast.\n"
881  "\n"
882  " = Press to perform histogram equalization on the image.\n"
883  "\n"
884  " Shft-N Press to perform histogram normalization on the image.\n"
885  "\n"
886  " Shft-~ Press to negate the colors of the image.\n"
887  "\n"
888  " . Press to convert the image colors to gray.\n"
889  "\n"
890  " Shft-# Press to set the maximum number of unique colors in the\n"
891  " image.\n"
892  "\n"
893  " F2 Press to reduce the speckles in an image.\n"
894  "\n"
895  " F3 Press to eliminate peak noise from an image.\n"
896  "\n"
897  " F4 Press to add noise to an image.\n"
898  "\n"
899  " F5 Press to sharpen an image.\n"
900  "\n"
901  " F6 Press to delete an image file.\n"
902  "\n"
903  " F7 Press to threshold the image.\n"
904  "\n"
905  " F8 Press to detect edges within an image.\n"
906  "\n"
907  " F9 Press to emboss an image.\n"
908  "\n"
909  " F10 Press to displace pixels by a random amount.\n"
910  "\n"
911  " F11 Press to negate all pixels above the threshold level.\n"
912  "\n"
913  " F12 Press to shade the image using a distant light source.\n"
914  "\n"
915  " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
916  "\n"
917  " F14 Press to segment the image by color.\n"
918  "\n"
919  " Meta-S Press to swirl image pixels about the center.\n"
920  "\n"
921  " Meta-I Press to implode image pixels about the center.\n"
922  "\n"
923  " Meta-W Press to alter an image along a sine wave.\n"
924  "\n"
925  " Meta-P Press to simulate an oil painting.\n"
926  "\n"
927  " Meta-C Press to simulate a charcoal drawing.\n"
928  "\n"
929  " Alt-A Press to annotate the image with text.\n"
930  "\n"
931  " Alt-D Press to draw on an image.\n"
932  "\n"
933  " Alt-P Press to edit an image pixel color.\n"
934  "\n"
935  " Alt-M Press to edit the image matte information.\n"
936  "\n"
937  " Alt-V Press to composite the image with another.\n"
938  "\n"
939  " Alt-B Press to add a border to the image.\n"
940  "\n"
941  " Alt-F Press to add an ornamental border to the image.\n"
942  "\n"
943  " Alt-Shft-!\n"
944  " Press to add an image comment.\n"
945  "\n"
946  " Ctl-A Press to apply image processing techniques to a region\n"
947  " of interest.\n"
948  "\n"
949  " Shft-? Press to display information about the image.\n"
950  "\n"
951  " Shft-+ Press to map the zoom image window.\n"
952  "\n"
953  " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
954  "\n"
955  " F1 Press to display helpful information about display(1).\n"
956  "\n"
957  " Find Press to browse documentation about ImageMagick.\n"
958  "\n"
959  " 1-9 Press to change the level of magnification.\n"
960  "\n"
961  " Use the arrow keys to move the image one pixel up, down,\n"
962  " left, or right within the magnify window. Be sure to first\n"
963  " map the magnify window by pressing button 2.\n"
964  "\n"
965  " Press ALT and one of the arrow keys to trim off one pixel\n"
966  " from any side of the image.\n"
967  },
968  ImageMatteEditHelp[] =
969  {
970  "Matte information within an image is useful for some\n"
971  "operations such as image compositing (See IMAGE\n"
972  "COMPOSITING). This extra channel usually defines a mask\n"
973  "which represents a sort of a cookie-cutter for the image.\n"
974  "This the case when matte is opaque (full coverage) for\n"
975  "pixels inside the shape, zero outside, and between 0 and\n"
976  "QuantumRange on the boundary.\n"
977  "\n"
978  "A small window appears showing the location of the cursor in\n"
979  "the image window. You are now in matte edit mode. To exit\n"
980  "immediately, press Dismiss. In matte edit mode, the Command\n"
981  "widget has these options:\n"
982  "\n"
983  " Method\n"
984  " point\n"
985  " replace\n"
986  " floodfill\n"
987  " filltoborder\n"
988  " reset\n"
989  " Border Color\n"
990  " black\n"
991  " blue\n"
992  " cyan\n"
993  " green\n"
994  " gray\n"
995  " red\n"
996  " magenta\n"
997  " yellow\n"
998  " white\n"
999  " Browser...\n"
1000  " Fuzz\n"
1001  " 0%\n"
1002  " 2%\n"
1003  " 5%\n"
1004  " 10%\n"
1005  " 15%\n"
1006  " Dialog...\n"
1007  " Matte\n"
1008  " Opaque\n"
1009  " Transparent\n"
1010  " Dialog...\n"
1011  " Undo\n"
1012  " Help\n"
1013  " Dismiss\n"
1014  "\n"
1015  "Choose a matte editing method from the Method sub-menu of\n"
1016  "the Command widget. The point method changes the matte value\n"
1017  "of any pixel selected with the pointer until the button is\n"
1018  "is released. The replace method changes the matte value of\n"
1019  "any pixel that matches the color of the pixel you select with\n"
1020  "a button press. Floodfill changes the matte value of any pixel\n"
1021  "that matches the color of the pixel you select with a button\n"
1022  "press and is a neighbor. Whereas filltoborder changes the matte\n"
1023  "value any neighbor pixel that is not the border color. Finally\n"
1024  "reset changes the entire image to the designated matte value.\n"
1025  "\n"
1026  "Choose Matte Value and pick Opaque or Transparent. For other values\n"
1027  "select the Dialog entry. Here a dialog appears requesting a matte\n"
1028  "value. The value you select is assigned as the opacity value of the\n"
1029  "selected pixel or pixels.\n"
1030  "\n"
1031  "Now, press any button to select a pixel within the image\n"
1032  "window to change its matte value.\n"
1033  "\n"
1034  "If the Magnify widget is mapped, it can be helpful in positioning\n"
1035  "your pointer within the image (refer to button 2).\n"
1036  "\n"
1037  "Matte information is only valid in a DirectClass image.\n"
1038  "Therefore, any PseudoClass image is promoted to DirectClass\n"
1039  "(see miff(5)). Note that matte information for PseudoClass\n"
1040  "is not retained for colormapped X server visuals (e.g.\n"
1041  "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1042  "immediately save your image to a file (refer to Write).\n"
1043  "Correct matte editing behavior may require a TrueColor or\n"
1044  "DirectColor visual or a Standard Colormap.\n"
1045  },
1046  ImagePanHelp[] =
1047  {
1048  "When an image exceeds the width or height of the X server\n"
1049  "screen, display maps a small panning icon. The rectangle\n"
1050  "within the panning icon shows the area that is currently\n"
1051  "displayed in the image window. To pan about the image,\n"
1052  "press any button and drag the pointer within the panning\n"
1053  "icon. The pan rectangle moves with the pointer and the\n"
1054  "image window is updated to reflect the location of the\n"
1055  "rectangle within the panning icon. When you have selected\n"
1056  "the area of the image you wish to view, release the button.\n"
1057  "\n"
1058  "Use the arrow keys to pan the image one pixel up, down,\n"
1059  "left, or right within the image window.\n"
1060  "\n"
1061  "The panning icon is withdrawn if the image becomes smaller\n"
1062  "than the dimensions of the X server screen.\n"
1063  },
1064  ImagePasteHelp[] =
1065  {
1066  "A small window appears showing the location of the cursor in\n"
1067  "the image window. You are now in paste mode. To exit\n"
1068  "immediately, press Dismiss. In paste mode, the Command\n"
1069  "widget has these options:\n"
1070  "\n"
1071  " Operators\n"
1072  " over\n"
1073  " in\n"
1074  " out\n"
1075  " atop\n"
1076  " xor\n"
1077  " plus\n"
1078  " minus\n"
1079  " add\n"
1080  " subtract\n"
1081  " difference\n"
1082  " replace\n"
1083  " Help\n"
1084  " Dismiss\n"
1085  "\n"
1086  "Choose a composite operation from the Operators sub-menu of\n"
1087  "the Command widget. How each operator behaves is described\n"
1088  "below. Image window is the image currently displayed on\n"
1089  "your X server and image is the image obtained with the File\n"
1090  "Browser widget.\n"
1091  "\n"
1092  "Over The result is the union of the two image shapes,\n"
1093  " with image obscuring image window in the region of\n"
1094  " overlap.\n"
1095  "\n"
1096  "In The result is simply image cut by the shape of\n"
1097  " image window. None of the image data of image\n"
1098  " window is in the result.\n"
1099  "\n"
1100  "Out The resulting image is image with the shape of\n"
1101  " image window cut out.\n"
1102  "\n"
1103  "Atop The result is the same shape as the image window,\n"
1104  " with image obscuring image window where the image\n"
1105  " shapes overlap. Note this differs from over\n"
1106  " because the portion of image outside image window's\n"
1107  " shape does not appear in the result.\n"
1108  "\n"
1109  "Xor The result is the image data from both image and\n"
1110  " image window that is outside the overlap region.\n"
1111  " The overlap region is blank.\n"
1112  "\n"
1113  "Plus The result is just the sum of the image data.\n"
1114  " Output values are cropped to QuantumRange (no overflow).\n"
1115  " This operation is independent of the matte\n"
1116  " channels.\n"
1117  "\n"
1118  "Minus The result of image - image window, with underflow\n"
1119  " cropped to zero.\n"
1120  "\n"
1121  "Add The result of image + image window, with overflow\n"
1122  " wrapping around (mod 256).\n"
1123  "\n"
1124  "Subtract The result of image - image window, with underflow\n"
1125  " wrapping around (mod 256). The add and subtract\n"
1126  " operators can be used to perform reversible\n"
1127  " transformations.\n"
1128  "\n"
1129  "Difference\n"
1130  " The result of abs(image - image window). This\n"
1131  " useful for comparing two very similar images.\n"
1132  "\n"
1133  "Copy The resulting image is image window replaced with\n"
1134  " image. Here the matte information is ignored.\n"
1135  "\n"
1136  "CopyRed The red layer of the image window is replace with\n"
1137  " the red layer of the image. The other layers are\n"
1138  " untouched.\n"
1139  "\n"
1140  "CopyGreen\n"
1141  " The green layer of the image window is replace with\n"
1142  " the green layer of the image. The other layers are\n"
1143  " untouched.\n"
1144  "\n"
1145  "CopyBlue The blue layer of the image window is replace with\n"
1146  " the blue layer of the image. The other layers are\n"
1147  " untouched.\n"
1148  "\n"
1149  "CopyOpacity\n"
1150  " The matte layer of the image window is replace with\n"
1151  " the matte layer of the image. The other layers are\n"
1152  " untouched.\n"
1153  "\n"
1154  "The image compositor requires a matte, or alpha channel in\n"
1155  "the image for some operations. This extra channel usually\n"
1156  "defines a mask which represents a sort of a cookie-cutter\n"
1157  "for the image. This the case when matte is opaque (full\n"
1158  "coverage) for pixels inside the shape, zero outside, and\n"
1159  "between 0 and QuantumRange on the boundary. If image does not\n"
1160  "have a matte channel, it is initialized with 0 for any pixel\n"
1161  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1162  "\n"
1163  "Note that matte information for image window is not retained\n"
1164  "for colormapped X server visuals (e.g. StaticColor,\n"
1165  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1166  "behavior may require a TrueColor or DirectColor visual or a\n"
1167  "Standard Colormap.\n"
1168  "\n"
1169  "Choosing a composite operator is optional. The default\n"
1170  "operator is replace. However, you must choose a location to\n"
1171  "paste your image and press button 1. Press and hold the\n"
1172  "button before releasing and an outline of the image will\n"
1173  "appear to help you identify your location.\n"
1174  "\n"
1175  "The actual colors of the pasted image is saved. However,\n"
1176  "the color that appears in image window may be different.\n"
1177  "For example, on a monochrome screen image window will appear\n"
1178  "black or white even though your pasted image may have\n"
1179  "many colors. If the image is saved to a file it is written\n"
1180  "with the correct colors. To assure the correct colors are\n"
1181  "saved in the final image, any PseudoClass image is promoted\n"
1182  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1183  "to remain PseudoClass, use -colors.\n"
1184  },
1185  ImageROIHelp[] =
1186  {
1187  "In region of interest mode, the Command widget has these\n"
1188  "options:\n"
1189  "\n"
1190  " Help\n"
1191  " Dismiss\n"
1192  "\n"
1193  "To define a region of interest, press button 1 and drag.\n"
1194  "The region of interest is defined by a highlighted rectangle\n"
1195  "that expands or contracts as it follows the pointer. Once\n"
1196  "you are satisfied with the region of interest, release the\n"
1197  "button. You are now in apply mode. In apply mode the\n"
1198  "Command widget has these options:\n"
1199  "\n"
1200  " File\n"
1201  " Save...\n"
1202  " Print...\n"
1203  " Edit\n"
1204  " Undo\n"
1205  " Redo\n"
1206  " Transform\n"
1207  " Flop\n"
1208  " Flip\n"
1209  " Rotate Right\n"
1210  " Rotate Left\n"
1211  " Enhance\n"
1212  " Hue...\n"
1213  " Saturation...\n"
1214  " Brightness...\n"
1215  " Gamma...\n"
1216  " Spiff\n"
1217  " Dull\n"
1218  " Contrast Stretch\n"
1219  " Sigmoidal Contrast...\n"
1220  " Normalize\n"
1221  " Equalize\n"
1222  " Negate\n"
1223  " Grayscale\n"
1224  " Map...\n"
1225  " Quantize...\n"
1226  " Effects\n"
1227  " Despeckle\n"
1228  " Emboss\n"
1229  " Reduce Noise\n"
1230  " Sharpen...\n"
1231  " Blur...\n"
1232  " Threshold...\n"
1233  " Edge Detect...\n"
1234  " Spread...\n"
1235  " Shade...\n"
1236  " Raise...\n"
1237  " Segment...\n"
1238  " F/X\n"
1239  " Solarize...\n"
1240  " Sepia Tone...\n"
1241  " Swirl...\n"
1242  " Implode...\n"
1243  " Vignette...\n"
1244  " Wave...\n"
1245  " Oil Painting...\n"
1246  " Charcoal Drawing...\n"
1247  " Miscellany\n"
1248  " Image Info\n"
1249  " Zoom Image\n"
1250  " Show Preview...\n"
1251  " Show Histogram\n"
1252  " Show Matte\n"
1253  " Help\n"
1254  " Dismiss\n"
1255  "\n"
1256  "You can make adjustments to the region of interest by moving\n"
1257  "the pointer to one of the rectangle corners, pressing a\n"
1258  "button, and dragging. Finally, choose an image processing\n"
1259  "technique from the Command widget. You can choose more than\n"
1260  "one image processing technique to apply to an area.\n"
1261  "Alternatively, you can move the region of interest before\n"
1262  "applying another image processing technique. To exit, press\n"
1263  "Dismiss.\n"
1264  },
1265  ImageRotateHelp[] =
1266  {
1267  "In rotate mode, the Command widget has these options:\n"
1268  "\n"
1269  " Pixel Color\n"
1270  " black\n"
1271  " blue\n"
1272  " cyan\n"
1273  " green\n"
1274  " gray\n"
1275  " red\n"
1276  " magenta\n"
1277  " yellow\n"
1278  " white\n"
1279  " Browser...\n"
1280  " Direction\n"
1281  " horizontal\n"
1282  " vertical\n"
1283  " Help\n"
1284  " Dismiss\n"
1285  "\n"
1286  "Choose a background color from the Pixel Color sub-menu.\n"
1287  "Additional background colors can be specified with the color\n"
1288  "browser. You can change the menu colors by setting the X\n"
1289  "resources pen1 through pen9.\n"
1290  "\n"
1291  "If you choose the color browser and press Grab, you can\n"
1292  "select the background color by moving the pointer to the\n"
1293  "desired color on the screen and press any button.\n"
1294  "\n"
1295  "Choose a point in the image window and press this button and\n"
1296  "hold. Next, move the pointer to another location in the\n"
1297  "image. As you move a line connects the initial location and\n"
1298  "the pointer. When you release the button, the degree of\n"
1299  "image rotation is determined by the slope of the line you\n"
1300  "just drew. The slope is relative to the direction you\n"
1301  "choose from the Direction sub-menu of the Command widget.\n"
1302  "\n"
1303  "To cancel the image rotation, move the pointer back to the\n"
1304  "starting point of the line and release the button.\n"
1305  };
1306 
1307 /*
1308  Enumeration declarations.
1309 */
1310 typedef enum
1311 {
1312  CopyMode,
1313  CropMode,
1314  CutMode
1315 } ClipboardMode;
1316 
1317 typedef enum
1318 {
1319  OpenCommand,
1320  NextCommand,
1321  FormerCommand,
1322  SelectCommand,
1323  SaveCommand,
1324  PrintCommand,
1325  DeleteCommand,
1326  NewCommand,
1327  VisualDirectoryCommand,
1328  QuitCommand,
1329  UndoCommand,
1330  RedoCommand,
1331  CutCommand,
1332  CopyCommand,
1333  PasteCommand,
1334  HalfSizeCommand,
1335  OriginalSizeCommand,
1336  DoubleSizeCommand,
1337  ResizeCommand,
1338  ApplyCommand,
1339  RefreshCommand,
1340  RestoreCommand,
1341  CropCommand,
1342  ChopCommand,
1343  FlopCommand,
1344  FlipCommand,
1345  RotateRightCommand,
1346  RotateLeftCommand,
1347  RotateCommand,
1348  ShearCommand,
1349  RollCommand,
1350  TrimCommand,
1351  HueCommand,
1352  SaturationCommand,
1353  BrightnessCommand,
1354  GammaCommand,
1355  SpiffCommand,
1356  DullCommand,
1357  ContrastStretchCommand,
1358  SigmoidalContrastCommand,
1359  NormalizeCommand,
1360  EqualizeCommand,
1361  NegateCommand,
1362  GrayscaleCommand,
1363  MapCommand,
1364  QuantizeCommand,
1365  DespeckleCommand,
1366  EmbossCommand,
1367  ReduceNoiseCommand,
1368  AddNoiseCommand,
1369  SharpenCommand,
1370  BlurCommand,
1371  ThresholdCommand,
1372  EdgeDetectCommand,
1373  SpreadCommand,
1374  ShadeCommand,
1375  RaiseCommand,
1376  SegmentCommand,
1377  SolarizeCommand,
1378  SepiaToneCommand,
1379  SwirlCommand,
1380  ImplodeCommand,
1381  VignetteCommand,
1382  WaveCommand,
1383  OilPaintCommand,
1384  CharcoalDrawCommand,
1385  AnnotateCommand,
1386  DrawCommand,
1387  ColorCommand,
1388  MatteCommand,
1389  CompositeCommand,
1390  AddBorderCommand,
1391  AddFrameCommand,
1392  CommentCommand,
1393  LaunchCommand,
1394  RegionOfInterestCommand,
1395  ROIHelpCommand,
1396  ROIDismissCommand,
1397  InfoCommand,
1398  ZoomCommand,
1399  ShowPreviewCommand,
1400  ShowHistogramCommand,
1401  ShowMatteCommand,
1402  BackgroundCommand,
1403  SlideShowCommand,
1404  PreferencesCommand,
1405  HelpCommand,
1406  BrowseDocumentationCommand,
1407  VersionCommand,
1408  SaveToUndoBufferCommand,
1409  FreeBuffersCommand,
1410  NullCommand
1411 } DisplayCommand;
1412 
1413 typedef enum
1414 {
1415  AnnotateNameCommand,
1416  AnnotateFontColorCommand,
1417  AnnotateBackgroundColorCommand,
1418  AnnotateRotateCommand,
1419  AnnotateHelpCommand,
1420  AnnotateDismissCommand,
1421  TextHelpCommand,
1422  TextApplyCommand,
1423  ChopDirectionCommand,
1424  ChopHelpCommand,
1425  ChopDismissCommand,
1426  HorizontalChopCommand,
1427  VerticalChopCommand,
1428  ColorEditMethodCommand,
1429  ColorEditColorCommand,
1430  ColorEditBorderCommand,
1431  ColorEditFuzzCommand,
1432  ColorEditUndoCommand,
1433  ColorEditHelpCommand,
1434  ColorEditDismissCommand,
1435  CompositeOperatorsCommand,
1436  CompositeDissolveCommand,
1437  CompositeDisplaceCommand,
1438  CompositeHelpCommand,
1439  CompositeDismissCommand,
1440  CropHelpCommand,
1441  CropDismissCommand,
1442  RectifyCopyCommand,
1443  RectifyHelpCommand,
1444  RectifyDismissCommand,
1445  DrawElementCommand,
1446  DrawColorCommand,
1447  DrawStippleCommand,
1448  DrawWidthCommand,
1449  DrawUndoCommand,
1450  DrawHelpCommand,
1451  DrawDismissCommand,
1452  MatteEditMethod,
1453  MatteEditBorderCommand,
1454  MatteEditFuzzCommand,
1455  MatteEditValueCommand,
1456  MatteEditUndoCommand,
1457  MatteEditHelpCommand,
1458  MatteEditDismissCommand,
1459  PasteOperatorsCommand,
1460  PasteHelpCommand,
1461  PasteDismissCommand,
1462  RotateColorCommand,
1463  RotateDirectionCommand,
1464  RotateCropCommand,
1465  RotateSharpenCommand,
1466  RotateHelpCommand,
1467  RotateDismissCommand,
1468  HorizontalRotateCommand,
1469  VerticalRotateCommand,
1470  TileLoadCommand,
1471  TileNextCommand,
1472  TileFormerCommand,
1473  TileDeleteCommand,
1474  TileUpdateCommand
1475 } ModeType;
1476 
1477 /*
1478  Stipples.
1479 */
1480 #define BricksWidth 20
1481 #define BricksHeight 20
1482 #define DiagonalWidth 16
1483 #define DiagonalHeight 16
1484 #define HighlightWidth 8
1485 #define HighlightHeight 8
1486 #define OpaqueWidth 8
1487 #define OpaqueHeight 8
1488 #define ScalesWidth 16
1489 #define ScalesHeight 16
1490 #define ShadowWidth 8
1491 #define ShadowHeight 8
1492 #define VerticalWidth 16
1493 #define VerticalHeight 16
1494 #define WavyWidth 16
1495 #define WavyHeight 16
1496 
1497 /*
1498  Constant declaration.
1499 */
1500 static const int
1501  RoiDelta = 8;
1502 
1503 static const unsigned char
1504  BricksBitmap[] =
1505  {
1506  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1507  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1508  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1509  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1510  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1511  },
1512  DiagonalBitmap[] =
1513  {
1514  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1515  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1516  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1517  },
1518  ScalesBitmap[] =
1519  {
1520  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1521  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1522  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1523  },
1524  VerticalBitmap[] =
1525  {
1526  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1527  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1528  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1529  },
1530  WavyBitmap[] =
1531  {
1532  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1533  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1534  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1535  };
1536 
1537 /*
1538  Function prototypes.
1539 */
1540 static DisplayCommand
1541  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1542  const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1543 
1544 static Image
1545  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
1546  Image **,ExceptionInfo *),
1547  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1548  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1549  ExceptionInfo *),
1550  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1551  ExceptionInfo *);
1552 
1553 static MagickBooleanType
1554  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1555  ExceptionInfo *),
1556  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1557  ExceptionInfo *),
1558  XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1559  ExceptionInfo *),
1560  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1561  ExceptionInfo *),
1562  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1563  ExceptionInfo *),
1564  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1565  ExceptionInfo *),
1566  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1567  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1568  ExceptionInfo *),
1569  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1570  ExceptionInfo *),
1571  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1572  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1573  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1574  ExceptionInfo *),
1575  XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1576  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1577  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1578 
1579 static void
1580  XDrawPanRectangle(Display *,XWindows *),
1581  XImageCache(Display *,XResourceInfo *,XWindows *,const DisplayCommand,Image **,
1582  ExceptionInfo *),
1583  XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1584  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1585  XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1586  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1587  const KeySym,ExceptionInfo *),
1588  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1589  XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1590  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1591 
1592 /*
1593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1594 % %
1595 % %
1596 % %
1597 % D i s p l a y I m a g e s %
1598 % %
1599 % %
1600 % %
1601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1602 %
1603 % DisplayImages() displays an image sequence to any X window screen. It
1604 % returns a value other than 0 if successful. Check the exception member
1605 % of image to determine the reason for any failure.
1606 %
1607 % The format of the DisplayImages method is:
1608 %
1609 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1610 % Image *images,ExceptionInfo *exception)
1611 %
1612 % A description of each parameter follows:
1613 %
1614 % o image_info: the image info.
1615 %
1616 % o image: the image.
1617 %
1618 % o exception: return any errors or warnings in this structure.
1619 %
1620 */
1621 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1622  Image *images,ExceptionInfo *exception)
1623 {
1624  char
1625  *argv[1];
1626 
1627  Display
1628  *display;
1629 
1630  Image
1631  *image;
1632 
1633  size_t
1634  state;
1635 
1636  ssize_t
1637  i;
1638 
1639  XrmDatabase
1640  resource_database;
1641 
1642  XResourceInfo
1643  resource_info;
1644 
1645  assert(image_info != (const ImageInfo *) NULL);
1646  assert(image_info->signature == MagickCoreSignature);
1647  assert(images != (Image *) NULL);
1648  assert(images->signature == MagickCoreSignature);
1649  if (IsEventLogging() != MagickFalse)
1650  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1651  display=XOpenDisplay(image_info->server_name);
1652  if (display == (Display *) NULL)
1653  {
1654  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1655  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1656  return(MagickFalse);
1657  }
1658  if (exception->severity != UndefinedException)
1659  CatchException(exception);
1660  (void) XSetErrorHandler(XError);
1661  resource_database=XGetResourceDatabase(display,GetClientName());
1662  (void) memset(&resource_info,0,sizeof(resource_info));
1663  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1664  if (image_info->page != (char *) NULL)
1665  resource_info.image_geometry=AcquireString(image_info->page);
1666  resource_info.immutable=MagickTrue;
1667  argv[0]=AcquireString(GetClientName());
1668  state=DefaultState;
1669  for (i=0; (state & ExitState) == 0; i++)
1670  {
1671  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1672  break;
1673  image=GetImageFromList(images,i % (ssize_t) GetImageListLength(images));
1674  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1675  }
1676  (void) SetErrorHandler((ErrorHandler) NULL);
1677  (void) SetWarningHandler((WarningHandler) NULL);
1678  argv[0]=DestroyString(argv[0]);
1679  (void) XCloseDisplay(display);
1680  XDestroyResourceInfo(&resource_info);
1681  if (exception->severity != UndefinedException)
1682  return(MagickFalse);
1683  return(MagickTrue);
1684 }
1685 
1686 /*
1687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688 % %
1689 % %
1690 % %
1691 % R e m o t e D i s p l a y C o m m a n d %
1692 % %
1693 % %
1694 % %
1695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696 %
1697 % RemoteDisplayCommand() encourages a remote display program to display the
1698 % specified image filename.
1699 %
1700 % The format of the RemoteDisplayCommand method is:
1701 %
1702 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703 % const char *window,const char *filename,ExceptionInfo *exception)
1704 %
1705 % A description of each parameter follows:
1706 %
1707 % o image_info: the image info.
1708 %
1709 % o window: Specifies the name or id of an X window.
1710 %
1711 % o filename: the name of the image filename to display.
1712 %
1713 % o exception: return any errors or warnings in this structure.
1714 %
1715 */
1716 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717  const char *window,const char *filename,ExceptionInfo *exception)
1718 {
1719  Display
1720  *display;
1721 
1722  MagickStatusType
1723  status;
1724 
1725  assert(image_info != (const ImageInfo *) NULL);
1726  assert(image_info->signature == MagickCoreSignature);
1727  assert(filename != (char *) NULL);
1728  if (IsEventLogging() != MagickFalse)
1729  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1730  display=XOpenDisplay(image_info->server_name);
1731  if (display == (Display *) NULL)
1732  {
1733  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1734  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1735  return(MagickFalse);
1736  }
1737  (void) XSetErrorHandler(XError);
1738  status=XRemoteCommand(display,window,filename);
1739  (void) XCloseDisplay(display);
1740  return(status != 0 ? MagickTrue : MagickFalse);
1741 }
1742 
1743 /*
1744 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1745 % %
1746 % %
1747 % %
1748 + X A n n o t a t e E d i t I m a g e %
1749 % %
1750 % %
1751 % %
1752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1753 %
1754 % XAnnotateEditImage() annotates the image with text.
1755 %
1756 % The format of the XAnnotateEditImage method is:
1757 %
1758 % MagickBooleanType XAnnotateEditImage(Display *display,
1759 % XResourceInfo *resource_info,XWindows *windows,Image *image,
1760 % ExceptionInfo *exception)
1761 %
1762 % A description of each parameter follows:
1763 %
1764 % o display: Specifies a connection to an X server; returned from
1765 % XOpenDisplay.
1766 %
1767 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1768 %
1769 % o windows: Specifies a pointer to a XWindows structure.
1770 %
1771 % o image: the image; returned from ReadImage.
1772 %
1773 */
1774 
1775 static MagickBooleanType XAnnotateEditImage(Display *display,
1776  XResourceInfo *resource_info,XWindows *windows,Image *image,
1777  ExceptionInfo *exception)
1778 {
1779  const char
1780  *const AnnotateMenu[] =
1781  {
1782  "Font Name",
1783  "Font Color",
1784  "Box Color",
1785  "Rotate Text",
1786  "Help",
1787  "Dismiss",
1788  (char *) NULL
1789  },
1790  *const TextMenu[] =
1791  {
1792  "Help",
1793  "Apply",
1794  (char *) NULL
1795  };
1796 
1797  static const ModeType
1798  AnnotateCommands[] =
1799  {
1800  AnnotateNameCommand,
1801  AnnotateFontColorCommand,
1802  AnnotateBackgroundColorCommand,
1803  AnnotateRotateCommand,
1804  AnnotateHelpCommand,
1805  AnnotateDismissCommand
1806  },
1807  TextCommands[] =
1808  {
1809  TextHelpCommand,
1810  TextApplyCommand
1811  };
1812 
1813  static MagickBooleanType
1814  transparent_box = MagickTrue,
1815  transparent_pen = MagickFalse;
1816 
1817  static double
1818  degrees = 0.0;
1819 
1820  static unsigned int
1821  box_id = MaxNumberPens-2,
1822  font_id = 0,
1823  pen_id = 0;
1824 
1825  char
1826  command[MagickPathExtent],
1827  *p,
1828  text[MagickPathExtent];
1829 
1830  const char
1831  *ColorMenu[MaxNumberPens+1];
1832 
1833  Cursor
1834  cursor;
1835 
1836  GC
1837  annotate_context;
1838 
1839  int
1840  id,
1841  pen_number,
1842  status,
1843  x,
1844  y;
1845 
1846  KeySym
1847  key_symbol;
1848 
1849  size_t
1850  state;
1851 
1852  ssize_t
1853  i;
1854 
1855  unsigned int
1856  height,
1857  width;
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 < (windows->info.x+(int) windows->info.width)) &&
2185  (y < (windows->info.y+(int) windows->info.height)))
2186  (void) XWithdrawWindow(display,windows->info.id,
2187  windows->info.screen);
2188  }
2189  else
2190  if ((x > (windows->info.x+(int) windows->info.width)) ||
2191  (y > (windows->info.y+(int) 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/(size_t) MagickMax(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+(int) annotate_info->width;
2422  y=annotate_info->y;
2423  if (annotate_info->width != 0)
2424  p+=(ptrdiff_t) 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  magick_fallthrough;
2487  }
2488  case XK_Return:
2489  case XK_KP_Enter:
2490  {
2491  /*
2492  Advance to the next line of text.
2493  */
2494  *p='\0';
2495  annotate_info->width=(unsigned int) XTextWidth(font_info,
2496  annotate_info->text,(int) strlen(annotate_info->text));
2497  if (annotate_info->next != (XAnnotateInfo *) NULL)
2498  {
2499  /*
2500  Line of text already exists.
2501  */
2502  annotate_info=annotate_info->next;
2503  x=annotate_info->x;
2504  y=annotate_info->y;
2505  p=annotate_info->text;
2506  break;
2507  }
2508  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2509  sizeof(*annotate_info->next));
2510  if (annotate_info->next == (XAnnotateInfo *) NULL)
2511  return(MagickFalse);
2512  *annotate_info->next=(*annotate_info);
2513  annotate_info->next->previous=annotate_info;
2514  annotate_info=annotate_info->next;
2515  annotate_info->text=(char *) AcquireQuantumMemory((size_t) (
2516  (ssize_t) windows->image.width/MagickMax((ssize_t)
2517  font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2518  if (annotate_info->text == (char *) NULL)
2519  return(MagickFalse);
2520  annotate_info->y+=(ssize_t) annotate_info->height;
2521  if (annotate_info->y > (int) windows->image.height)
2522  annotate_info->y=(int) annotate_info->height;
2523  annotate_info->next=(XAnnotateInfo *) NULL;
2524  x=annotate_info->x;
2525  y=annotate_info->y;
2526  p=annotate_info->text;
2527  break;
2528  }
2529  }
2530  break;
2531  }
2532  case KeyRelease:
2533  {
2534  /*
2535  Respond to a user key release.
2536  */
2537  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2538  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2539  state&=(size_t) (~ModifierState);
2540  break;
2541  }
2542  case SelectionNotify:
2543  {
2544  Atom
2545  type;
2546 
2547  int
2548  format;
2549 
2550  unsigned char
2551  *data;
2552 
2553  unsigned long
2554  after,
2555  length;
2556 
2557  /*
2558  Obtain response from primary selection.
2559  */
2560  if (event.xselection.property == (Atom) None)
2561  break;
2562  status=XGetWindowProperty(display,event.xselection.requestor,
2563  event.xselection.property,0L,(long) MagickPathExtent,True,XA_STRING,
2564  &type,&format,&length,&after,&data);
2565  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2566  (length == 0))
2567  break;
2568  /*
2569  Annotate Image window with primary selection.
2570  */
2571  for (i=0; i < (ssize_t) length; i++)
2572  {
2573  if ((char) data[i] != '\n')
2574  {
2575  /*
2576  Draw a single character on the Image window.
2577  */
2578  *p=(char) data[i];
2579  (void) XDrawString(display,windows->image.id,annotate_context,
2580  x,y,p,1);
2581  x+=XTextWidth(font_info,p,1);
2582  p++;
2583  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2584  continue;
2585  }
2586  /*
2587  Advance to the next line of text.
2588  */
2589  *p='\0';
2590  annotate_info->width=(unsigned int) XTextWidth(font_info,
2591  annotate_info->text,(int) strlen(annotate_info->text));
2592  if (annotate_info->next != (XAnnotateInfo *) NULL)
2593  {
2594  /*
2595  Line of text already exists.
2596  */
2597  annotate_info=annotate_info->next;
2598  x=annotate_info->x;
2599  y=annotate_info->y;
2600  p=annotate_info->text;
2601  continue;
2602  }
2603  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2604  sizeof(*annotate_info->next));
2605  if (annotate_info->next == (XAnnotateInfo *) NULL)
2606  return(MagickFalse);
2607  *annotate_info->next=(*annotate_info);
2608  annotate_info->next->previous=annotate_info;
2609  annotate_info=annotate_info->next;
2610  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2611  (windows->image.width/MagickMax((ssize_t)
2612  font_info->min_bounds.width,1)+2L),sizeof(*annotate_info->text));
2613  if (annotate_info->text == (char *) NULL)
2614  return(MagickFalse);
2615  annotate_info->y+=(ssize_t) annotate_info->height;
2616  if (annotate_info->y > (int) windows->image.height)
2617  annotate_info->y=(int) annotate_info->height;
2618  annotate_info->next=(XAnnotateInfo *) NULL;
2619  x=annotate_info->x;
2620  y=annotate_info->y;
2621  p=annotate_info->text;
2622  }
2623  (void) XFree((void *) data);
2624  break;
2625  }
2626  default:
2627  break;
2628  }
2629  } while ((state & ExitState) == 0);
2630  (void) XFreeCursor(display,cursor);
2631  /*
2632  Annotation is relative to image configuration.
2633  */
2634  width=(unsigned int) image->columns;
2635  height=(unsigned int) image->rows;
2636  x=0;
2637  y=0;
2638  if (windows->image.crop_geometry != (char *) NULL)
2639  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2640  /*
2641  Initialize annotated image.
2642  */
2643  XSetCursorState(display,windows,MagickTrue);
2644  XCheckRefreshWindows(display,windows);
2645  while (annotate_info != (XAnnotateInfo *) NULL)
2646  {
2647  if (annotate_info->width == 0)
2648  {
2649  /*
2650  No text on this line-- go to the next line of text.
2651  */
2652  previous_info=annotate_info->previous;
2653  annotate_info->text=(char *)
2654  RelinquishMagickMemory(annotate_info->text);
2655  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2656  annotate_info=previous_info;
2657  continue;
2658  }
2659  /*
2660  Determine pixel index for box and pen color.
2661  */
2662  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2663  if (windows->pixel_info->colors != 0)
2664  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2665  if (windows->pixel_info->pixels[i] ==
2666  windows->pixel_info->pen_colors[box_id].pixel)
2667  {
2668  windows->pixel_info->box_index=(unsigned short) i;
2669  break;
2670  }
2671  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2672  if (windows->pixel_info->colors != 0)
2673  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2674  if (windows->pixel_info->pixels[i] ==
2675  windows->pixel_info->pen_colors[pen_id].pixel)
2676  {
2677  windows->pixel_info->pen_index=(unsigned short) i;
2678  break;
2679  }
2680  /*
2681  Define the annotate geometry string.
2682  */
2683  annotate_info->x=(int)
2684  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2685  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2686  windows->image.y)/windows->image.ximage->height;
2687  (void) FormatLocaleString(annotate_info->geometry,MagickPathExtent,
2688  "%gx%g%+g%+g",(double) width*annotate_info->width/
2689  windows->image.ximage->width,(double) height*annotate_info->height/
2690  windows->image.ximage->height,(double) annotate_info->x+x,(double)
2691  annotate_info->y+y);
2692  /*
2693  Annotate image with text.
2694  */
2695  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2696  exception) == MagickFalse ? 0 : 1;
2697  if (status == 0)
2698  return(MagickFalse);
2699  /*
2700  Free up memory.
2701  */
2702  previous_info=annotate_info->previous;
2703  annotate_info->text=DestroyString(annotate_info->text);
2704  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2705  annotate_info=previous_info;
2706  }
2707  (void) XSetForeground(display,annotate_context,
2708  windows->pixel_info->foreground_color.pixel);
2709  (void) XSetBackground(display,annotate_context,
2710  windows->pixel_info->background_color.pixel);
2711  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2712  XSetCursorState(display,windows,MagickFalse);
2713  (void) XFreeFont(display,font_info);
2714  /*
2715  Update image configuration.
2716  */
2717  XConfigureImageColormap(display,resource_info,windows,image,exception);
2718  (void) XConfigureImage(display,resource_info,windows,image,exception);
2719  return(MagickTrue);
2720 }
2721 
2722 /*
2723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2724 % %
2725 % %
2726 % %
2727 + X B a c k g r o u n d I m a g e %
2728 % %
2729 % %
2730 % %
2731 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2732 %
2733 % XBackgroundImage() displays the image in the background of a window.
2734 %
2735 % The format of the XBackgroundImage method is:
2736 %
2737 % MagickBooleanType XBackgroundImage(Display *display,
2738 % XResourceInfo *resource_info,XWindows *windows,Image **image,
2739 % ExceptionInfo *exception)
2740 %
2741 % A description of each parameter follows:
2742 %
2743 % o display: Specifies a connection to an X server; returned from
2744 % XOpenDisplay.
2745 %
2746 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2747 %
2748 % o windows: Specifies a pointer to a XWindows structure.
2749 %
2750 % o image: the image.
2751 %
2752 % o exception: return any errors or warnings in this structure.
2753 %
2754 */
2755 static MagickBooleanType XBackgroundImage(Display *display,
2756  XResourceInfo *resource_info,XWindows *windows,Image **image,
2757  ExceptionInfo *exception)
2758 {
2759 #define BackgroundImageTag "Background/Image"
2760 
2761  int
2762  status;
2763 
2764  static char
2765  window_id[MagickPathExtent] = "root";
2766 
2767  XResourceInfo
2768  background_resources;
2769 
2770  /*
2771  Put image in background.
2772  */
2773  status=XDialogWidget(display,windows,"Background",
2774  "Enter window id (id 0x00 selects window with pointer):",window_id);
2775  if (*window_id == '\0')
2776  return(MagickFalse);
2777  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2778  exception);
2779  XInfoWidget(display,windows,BackgroundImageTag);
2780  XSetCursorState(display,windows,MagickTrue);
2781  XCheckRefreshWindows(display,windows);
2782  background_resources=(*resource_info);
2783  background_resources.window_id=window_id;
2784  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2785  status=XDisplayBackgroundImage(display,&background_resources,*image,
2786  exception) == MagickFalse ? 0 : 1;
2787  if (status != MagickFalse)
2788  XClientMessage(display,windows->image.id,windows->im_protocols,
2789  windows->im_retain_colors,CurrentTime);
2790  XSetCursorState(display,windows,MagickFalse);
2791  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2792  exception);
2793  return(MagickTrue);
2794 }
2795 
2796 /*
2797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2798 % %
2799 % %
2800 % %
2801 + X C h o p I m a g e %
2802 % %
2803 % %
2804 % %
2805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2806 %
2807 % XChopImage() chops the X image.
2808 %
2809 % The format of the XChopImage method is:
2810 %
2811 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2812 % XWindows *windows,Image **image,ExceptionInfo *exception)
2813 %
2814 % A description of each parameter follows:
2815 %
2816 % o display: Specifies a connection to an X server; returned from
2817 % XOpenDisplay.
2818 %
2819 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2820 %
2821 % o windows: Specifies a pointer to a XWindows structure.
2822 %
2823 % o image: the image.
2824 %
2825 % o exception: return any errors or warnings in this structure.
2826 %
2827 */
2828 static MagickBooleanType XChopImage(Display *display,
2829  XResourceInfo *resource_info,XWindows *windows,Image **image,
2830  ExceptionInfo *exception)
2831 {
2832  const char
2833  *const ChopMenu[] =
2834  {
2835  "Direction",
2836  "Help",
2837  "Dismiss",
2838  (char *) NULL
2839  };
2840 
2841  static ModeType
2842  direction = HorizontalChopCommand;
2843 
2844  static const ModeType
2845  ChopCommands[] =
2846  {
2847  ChopDirectionCommand,
2848  ChopHelpCommand,
2849  ChopDismissCommand
2850  },
2851  DirectionCommands[] =
2852  {
2853  HorizontalChopCommand,
2854  VerticalChopCommand
2855  };
2856 
2857  char
2858  text[MagickPathExtent];
2859 
2860  double
2861  scale_factor;
2862 
2863  Image
2864  *chop_image;
2865 
2866  int
2867  id,
2868  x,
2869  y;
2870 
2872  chop_info;
2873 
2874  size_t
2875  state;
2876 
2877  unsigned int
2878  distance,
2879  height,
2880  width;
2881 
2882  XEvent
2883  event;
2884 
2885  XSegment
2886  segment_info;
2887 
2888  /*
2889  Map Command widget.
2890  */
2891  (void) CloneString(&windows->command.name,"Chop");
2892  windows->command.data=1;
2893  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894  (void) XMapRaised(display,windows->command.id);
2895  XClientMessage(display,windows->image.id,windows->im_protocols,
2896  windows->im_update_widget,CurrentTime);
2897  /*
2898  Track pointer until button 1 is pressed.
2899  */
2900  XQueryPosition(display,windows->image.id,&x,&y);
2901  (void) XSelectInput(display,windows->image.id,
2902  windows->image.attributes.event_mask | PointerMotionMask);
2903  state=DefaultState;
2904  (void) memset(&segment_info,0,sizeof(segment_info));
2905  do
2906  {
2907  if (windows->info.mapped != MagickFalse)
2908  {
2909  /*
2910  Display pointer position.
2911  */
2912  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
2913  x+windows->image.x,y+windows->image.y);
2914  XInfoWidget(display,windows,text);
2915  }
2916  /*
2917  Wait for next event.
2918  */
2919  XScreenEvent(display,windows,&event,exception);
2920  if (event.xany.window == windows->command.id)
2921  {
2922  /*
2923  Select a command from the Command widget.
2924  */
2925  id=XCommandWidget(display,windows,ChopMenu,&event);
2926  if (id < 0)
2927  continue;
2928  switch (ChopCommands[id])
2929  {
2930  case ChopDirectionCommand:
2931  {
2932  char
2933  command[MagickPathExtent];
2934 
2935  const char
2936  *const Directions[] =
2937  {
2938  "horizontal",
2939  "vertical",
2940  (char *) NULL,
2941  };
2942 
2943  /*
2944  Select a command from the pop-up menu.
2945  */
2946  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2947  if (id >= 0)
2948  direction=DirectionCommands[id];
2949  break;
2950  }
2951  case ChopHelpCommand:
2952  {
2953  XTextViewHelp(display,resource_info,windows,MagickFalse,
2954  "Help Viewer - Image Chop",ImageChopHelp);
2955  break;
2956  }
2957  case ChopDismissCommand:
2958  {
2959  /*
2960  Prematurely exit.
2961  */
2962  state|=EscapeState;
2963  state|=ExitState;
2964  break;
2965  }
2966  default:
2967  break;
2968  }
2969  continue;
2970  }
2971  switch (event.type)
2972  {
2973  case ButtonPress:
2974  {
2975  if (event.xbutton.button != Button1)
2976  break;
2977  if (event.xbutton.window != windows->image.id)
2978  break;
2979  /*
2980  User has committed to start point of chopping line.
2981  */
2982  segment_info.x1=(short int) event.xbutton.x;
2983  segment_info.x2=(short int) event.xbutton.x;
2984  segment_info.y1=(short int) event.xbutton.y;
2985  segment_info.y2=(short int) event.xbutton.y;
2986  state|=ExitState;
2987  break;
2988  }
2989  case ButtonRelease:
2990  break;
2991  case Expose:
2992  break;
2993  case KeyPress:
2994  {
2995  char
2996  command[MagickPathExtent];
2997 
2998  KeySym
2999  key_symbol;
3000 
3001  if (event.xkey.window != windows->image.id)
3002  break;
3003  /*
3004  Respond to a user key press.
3005  */
3006  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3007  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3008  switch ((int) key_symbol)
3009  {
3010  case XK_Escape:
3011  case XK_F20:
3012  {
3013  /*
3014  Prematurely exit.
3015  */
3016  state|=EscapeState;
3017  state|=ExitState;
3018  break;
3019  }
3020  case XK_F1:
3021  case XK_Help:
3022  {
3023  (void) XSetFunction(display,windows->image.highlight_context,
3024  GXcopy);
3025  XTextViewHelp(display,resource_info,windows,MagickFalse,
3026  "Help Viewer - Image Chop",ImageChopHelp);
3027  (void) XSetFunction(display,windows->image.highlight_context,
3028  GXinvert);
3029  break;
3030  }
3031  default:
3032  {
3033  (void) XBell(display,0);
3034  break;
3035  }
3036  }
3037  break;
3038  }
3039  case MotionNotify:
3040  {
3041  /*
3042  Map and unmap Info widget as text cursor crosses its boundaries.
3043  */
3044  x=event.xmotion.x;
3045  y=event.xmotion.y;
3046  if (windows->info.mapped != MagickFalse)
3047  {
3048  if ((x < (windows->info.x+(int) windows->info.width)) &&
3049  (y < (windows->info.y+(int) windows->info.height)))
3050  (void) XWithdrawWindow(display,windows->info.id,
3051  windows->info.screen);
3052  }
3053  else
3054  if ((x > (windows->info.x+(int) windows->info.width)) ||
3055  (y > (windows->info.y+(int) windows->info.height)))
3056  (void) XMapWindow(display,windows->info.id);
3057  }
3058  }
3059  } while ((state & ExitState) == 0);
3060  (void) XSelectInput(display,windows->image.id,
3061  windows->image.attributes.event_mask);
3062  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3063  if ((state & EscapeState) != 0)
3064  return(MagickTrue);
3065  /*
3066  Draw line as pointer moves until the mouse button is released.
3067  */
3068  chop_info.width=0;
3069  chop_info.height=0;
3070  chop_info.x=0;
3071  chop_info.y=0;
3072  distance=0;
3073  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3074  state=DefaultState;
3075  do
3076  {
3077  if (distance > 9)
3078  {
3079  /*
3080  Display info and draw chopping line.
3081  */
3082  if (windows->info.mapped == MagickFalse)
3083  (void) XMapWindow(display,windows->info.id);
3084  (void) FormatLocaleString(text,MagickPathExtent,
3085  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3086  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3087  XInfoWidget(display,windows,text);
3088  XHighlightLine(display,windows->image.id,
3089  windows->image.highlight_context,&segment_info);
3090  }
3091  else
3092  if (windows->info.mapped != MagickFalse)
3093  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094  /*
3095  Wait for next event.
3096  */
3097  XScreenEvent(display,windows,&event,exception);
3098  if (distance > 9)
3099  XHighlightLine(display,windows->image.id,
3100  windows->image.highlight_context,&segment_info);
3101  switch (event.type)
3102  {
3103  case ButtonPress:
3104  {
3105  segment_info.x2=(short int) event.xmotion.x;
3106  segment_info.y2=(short int) event.xmotion.y;
3107  break;
3108  }
3109  case ButtonRelease:
3110  {
3111  /*
3112  User has committed to chopping line.
3113  */
3114  segment_info.x2=(short int) event.xbutton.x;
3115  segment_info.y2=(short int) event.xbutton.y;
3116  state|=ExitState;
3117  break;
3118  }
3119  case Expose:
3120  break;
3121  case MotionNotify:
3122  {
3123  segment_info.x2=(short int) event.xmotion.x;
3124  segment_info.y2=(short int) event.xmotion.y;
3125  }
3126  default:
3127  break;
3128  }
3129  /*
3130  Check boundary conditions.
3131  */
3132  if (segment_info.x2 < 0)
3133  segment_info.x2=0;
3134  else
3135  if (segment_info.x2 > windows->image.ximage->width)
3136  segment_info.x2=windows->image.ximage->width;
3137  if (segment_info.y2 < 0)
3138  segment_info.y2=0;
3139  else
3140  if (segment_info.y2 > windows->image.ximage->height)
3141  segment_info.y2=windows->image.ximage->height;
3142  distance=(unsigned int)
3143  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3144  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3145  /*
3146  Compute chopping geometry.
3147  */
3148  if (direction == HorizontalChopCommand)
3149  {
3150  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3151  chop_info.height=0;
3152  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3153  chop_info.y=0;
3154  if (segment_info.x1 > segment_info.x2)
3155  {
3156  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3157  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3158  }
3159  }
3160  else
3161  {
3162  chop_info.width=0;
3163  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3164  chop_info.x=0;
3165  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3166  if (segment_info.y1 > segment_info.y2)
3167  {
3168  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3169  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3170  }
3171  }
3172  } while ((state & ExitState) == 0);
3173  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175  if (distance <= 9)
3176  return(MagickTrue);
3177  /*
3178  Image chopping is relative to image configuration.
3179  */
3180  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3181  exception);
3182  XSetCursorState(display,windows,MagickTrue);
3183  XCheckRefreshWindows(display,windows);
3184  windows->image.window_changes.width=windows->image.ximage->width-
3185  (int) chop_info.width;
3186  windows->image.window_changes.height=windows->image.ximage->height-
3187  (int) chop_info.height;
3188  width=(unsigned int) (*image)->columns;
3189  height=(unsigned int) (*image)->rows;
3190  x=0;
3191  y=0;
3192  if (windows->image.crop_geometry != (char *) NULL)
3193  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3194  scale_factor=(double) width/windows->image.ximage->width;
3195  chop_info.x+=x;
3196  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3197  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3198  scale_factor=(double) height/windows->image.ximage->height;
3199  chop_info.y+=y;
3200  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3201  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3202  /*
3203  Chop image.
3204  */
3205  chop_image=ChopImage(*image,&chop_info,exception);
3206  XSetCursorState(display,windows,MagickFalse);
3207  if (chop_image == (Image *) NULL)
3208  return(MagickFalse);
3209  *image=DestroyImage(*image);
3210  *image=chop_image;
3211  /*
3212  Update image configuration.
3213  */
3214  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3215  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3216  return(MagickTrue);
3217 }
3218 
3219 /*
3220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3221 % %
3222 % %
3223 % %
3224 + X C o l o r E d i t I m a g e %
3225 % %
3226 % %
3227 % %
3228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3229 %
3230 % XColorEditImage() allows the user to interactively change the color of one
3231 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3232 %
3233 % The format of the XColorEditImage method is:
3234 %
3235 % MagickBooleanType XColorEditImage(Display *display,
3236 % XResourceInfo *resource_info,XWindows *windows,Image **image,
3237 % ExceptionInfo *exception)
3238 %
3239 % A description of each parameter follows:
3240 %
3241 % o display: Specifies a connection to an X server; returned from
3242 % XOpenDisplay.
3243 %
3244 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3245 %
3246 % o windows: Specifies a pointer to a XWindows structure.
3247 %
3248 % o image: the image; returned from ReadImage.
3249 %
3250 % o exception: return any errors or warnings in this structure.
3251 %
3252 */
3253 static MagickBooleanType XColorEditImage(Display *display,
3254  XResourceInfo *resource_info,XWindows *windows,Image **image,
3255  ExceptionInfo *exception)
3256 {
3257  const char
3258  *const ColorEditMenu[] =
3259  {
3260  "Method",
3261  "Pixel Color",
3262  "Border Color",
3263  "Fuzz",
3264  "Undo",
3265  "Help",
3266  "Dismiss",
3267  (char *) NULL
3268  };
3269 
3270  static const ModeType
3271  ColorEditCommands[] =
3272  {
3273  ColorEditMethodCommand,
3274  ColorEditColorCommand,
3275  ColorEditBorderCommand,
3276  ColorEditFuzzCommand,
3277  ColorEditUndoCommand,
3278  ColorEditHelpCommand,
3279  ColorEditDismissCommand
3280  };
3281 
3282  static PaintMethod
3283  method = PointMethod;
3284 
3285  static unsigned int
3286  pen_id = 0;
3287 
3288  static XColor
3289  border_color = { 0, 0, 0, 0, 0, 0 };
3290 
3291  char
3292  command[MagickPathExtent],
3293  text[MagickPathExtent];
3294 
3295  Cursor
3296  cursor;
3297 
3298  int
3299  entry,
3300  id,
3301  x,
3302  x_offset,
3303  y,
3304  y_offset;
3305 
3306  Quantum
3307  *q;
3308 
3309  size_t
3310  state;
3311 
3312  ssize_t
3313  i;
3314 
3315  unsigned int
3316  height,
3317  width;
3318 
3319  XColor
3320  color;
3321 
3322  XEvent
3323  event;
3324 
3325  /*
3326  Map Command widget.
3327  */
3328  (void) CloneString(&windows->command.name,"Color Edit");
3329  windows->command.data=4;
3330  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331  (void) XMapRaised(display,windows->command.id);
3332  XClientMessage(display,windows->image.id,windows->im_protocols,
3333  windows->im_update_widget,CurrentTime);
3334  /*
3335  Make cursor.
3336  */
3337  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338  resource_info->background_color,resource_info->foreground_color);
3339  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340  /*
3341  Track pointer until button 1 is pressed.
3342  */
3343  XQueryPosition(display,windows->image.id,&x,&y);
3344  (void) XSelectInput(display,windows->image.id,
3345  windows->image.attributes.event_mask | PointerMotionMask);
3346  state=DefaultState;
3347  do
3348  {
3349  if (windows->info.mapped != MagickFalse)
3350  {
3351  /*
3352  Display pointer position.
3353  */
3354  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
3355  x+windows->image.x,y+windows->image.y);
3356  XInfoWidget(display,windows,text);
3357  }
3358  /*
3359  Wait for next event.
3360  */
3361  XScreenEvent(display,windows,&event,exception);
3362  if (event.xany.window == windows->command.id)
3363  {
3364  /*
3365  Select a command from the Command widget.
3366  */
3367  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368  if (id < 0)
3369  {
3370  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371  continue;
3372  }
3373  switch (ColorEditCommands[id])
3374  {
3375  case ColorEditMethodCommand:
3376  {
3377  char
3378  **methods;
3379 
3380  /*
3381  Select a method from the pop-up menu.
3382  */
3383  methods=(char **) GetCommandOptions(MagickMethodOptions);
3384  if (methods == (char **) NULL)
3385  break;
3386  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387  (const char **) methods,command);
3388  if (entry >= 0)
3389  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3390  MagickFalse,methods[entry]);
3391  methods=DestroyStringList(methods);
3392  break;
3393  }
3394  case ColorEditColorCommand:
3395  {
3396  const char
3397  *ColorMenu[MaxNumberPens];
3398 
3399  int
3400  pen_number;
3401 
3402  /*
3403  Initialize menu selections.
3404  */
3405  for (i=0; i < (int) (MaxNumberPens-2); i++)
3406  ColorMenu[i]=resource_info->pen_colors[i];
3407  ColorMenu[MaxNumberPens-2]="Browser...";
3408  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409  /*
3410  Select a pen color from the pop-up menu.
3411  */
3412  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413  (const char **) ColorMenu,command);
3414  if (pen_number < 0)
3415  break;
3416  if (pen_number == (MaxNumberPens-2))
3417  {
3418  static char
3419  color_name[MagickPathExtent] = "gray";
3420 
3421  /*
3422  Select a pen color from a dialog.
3423  */
3424  resource_info->pen_colors[pen_number]=color_name;
3425  XColorBrowserWidget(display,windows,"Select",color_name);
3426  if (*color_name == '\0')
3427  break;
3428  }
3429  /*
3430  Set pen color.
3431  */
3432  (void) XParseColor(display,windows->map_info->colormap,
3433  resource_info->pen_colors[pen_number],&color);
3434  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435  (unsigned int) MaxColors,&color);
3436  windows->pixel_info->pen_colors[pen_number]=color;
3437  pen_id=(unsigned int) pen_number;
3438  break;
3439  }
3440  case ColorEditBorderCommand:
3441  {
3442  const char
3443  *ColorMenu[MaxNumberPens];
3444 
3445  int
3446  pen_number;
3447 
3448  /*
3449  Initialize menu selections.
3450  */
3451  for (i=0; i < (int) (MaxNumberPens-2); i++)
3452  ColorMenu[i]=resource_info->pen_colors[i];
3453  ColorMenu[MaxNumberPens-2]="Browser...";
3454  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455  /*
3456  Select a pen color from the pop-up menu.
3457  */
3458  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459  (const char **) ColorMenu,command);
3460  if (pen_number < 0)
3461  break;
3462  if (pen_number == (MaxNumberPens-2))
3463  {
3464  static char
3465  color_name[MagickPathExtent] = "gray";
3466 
3467  /*
3468  Select a pen color from a dialog.
3469  */
3470  resource_info->pen_colors[pen_number]=color_name;
3471  XColorBrowserWidget(display,windows,"Select",color_name);
3472  if (*color_name == '\0')
3473  break;
3474  }
3475  /*
3476  Set border color.
3477  */
3478  (void) XParseColor(display,windows->map_info->colormap,
3479  resource_info->pen_colors[pen_number],&border_color);
3480  break;
3481  }
3482  case ColorEditFuzzCommand:
3483  {
3484  const char
3485  *const FuzzMenu[] =
3486  {
3487  "0%",
3488  "2%",
3489  "5%",
3490  "10%",
3491  "15%",
3492  "Dialog...",
3493  (char *) NULL,
3494  };
3495 
3496  static char
3497  fuzz[MagickPathExtent];
3498 
3499  /*
3500  Select a command from the pop-up menu.
3501  */
3502  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503  command);
3504  if (entry < 0)
3505  break;
3506  if (entry != 5)
3507  {
3508  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3509  QuantumRange+1.0);
3510  break;
3511  }
3512  (void) (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
3513  (void) XDialogWidget(display,windows,"Ok",
3514  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515  if (*fuzz == '\0')
3516  break;
3517  (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
3518  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3519  1.0);
3520  break;
3521  }
3522  case ColorEditUndoCommand:
3523  {
3524  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3525  image,exception);
3526  break;
3527  }
3528  case ColorEditHelpCommand:
3529  default:
3530  {
3531  XTextViewHelp(display,resource_info,windows,MagickFalse,
3532  "Help Viewer - Image Annotation",ImageColorEditHelp);
3533  break;
3534  }
3535  case ColorEditDismissCommand:
3536  {
3537  /*
3538  Prematurely exit.
3539  */
3540  state|=EscapeState;
3541  state|=ExitState;
3542  break;
3543  }
3544  }
3545  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3546  continue;
3547  }
3548  switch (event.type)
3549  {
3550  case ButtonPress:
3551  {
3552  if (event.xbutton.button != Button1)
3553  break;
3554  if ((event.xbutton.window != windows->image.id) &&
3555  (event.xbutton.window != windows->magnify.id))
3556  break;
3557  /*
3558  exit loop.
3559  */
3560  x=event.xbutton.x;
3561  y=event.xbutton.y;
3562  (void) XMagickCommand(display,resource_info,windows,
3563  SaveToUndoBufferCommand,image,exception);
3564  state|=UpdateConfigurationState;
3565  break;
3566  }
3567  case ButtonRelease:
3568  {
3569  if (event.xbutton.button != Button1)
3570  break;
3571  if ((event.xbutton.window != windows->image.id) &&
3572  (event.xbutton.window != windows->magnify.id))
3573  break;
3574  /*
3575  Update colormap information.
3576  */
3577  x=event.xbutton.x;
3578  y=event.xbutton.y;
3579  XConfigureImageColormap(display,resource_info,windows,*image,exception);
3580  (void) XConfigureImage(display,resource_info,windows,*image,exception);
3581  XInfoWidget(display,windows,text);
3582  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583  state&=(size_t) (~UpdateConfigurationState);
3584  break;
3585  }
3586  case Expose:
3587  break;
3588  case KeyPress:
3589  {
3590  KeySym
3591  key_symbol;
3592 
3593  if (event.xkey.window == windows->magnify.id)
3594  {
3595  Window
3596  window;
3597 
3598  window=windows->magnify.id;
3599  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3600  }
3601  if (event.xkey.window != windows->image.id)
3602  break;
3603  /*
3604  Respond to a user key press.
3605  */
3606  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3607  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3608  switch ((int) key_symbol)
3609  {
3610  case XK_Escape:
3611  case XK_F20:
3612  {
3613  /*
3614  Prematurely exit.
3615  */
3616  state|=ExitState;
3617  break;
3618  }
3619  case XK_F1:
3620  case XK_Help:
3621  {
3622  XTextViewHelp(display,resource_info,windows,MagickFalse,
3623  "Help Viewer - Image Annotation",ImageColorEditHelp);
3624  break;
3625  }
3626  default:
3627  {
3628  (void) XBell(display,0);
3629  break;
3630  }
3631  }
3632  break;
3633  }
3634  case MotionNotify:
3635  {
3636  /*
3637  Map and unmap Info widget as cursor crosses its boundaries.
3638  */
3639  x=event.xmotion.x;
3640  y=event.xmotion.y;
3641  if (windows->info.mapped != MagickFalse)
3642  {
3643  if ((x < (windows->info.x+(int) windows->info.width)) &&
3644  (y < (windows->info.y+(int) windows->info.height)))
3645  (void) XWithdrawWindow(display,windows->info.id,
3646  windows->info.screen);
3647  }
3648  else
3649  if ((x > (windows->info.x+(int) windows->info.width)) ||
3650  (y > (windows->info.y+(int) windows->info.height)))
3651  (void) XMapWindow(display,windows->info.id);
3652  break;
3653  }
3654  default:
3655  break;
3656  }
3657  if (event.xany.window == windows->magnify.id)
3658  {
3659  x=windows->magnify.x-windows->image.x;
3660  y=windows->magnify.y-windows->image.y;
3661  }
3662  x_offset=x;
3663  y_offset=y;
3664  if ((state & UpdateConfigurationState) != 0)
3665  {
3666  CacheView
3667  *image_view;
3668 
3669  int
3670  x,
3671  y;
3672 
3673  /*
3674  Pixel edit is relative to image configuration.
3675  */
3676  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3677  MagickTrue);
3678  color=windows->pixel_info->pen_colors[pen_id];
3679  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3680  width=(unsigned int) (*image)->columns;
3681  height=(unsigned int) (*image)->rows;
3682  x=0;
3683  y=0;
3684  if (windows->image.crop_geometry != (char *) NULL)
3685  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3686  &width,&height);
3687  x_offset=((int) width*(windows->image.x+x_offset)/(int)
3688  windows->image.ximage->width+x);
3689  y_offset=((int) height*(windows->image.y+y_offset)/(int)
3690  windows->image.ximage->height+y);
3691  if ((x_offset < 0) || (y_offset < 0))
3692  continue;
3693  if ((x_offset >= (int) (*image)->columns) ||
3694  (y_offset >= (int) (*image)->rows))
3695  continue;
3696  image_view=AcquireAuthenticCacheView(*image,exception);
3697  switch (method)
3698  {
3699  case PointMethod:
3700  default:
3701  {
3702  /*
3703  Update color information using point algorithm.
3704  */
3705  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3706  return(MagickFalse);
3707  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3708  (ssize_t) y_offset,1,1,exception);
3709  if (q == (Quantum *) NULL)
3710  break;
3711  SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3712  SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3713  SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3714  (void) SyncCacheViewAuthenticPixels(image_view,exception);
3715  break;
3716  }
3717  case ReplaceMethod:
3718  {
3719  PixelInfo
3720  pixel,
3721  target;
3722 
3723  /*
3724  Update color information using replace algorithm.
3725  */
3726  (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3727  x_offset,(ssize_t) y_offset,&target,exception);
3728  if ((*image)->storage_class == DirectClass)
3729  {
3730  for (y=0; y < (int) (*image)->rows; y++)
3731  {
3732  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3733  (*image)->columns,1,exception);
3734  if (q == (Quantum *) NULL)
3735  break;
3736  for (x=0; x < (int) (*image)->columns; x++)
3737  {
3738  GetPixelInfoPixel(*image,q,&pixel);
3739  if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3740  {
3741  SetPixelRed(*image,ScaleShortToQuantum(
3742  color.red),q);
3743  SetPixelGreen(*image,ScaleShortToQuantum(
3744  color.green),q);
3745  SetPixelBlue(*image,ScaleShortToQuantum(
3746  color.blue),q);
3747  }
3748  q+=(ptrdiff_t) GetPixelChannels(*image);
3749  }
3750  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3751  break;
3752  }
3753  }
3754  else
3755  {
3756  for (i=0; i < (ssize_t) (*image)->colors; i++)
3757  if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3758  {
3759  (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3760  color.red);
3761  (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3762  color.green);
3763  (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3764  color.blue);
3765  }
3766  (void) SyncImage(*image,exception);
3767  }
3768  break;
3769  }
3770  case FloodfillMethod:
3771  case FillToBorderMethod:
3772  {
3773  DrawInfo
3774  *draw_info;
3775 
3776  PixelInfo
3777  target;
3778 
3779  /*
3780  Update color information using floodfill algorithm.
3781  */
3782  (void) GetOneVirtualPixelInfo(*image,
3783  GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3784  y_offset,&target,exception);
3785  if (method == FillToBorderMethod)
3786  {
3787  target.red=(double)
3788  ScaleShortToQuantum(border_color.red);
3789  target.green=(double)
3790  ScaleShortToQuantum(border_color.green);
3791  target.blue=(double)
3792  ScaleShortToQuantum(border_color.blue);
3793  }
3794  draw_info=CloneDrawInfo(resource_info->image_info,
3795  (DrawInfo *) NULL);
3796  (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3797  AllCompliance,&draw_info->fill,exception);
3798  (void) FloodfillPaintImage(*image,draw_info,&target,
3799  (ssize_t)x_offset,(ssize_t)y_offset,
3800  method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
3801  draw_info=DestroyDrawInfo(draw_info);
3802  break;
3803  }
3804  case ResetMethod:
3805  {
3806  /*
3807  Update color information using reset algorithm.
3808  */
3809  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3810  return(MagickFalse);
3811  for (y=0; y < (int) (*image)->rows; y++)
3812  {
3813  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3814  (*image)->columns,1,exception);
3815  if (q == (Quantum *) NULL)
3816  break;
3817  for (x=0; x < (int) (*image)->columns; x++)
3818  {
3819  SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3820  SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3821  SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3822  q+=(ptrdiff_t) GetPixelChannels(*image);
3823  }
3824  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3825  break;
3826  }
3827  break;
3828  }
3829  }
3830  image_view=DestroyCacheView(image_view);
3831  state&=(~0x0080U);
3832  }
3833  } while ((state & ExitState) == 0);
3834  (void) XSelectInput(display,windows->image.id,
3835  windows->image.attributes.event_mask);
3836  XSetCursorState(display,windows,MagickFalse);
3837  (void) XFreeCursor(display,cursor);
3838  return(MagickTrue);
3839 }
3840 
3841 /*
3842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3843 % %
3844 % %
3845 % %
3846 + X C o m p o s i t e I m a g e %
3847 % %
3848 % %
3849 % %
3850 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3851 %
3852 % XCompositeImage() requests an image name from the user, reads the image and
3853 % composites it with the X window image at a location the user chooses with
3854 % the pointer.
3855 %
3856 % The format of the XCompositeImage method is:
3857 %
3858 % MagickBooleanType XCompositeImage(Display *display,
3859 % XResourceInfo *resource_info,XWindows *windows,Image *image,
3860 % ExceptionInfo *exception)
3861 %
3862 % A description of each parameter follows:
3863 %
3864 % o display: Specifies a connection to an X server; returned from
3865 % XOpenDisplay.
3866 %
3867 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3868 %
3869 % o windows: Specifies a pointer to a XWindows structure.
3870 %
3871 % o image: the image; returned from ReadImage.
3872 %
3873 % o exception: return any errors or warnings in this structure.
3874 %
3875 */
3876 static MagickBooleanType XCompositeImage(Display *display,
3877  XResourceInfo *resource_info,XWindows *windows,Image *image,
3878  ExceptionInfo *exception)
3879 {
3880  const char
3881  *const CompositeMenu[] =
3882  {
3883  "Operators",
3884  "Dissolve",
3885  "Displace",
3886  "Help",
3887  "Dismiss",
3888  (char *) NULL
3889  };
3890 
3891  static char
3892  displacement_geometry[MagickPathExtent] = "30x30",
3893  filename[MagickPathExtent] = "\0";
3894 
3895  static CompositeOperator
3896  compose = CopyCompositeOp;
3897 
3898  static const ModeType
3899  CompositeCommands[] =
3900  {
3901  CompositeOperatorsCommand,
3902  CompositeDissolveCommand,
3903  CompositeDisplaceCommand,
3904  CompositeHelpCommand,
3905  CompositeDismissCommand
3906  };
3907 
3908  char
3909  text[MagickPathExtent];
3910 
3911  Cursor
3912  cursor;
3913 
3914  Image
3915  *composite_image;
3916 
3917  int
3918  entry,
3919  id,
3920  x,
3921  y;
3922 
3923  double
3924  blend,
3925  scale_factor;
3926 
3928  highlight_info,
3929  composite_info;
3930 
3931  unsigned int
3932  height,
3933  width;
3934 
3935  size_t
3936  state;
3937 
3938  XEvent
3939  event;
3940 
3941  /*
3942  Request image file name from user.
3943  */
3944  XFileBrowserWidget(display,windows,"Composite",filename);
3945  if (*filename == '\0')
3946  return(MagickTrue);
3947  /*
3948  Read image.
3949  */
3950  XSetCursorState(display,windows,MagickTrue);
3951  XCheckRefreshWindows(display,windows);
3952  (void) CopyMagickString(resource_info->image_info->filename,filename,
3953  MagickPathExtent);
3954  composite_image=ReadImage(resource_info->image_info,exception);
3955  CatchException(exception);
3956  XSetCursorState(display,windows,MagickFalse);
3957  if (composite_image == (Image *) NULL)
3958  return(MagickFalse);
3959  /*
3960  Map Command widget.
3961  */
3962  (void) CloneString(&windows->command.name,"Composite");
3963  windows->command.data=1;
3964  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3965  (void) XMapRaised(display,windows->command.id);
3966  XClientMessage(display,windows->image.id,windows->im_protocols,
3967  windows->im_update_widget,CurrentTime);
3968  /*
3969  Track pointer until button 1 is pressed.
3970  */
3971  XQueryPosition(display,windows->image.id,&x,&y);
3972  (void) XSelectInput(display,windows->image.id,
3973  windows->image.attributes.event_mask | PointerMotionMask);
3974  composite_info.x=(ssize_t) windows->image.x+x;
3975  composite_info.y=(ssize_t) windows->image.y+y;
3976  composite_info.width=0;
3977  composite_info.height=0;
3978  cursor=XCreateFontCursor(display,XC_ul_angle);
3979  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3980  blend=0.0;
3981  state=DefaultState;
3982  do
3983  {
3984  if (windows->info.mapped != MagickFalse)
3985  {
3986  /*
3987  Display pointer position.
3988  */
3989  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
3990  (long) composite_info.x,(long) composite_info.y);
3991  XInfoWidget(display,windows,text);
3992  }
3993  highlight_info=composite_info;
3994  highlight_info.x=composite_info.x-windows->image.x;
3995  highlight_info.y=composite_info.y-windows->image.y;
3996  XHighlightRectangle(display,windows->image.id,
3997  windows->image.highlight_context,&highlight_info);
3998  /*
3999  Wait for next event.
4000  */
4001  XScreenEvent(display,windows,&event,exception);
4002  XHighlightRectangle(display,windows->image.id,
4003  windows->image.highlight_context,&highlight_info);
4004  if (event.xany.window == windows->command.id)
4005  {
4006  /*
4007  Select a command from the Command widget.
4008  */
4009  id=XCommandWidget(display,windows,CompositeMenu,&event);
4010  if (id < 0)
4011  continue;
4012  switch (CompositeCommands[id])
4013  {
4014  case CompositeOperatorsCommand:
4015  {
4016  char
4017  command[MagickPathExtent],
4018  **operators;
4019 
4020  /*
4021  Select a command from the pop-up menu.
4022  */
4023  operators=GetCommandOptions(MagickComposeOptions);
4024  if (operators == (char **) NULL)
4025  break;
4026  entry=XMenuWidget(display,windows,CompositeMenu[id],
4027  (const char **) operators,command);
4028  if (entry >= 0)
4029  compose=(CompositeOperator) ParseCommandOption(
4030  MagickComposeOptions,MagickFalse,operators[entry]);
4031  operators=DestroyStringList(operators);
4032  break;
4033  }
4034  case CompositeDissolveCommand:
4035  {
4036  static char
4037  factor[MagickPathExtent] = "20.0";
4038 
4039  /*
4040  Dissolve the two images a given percent.
4041  */
4042  (void) XSetFunction(display,windows->image.highlight_context,
4043  GXcopy);
4044  (void) XDialogWidget(display,windows,"Dissolve",
4045  "Enter the blend factor (0.0 - 99.9%):",factor);
4046  (void) XSetFunction(display,windows->image.highlight_context,
4047  GXinvert);
4048  if (*factor == '\0')
4049  break;
4050  blend=StringToDouble(factor,(char **) NULL);
4051  compose=DissolveCompositeOp;
4052  break;
4053  }
4054  case CompositeDisplaceCommand:
4055  {
4056  /*
4057  Get horizontal and vertical scale displacement geometry.
4058  */
4059  (void) XSetFunction(display,windows->image.highlight_context,
4060  GXcopy);
4061  (void) XDialogWidget(display,windows,"Displace",
4062  "Enter the horizontal and vertical scale:",displacement_geometry);
4063  (void) XSetFunction(display,windows->image.highlight_context,
4064  GXinvert);
4065  if (*displacement_geometry == '\0')
4066  break;
4067  compose=DisplaceCompositeOp;
4068  break;
4069  }
4070  case CompositeHelpCommand:
4071  {
4072  (void) XSetFunction(display,windows->image.highlight_context,
4073  GXcopy);
4074  XTextViewHelp(display,resource_info,windows,MagickFalse,
4075  "Help Viewer - Image Composite",ImageCompositeHelp);
4076  (void) XSetFunction(display,windows->image.highlight_context,
4077  GXinvert);
4078  break;
4079  }
4080  case CompositeDismissCommand:
4081  {
4082  /*
4083  Prematurely exit.
4084  */
4085  state|=EscapeState;
4086  state|=ExitState;
4087  break;
4088  }
4089  default:
4090  break;
4091  }
4092  continue;
4093  }
4094  switch (event.type)
4095  {
4096  case ButtonPress:
4097  {
4098  if (resource_info->debug != MagickFalse)
4099  (void) LogMagickEvent(X11Event,GetMagickModule(),
4100  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4101  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4102  if (event.xbutton.button != Button1)
4103  break;
4104  if (event.xbutton.window != windows->image.id)
4105  break;
4106  /*
4107  Change cursor.
4108  */
4109  composite_info.width=composite_image->columns;
4110  composite_info.height=composite_image->rows;
4111  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4112  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4113  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4114  break;
4115  }
4116  case ButtonRelease:
4117  {
4118  if (resource_info->debug != MagickFalse)
4119  (void) LogMagickEvent(X11Event,GetMagickModule(),
4120  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4121  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4122  if (event.xbutton.button != Button1)
4123  break;
4124  if (event.xbutton.window != windows->image.id)
4125  break;
4126  if ((composite_info.width != 0) && (composite_info.height != 0))
4127  {
4128  /*
4129  User has selected the location of the composite image.
4130  */
4131  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4132  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4133  state|=ExitState;
4134  }
4135  break;
4136  }
4137  case Expose:
4138  break;
4139  case KeyPress:
4140  {
4141  char
4142  command[MagickPathExtent];
4143 
4144  KeySym
4145  key_symbol;
4146 
4147  int
4148  length;
4149 
4150  if (event.xkey.window != windows->image.id)
4151  break;
4152  /*
4153  Respond to a user key press.
4154  */
4155  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4156  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4157  *(command+length)='\0';
4158  if (resource_info->debug != MagickFalse)
4159  (void) LogMagickEvent(X11Event,GetMagickModule(),
4160  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4161  switch ((int) key_symbol)
4162  {
4163  case XK_Escape:
4164  case XK_F20:
4165  {
4166  /*
4167  Prematurely exit.
4168  */
4169  composite_image=DestroyImage(composite_image);
4170  state|=EscapeState;
4171  state|=ExitState;
4172  break;
4173  }
4174  case XK_F1:
4175  case XK_Help:
4176  {
4177  (void) XSetFunction(display,windows->image.highlight_context,
4178  GXcopy);
4179  XTextViewHelp(display,resource_info,windows,MagickFalse,
4180  "Help Viewer - Image Composite",ImageCompositeHelp);
4181  (void) XSetFunction(display,windows->image.highlight_context,
4182  GXinvert);
4183  break;
4184  }
4185  default:
4186  {
4187  (void) XBell(display,0);
4188  break;
4189  }
4190  }
4191  break;
4192  }
4193  case MotionNotify:
4194  {
4195  /*
4196  Map and unmap Info widget as text cursor crosses its boundaries.
4197  */
4198  x=event.xmotion.x;
4199  y=event.xmotion.y;
4200  if (windows->info.mapped != MagickFalse)
4201  {
4202  if ((x < (windows->info.x+(int) windows->info.width)) &&
4203  (y < (windows->info.y+(int) windows->info.height)))
4204  (void) XWithdrawWindow(display,windows->info.id,
4205  windows->info.screen);
4206  }
4207  else
4208  if ((x > (windows->info.x+(int) windows->info.width)) ||
4209  (y > (windows->info.y+(int) windows->info.height)))
4210  (void) XMapWindow(display,windows->info.id);
4211  composite_info.x=(ssize_t) windows->image.x+x;
4212  composite_info.y=(ssize_t) windows->image.y+y;
4213  break;
4214  }
4215  default:
4216  {
4217  if (resource_info->debug != MagickFalse)
4218  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4219  event.type);
4220  break;
4221  }
4222  }
4223  } while ((state & ExitState) == 0);
4224  (void) XSelectInput(display,windows->image.id,
4225  windows->image.attributes.event_mask);
4226  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4227  XSetCursorState(display,windows,MagickFalse);
4228  (void) XFreeCursor(display,cursor);
4229  if ((state & EscapeState) != 0)
4230  return(MagickTrue);
4231  /*
4232  Image compositing is relative to image configuration.
4233  */
4234  XSetCursorState(display,windows,MagickTrue);
4235  XCheckRefreshWindows(display,windows);
4236  width=(unsigned int) image->columns;
4237  height=(unsigned int) image->rows;
4238  x=0;
4239  y=0;
4240  if (windows->image.crop_geometry != (char *) NULL)
4241  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4242  scale_factor=(double) width/windows->image.ximage->width;
4243  composite_info.x+=x;
4244  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4245  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4246  scale_factor=(double) height/windows->image.ximage->height;
4247  composite_info.y+=y;
4248  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4249  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4250  if ((composite_info.width != composite_image->columns) ||
4251  (composite_info.height != composite_image->rows))
4252  {
4253  Image
4254  *resize_image;
4255 
4256  /*
4257  Scale composite image.
4258  */
4259  resize_image=ResizeImage(composite_image,composite_info.width,
4260  composite_info.height,composite_image->filter,exception);
4261  composite_image=DestroyImage(composite_image);
4262  if (resize_image == (Image *) NULL)
4263  {
4264  XSetCursorState(display,windows,MagickFalse);
4265  return(MagickFalse);
4266  }
4267  composite_image=resize_image;
4268  }
4269  if (compose == DisplaceCompositeOp)
4270  (void) SetImageArtifact(composite_image,"compose:args",
4271  displacement_geometry);
4272  if (blend != 0.0)
4273  {
4274  CacheView
4275  *image_view;
4276 
4277  int
4278  y;
4279 
4280  Quantum
4281  opacity;
4282 
4283  int
4284  x;
4285 
4286  Quantum
4287  *q;
4288 
4289  /*
4290  Create mattes for blending.
4291  */
4292  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4293  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4294  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4295  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4296  return(MagickFalse);
4297  image->alpha_trait=BlendPixelTrait;
4298  image_view=AcquireAuthenticCacheView(image,exception);
4299  for (y=0; y < (int) image->rows; y++)
4300  {
4301  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302  exception);
4303  if (q == (Quantum *) NULL)
4304  break;
4305  for (x=0; x < (int) image->columns; x++)
4306  {
4307  SetPixelAlpha(image,opacity,q);
4308  q+=(ptrdiff_t) GetPixelChannels(image);
4309  }
4310  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311  break;
4312  }
4313  image_view=DestroyCacheView(image_view);
4314  }
4315  /*
4316  Composite image with X Image window.
4317  */
4318  (void) CompositeImage(image,composite_image,compose,MagickTrue,
4319  composite_info.x,composite_info.y,exception);
4320  composite_image=DestroyImage(composite_image);
4321  XSetCursorState(display,windows,MagickFalse);
4322  /*
4323  Update image configuration.
4324  */
4325  XConfigureImageColormap(display,resource_info,windows,image,exception);
4326  (void) XConfigureImage(display,resource_info,windows,image,exception);
4327  return(MagickTrue);
4328 }
4329 
4330 /*
4331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332 % %
4333 % %
4334 % %
4335 + X C o n f i g u r e I m a g e %
4336 % %
4337 % %
4338 % %
4339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340 %
4341 % XConfigureImage() creates a new X image. It also notifies the window
4342 % manager of the new image size and configures the transient widows.
4343 %
4344 % The format of the XConfigureImage method is:
4345 %
4346 % MagickBooleanType XConfigureImage(Display *display,
4347 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4348 % ExceptionInfo *exception)
4349 %
4350 % A description of each parameter follows:
4351 %
4352 % o display: Specifies a connection to an X server; returned from
4353 % XOpenDisplay.
4354 %
4355 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4356 %
4357 % o windows: Specifies a pointer to a XWindows structure.
4358 %
4359 % o image: the image.
4360 %
4361 % o exception: return any errors or warnings in this structure.
4362 %
4363 % o exception: return any errors or warnings in this structure.
4364 %
4365 */
4366 static MagickBooleanType XConfigureImage(Display *display,
4367  XResourceInfo *resource_info,XWindows *windows,Image *image,
4368  ExceptionInfo *exception)
4369 {
4370  char
4371  geometry[MagickPathExtent];
4372 
4373  MagickStatusType
4374  status;
4375 
4376  size_t
4377  mask,
4378  height,
4379  width;
4380 
4381  ssize_t
4382  x,
4383  y;
4384 
4385  XSizeHints
4386  *size_hints;
4387 
4388  XWindowChanges
4389  window_changes;
4390 
4391  /*
4392  Dismiss if window dimensions are zero.
4393  */
4394  width=(unsigned int) windows->image.window_changes.width;
4395  height=(unsigned int) windows->image.window_changes.height;
4396  if (resource_info->debug != MagickFalse)
4397  (void) LogMagickEvent(X11Event,GetMagickModule(),
4398  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4399  windows->image.ximage->height,(double) width,(double) height);
4400  if ((width*height) == 0)
4401  return(MagickTrue);
4402  x=0;
4403  y=0;
4404  /*
4405  Resize image to fit Image window dimensions.
4406  */
4407  XSetCursorState(display,windows,MagickTrue);
4408  (void) XFlush(display);
4409  if (((int) width != windows->image.ximage->width) ||
4410  ((int) height != windows->image.ximage->height))
4411  image->taint=MagickTrue;
4412  windows->magnify.x=(int)
4413  width*windows->magnify.x/windows->image.ximage->width;
4414  windows->magnify.y=(int)
4415  height*windows->magnify.y/windows->image.ximage->height;
4416  windows->image.x=((int) width*windows->image.x/windows->image.ximage->width);
4417  windows->image.y=((int) height*windows->image.y/
4418  windows->image.ximage->height);
4419  status=XMakeImage(display,resource_info,&windows->image,image,
4420  (unsigned int) width,(unsigned int) height,exception);
4421  if (status == MagickFalse)
4422  XNoticeWidget(display,windows,"Unable to configure X image:",
4423  windows->image.name);
4424  /*
4425  Notify window manager of the new configuration.
4426  */
4427  if (resource_info->image_geometry != (char *) NULL)
4428  (void) FormatLocaleString(geometry,MagickPathExtent,"%s>!",
4429  resource_info->image_geometry);
4430  else
4431  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
4432  XDisplayWidth(display,windows->image.screen),
4433  XDisplayHeight(display,windows->image.screen));
4434  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4435  window_changes.width=(int) width;
4436  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4437  window_changes.width=XDisplayWidth(display,windows->image.screen);
4438  window_changes.height=(int) height;
4439  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4440  window_changes.height=XDisplayHeight(display,windows->image.screen);
4441  mask=(size_t) (CWWidth | CWHeight);
4442  if (resource_info->backdrop)
4443  {
4444  mask|=CWX | CWY;
4445  window_changes.x=((XDisplayWidth(display,windows->image.screen)/2)-
4446  ((int) width/2));
4447  window_changes.y=((XDisplayHeight(display,windows->image.screen)/2)-
4448  ((int) height/2));
4449  }
4450  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4451  (unsigned int) mask,&window_changes);
4452  (void) XClearWindow(display,windows->image.id);
4453  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4454  /*
4455  Update Magnify window configuration.
4456  */
4457  if (windows->magnify.mapped != MagickFalse)
4458  XMakeMagnifyImage(display,windows,exception);
4459  windows->pan.crop_geometry=windows->image.crop_geometry;
4460  XBestIconSize(display,&windows->pan,image);
4461  while (((windows->pan.width << 1) < MaxIconSize) &&
4462  ((windows->pan.height << 1) < MaxIconSize))
4463  {
4464  windows->pan.width<<=1;
4465  windows->pan.height<<=1;
4466  }
4467  if (windows->pan.geometry != (char *) NULL)
4468  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4469  &windows->pan.width,&windows->pan.height);
4470  window_changes.width=(int) windows->pan.width;
4471  window_changes.height=(int) windows->pan.height;
4472  size_hints=XAllocSizeHints();
4473  if (size_hints != (XSizeHints *) NULL)
4474  {
4475  /*
4476  Set new size hints.
4477  */
4478  size_hints->flags=PSize | PMinSize | PMaxSize;
4479  size_hints->width=window_changes.width;
4480  size_hints->height=window_changes.height;
4481  size_hints->min_width=size_hints->width;
4482  size_hints->min_height=size_hints->height;
4483  size_hints->max_width=size_hints->width;
4484  size_hints->max_height=size_hints->height;
4485  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4486  (void) XFree((void *) size_hints);
4487  }
4488  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4489  (unsigned int) (CWWidth | CWHeight),&window_changes);
4490  /*
4491  Update icon window configuration.
4492  */
4493  windows->icon.crop_geometry=windows->image.crop_geometry;
4494  XBestIconSize(display,&windows->icon,image);
4495  window_changes.width=(int) windows->icon.width;
4496  window_changes.height=(int) windows->icon.height;
4497  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4498  (unsigned int) (CWWidth | CWHeight),&window_changes);
4499  XSetCursorState(display,windows,MagickFalse);
4500  return(status != 0 ? MagickTrue : MagickFalse);
4501 }
4502 
4503 /*
4504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4505 % %
4506 % %
4507 % %
4508 + X C r o p I m a g e %
4509 % %
4510 % %
4511 % %
4512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4513 %
4514 % XCropImage() allows the user to select a region of the image and crop, copy,
4515 % or cut it. For copy or cut, the image can subsequently be composited onto
4516 % the image with XPasteImage.
4517 %
4518 % The format of the XCropImage method is:
4519 %
4520 % MagickBooleanType XCropImage(Display *display,
4521 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4522 % const ClipboardMode mode,ExceptionInfo *exception)
4523 %
4524 % A description of each parameter follows:
4525 %
4526 % o display: Specifies a connection to an X server; returned from
4527 % XOpenDisplay.
4528 %
4529 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4530 %
4531 % o windows: Specifies a pointer to a XWindows structure.
4532 %
4533 % o image: the image; returned from ReadImage.
4534 %
4535 % o mode: This unsigned value specified whether the image should be
4536 % cropped, copied, or cut.
4537 %
4538 % o exception: return any errors or warnings in this structure.
4539 %
4540 */
4541 static MagickBooleanType XCropImage(Display *display,
4542  XResourceInfo *resource_info,XWindows *windows,Image *image,
4543  const ClipboardMode mode,ExceptionInfo *exception)
4544 {
4545  const char
4546  *const CropModeMenu[] =
4547  {
4548  "Help",
4549  "Dismiss",
4550  (char *) NULL
4551  },
4552  *RectifyModeMenu[] =
4553  {
4554  "Crop",
4555  "Help",
4556  "Dismiss",
4557  (char *) NULL
4558  };
4559 
4560  static const ModeType
4561  CropCommands[] =
4562  {
4563  CropHelpCommand,
4564  CropDismissCommand
4565  },
4566  RectifyCommands[] =
4567  {
4568  RectifyCopyCommand,
4569  RectifyHelpCommand,
4570  RectifyDismissCommand
4571  };
4572 
4573  CacheView
4574  *image_view;
4575 
4576  char
4577  command[MagickPathExtent],
4578  text[MagickPathExtent];
4579 
4580  Cursor
4581  cursor;
4582 
4583  int
4584  id,
4585  x,
4586  y;
4587 
4588  KeySym
4589  key_symbol;
4590 
4591  Image
4592  *crop_image;
4593 
4594  double
4595  scale_factor;
4596 
4598  crop_info,
4599  highlight_info;
4600 
4601  Quantum
4602  *q;
4603 
4604  unsigned int
4605  height,
4606  width;
4607 
4608  size_t
4609  state;
4610 
4611  XEvent
4612  event;
4613 
4614  /*
4615  Map Command widget.
4616  */
4617  switch (mode)
4618  {
4619  case CopyMode:
4620  {
4621  (void) CloneString(&windows->command.name,"Copy");
4622  break;
4623  }
4624  case CropMode:
4625  {
4626  (void) CloneString(&windows->command.name,"Crop");
4627  break;
4628  }
4629  case CutMode:
4630  {
4631  (void) CloneString(&windows->command.name,"Cut");
4632  break;
4633  }
4634  }
4635  RectifyModeMenu[0]=windows->command.name;
4636  windows->command.data=0;
4637  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4638  (void) XMapRaised(display,windows->command.id);
4639  XClientMessage(display,windows->image.id,windows->im_protocols,
4640  windows->im_update_widget,CurrentTime);
4641  /*
4642  Track pointer until button 1 is pressed.
4643  */
4644  XQueryPosition(display,windows->image.id,&x,&y);
4645  (void) XSelectInput(display,windows->image.id,
4646  windows->image.attributes.event_mask | PointerMotionMask);
4647  crop_info.x=(ssize_t) windows->image.x+x;
4648  crop_info.y=(ssize_t) windows->image.y+y;
4649  crop_info.width=0;
4650  crop_info.height=0;
4651  cursor=XCreateFontCursor(display,XC_fleur);
4652  state=DefaultState;
4653  do
4654  {
4655  if (windows->info.mapped != MagickFalse)
4656  {
4657  /*
4658  Display pointer position.
4659  */
4660  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
4661  (long) crop_info.x,(long) crop_info.y);
4662  XInfoWidget(display,windows,text);
4663  }
4664  /*
4665  Wait for next event.
4666  */
4667  XScreenEvent(display,windows,&event,exception);
4668  if (event.xany.window == windows->command.id)
4669  {
4670  /*
4671  Select a command from the Command widget.
4672  */
4673  id=XCommandWidget(display,windows,CropModeMenu,&event);
4674  if (id < 0)
4675  continue;
4676  switch (CropCommands[id])
4677  {
4678  case CropHelpCommand:
4679  {
4680  switch (mode)
4681  {
4682  case CopyMode:
4683  {
4684  XTextViewHelp(display,resource_info,windows,MagickFalse,
4685  "Help Viewer - Image Copy",ImageCopyHelp);
4686  break;
4687  }
4688  case CropMode:
4689  {
4690  XTextViewHelp(display,resource_info,windows,MagickFalse,
4691  "Help Viewer - Image Crop",ImageCropHelp);
4692  break;
4693  }
4694  case CutMode:
4695  {
4696  XTextViewHelp(display,resource_info,windows,MagickFalse,
4697  "Help Viewer - Image Cut",ImageCutHelp);
4698  break;
4699  }
4700  }
4701  break;
4702  }
4703  case CropDismissCommand:
4704  {
4705  /*
4706  Prematurely exit.
4707  */
4708  state|=EscapeState;
4709  state|=ExitState;
4710  break;
4711  }
4712  default:
4713  break;
4714  }
4715  continue;
4716  }
4717  switch (event.type)
4718  {
4719  case ButtonPress:
4720  {
4721  if (event.xbutton.button != Button1)
4722  break;
4723  if (event.xbutton.window != windows->image.id)
4724  break;
4725  /*
4726  Note first corner of cropping rectangle-- exit loop.
4727  */
4728  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4729  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4730  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4731  state|=ExitState;
4732  break;
4733  }
4734  case ButtonRelease:
4735  break;
4736  case Expose:
4737  break;
4738  case KeyPress:
4739  {
4740  if (event.xkey.window != windows->image.id)
4741  break;
4742  /*
4743  Respond to a user key press.
4744  */
4745  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4746  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4747  switch ((int) key_symbol)
4748  {
4749  case XK_Escape:
4750  case XK_F20:
4751  {
4752  /*
4753  Prematurely exit.
4754  */
4755  state|=EscapeState;
4756  state|=ExitState;
4757  break;
4758  }
4759  case XK_F1:
4760  case XK_Help:
4761  {
4762  switch (mode)
4763  {
4764  case CopyMode:
4765  {
4766  XTextViewHelp(display,resource_info,windows,MagickFalse,
4767  "Help Viewer - Image Copy",ImageCopyHelp);
4768  break;
4769  }
4770  case CropMode:
4771  {
4772  XTextViewHelp(display,resource_info,windows,MagickFalse,
4773  "Help Viewer - Image Crop",ImageCropHelp);
4774  break;
4775  }
4776  case CutMode:
4777  {
4778  XTextViewHelp(display,resource_info,windows,MagickFalse,
4779  "Help Viewer - Image Cut",ImageCutHelp);
4780  break;
4781  }
4782  }
4783  break;
4784  }
4785  default:
4786  {
4787  (void) XBell(display,0);
4788  break;
4789  }
4790  }
4791  break;
4792  }
4793  case MotionNotify:
4794  {
4795  if (event.xmotion.window != windows->image.id)
4796  break;
4797  /*
4798  Map and unmap Info widget as text cursor crosses its boundaries.
4799  */
4800  x=event.xmotion.x;
4801  y=event.xmotion.y;
4802  if (windows->info.mapped != MagickFalse)
4803  {
4804  if ((x < (windows->info.x+(int) windows->info.width)) &&
4805  (y < (windows->info.y+(int) windows->info.height)))
4806  (void) XWithdrawWindow(display,windows->info.id,
4807  windows->info.screen);
4808  }
4809  else
4810  if ((x > (windows->info.x+(int) windows->info.width)) ||
4811  (y > (windows->info.y+(int) windows->info.height)))
4812  (void) XMapWindow(display,windows->info.id);
4813  crop_info.x=(ssize_t) windows->image.x+x;
4814  crop_info.y=(ssize_t) windows->image.y+y;
4815  break;
4816  }
4817  default:
4818  break;
4819  }
4820  } while ((state & ExitState) == 0);
4821  (void) XSelectInput(display,windows->image.id,
4822  windows->image.attributes.event_mask);
4823  if ((state & EscapeState) != 0)
4824  {
4825  /*
4826  User want to exit without cropping.
4827  */
4828  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4829  (void) XFreeCursor(display,cursor);
4830  return(MagickTrue);
4831  }
4832  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4833  do
4834  {
4835  /*
4836  Size rectangle as pointer moves until the mouse button is released.
4837  */
4838  x=(int) crop_info.x;
4839  y=(int) crop_info.y;
4840  crop_info.width=0;
4841  crop_info.height=0;
4842  state=DefaultState;
4843  do
4844  {
4845  highlight_info=crop_info;
4846  highlight_info.x=crop_info.x-windows->image.x;
4847  highlight_info.y=crop_info.y-windows->image.y;
4848  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4849  {
4850  /*
4851  Display info and draw cropping rectangle.
4852  */
4853  if (windows->info.mapped == MagickFalse)
4854  (void) XMapWindow(display,windows->info.id);
4855  (void) FormatLocaleString(text,MagickPathExtent,
4856  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4857  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4858  XInfoWidget(display,windows,text);
4859  XHighlightRectangle(display,windows->image.id,
4860  windows->image.highlight_context,&highlight_info);
4861  }
4862  else
4863  if (windows->info.mapped != MagickFalse)
4864  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4865  /*
4866  Wait for next event.
4867  */
4868  XScreenEvent(display,windows,&event,exception);
4869  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4870  XHighlightRectangle(display,windows->image.id,
4871  windows->image.highlight_context,&highlight_info);
4872  switch (event.type)
4873  {
4874  case ButtonPress:
4875  {
4876  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4877  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4878  break;
4879  }
4880  case ButtonRelease:
4881  {
4882  /*
4883  User has committed to cropping rectangle.
4884  */
4885  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4886  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4887  XSetCursorState(display,windows,MagickFalse);
4888  state|=ExitState;
4889  windows->command.data=0;
4890  (void) XCommandWidget(display,windows,RectifyModeMenu,
4891  (XEvent *) NULL);
4892  break;
4893  }
4894  case Expose:
4895  break;
4896  case MotionNotify:
4897  {
4898  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4899  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4900  }
4901  default:
4902  break;
4903  }
4904  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4905  ((state & ExitState) != 0))
4906  {
4907  /*
4908  Check boundary conditions.
4909  */
4910  if (crop_info.x < 0)
4911  crop_info.x=0;
4912  else
4913  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4914  crop_info.x=(ssize_t) windows->image.ximage->width;
4915  if ((int) crop_info.x < x)
4916  crop_info.width=(unsigned int) (x-crop_info.x);
4917  else
4918  {
4919  crop_info.width=(unsigned int) (crop_info.x-x);
4920  crop_info.x=(ssize_t) x;
4921  }
4922  if (crop_info.y < 0)
4923  crop_info.y=0;
4924  else
4925  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4926  crop_info.y=(ssize_t) windows->image.ximage->height;
4927  if ((int) crop_info.y < y)
4928  crop_info.height=(unsigned int) (y-crop_info.y);
4929  else
4930  {
4931  crop_info.height=(unsigned int) (crop_info.y-y);
4932  crop_info.y=(ssize_t) y;
4933  }
4934  }
4935  } while ((state & ExitState) == 0);
4936  /*
4937  Wait for user to grab a corner of the rectangle or press return.
4938  */
4939  state=DefaultState;
4940  (void) XMapWindow(display,windows->info.id);
4941  do
4942  {
4943  if (windows->info.mapped != MagickFalse)
4944  {
4945  /*
4946  Display pointer position.
4947  */
4948  (void) FormatLocaleString(text,MagickPathExtent,
4949  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4950  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4951  XInfoWidget(display,windows,text);
4952  }
4953  highlight_info=crop_info;
4954  highlight_info.x=crop_info.x-windows->image.x;
4955  highlight_info.y=crop_info.y-windows->image.y;
4956  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4957  {
4958  state|=EscapeState;
4959  state|=ExitState;
4960  break;
4961  }
4962  XHighlightRectangle(display,windows->image.id,
4963  windows->image.highlight_context,&highlight_info);
4964  XScreenEvent(display,windows,&event,exception);
4965  if (event.xany.window == windows->command.id)
4966  {
4967  /*
4968  Select a command from the Command widget.
4969  */
4970  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4971  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4972  (void) XSetFunction(display,windows->image.highlight_context,
4973  GXinvert);
4974  XHighlightRectangle(display,windows->image.id,
4975  windows->image.highlight_context,&highlight_info);
4976  if (id >= 0)
4977  switch (RectifyCommands[id])
4978  {
4979  case RectifyCopyCommand:
4980  {
4981  state|=ExitState;
4982  break;
4983  }
4984  case RectifyHelpCommand:
4985  {
4986  (void) XSetFunction(display,windows->image.highlight_context,
4987  GXcopy);
4988  switch (mode)
4989  {
4990  case CopyMode:
4991  {
4992  XTextViewHelp(display,resource_info,windows,MagickFalse,
4993  "Help Viewer - Image Copy",ImageCopyHelp);
4994  break;
4995  }
4996  case CropMode:
4997  {
4998  XTextViewHelp(display,resource_info,windows,MagickFalse,
4999  "Help Viewer - Image Crop",ImageCropHelp);
5000  break;
5001  }
5002  case CutMode:
5003  {
5004  XTextViewHelp(display,resource_info,windows,MagickFalse,
5005  "Help Viewer - Image Cut",ImageCutHelp);
5006  break;
5007  }
5008  }
5009  (void) XSetFunction(display,windows->image.highlight_context,
5010  GXinvert);
5011  break;
5012  }
5013  case RectifyDismissCommand:
5014  {
5015  /*
5016  Prematurely exit.
5017  */
5018  state|=EscapeState;
5019  state|=ExitState;
5020  break;
5021  }
5022  default:
5023  break;
5024  }
5025  continue;
5026  }
5027  XHighlightRectangle(display,windows->image.id,
5028  windows->image.highlight_context,&highlight_info);
5029  switch (event.type)
5030  {
5031  case ButtonPress:
5032  {
5033  if (event.xbutton.button != Button1)
5034  break;
5035  if (event.xbutton.window != windows->image.id)
5036  break;
5037  x=windows->image.x+event.xbutton.x;
5038  y=windows->image.y+event.xbutton.y;
5039  if ((x < (int) (crop_info.x+RoiDelta)) &&
5040  (x > (int) (crop_info.x-RoiDelta)) &&
5041  (y < (int) (crop_info.y+RoiDelta)) &&
5042  (y > (int) (crop_info.y-RoiDelta)))
5043  {
5044  crop_info.x=crop_info.x+(ssize_t) crop_info.width;
5045  crop_info.y=crop_info.y+(ssize_t) crop_info.height;
5046  state|=UpdateConfigurationState;
5047  break;
5048  }
5049  if ((x < (int) (crop_info.x+RoiDelta)) &&
5050  (x > (int) (crop_info.x-RoiDelta)) &&
5051  (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052  (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5053  {
5054  crop_info.x=(crop_info.x+(int) crop_info.width);
5055  state|=UpdateConfigurationState;
5056  break;
5057  }
5058  if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059  (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5060  (y < (int) (crop_info.y+RoiDelta)) &&
5061  (y > (int) (crop_info.y-RoiDelta)))
5062  {
5063  crop_info.y=(crop_info.y+(ssize_t) crop_info.height);
5064  state|=UpdateConfigurationState;
5065  break;
5066  }
5067  if ((x < (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068  (x > (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069  (y < (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070  (y > (crop_info.y+(int) crop_info.height-RoiDelta)))
5071  {
5072  state|=UpdateConfigurationState;
5073  break;
5074  }
5075  magick_fallthrough;
5076  }
5077  case ButtonRelease:
5078  {
5079  if (event.xbutton.window == windows->pan.id)
5080  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5081  (highlight_info.y != crop_info.y-windows->image.y))
5082  XHighlightRectangle(display,windows->image.id,
5083  windows->image.highlight_context,&highlight_info);
5084  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5085  event.xbutton.time);
5086  break;
5087  }
5088  case Expose:
5089  {
5090  if (event.xexpose.window == windows->image.id)
5091  if (event.xexpose.count == 0)
5092  {
5093  event.xexpose.x=(int) highlight_info.x;
5094  event.xexpose.y=(int) highlight_info.y;
5095  event.xexpose.width=(int) highlight_info.width;
5096  event.xexpose.height=(int) highlight_info.height;
5097  XRefreshWindow(display,&windows->image,&event);
5098  }
5099  if (event.xexpose.window == windows->info.id)
5100  if (event.xexpose.count == 0)
5101  XInfoWidget(display,windows,text);
5102  break;
5103  }
5104  case KeyPress:
5105  {
5106  if (event.xkey.window != windows->image.id)
5107  break;
5108  /*
5109  Respond to a user key press.
5110  */
5111  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5112  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5113  switch ((int) key_symbol)
5114  {
5115  case XK_Escape:
5116  case XK_F20:
5117  {
5118  state|=EscapeState;
5119  magick_fallthrough;
5120  }
5121  case XK_Return:
5122  {
5123  state|=ExitState;
5124  break;
5125  }
5126  case XK_Home:
5127  case XK_KP_Home:
5128  {
5129  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5130  2L);
5131  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5132  2L);
5133  break;
5134  }
5135  case XK_Left:
5136  case XK_KP_Left:
5137  {
5138  crop_info.x--;
5139  break;
5140  }
5141  case XK_Up:
5142  case XK_KP_Up:
5143  case XK_Next:
5144  {
5145  crop_info.y--;
5146  break;
5147  }
5148  case XK_Right:
5149  case XK_KP_Right:
5150  {
5151  crop_info.x++;
5152  break;
5153  }
5154  case XK_Prior:
5155  case XK_Down:
5156  case XK_KP_Down:
5157  {
5158  crop_info.y++;
5159  break;
5160  }
5161  case XK_F1:
5162  case XK_Help:
5163  {
5164  (void) XSetFunction(display,windows->image.highlight_context,
5165  GXcopy);
5166  switch (mode)
5167  {
5168  case CopyMode:
5169  {
5170  XTextViewHelp(display,resource_info,windows,MagickFalse,
5171  "Help Viewer - Image Copy",ImageCopyHelp);
5172  break;
5173  }
5174  case CropMode:
5175  {
5176  XTextViewHelp(display,resource_info,windows,MagickFalse,
5177  "Help Viewer - Image Cropg",ImageCropHelp);
5178  break;
5179  }
5180  case CutMode:
5181  {
5182  XTextViewHelp(display,resource_info,windows,MagickFalse,
5183  "Help Viewer - Image Cutg",ImageCutHelp);
5184  break;
5185  }
5186  }
5187  (void) XSetFunction(display,windows->image.highlight_context,
5188  GXinvert);
5189  break;
5190  }
5191  default:
5192  {
5193  (void) XBell(display,0);
5194  break;
5195  }
5196  }
5197  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5198  event.xkey.time);
5199  break;
5200  }
5201  case KeyRelease:
5202  break;
5203  case MotionNotify:
5204  {
5205  if (event.xmotion.window != windows->image.id)
5206  break;
5207  /*
5208  Map and unmap Info widget as text cursor crosses its boundaries.
5209  */
5210  x=event.xmotion.x;
5211  y=event.xmotion.y;
5212  if (windows->info.mapped != MagickFalse)
5213  {
5214  if ((x < (windows->info.x+(int) windows->info.width)) &&
5215  (y < (windows->info.y+(int) windows->info.height)))
5216  (void) XWithdrawWindow(display,windows->info.id,
5217  windows->info.screen);
5218  }
5219  else
5220  if ((x > (windows->info.x+(int) windows->info.width)) ||
5221  (y > (windows->info.y+(int) windows->info.height)))
5222  (void) XMapWindow(display,windows->info.id);
5223  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5224  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5225  break;
5226  }
5227  case SelectionRequest:
5228  {
5229  XSelectionEvent
5230  notify;
5231 
5232  XSelectionRequestEvent
5233  *request;
5234 
5235  /*
5236  Set primary selection.
5237  */
5238  (void) FormatLocaleString(text,MagickPathExtent,
5239  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5240  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5241  request=(&(event.xselectionrequest));
5242  (void) XChangeProperty(request->display,request->requestor,
5243  request->property,request->target,8,PropModeReplace,
5244  (unsigned char *) text,(int) strlen(text));
5245  notify.type=SelectionNotify;
5246  notify.display=request->display;
5247  notify.requestor=request->requestor;
5248  notify.selection=request->selection;
5249  notify.target=request->target;
5250  notify.time=request->time;
5251  if (request->property == None)
5252  notify.property=request->target;
5253  else
5254  notify.property=request->property;
5255  (void) XSendEvent(request->display,request->requestor,False,0,
5256  (XEvent *) &notify);
5257  }
5258  default:
5259  break;
5260  }
5261  if ((state & UpdateConfigurationState) != 0)
5262  {
5263  (void) XPutBackEvent(display,&event);
5264  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5265  break;
5266  }
5267  } while ((state & ExitState) == 0);
5268  } while ((state & ExitState) == 0);
5269  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5270  XSetCursorState(display,windows,MagickFalse);
5271  if ((state & EscapeState) != 0)
5272  return(MagickTrue);
5273  if (mode == CropMode)
5274  if (((int) crop_info.width != windows->image.ximage->width) ||
5275  ((int) crop_info.height != windows->image.ximage->height))
5276  {
5277  /*
5278  Reconfigure Image window as defined by cropping rectangle.
5279  */
5280  XSetCropGeometry(display,windows,&crop_info,image);
5281  windows->image.window_changes.width=(int) crop_info.width;
5282  windows->image.window_changes.height=(int) crop_info.height;
5283  (void) XConfigureImage(display,resource_info,windows,image,exception);
5284  return(MagickTrue);
5285  }
5286  /*
5287  Copy image before applying image transforms.
5288  */
5289  XSetCursorState(display,windows,MagickTrue);
5290  XCheckRefreshWindows(display,windows);
5291  width=(unsigned int) image->columns;
5292  height=(unsigned int) image->rows;
5293  x=0;
5294  y=0;
5295  if (windows->image.crop_geometry != (char *) NULL)
5296  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5297  scale_factor=(double) width/windows->image.ximage->width;
5298  crop_info.x+=x;
5299  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5300  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5301  scale_factor=(double) height/windows->image.ximage->height;
5302  crop_info.y+=y;
5303  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5304  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5305  crop_info.x+=image->page.x;
5306  crop_info.y+=image->page.y;
5307  crop_image=CropImage(image,&crop_info,exception);
5308  XSetCursorState(display,windows,MagickFalse);
5309  if (crop_image == (Image *) NULL)
5310  return(MagickFalse);
5311  if (resource_info->copy_image != (Image *) NULL)
5312  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5313  resource_info->copy_image=crop_image;
5314  if (mode == CopyMode)
5315  {
5316  (void) XConfigureImage(display,resource_info,windows,image,exception);
5317  return(MagickTrue);
5318  }
5319  /*
5320  Cut image.
5321  */
5322  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5323  return(MagickFalse);
5324  image->alpha_trait=BlendPixelTrait;
5325  image_view=AcquireAuthenticCacheView(image,exception);
5326  for (y=0; y < (int) crop_info.height; y++)
5327  {
5328  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5329  crop_info.width,1,exception);
5330  if (q == (Quantum *) NULL)
5331  break;
5332  for (x=0; x < (int) crop_info.width; x++)
5333  {
5334  SetPixelAlpha(image,TransparentAlpha,q);
5335  q+=(ptrdiff_t) GetPixelChannels(image);
5336  }
5337  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5338  break;
5339  }
5340  image_view=DestroyCacheView(image_view);
5341  /*
5342  Update image configuration.
5343  */
5344  XConfigureImageColormap(display,resource_info,windows,image,exception);
5345  (void) XConfigureImage(display,resource_info,windows,image,exception);
5346  return(MagickTrue);
5347 }
5348 
5349 /*
5350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5351 % %
5352 % %
5353 % %
5354 + X D r a w I m a g e %
5355 % %
5356 % %
5357 % %
5358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5359 %
5360 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5361 % the image.
5362 %
5363 % The format of the XDrawEditImage method is:
5364 %
5365 % MagickBooleanType XDrawEditImage(Display *display,
5366 % XResourceInfo *resource_info,XWindows *windows,Image **image,
5367 % ExceptionInfo *exception)
5368 %
5369 % A description of each parameter follows:
5370 %
5371 % o display: Specifies a connection to an X server; returned from
5372 % XOpenDisplay.
5373 %
5374 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5375 %
5376 % o windows: Specifies a pointer to a XWindows structure.
5377 %
5378 % o image: the image.
5379 %
5380 % o exception: return any errors or warnings in this structure.
5381 %
5382 */
5383 static MagickBooleanType XDrawEditImage(Display *display,
5384  XResourceInfo *resource_info,XWindows *windows,Image **image,
5385  ExceptionInfo *exception)
5386 {
5387  const char
5388  *const DrawMenu[] =
5389  {
5390  "Element",
5391  "Color",
5392  "Stipple",
5393  "Width",
5394  "Undo",
5395  "Help",
5396  "Dismiss",
5397  (char *) NULL
5398  };
5399 
5400  static ElementType
5401  element = PointElement;
5402 
5403  static const ModeType
5404  DrawCommands[] =
5405  {
5406  DrawElementCommand,
5407  DrawColorCommand,
5408  DrawStippleCommand,
5409  DrawWidthCommand,
5410  DrawUndoCommand,
5411  DrawHelpCommand,
5412  DrawDismissCommand
5413  };
5414 
5415  static Pixmap
5416  stipple = (Pixmap) NULL;
5417 
5418  static unsigned int
5419  pen_id = 0,
5420  line_width = 1;
5421 
5422  char
5423  command[MagickPathExtent],
5424  text[MagickPathExtent];
5425 
5426  Cursor
5427  cursor;
5428 
5429  int
5430  entry,
5431  id,
5432  number_coordinates,
5433  x,
5434  y;
5435 
5436  double
5437  degrees;
5438 
5439  MagickStatusType
5440  status;
5441 
5443  rectangle_info;
5444 
5445  int
5446  i;
5447 
5448  unsigned int
5449  distance,
5450  height,
5451  max_coordinates,
5452  width;
5453 
5454  size_t
5455  state;
5456 
5457  Window
5458  root_window;
5459 
5460  XDrawInfo
5461  draw_info;
5462 
5463  XEvent
5464  event;
5465 
5466  XPoint
5467  *coordinate_info;
5468 
5469  XSegment
5470  line_info;
5471 
5472  /*
5473  Allocate polygon info.
5474  */
5475  max_coordinates=2048;
5476  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5477  sizeof(*coordinate_info));
5478  if (coordinate_info == (XPoint *) NULL)
5479  {
5480  (void) ThrowMagickException(exception,GetMagickModule(),
5481  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5482  return(MagickFalse);
5483  }
5484  /*
5485  Map Command widget.
5486  */
5487  (void) CloneString(&windows->command.name,"Draw");
5488  windows->command.data=4;
5489  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5490  (void) XMapRaised(display,windows->command.id);
5491  XClientMessage(display,windows->image.id,windows->im_protocols,
5492  windows->im_update_widget,CurrentTime);
5493  /*
5494  Wait for first button press.
5495  */
5496  root_window=XRootWindow(display,XDefaultScreen(display));
5497  draw_info.stencil=OpaqueStencil;
5498  status=MagickTrue;
5499  cursor=XCreateFontCursor(display,XC_tcross);
5500  for ( ; ; )
5501  {
5502  XQueryPosition(display,windows->image.id,&x,&y);
5503  (void) XSelectInput(display,windows->image.id,
5504  windows->image.attributes.event_mask | PointerMotionMask);
5505  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5506  state=DefaultState;
5507  do
5508  {
5509  if (windows->info.mapped != MagickFalse)
5510  {
5511  /*
5512  Display pointer position.
5513  */
5514  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
5515  x+windows->image.x,y+windows->image.y);
5516  XInfoWidget(display,windows,text);
5517  }
5518  /*
5519  Wait for next event.
5520  */
5521  XScreenEvent(display,windows,&event,exception);
5522  if (event.xany.window == windows->command.id)
5523  {
5524  /*
5525  Select a command from the Command widget.
5526  */
5527  id=XCommandWidget(display,windows,DrawMenu,&event);
5528  if (id < 0)
5529  continue;
5530  switch (DrawCommands[id])
5531  {
5532  case DrawElementCommand:
5533  {
5534  const char
5535  *const Elements[] =
5536  {
5537  "point",
5538  "line",
5539  "rectangle",
5540  "fill rectangle",
5541  "circle",
5542  "fill circle",
5543  "ellipse",
5544  "fill ellipse",
5545  "polygon",
5546  "fill polygon",
5547  (char *) NULL,
5548  };
5549 
5550  /*
5551  Select a command from the pop-up menu.
5552  */
5553  element=(ElementType) (XMenuWidget(display,windows,
5554  DrawMenu[id],Elements,command)+1);
5555  break;
5556  }
5557  case DrawColorCommand:
5558  {
5559  const char
5560  *ColorMenu[MaxNumberPens+1];
5561 
5562  int
5563  pen_number;
5564 
5565  MagickBooleanType
5566  transparent;
5567 
5568  XColor
5569  color;
5570 
5571  /*
5572  Initialize menu selections.
5573  */
5574  for (i=0; i < (int) (MaxNumberPens-2); i++)
5575  ColorMenu[i]=resource_info->pen_colors[i];
5576  ColorMenu[MaxNumberPens-2]="transparent";
5577  ColorMenu[MaxNumberPens-1]="Browser...";
5578  ColorMenu[MaxNumberPens]=(char *) NULL;
5579  /*
5580  Select a pen color from the pop-up menu.
5581  */
5582  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5583  (const char **) ColorMenu,command);
5584  if (pen_number < 0)
5585  break;
5586  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5587  MagickFalse;
5588  if (transparent != MagickFalse)
5589  {
5590  draw_info.stencil=TransparentStencil;
5591  break;
5592  }
5593  if (pen_number == (MaxNumberPens-1))
5594  {
5595  static char
5596  color_name[MagickPathExtent] = "gray";
5597 
5598  /*
5599  Select a pen color from a dialog.
5600  */
5601  resource_info->pen_colors[pen_number]=color_name;
5602  XColorBrowserWidget(display,windows,"Select",color_name);
5603  if (*color_name == '\0')
5604  break;
5605  }
5606  /*
5607  Set pen color.
5608  */
5609  (void) XParseColor(display,windows->map_info->colormap,
5610  resource_info->pen_colors[pen_number],&color);
5611  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5612  (unsigned int) MaxColors,&color);
5613  windows->pixel_info->pen_colors[pen_number]=color;
5614  pen_id=(unsigned int) pen_number;
5615  draw_info.stencil=OpaqueStencil;
5616  break;
5617  }
5618  case DrawStippleCommand:
5619  {
5620  const char
5621  *StipplesMenu[] =
5622  {
5623  "Brick",
5624  "Diagonal",
5625  "Scales",
5626  "Vertical",
5627  "Wavy",
5628  "Translucent",
5629  "Opaque",
5630  (char *) NULL,
5631  (char *) NULL,
5632  };
5633 
5634  Image
5635  *stipple_image;
5636 
5637  ImageInfo
5638  *image_info;
5639 
5640  int
5641  status;
5642 
5643  static char
5644  filename[MagickPathExtent] = "\0";
5645 
5646  /*
5647  Select a command from the pop-up menu.
5648  */
5649  StipplesMenu[7]="Open...";
5650  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5651  command);
5652  if (entry < 0)
5653  break;
5654  if (stipple != (Pixmap) NULL)
5655  (void) XFreePixmap(display,stipple);
5656  stipple=(Pixmap) NULL;
5657  if (entry != 7)
5658  {
5659  switch (entry)
5660  {
5661  case 0:
5662  {
5663  stipple=XCreateBitmapFromData(display,root_window,
5664  (char *) BricksBitmap,BricksWidth,BricksHeight);
5665  break;
5666  }
5667  case 1:
5668  {
5669  stipple=XCreateBitmapFromData(display,root_window,
5670  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5671  break;
5672  }
5673  case 2:
5674  {
5675  stipple=XCreateBitmapFromData(display,root_window,
5676  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5677  break;
5678  }
5679  case 3:
5680  {
5681  stipple=XCreateBitmapFromData(display,root_window,
5682  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5683  break;
5684  }
5685  case 4:
5686  {
5687  stipple=XCreateBitmapFromData(display,root_window,
5688  (char *) WavyBitmap,WavyWidth,WavyHeight);
5689  break;
5690  }
5691  case 5:
5692  {
5693  stipple=XCreateBitmapFromData(display,root_window,
5694  (char *) HighlightBitmap,HighlightWidth,
5695  HighlightHeight);
5696  break;
5697  }
5698  case 6:
5699  default:
5700  {
5701  stipple=XCreateBitmapFromData(display,root_window,
5702  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5703  break;
5704  }
5705  }
5706  break;
5707  }
5708  XFileBrowserWidget(display,windows,"Stipple",filename);
5709  if (*filename == '\0')
5710  break;
5711  /*
5712  Read image.
5713  */
5714  XSetCursorState(display,windows,MagickTrue);
5715  XCheckRefreshWindows(display,windows);
5716  image_info=AcquireImageInfo();
5717  (void) CopyMagickString(image_info->filename,filename,
5718  MagickPathExtent);
5719  stipple_image=ReadImage(image_info,exception);
5720  CatchException(exception);
5721  XSetCursorState(display,windows,MagickFalse);
5722  if (stipple_image == (Image *) NULL)
5723  break;
5724  (void) AcquireUniqueFileResource(filename);
5725  (void) FormatLocaleString(stipple_image->filename,
5726  MagickPathExtent,"xbm:%s",filename);
5727  (void) WriteImage(image_info,stipple_image,exception);
5728  stipple_image=DestroyImage(stipple_image);
5729  image_info=DestroyImageInfo(image_info);
5730  status=XReadBitmapFile(display,root_window,filename,&width,
5731  &height,&stipple,&x,&y);
5732  (void) RelinquishUniqueFileResource(filename);
5733  if ((status != BitmapSuccess) != 0)
5734  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5735  filename);
5736  break;
5737  }
5738  case DrawWidthCommand:
5739  {
5740  const char
5741  *const WidthsMenu[] =
5742  {
5743  "1",
5744  "2",
5745  "4",
5746  "8",
5747  "16",
5748  "Dialog...",
5749  (char *) NULL,
5750  };
5751 
5752  static char
5753  width[MagickPathExtent] = "0";
5754 
5755  /*
5756  Select a command from the pop-up menu.
5757  */
5758  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5759  command);
5760  if (entry < 0)
5761  break;
5762  if (entry != 5)
5763  {
5764  line_width=(unsigned int) StringToUnsignedLong(
5765  WidthsMenu[entry]);
5766  break;
5767  }
5768  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5769  width);
5770  if (*width == '\0')
5771  break;
5772  line_width=(unsigned int) StringToUnsignedLong(width);
5773  break;
5774  }
5775  case DrawUndoCommand:
5776  {
5777  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5778  image,exception);
5779  break;
5780  }
5781  case DrawHelpCommand:
5782  {
5783  XTextViewHelp(display,resource_info,windows,MagickFalse,
5784  "Help Viewer - Image Rotation",ImageDrawHelp);
5785  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5786  break;
5787  }
5788  case DrawDismissCommand:
5789  {
5790  /*
5791  Prematurely exit.
5792  */
5793  state|=EscapeState;
5794  state|=ExitState;
5795  break;
5796  }
5797  default:
5798  break;
5799  }
5800  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5801  continue;
5802  }
5803  switch (event.type)
5804  {
5805  case ButtonPress:
5806  {
5807  if (event.xbutton.button != Button1)
5808  break;
5809  if (event.xbutton.window != windows->image.id)
5810  break;
5811  /*
5812  exit loop.
5813  */
5814  x=event.xbutton.x;
5815  y=event.xbutton.y;
5816  state|=ExitState;
5817  break;
5818  }
5819  case ButtonRelease:
5820  break;
5821  case Expose:
5822  break;
5823  case KeyPress:
5824  {
5825  KeySym
5826  key_symbol;
5827 
5828  if (event.xkey.window != windows->image.id)
5829  break;
5830  /*
5831  Respond to a user key press.
5832  */
5833  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5834  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5835  switch ((int) key_symbol)
5836  {
5837  case XK_Escape:
5838  case XK_F20:
5839  {
5840  /*
5841  Prematurely exit.
5842  */
5843  state|=EscapeState;
5844  state|=ExitState;
5845  break;
5846  }
5847  case XK_F1:
5848  case XK_Help:
5849  {
5850  XTextViewHelp(display,resource_info,windows,MagickFalse,
5851  "Help Viewer - Image Rotation",ImageDrawHelp);
5852  break;
5853  }
5854  default:
5855  {
5856  (void) XBell(display,0);
5857  break;
5858  }
5859  }
5860  break;
5861  }
5862  case MotionNotify:
5863  {
5864  /*
5865  Map and unmap Info widget as text cursor crosses its boundaries.
5866  */
5867  x=event.xmotion.x;
5868  y=event.xmotion.y;
5869  if (windows->info.mapped != MagickFalse)
5870  {
5871  if ((x < (windows->info.x+(int) windows->info.width)) &&
5872  (y < (windows->info.y+(int) windows->info.height)))
5873  (void) XWithdrawWindow(display,windows->info.id,
5874  windows->info.screen);
5875  }
5876  else
5877  if ((x > (windows->info.x+(int) windows->info.width)) ||
5878  (y > (windows->info.y+(int) windows->info.height)))
5879  (void) XMapWindow(display,windows->info.id);
5880  break;
5881  }
5882  }
5883  } while ((state & ExitState) == 0);
5884  (void) XSelectInput(display,windows->image.id,
5885  windows->image.attributes.event_mask);
5886  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5887  if ((state & EscapeState) != 0)
5888  break;
5889  /*
5890  Draw element as pointer moves until the button is released.
5891  */
5892  distance=0;
5893  degrees=0.0;
5894  line_info.x1=x;
5895  line_info.y1=y;
5896  line_info.x2=x;
5897  line_info.y2=y;
5898  rectangle_info.x=(ssize_t) x;
5899  rectangle_info.y=(ssize_t) y;
5900  rectangle_info.width=0;
5901  rectangle_info.height=0;
5902  number_coordinates=1;
5903  coordinate_info->x=x;
5904  coordinate_info->y=y;
5905  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5906  state=DefaultState;
5907  do
5908  {
5909  switch (element)
5910  {
5911  case PointElement:
5912  default:
5913  {
5914  if (number_coordinates > 1)
5915  {
5916  (void) XDrawLines(display,windows->image.id,
5917  windows->image.highlight_context,coordinate_info,
5918  number_coordinates,CoordModeOrigin);
5919  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d",
5920  coordinate_info[number_coordinates-1].x,
5921  coordinate_info[number_coordinates-1].y);
5922  XInfoWidget(display,windows,text);
5923  }
5924  break;
5925  }
5926  case LineElement:
5927  {
5928  if (distance > 9)
5929  {
5930  /*
5931  Display angle of the line.
5932  */
5933  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5934  line_info.y1),(double) (line_info.x2-line_info.x1)));
5935  (void) FormatLocaleString(text,MagickPathExtent," %g",
5936  (double) degrees);
5937  XInfoWidget(display,windows,text);
5938  XHighlightLine(display,windows->image.id,
5939  windows->image.highlight_context,&line_info);
5940  }
5941  else
5942  if (windows->info.mapped != MagickFalse)
5943  (void) XWithdrawWindow(display,windows->info.id,
5944  windows->info.screen);
5945  break;
5946  }
5947  case RectangleElement:
5948  case FillRectangleElement:
5949  {
5950  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5951  {
5952  /*
5953  Display info and draw drawing rectangle.
5954  */
5955  (void) FormatLocaleString(text,MagickPathExtent,
5956  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5957  (double) rectangle_info.height,(double) rectangle_info.x,
5958  (double) rectangle_info.y);
5959  XInfoWidget(display,windows,text);
5960  XHighlightRectangle(display,windows->image.id,
5961  windows->image.highlight_context,&rectangle_info);
5962  }
5963  else
5964  if (windows->info.mapped != MagickFalse)
5965  (void) XWithdrawWindow(display,windows->info.id,
5966  windows->info.screen);
5967  break;
5968  }
5969  case CircleElement:
5970  case FillCircleElement:
5971  case EllipseElement:
5972  case FillEllipseElement:
5973  {
5974  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975  {
5976  /*
5977  Display info and draw drawing rectangle.
5978  */
5979  (void) FormatLocaleString(text,MagickPathExtent,
5980  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981  (double) rectangle_info.height,(double) rectangle_info.x,
5982  (double) rectangle_info.y);
5983  XInfoWidget(display,windows,text);
5984  XHighlightEllipse(display,windows->image.id,
5985  windows->image.highlight_context,&rectangle_info);
5986  }
5987  else
5988  if (windows->info.mapped != MagickFalse)
5989  (void) XWithdrawWindow(display,windows->info.id,
5990  windows->info.screen);
5991  break;
5992  }
5993  case PolygonElement:
5994  case FillPolygonElement:
5995  {
5996  if (number_coordinates > 1)
5997  (void) XDrawLines(display,windows->image.id,
5998  windows->image.highlight_context,coordinate_info,
5999  number_coordinates,CoordModeOrigin);
6000  if (distance > 9)
6001  {
6002  /*
6003  Display angle of the line.
6004  */
6005  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6006  line_info.y1),(double) (line_info.x2-line_info.x1)));
6007  (void) FormatLocaleString(text,MagickPathExtent," %g",
6008  (double) degrees);
6009  XInfoWidget(display,windows,text);
6010  XHighlightLine(display,windows->image.id,
6011  windows->image.highlight_context,&line_info);
6012  }
6013  else
6014  if (windows->info.mapped != MagickFalse)
6015  (void) XWithdrawWindow(display,windows->info.id,
6016  windows->info.screen);
6017  break;
6018  }
6019  }
6020  /*
6021  Wait for next event.
6022  */
6023  XScreenEvent(display,windows,&event,exception);
6024  switch (element)
6025  {
6026  case PointElement:
6027  default:
6028  {
6029  if (number_coordinates > 1)
6030  (void) XDrawLines(display,windows->image.id,
6031  windows->image.highlight_context,coordinate_info,
6032  number_coordinates,CoordModeOrigin);
6033  break;
6034  }
6035  case LineElement:
6036  {
6037  if (distance > 9)
6038  XHighlightLine(display,windows->image.id,
6039  windows->image.highlight_context,&line_info);
6040  break;
6041  }
6042  case RectangleElement:
6043  case FillRectangleElement:
6044  {
6045  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6046  XHighlightRectangle(display,windows->image.id,
6047  windows->image.highlight_context,&rectangle_info);
6048  break;
6049  }
6050  case CircleElement:
6051  case FillCircleElement:
6052  case EllipseElement:
6053  case FillEllipseElement:
6054  {
6055  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6056  XHighlightEllipse(display,windows->image.id,
6057  windows->image.highlight_context,&rectangle_info);
6058  break;
6059  }
6060  case PolygonElement:
6061  case FillPolygonElement:
6062  {
6063  if (number_coordinates > 1)
6064  (void) XDrawLines(display,windows->image.id,
6065  windows->image.highlight_context,coordinate_info,
6066  number_coordinates,CoordModeOrigin);
6067  if (distance > 9)
6068  XHighlightLine(display,windows->image.id,
6069  windows->image.highlight_context,&line_info);
6070  break;
6071  }
6072  }
6073  switch (event.type)
6074  {
6075  case ButtonPress:
6076  break;
6077  case ButtonRelease:
6078  {
6079  /*
6080  User has committed to element.
6081  */
6082  line_info.x2=event.xbutton.x;
6083  line_info.y2=event.xbutton.y;
6084  rectangle_info.x=(ssize_t) event.xbutton.x;
6085  rectangle_info.y=(ssize_t) event.xbutton.y;
6086  coordinate_info[number_coordinates].x=event.xbutton.x;
6087  coordinate_info[number_coordinates].y=event.xbutton.y;
6088  if (((element != PolygonElement) &&
6089  (element != FillPolygonElement)) || (distance <= 9))
6090  {
6091  state|=ExitState;
6092  break;
6093  }
6094  number_coordinates++;
6095  if (number_coordinates < (int) max_coordinates)
6096  {
6097  line_info.x1=event.xbutton.x;
6098  line_info.y1=event.xbutton.y;
6099  break;
6100  }
6101  max_coordinates<<=1;
6102  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6103  max_coordinates,sizeof(*coordinate_info));
6104  if (coordinate_info == (XPoint *) NULL)
6105  (void) ThrowMagickException(exception,GetMagickModule(),
6106  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6107  break;
6108  }
6109  case Expose:
6110  break;
6111  case MotionNotify:
6112  {
6113  if (event.xmotion.window != windows->image.id)
6114  break;
6115  if (element != PointElement)
6116  {
6117  line_info.x2=event.xmotion.x;
6118  line_info.y2=event.xmotion.y;
6119  rectangle_info.x=(ssize_t) event.xmotion.x;
6120  rectangle_info.y=(ssize_t) event.xmotion.y;
6121  break;
6122  }
6123  coordinate_info[number_coordinates].x=event.xbutton.x;
6124  coordinate_info[number_coordinates].y=event.xbutton.y;
6125  number_coordinates++;
6126  if (number_coordinates < (int) max_coordinates)
6127  break;
6128  max_coordinates<<=1;
6129  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6130  max_coordinates,sizeof(*coordinate_info));
6131  if (coordinate_info == (XPoint *) NULL)
6132  (void) ThrowMagickException(exception,GetMagickModule(),
6133  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6134  break;
6135  }
6136  default:
6137  break;
6138  }
6139  /*
6140  Check boundary conditions.
6141  */
6142  if (line_info.x2 < 0)
6143  line_info.x2=0;
6144  else
6145  if (line_info.x2 > (int) windows->image.width)
6146  line_info.x2=(short) windows->image.width;
6147  if (line_info.y2 < 0)
6148  line_info.y2=0;
6149  else
6150  if (line_info.y2 > (int) windows->image.height)
6151  line_info.y2=(short) windows->image.height;
6152  distance=(unsigned int)
6153  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6154  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6155  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6156  ((state & ExitState) != 0))
6157  {
6158  if (rectangle_info.x < 0)
6159  rectangle_info.x=0;
6160  else
6161  if (rectangle_info.x > (ssize_t) windows->image.width)
6162  rectangle_info.x=(ssize_t) windows->image.width;
6163  if ((int) rectangle_info.x < x)
6164  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6165  else
6166  {
6167  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6168  rectangle_info.x=(ssize_t) x;
6169  }
6170  if (rectangle_info.y < 0)
6171  rectangle_info.y=0;
6172  else
6173  if (rectangle_info.y > (ssize_t) windows->image.height)
6174  rectangle_info.y=(ssize_t) windows->image.height;
6175  if ((int) rectangle_info.y < y)
6176  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6177  else
6178  {
6179  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6180  rectangle_info.y=(ssize_t) y;
6181  }
6182  }
6183  } while ((state & ExitState) == 0);
6184  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6185  if ((element == PointElement) || (element == PolygonElement) ||
6186  (element == FillPolygonElement))
6187  {
6188  /*
6189  Determine polygon bounding box.
6190  */
6191  rectangle_info.x=(ssize_t) coordinate_info->x;
6192  rectangle_info.y=(ssize_t) coordinate_info->y;
6193  x=coordinate_info->x;
6194  y=coordinate_info->y;
6195  for (i=1; i < number_coordinates; i++)
6196  {
6197  if (coordinate_info[i].x > x)
6198  x=coordinate_info[i].x;
6199  if (coordinate_info[i].y > y)
6200  y=coordinate_info[i].y;
6201  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6202  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6203  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6204  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6205  }
6206  rectangle_info.width=(size_t) (x-rectangle_info.x);
6207  rectangle_info.height=(size_t) (y-rectangle_info.y);
6208  for (i=0; i < number_coordinates; i++)
6209  {
6210  coordinate_info[i].x-=rectangle_info.x;
6211  coordinate_info[i].y-=rectangle_info.y;
6212  }
6213  }
6214  else
6215  if (distance <= 9)
6216  continue;
6217  else
6218  if ((element == RectangleElement) ||
6219  (element == CircleElement) || (element == EllipseElement))
6220  {
6221  rectangle_info.width--;
6222  rectangle_info.height--;
6223  }
6224  /*
6225  Drawing is relative to image configuration.
6226  */
6227  draw_info.x=(int) rectangle_info.x;
6228  draw_info.y=(int) rectangle_info.y;
6229  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6230  image,exception);
6231  width=(unsigned int) (*image)->columns;
6232  height=(unsigned int) (*image)->rows;
6233  x=0;
6234  y=0;
6235  if (windows->image.crop_geometry != (char *) NULL)
6236  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6237  draw_info.x+=windows->image.x-((int) line_width/2);
6238  if (draw_info.x < 0)
6239  draw_info.x=0;
6240  draw_info.x=(int) width*draw_info.x/windows->image.ximage->width;
6241  draw_info.y+=windows->image.y-((int) line_width/2);
6242  if (draw_info.y < 0)
6243  draw_info.y=0;
6244  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6245  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6246  if (draw_info.width > (unsigned int) (*image)->columns)
6247  draw_info.width=(unsigned int) (*image)->columns;
6248  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6249  if (draw_info.height > (unsigned int) (*image)->rows)
6250  draw_info.height=(unsigned int) (*image)->rows;
6251  (void) FormatLocaleString(draw_info.geometry,MagickPathExtent,"%ux%u%+d%+d",
6252  width*draw_info.width/(unsigned int) windows->image.ximage->width,
6253  height*draw_info.height/(unsigned int) windows->image.ximage->height,
6254  draw_info.x+x,draw_info.y+y);
6255  /*
6256  Initialize drawing attributes.
6257  */
6258  draw_info.degrees=0.0;
6259  draw_info.element=element;
6260  draw_info.stipple=stipple;
6261  draw_info.line_width=line_width;
6262  draw_info.line_info=line_info;
6263  if (line_info.x1 > (int) (line_width/2))
6264  draw_info.line_info.x1=(short) line_width/2;
6265  if (line_info.y1 > (int) (line_width/2))
6266  draw_info.line_info.y1=(short) line_width/2;
6267  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+
6268  ((int) line_width/2));
6269  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+
6270  ((int) line_width/2));
6271  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6272  {
6273  draw_info.line_info.x2=(-draw_info.line_info.x2);
6274  draw_info.line_info.y2=(-draw_info.line_info.y2);
6275  }
6276  if (draw_info.line_info.x2 < 0)
6277  {
6278  draw_info.line_info.x2=(-draw_info.line_info.x2);
6279  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6280  }
6281  if (draw_info.line_info.y2 < 0)
6282  {
6283  draw_info.line_info.y2=(-draw_info.line_info.y2);
6284  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6285  }
6286  draw_info.rectangle_info=rectangle_info;
6287  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6288  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6289  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6290  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6291  draw_info.number_coordinates=(unsigned int) number_coordinates;
6292  draw_info.coordinate_info=coordinate_info;
6293  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6294  /*
6295  Draw element on image.
6296  */
6297  XSetCursorState(display,windows,MagickTrue);
6298  XCheckRefreshWindows(display,windows);
6299  status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6300  XSetCursorState(display,windows,MagickFalse);
6301  /*
6302  Update image colormap and return to image drawing.
6303  */
6304  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6305  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6306  }
6307  XSetCursorState(display,windows,MagickFalse);
6308  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6309  return(status != 0 ? MagickTrue : MagickFalse);
6310 }
6311 
6312 /*
6313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314 % %
6315 % %
6316 % %
6317 + X D r a w P a n R e c t a n g l e %
6318 % %
6319 % %
6320 % %
6321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322 %
6323 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6324 % displays a zoom image and the rectangle shows which portion of the image is
6325 % displayed in the Image window.
6326 %
6327 % The format of the XDrawPanRectangle method is:
6328 %
6329 % XDrawPanRectangle(Display *display,XWindows *windows)
6330 %
6331 % A description of each parameter follows:
6332 %
6333 % o display: Specifies a connection to an X server; returned from
6334 % XOpenDisplay.
6335 %
6336 % o windows: Specifies a pointer to a XWindows structure.
6337 %
6338 */
6339 static void XDrawPanRectangle(Display *display,XWindows *windows)
6340 {
6341  double
6342  scale_factor;
6343 
6345  highlight_info;
6346 
6347  /*
6348  Determine dimensions of the panning rectangle.
6349  */
6350  scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6351  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6352  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6353  scale_factor=(double)
6354  windows->pan.height/windows->image.ximage->height;
6355  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6356  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6357  /*
6358  Display the panning rectangle.
6359  */
6360  (void) XClearWindow(display,windows->pan.id);
6361  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6362  &highlight_info);
6363 }
6364 
6365 /*
6366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367 % %
6368 % %
6369 % %
6370 + X I m a g e C a c h e %
6371 % %
6372 % %
6373 % %
6374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6375 %
6376 % XImageCache() handles the creation, manipulation, and destruction of the
6377 % image cache (undo and redo buffers).
6378 %
6379 % The format of the XImageCache method is:
6380 %
6381 % void XImageCache(Display *display,XResourceInfo *resource_info,
6382 % XWindows *windows,const DisplayCommand command,Image **image,
6383 % ExceptionInfo *exception)
6384 %
6385 % A description of each parameter follows:
6386 %
6387 % o display: Specifies a connection to an X server; returned from
6388 % XOpenDisplay.
6389 %
6390 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6391 %
6392 % o windows: Specifies a pointer to a XWindows structure.
6393 %
6394 % o command: Specifies a command to perform.
6395 %
6396 % o image: the image; XImageCache may transform the image and return a new
6397 % image pointer.
6398 %
6399 % o exception: return any errors or warnings in this structure.
6400 %
6401 */
6402 static void XImageCache(Display *display,XResourceInfo *resource_info,
6403  XWindows *windows,const DisplayCommand command,Image **image,
6404  ExceptionInfo *exception)
6405 {
6406  Image
6407  *cache_image;
6408 
6409  static Image
6410  *redo_image = (Image *) NULL,
6411  *undo_image = (Image *) NULL;
6412 
6413  switch (command)
6414  {
6415  case FreeBuffersCommand:
6416  {
6417  /*
6418  Free memory from the undo and redo cache.
6419  */
6420  while (undo_image != (Image *) NULL)
6421  {
6422  cache_image=undo_image;
6423  undo_image=GetPreviousImageInList(undo_image);
6424  cache_image->list=DestroyImage(cache_image->list);
6425  cache_image=DestroyImage(cache_image);
6426  }
6427  undo_image=NewImageList();
6428  if (redo_image != (Image *) NULL)
6429  redo_image=DestroyImage(redo_image);
6430  redo_image=NewImageList();
6431  return;
6432  }
6433  case UndoCommand:
6434  {
6435  char
6436  image_geometry[MagickPathExtent];
6437 
6438  /*
6439  Undo the last image transformation.
6440  */
6441  if (undo_image == (Image *) NULL)
6442  {
6443  (void) XBell(display,0);
6444  ThrowXWindowException(ImageError,"NoImagesWereFound",
6445  (*image)->filename);
6446  return;
6447  }
6448  cache_image=undo_image;
6449  undo_image=GetPreviousImageInList(undo_image);
6450  windows->image.window_changes.width=(int) cache_image->columns;
6451  windows->image.window_changes.height=(int) cache_image->rows;
6452  (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
6453  windows->image.ximage->width,windows->image.ximage->height);
6454  (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6455  exception);
6456  if (windows->image.crop_geometry != (char *) NULL)
6457  windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6458  windows->image.crop_geometry);
6459  windows->image.crop_geometry=cache_image->geometry;
6460  if (redo_image != (Image *) NULL)
6461  redo_image=DestroyImage(redo_image);
6462  redo_image=(*image);
6463  *image=cache_image->list;
6464  cache_image=DestroyImage(cache_image);
6465  if (windows->image.orphan != MagickFalse)
6466  return;
6467  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6468  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6469  return;
6470  }
6471  case CutCommand:
6472  case PasteCommand:
6473  case ApplyCommand:
6474  case HalfSizeCommand:
6475  case OriginalSizeCommand:
6476  case DoubleSizeCommand:
6477  case ResizeCommand:
6478  case TrimCommand:
6479  case CropCommand:
6480  case ChopCommand:
6481  case FlipCommand:
6482  case FlopCommand:
6483  case RotateRightCommand:
6484  case RotateLeftCommand:
6485  case RotateCommand:
6486  case ShearCommand:
6487  case RollCommand:
6488  case NegateCommand:
6489  case ContrastStretchCommand:
6490  case SigmoidalContrastCommand:
6491  case NormalizeCommand:
6492  case EqualizeCommand:
6493  case HueCommand:
6494  case SaturationCommand:
6495  case BrightnessCommand:
6496  case GammaCommand:
6497  case SpiffCommand:
6498  case DullCommand:
6499  case GrayscaleCommand:
6500  case MapCommand:
6501  case QuantizeCommand:
6502  case DespeckleCommand:
6503  case EmbossCommand:
6504  case ReduceNoiseCommand:
6505  case AddNoiseCommand:
6506  case SharpenCommand:
6507  case BlurCommand:
6508  case ThresholdCommand:
6509  case EdgeDetectCommand:
6510  case SpreadCommand:
6511  case ShadeCommand:
6512  case RaiseCommand:
6513  case SegmentCommand:
6514  case SolarizeCommand:
6515  case SepiaToneCommand:
6516  case SwirlCommand:
6517  case ImplodeCommand:
6518  case VignetteCommand:
6519  case WaveCommand:
6520  case OilPaintCommand:
6521  case CharcoalDrawCommand:
6522  case AnnotateCommand:
6523  case AddBorderCommand:
6524  case AddFrameCommand:
6525  case CompositeCommand:
6526  case CommentCommand:
6527  case LaunchCommand:
6528  case RegionOfInterestCommand:
6529  case SaveToUndoBufferCommand:
6530  case RedoCommand:
6531  {
6532  Image
6533  *previous_image;
6534 
6535  size_t
6536  bytes;
6537 
6538  bytes=(*image)->columns*(*image)->rows*sizeof(PixelInfo);
6539  if (undo_image != (Image *) NULL)
6540  {
6541  /*
6542  Ensure the undo cache has enough memory available.
6543  */
6544  previous_image=undo_image;
6545  while (previous_image != (Image *) NULL)
6546  {
6547  bytes+=previous_image->list->columns*previous_image->list->rows*
6548  sizeof(PixelInfo);
6549  if (bytes <= (resource_info->undo_cache << 20))
6550  {
6551  previous_image=GetPreviousImageInList(previous_image);
6552  continue;
6553  }
6554  bytes-=previous_image->list->columns*previous_image->list->rows*
6555  sizeof(PixelInfo);
6556  if (previous_image == undo_image)
6557  undo_image=NewImageList();
6558  else
6559  previous_image->next->previous=NewImageList();
6560  break;
6561  }
6562  while (previous_image != (Image *) NULL)
6563  {
6564  /*
6565  Delete any excess memory from undo cache.
6566  */
6567  cache_image=previous_image;
6568  previous_image=GetPreviousImageInList(previous_image);
6569  cache_image->list=DestroyImage(cache_image->list);
6570  cache_image=DestroyImage(cache_image);
6571  }
6572  }
6573  if (bytes > (resource_info->undo_cache << 20))
6574  break;
6575  /*
6576  Save image before transformations are applied.
6577  */
6578  cache_image=AcquireImage((ImageInfo *) NULL,exception);
6579  if (cache_image == (Image *) NULL)
6580  break;
6581  XSetCursorState(display,windows,MagickTrue);
6582  XCheckRefreshWindows(display,windows);
6583  cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6584  XSetCursorState(display,windows,MagickFalse);
6585  if (cache_image->list == (Image *) NULL)
6586  {
6587  cache_image=DestroyImage(cache_image);
6588  break;
6589  }
6590  cache_image->columns=(size_t) windows->image.ximage->width;
6591  cache_image->rows=(size_t) windows->image.ximage->height;
6592  cache_image->geometry=windows->image.crop_geometry;
6593  if (windows->image.crop_geometry != (char *) NULL)
6594  {
6595  cache_image->geometry=AcquireString((char *) NULL);
6596  (void) CopyMagickString(cache_image->geometry,
6597  windows->image.crop_geometry,MagickPathExtent);
6598  }
6599  if (undo_image == (Image *) NULL)
6600  {
6601  undo_image=cache_image;
6602  break;
6603  }
6604  undo_image->next=cache_image;
6605  undo_image->next->previous=undo_image;
6606  undo_image=undo_image->next;
6607  break;
6608  }
6609  default:
6610  break;
6611  }
6612  if (command == RedoCommand)
6613  {
6614  /*
6615  Redo the last image transformation.
6616  */
6617  if (redo_image == (Image *) NULL)
6618  {
6619  (void) XBell(display,0);
6620  return;
6621  }
6622  windows->image.window_changes.width=(int) redo_image->columns;
6623  windows->image.window_changes.height=(int) redo_image->rows;
6624  if (windows->image.crop_geometry != (char *) NULL)
6625  windows->image.crop_geometry=(char *)
6626  RelinquishMagickMemory(windows->image.crop_geometry);
6627  windows->image.crop_geometry=redo_image->geometry;
6628  *image=DestroyImage(*image);
6629  *image=redo_image;
6630  redo_image=NewImageList();
6631  if (windows->image.orphan != MagickFalse)
6632  return;
6633  XConfigureImageColormap(display,resource_info,windows,*image,exception);
6634  (void) XConfigureImage(display,resource_info,windows,*image,exception);
6635  return;
6636  }
6637  if (command != InfoCommand)
6638  return;
6639  /*
6640  Display image info.
6641  */
6642  XSetCursorState(display,windows,MagickTrue);
6643  XCheckRefreshWindows(display,windows);
6644  XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6645  XSetCursorState(display,windows,MagickFalse);
6646 }
6647 
6648 /*
6649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6650 % %
6651 % %
6652 % %
6653 + X I m a g e W i n d o w C o m m a n d %
6654 % %
6655 % %
6656 % %
6657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6658 %
6659 % XImageWindowCommand() makes a transform to the image or Image window as
6660 % specified by a user menu button or keyboard command.
6661 %
6662 % The format of the XImageWindowCommand method is:
6663 %
6664 % DisplayCommand XImageWindowCommand(Display *display,
6665 % XResourceInfo *resource_info,XWindows *windows,
6666 % const MagickStatusType state,KeySym key_symbol,Image **image,
6667 % ExceptionInfo *exception)
6668 %
6669 % A description of each parameter follows:
6670 %
6671 % o nexus: Method XImageWindowCommand returns an image when the
6672 % user chooses 'Open Image' from the command menu. Otherwise a null
6673 % image is returned.
6674 %
6675 % o display: Specifies a connection to an X server; returned from
6676 % XOpenDisplay.
6677 %
6678 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6679 %
6680 % o windows: Specifies a pointer to a XWindows structure.
6681 %
6682 % o state: key mask.
6683 %
6684 % o key_symbol: Specifies a command to perform.
6685 %
6686 % o image: the image; XImageWIndowCommand may transform the image and
6687 % return a new image pointer.
6688 %
6689 % o exception: return any errors or warnings in this structure.
6690 %
6691 */
6692 static DisplayCommand XImageWindowCommand(Display *display,
6693  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6694  KeySym key_symbol,Image **image,ExceptionInfo *exception)
6695 {
6696  static char
6697  delta[MagickPathExtent] = "";
6698 
6699  static const char
6700  Digits[] = "01234567890";
6701 
6702  static KeySym
6703  last_symbol = XK_0;
6704 
6705  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6706  {
6707  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6708  {
6709  *delta='\0';
6710  resource_info->quantum=1;
6711  }
6712  last_symbol=key_symbol;
6713  delta[strlen(delta)+1]='\0';
6714  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6715  resource_info->quantum=StringToLong(delta);
6716  return(NullCommand);
6717  }
6718  last_symbol=key_symbol;
6719  if (resource_info->immutable)
6720  {
6721  /*
6722  Virtual image window has a restricted command set.
6723  */
6724  switch (key_symbol)
6725  {
6726  case XK_question:
6727  return(InfoCommand);
6728  case XK_p:
6729  case XK_Print:
6730  return(PrintCommand);
6731  case XK_space:
6732  return(NextCommand);
6733  case XK_q:
6734  case XK_Escape:
6735  return(QuitCommand);
6736  default:
6737  break;
6738  }
6739  return(NullCommand);
6740  }
6741  switch ((int) key_symbol)
6742  {
6743  case XK_o:
6744  {
6745  if ((state & ControlMask) == 0)
6746  break;
6747  return(OpenCommand);
6748  }
6749  case XK_space:
6750  return(NextCommand);
6751  case XK_BackSpace:
6752  return(FormerCommand);
6753  case XK_s:
6754  {
6755  if ((state & Mod1Mask) != 0)
6756  return(SwirlCommand);
6757  if ((state & ControlMask) == 0)
6758  return(ShearCommand);
6759  return(SaveCommand);
6760  }
6761  case XK_p:
6762  case XK_Print:
6763  {
6764  if ((state & Mod1Mask) != 0)
6765  return(OilPaintCommand);
6766  if ((state & Mod4Mask) != 0)
6767  return(ColorCommand);
6768  if ((state & ControlMask) == 0)
6769  return(NullCommand);
6770  return(PrintCommand);
6771  }
6772  case XK_d:
6773  {
6774  if ((state & Mod4Mask) != 0)
6775  return(DrawCommand);
6776  if ((state & ControlMask) == 0)
6777  return(NullCommand);
6778  return(DeleteCommand);
6779  }
6780  case XK_Select:
6781  {
6782  if ((state & ControlMask) == 0)
6783  return(NullCommand);
6784  return(SelectCommand);
6785  }
6786  case XK_n:
6787  {
6788  if ((state & ControlMask) == 0)
6789  return(NullCommand);
6790  return(NewCommand);
6791  }
6792  case XK_q:
6793  case XK_Escape:
6794  return(QuitCommand);
6795  case XK_z:
6796  case XK_Undo:
6797  {
6798  if ((state & ControlMask) == 0)
6799  return(NullCommand);
6800  return(UndoCommand);
6801  }
6802  case XK_r:
6803  case XK_Redo:
6804  {
6805  if ((state & ControlMask) == 0)
6806  return(RollCommand);
6807  return(RedoCommand);
6808  }
6809  case XK_x:
6810  {
6811  if ((state & ControlMask) == 0)
6812  return(NullCommand);
6813  return(CutCommand);
6814  }
6815  case XK_c:
6816  {
6817  if ((state & Mod1Mask) != 0)
6818  return(CharcoalDrawCommand);
6819  if ((state & ControlMask) == 0)
6820  return(CropCommand);
6821  return(CopyCommand);
6822  }
6823  case XK_v:
6824  case XK_Insert:
6825  {
6826  if ((state & Mod4Mask) != 0)
6827  return(CompositeCommand);
6828  if ((state & ControlMask) == 0)
6829  return(FlipCommand);
6830  return(PasteCommand);
6831  }
6832  case XK_less:
6833  return(HalfSizeCommand);
6834  case XK_minus:
6835  return(OriginalSizeCommand);
6836  case XK_greater:
6837  return(DoubleSizeCommand);
6838  case XK_percent:
6839  return(ResizeCommand);
6840  case XK_at:
6841  return(RefreshCommand);
6842  case XK_bracketleft:
6843  return(ChopCommand);
6844  case XK_h:
6845  return(FlopCommand);
6846  case XK_slash:
6847  return(RotateRightCommand);
6848  case XK_backslash:
6849  return(RotateLeftCommand);
6850  case XK_asterisk:
6851  return(RotateCommand);
6852  case XK_t:
6853  return(TrimCommand);
6854  case XK_H:
6855  return(HueCommand);
6856  case XK_S:
6857  return(SaturationCommand);
6858  case XK_L:
6859  return(BrightnessCommand);
6860  case XK_G:
6861  return(GammaCommand);
6862  case XK_C:
6863  return(SpiffCommand);
6864  case XK_Z:
6865  return(DullCommand);
6866  case XK_N:
6867  return(NormalizeCommand);
6868  case XK_equal:
6869  return(EqualizeCommand);
6870  case XK_asciitilde:
6871  return(NegateCommand);
6872  case XK_period:
6873  return(GrayscaleCommand);
6874  case XK_numbersign:
6875  return(QuantizeCommand);
6876  case XK_F2:
6877  return(DespeckleCommand);
6878  case XK_F3:
6879  return(EmbossCommand);
6880  case XK_F4:
6881  return(ReduceNoiseCommand);
6882  case XK_F5:
6883  return(AddNoiseCommand);
6884  case XK_F6:
6885  return(SharpenCommand);
6886  case XK_F7:
6887  return(BlurCommand);
6888  case XK_F8:
6889  return(ThresholdCommand);
6890  case XK_F9:
6891  return(EdgeDetectCommand);
6892  case XK_F10:
6893  return(SpreadCommand);
6894  case XK_F11:
6895  return(ShadeCommand);
6896  case XK_F12:
6897  return(RaiseCommand);
6898  case XK_F13:
6899  return(SegmentCommand);
6900  case XK_i:
6901  {
6902  if ((state & Mod1Mask) == 0)
6903  return(NullCommand);
6904  return(ImplodeCommand);
6905  }
6906  case XK_w:
6907  {
6908  if ((state & Mod1Mask) == 0)
6909  return(NullCommand);
6910  return(WaveCommand);
6911  }
6912  case XK_m:
6913  {
6914  if ((state & Mod4Mask) == 0)
6915  return(NullCommand);
6916  return(MatteCommand);
6917  }
6918  case XK_b:
6919  {
6920  if ((state & Mod4Mask) == 0)
6921  return(NullCommand);
6922  return(AddBorderCommand);
6923  }
6924  case XK_f:
6925  {
6926  if ((state & Mod4Mask) == 0)
6927  return(NullCommand);
6928  return(AddFrameCommand);
6929  }
6930  case XK_exclam:
6931  {
6932  if ((state & Mod4Mask) == 0)
6933  return(NullCommand);
6934  return(CommentCommand);
6935  }
6936  case XK_a:
6937  {
6938  if ((state & Mod1Mask) != 0)
6939  return(ApplyCommand);
6940  if ((state & Mod4Mask) != 0)
6941  return(AnnotateCommand);
6942  if ((state & ControlMask) == 0)
6943  return(NullCommand);
6944  return(RegionOfInterestCommand);
6945  }
6946  case XK_question:
6947  return(InfoCommand);
6948  case XK_plus:
6949  return(ZoomCommand);
6950  case XK_P:
6951  {
6952  if ((state & ShiftMask) == 0)
6953  return(NullCommand);
6954  return(ShowPreviewCommand);
6955  }
6956  case XK_Execute:
6957  return(LaunchCommand);
6958  case XK_F1:
6959  return(HelpCommand);
6960  case XK_Find:
6961  return(BrowseDocumentationCommand);
6962  case XK_Menu:
6963  {
6964  (void) XMapRaised(display,windows->command.id);
6965  return(NullCommand);
6966  }
6967  case XK_Next:
6968  case XK_Prior:
6969  case XK_Home:
6970  case XK_KP_Home:
6971  {
6972  XTranslateImage(display,windows,*image,key_symbol);
6973  return(NullCommand);
6974  }
6975  case XK_Up:
6976  case XK_KP_Up:
6977  case XK_Down:
6978  case XK_KP_Down:
6979  case XK_Left:
6980  case XK_KP_Left:
6981  case XK_Right:
6982  case XK_KP_Right:
6983  {
6984  if ((state & Mod1Mask) != 0)
6985  {
6987  crop_info;
6988 
6989  /*
6990  Trim one pixel from edge of image.
6991  */
6992  crop_info.x=0;
6993  crop_info.y=0;
6994  crop_info.width=(size_t) windows->image.ximage->width;
6995  crop_info.height=(size_t) windows->image.ximage->height;
6996  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6997  {
6998  if (resource_info->quantum >= (int) crop_info.height)
6999  resource_info->quantum=(int) crop_info.height-1;
7000  crop_info.height-=(size_t) resource_info->quantum;
7001  }
7002  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7003  {
7004  if (resource_info->quantum >= ((int) crop_info.height-crop_info.y))
7005  resource_info->quantum=(int) crop_info.height-crop_info.y-1;
7006  crop_info.y+=resource_info->quantum;
7007  crop_info.height-=(size_t) resource_info->quantum;
7008  }
7009  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7010  {
7011  if (resource_info->quantum >= (int) crop_info.width)
7012  resource_info->quantum=(int) crop_info.width-1;
7013  crop_info.width-=(size_t) resource_info->quantum;
7014  }
7015  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7016  {
7017  if (resource_info->quantum >= ((int) crop_info.width-crop_info.x))
7018  resource_info->quantum=(int) crop_info.width-crop_info.x-1;
7019  crop_info.x+=resource_info->quantum;
7020  crop_info.width-=(size_t) resource_info->quantum;
7021  }
7022  if ((windows->image.x+(int) windows->image.width) > (int) crop_info.width)
7023  windows->image.x=(int) (crop_info.width-windows->image.width);
7024  if ((windows->image.y+(int) windows->image.height) > (int) crop_info.height)
7025  windows->image.y=(int) (crop_info.height-windows->image.height);
7026  XSetCropGeometry(display,windows,&crop_info,*image);
7027  windows->image.window_changes.width=(int) crop_info.width;
7028  windows->image.window_changes.height=(int) crop_info.height;
7029  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7030  (void) XConfigureImage(display,resource_info,windows,*image,
7031  exception);
7032  return(NullCommand);
7033  }
7034  XTranslateImage(display,windows,*image,key_symbol);
7035  return(NullCommand);
7036  }
7037  default:
7038  return(NullCommand);
7039  }
7040  return(NullCommand);
7041 }
7042 
7043 /*
7044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7045 % %
7046 % %
7047 % %
7048 + X M a g i c k C o m m a n d %
7049 % %
7050 % %
7051 % %
7052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7053 %
7054 % XMagickCommand() makes a transform to the image or Image window as
7055 % specified by a user menu button or keyboard command.
7056 %
7057 % The format of the XMagickCommand method is:
7058 %
7059 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7060 % XWindows *windows,const DisplayCommand command,Image **image,
7061 % ExceptionInfo *exception)
7062 %
7063 % A description of each parameter follows:
7064 %
7065 % o display: Specifies a connection to an X server; returned from
7066 % XOpenDisplay.
7067 %
7068 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7069 %
7070 % o windows: Specifies a pointer to a XWindows structure.
7071 %
7072 % o command: Specifies a command to perform.
7073 %
7074 % o image: the image; XMagickCommand may transform the image and return a
7075 % new image pointer.
7076 %
7077 % o exception: return any errors or warnings in this structure.
7078 %
7079 */
7080 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7081  XWindows *windows,const DisplayCommand command,Image **image,
7082  ExceptionInfo *exception)
7083 {
7084  char
7085  filename[MagickPathExtent],
7086  geometry[MagickPathExtent],
7087  modulate_factors[MagickPathExtent];
7088 
7089  GeometryInfo
7090  geometry_info;
7091 
7092  Image
7093  *nexus;
7094 
7095  ImageInfo
7096  *image_info;
7097 
7098  int
7099  x,
7100  y;
7101 
7102  MagickStatusType
7103  flags,
7104  status;
7105 
7106  QuantizeInfo
7107  quantize_info;
7108 
7110  page_geometry;
7111 
7112  int
7113  i;
7114 
7115  static char
7116  color[MagickPathExtent] = "gray";
7117 
7118  unsigned int
7119  height,
7120  width;
7121 
7122  /*
7123  Process user command.
7124  */
7125  XCheckRefreshWindows(display,windows);
7126  XImageCache(display,resource_info,windows,command,image,exception);
7127  nexus=NewImageList();
7128  windows->image.window_changes.width=windows->image.ximage->width;
7129  windows->image.window_changes.height=windows->image.ximage->height;
7130  image_info=CloneImageInfo(resource_info->image_info);
7131  SetGeometryInfo(&geometry_info);
7132  GetQuantizeInfo(&quantize_info);
7133  switch (command)
7134  {
7135  case OpenCommand:
7136  {
7137  /*
7138  Load image.
7139  */
7140  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7141  break;
7142  }
7143  case NextCommand:
7144  {
7145  /*
7146  Display next image.
7147  */
7148  for (i=0; i < resource_info->quantum; i++)
7149  XClientMessage(display,windows->image.id,windows->im_protocols,
7150  windows->im_next_image,CurrentTime);
7151  break;
7152  }
7153  case FormerCommand:
7154  {
7155  /*
7156  Display former image.
7157  */
7158  for (i=0; i < resource_info->quantum; i++)
7159  XClientMessage(display,windows->image.id,windows->im_protocols,
7160  windows->im_former_image,CurrentTime);
7161  break;
7162  }
7163  case SelectCommand:
7164  {
7165  int
7166  status;
7167 
7168  /*
7169  Select image.
7170  */
7171  if (*resource_info->home_directory == '\0')
7172  (void) CopyMagickString(resource_info->home_directory,".",
7173  MagickPathExtent);
7174  status=chdir(resource_info->home_directory);
7175  if (status == -1)
7176  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7177  "UnableToOpenFile","%s",resource_info->home_directory);
7178  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7179  break;
7180  }
7181  case SaveCommand:
7182  {
7183  /*
7184  Save image.
7185  */
7186  status=XSaveImage(display,resource_info,windows,*image,exception);
7187  if (status == MagickFalse)
7188  {
7189  char
7190  message[MagickPathExtent];
7191 
7192  (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7193  exception->reason != (char *) NULL ? exception->reason : "",
7194  exception->description != (char *) NULL ? exception->description :
7195  "");
7196  XNoticeWidget(display,windows,"Unable to save file:",message);
7197  break;
7198  }
7199  break;
7200  }
7201  case PrintCommand:
7202  {
7203  /*
7204  Print image.
7205  */
7206  status=XPrintImage(display,resource_info,windows,*image,exception);
7207  if (status == MagickFalse)
7208  {
7209  char
7210  message[MagickPathExtent];
7211 
7212  (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7213  exception->reason != (char *) NULL ? exception->reason : "",
7214  exception->description != (char *) NULL ? exception->description :
7215  "");
7216  XNoticeWidget(display,windows,"Unable to print file:",message);
7217  break;
7218  }
7219  break;
7220  }
7221  case DeleteCommand:
7222  {
7223  static char
7224  filename[MagickPathExtent] = "\0";
7225 
7226  /*
7227  Delete image file.
7228  */
7229  XFileBrowserWidget(display,windows,"Delete",filename);
7230  if (*filename == '\0')
7231  break;
7232  status=ShredFile(filename);
7233  if (remove_utf8(filename) < 0)
7234  status=MagickTrue;
7235  if (status != MagickFalse)
7236  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7237  break;
7238  }
7239  case NewCommand:
7240  {
7241  int
7242  status;
7243 
7244  static char
7245  color[MagickPathExtent] = "gray",
7246  geometry[MagickPathExtent] = "640x480";
7247 
7248  static const char
7249  *format = "gradient";
7250 
7251  /*
7252  Query user for canvas geometry.
7253  */
7254  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7255  geometry);
7256  if (*geometry == '\0')
7257  break;
7258  if (status == 0)
7259  format="xc";
7260  XColorBrowserWidget(display,windows,"Select",color);
7261  if (*color == '\0')
7262  break;
7263  /*
7264  Create canvas.
7265  */
7266  (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7267  "%s:%s",format,color);
7268  (void) CloneString(&image_info->size,geometry);
7269  nexus=ReadImage(image_info,exception);
7270  CatchException(exception);
7271  XClientMessage(display,windows->image.id,windows->im_protocols,
7272  windows->im_next_image,CurrentTime);
7273  break;
7274  }
7275  case VisualDirectoryCommand:
7276  {
7277  /*
7278  Visual Image directory.
7279  */
7280  nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7281  break;
7282  }
7283  case QuitCommand:
7284  {
7285  /*
7286  exit program.
7287  */
7288  if (resource_info->confirm_exit == MagickFalse)
7289  XClientMessage(display,windows->image.id,windows->im_protocols,
7290  windows->im_exit,CurrentTime);
7291  else
7292  {
7293  int
7294  status;
7295 
7296  /*
7297  Confirm program exit.
7298  */
7299  status=XConfirmWidget(display,windows,"Do you really want to exit",
7300  resource_info->client_name);
7301  if (status > 0)
7302  XClientMessage(display,windows->image.id,windows->im_protocols,
7303  windows->im_exit,CurrentTime);
7304  }
7305  break;
7306  }
7307  case CutCommand:
7308  {
7309  /*
7310  Cut image.
7311  */
7312  (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7313  break;
7314  }
7315  case CopyCommand:
7316  {
7317  /*
7318  Copy image.
7319  */
7320  (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7321  exception);
7322  break;
7323  }
7324  case PasteCommand:
7325  {
7326  /*
7327  Paste image.
7328  */
7329  status=XPasteImage(display,resource_info,windows,*image,exception);
7330  if (status == MagickFalse)
7331  {
7332  XNoticeWidget(display,windows,"Unable to paste X image",
7333  (*image)->filename);
7334  break;
7335  }
7336  break;
7337  }
7338  case HalfSizeCommand:
7339  {
7340  /*
7341  Half image size.
7342  */
7343  windows->image.window_changes.width=windows->image.ximage->width/2;
7344  windows->image.window_changes.height=windows->image.ximage->height/2;
7345  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7346  break;
7347  }
7348  case OriginalSizeCommand:
7349  {
7350  /*
7351  Original image size.
7352  */
7353  windows->image.window_changes.width=(int) (*image)->columns;
7354  windows->image.window_changes.height=(int) (*image)->rows;
7355  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7356  break;
7357  }
7358  case DoubleSizeCommand:
7359  {
7360  /*
7361  Double the image size.
7362  */
7363  windows->image.window_changes.width=windows->image.ximage->width << 1;
7364  windows->image.window_changes.height=windows->image.ximage->height << 1;
7365  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7366  break;
7367  }
7368  case ResizeCommand:
7369  {
7370  int
7371  status;
7372 
7373  size_t
7374  height,
7375  width;
7376 
7377  ssize_t
7378  x,
7379  y;
7380 
7381  /*
7382  Resize image.
7383  */
7384  width=(size_t) windows->image.ximage->width;
7385  height=(size_t) windows->image.ximage->height;
7386  x=0;
7387  y=0;
7388  (void) FormatLocaleString(geometry,MagickPathExtent,"%.20gx%.20g+0+0",
7389  (double) width,(double) height);
7390  status=XDialogWidget(display,windows,"Resize",
7391  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7392  if (*geometry == '\0')
7393  break;
7394  if (status == 0)
7395  (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7396  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7397  windows->image.window_changes.width=(int) width;
7398  windows->image.window_changes.height=(int) height;
7399  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7400  break;
7401  }
7402  case ApplyCommand:
7403  {
7404  char
7405  image_geometry[MagickPathExtent];
7406 
7407  if ((windows->image.crop_geometry == (char *) NULL) &&
7408  ((int) (*image)->columns == windows->image.ximage->width) &&
7409  ((int) (*image)->rows == windows->image.ximage->height))
7410  break;
7411  /*
7412  Apply size transforms to image.
7413  */
7414  XSetCursorState(display,windows,MagickTrue);
7415  XCheckRefreshWindows(display,windows);
7416  /*
7417  Crop and/or scale displayed image.
7418  */
7419  (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7420  windows->image.ximage->width,windows->image.ximage->height);
7421  (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7422  exception);
7423  if (windows->image.crop_geometry != (char *) NULL)
7424  windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7425  windows->image.crop_geometry);
7426  windows->image.x=0;
7427  windows->image.y=0;
7428  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7429  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7430  break;
7431  }
7432  case RefreshCommand:
7433  {
7434  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7435  break;
7436  }
7437  case RestoreCommand:
7438  {
7439  /*
7440  Restore Image window to its original size.
7441  */
7442  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7443  (windows->image.height == (unsigned int) (*image)->rows) &&
7444  (windows->image.crop_geometry == (char *) NULL))
7445  {
7446  (void) XBell(display,0);
7447  break;
7448  }
7449  windows->image.window_changes.width=(int) (*image)->columns;
7450  windows->image.window_changes.height=(int) (*image)->rows;
7451  if (windows->image.crop_geometry != (char *) NULL)
7452  {
7453  windows->image.crop_geometry=(char *)
7454  RelinquishMagickMemory(windows->image.crop_geometry);
7455  windows->image.crop_geometry=(char *) NULL;
7456  windows->image.x=0;
7457  windows->image.y=0;
7458  }
7459  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7460  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7461  break;
7462  }
7463  case CropCommand:
7464  {
7465  /*
7466  Crop image.
7467  */
7468  (void) XCropImage(display,resource_info,windows,*image,CropMode,
7469  exception);
7470  break;
7471  }
7472  case ChopCommand:
7473  {
7474  /*
7475  Chop image.
7476  */
7477  status=XChopImage(display,resource_info,windows,image,exception);
7478  if (status == MagickFalse)
7479  {
7480  XNoticeWidget(display,windows,"Unable to cut X image",
7481  (*image)->filename);
7482  break;
7483  }
7484  break;
7485  }
7486  case FlopCommand:
7487  {
7488  Image
7489  *flop_image;
7490 
7491  /*
7492  Flop image scanlines.
7493  */
7494  XSetCursorState(display,windows,MagickTrue);
7495  XCheckRefreshWindows(display,windows);
7496  flop_image=FlopImage(*image,exception);
7497  if (flop_image != (Image *) NULL)
7498  {
7499  *image=DestroyImage(*image);
7500  *image=flop_image;
7501  }
7502  CatchException(exception);
7503  XSetCursorState(display,windows,MagickFalse);
7504  if (windows->image.crop_geometry != (char *) NULL)
7505  {
7506  /*
7507  Flop crop geometry.
7508  */
7509  width=(unsigned int) (*image)->columns;
7510  height=(unsigned int) (*image)->rows;
7511  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7512  &width,&height);
7513  (void) FormatLocaleString(windows->image.crop_geometry,
7514  MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7515  (int) width-x,y);
7516  }
7517  if (windows->image.orphan != MagickFalse)
7518  break;
7519  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7520  break;
7521  }
7522  case FlipCommand:
7523  {
7524  Image
7525  *flip_image;
7526 
7527  /*
7528  Flip image scanlines.
7529  */
7530  XSetCursorState(display,windows,MagickTrue);
7531  XCheckRefreshWindows(display,windows);
7532  flip_image=FlipImage(*image,exception);
7533  if (flip_image != (Image *) NULL)
7534  {
7535  *image=DestroyImage(*image);
7536  *image=flip_image;
7537  }
7538  CatchException(exception);
7539  XSetCursorState(display,windows,MagickFalse);
7540  if (windows->image.crop_geometry != (char *) NULL)
7541  {
7542  /*
7543  Flip crop geometry.
7544  */
7545  width=(unsigned int) (*image)->columns;
7546  height=(unsigned int) (*image)->rows;
7547  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7548  &width,&height);
7549  (void) FormatLocaleString(windows->image.crop_geometry,
7550  MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7551  (int) height-y);
7552  }
7553  if (windows->image.orphan != MagickFalse)
7554  break;
7555  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7556  break;
7557  }
7558  case RotateRightCommand:
7559  {
7560  /*
7561  Rotate image 90 degrees clockwise.
7562  */
7563  status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7564  if (status == MagickFalse)
7565  {
7566  XNoticeWidget(display,windows,"Unable to rotate X image",
7567  (*image)->filename);
7568  break;
7569  }
7570  break;
7571  }
7572  case RotateLeftCommand:
7573  {
7574  /*
7575  Rotate image 90 degrees counter-clockwise.
7576  */
7577  status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7578  if (status == MagickFalse)
7579  {
7580  XNoticeWidget(display,windows,"Unable to rotate X image",
7581  (*image)->filename);
7582  break;
7583  }
7584  break;
7585  }
7586  case RotateCommand:
7587  {
7588  /*
7589  Rotate image.
7590  */
7591  status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7592  if (status == MagickFalse)
7593  {
7594  XNoticeWidget(display,windows,"Unable to rotate X image",
7595  (*image)->filename);
7596  break;
7597  }
7598  break;
7599  }
7600  case ShearCommand:
7601  {
7602  Image
7603  *shear_image;
7604 
7605  static char
7606  geometry[MagickPathExtent] = "45.0x45.0";
7607 
7608  /*
7609  Query user for shear color and geometry.
7610  */
7611  XColorBrowserWidget(display,windows,"Select",color);
7612  if (*color == '\0')
7613  break;
7614  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7615  geometry);
7616  if (*geometry == '\0')
7617  break;
7618  /*
7619  Shear image.
7620  */
7621  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7622  exception);
7623  XSetCursorState(display,windows,MagickTrue);
7624  XCheckRefreshWindows(display,windows);
7625  (void) QueryColorCompliance(color,AllCompliance,
7626  &(*image)->background_color,exception);
7627  flags=ParseGeometry(geometry,&geometry_info);
7628  if ((flags & SigmaValue) == 0)
7629  geometry_info.sigma=geometry_info.rho;
7630  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7631  exception);
7632  if (shear_image != (Image *) NULL)
7633  {
7634  *image=DestroyImage(*image);
7635  *image=shear_image;
7636  }
7637  CatchException(exception);
7638  XSetCursorState(display,windows,MagickFalse);
7639  if (windows->image.orphan != MagickFalse)
7640  break;
7641  windows->image.window_changes.width=(int) (*image)->columns;
7642  windows->image.window_changes.height=(int) (*image)->rows;
7643  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7644  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7645  break;
7646  }
7647  case RollCommand:
7648  {
7649  Image
7650  *roll_image;
7651 
7652  static char
7653  geometry[MagickPathExtent] = "+2+2";
7654 
7655  /*
7656  Query user for the roll geometry.
7657  */
7658  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7659  geometry);
7660  if (*geometry == '\0')
7661  break;
7662  /*
7663  Roll image.
7664  */
7665  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7666  exception);
7667  XSetCursorState(display,windows,MagickTrue);
7668  XCheckRefreshWindows(display,windows);
7669  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7670  exception);
7671  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7672  exception);
7673  if (roll_image != (Image *) NULL)
7674  {
7675  *image=DestroyImage(*image);
7676  *image=roll_image;
7677  }
7678  CatchException(exception);
7679  XSetCursorState(display,windows,MagickFalse);
7680  if (windows->image.orphan != MagickFalse)
7681  break;
7682  windows->image.window_changes.width=(int) (*image)->columns;
7683  windows->image.window_changes.height=(int) (*image)->rows;
7684  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7685  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7686  break;
7687  }
7688  case TrimCommand:
7689  {
7690  static char
7691  fuzz[MagickPathExtent];
7692 
7693  /*
7694  Query user for the fuzz factor.
7695  */
7696  (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7697  (*image)->fuzz/((double) QuantumRange+1.0));
7698  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7699  if (*fuzz == '\0')
7700  break;
7701  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7702  /*
7703  Trim image.
7704  */
7705  status=XTrimImage(display,resource_info,windows,*image,exception);
7706  if (status == MagickFalse)
7707  {
7708  XNoticeWidget(display,windows,"Unable to trim X image",
7709  (*image)->filename);
7710  break;
7711  }
7712  break;
7713  }
7714  case HueCommand:
7715  {
7716  static char
7717  hue_percent[MagickPathExtent] = "110";
7718 
7719  /*
7720  Query user for percent hue change.
7721  */
7722  (void) XDialogWidget(display,windows,"Apply",
7723  "Enter percent change in image hue (0-200):",hue_percent);
7724  if (*hue_percent == '\0')
7725  break;
7726  /*
7727  Vary the image hue.
7728  */
7729  XSetCursorState(display,windows,MagickTrue);
7730  XCheckRefreshWindows(display,windows);
7731  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7732  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7733  MagickPathExtent);
7734  (void) ModulateImage(*image,modulate_factors,exception);
7735  XSetCursorState(display,windows,MagickFalse);
7736  if (windows->image.orphan != MagickFalse)
7737  break;
7738  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7739  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7740  break;
7741  }
7742  case SaturationCommand:
7743  {
7744  static char
7745  saturation_percent[MagickPathExtent] = "110";
7746 
7747  /*
7748  Query user for percent saturation change.
7749  */
7750  (void) XDialogWidget(display,windows,"Apply",
7751  "Enter percent change in color saturation (0-200):",saturation_percent);
7752  if (*saturation_percent == '\0')
7753  break;
7754  /*
7755  Vary color saturation.
7756  */
7757  XSetCursorState(display,windows,MagickTrue);
7758  XCheckRefreshWindows(display,windows);
7759  (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7760  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7761  MagickPathExtent);
7762  (void) ModulateImage(*image,modulate_factors,exception);
7763  XSetCursorState(display,windows,MagickFalse);
7764  if (windows->image.orphan != MagickFalse)
7765  break;
7766  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7767  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7768  break;
7769  }
7770  case BrightnessCommand:
7771  {
7772  static char
7773  brightness_percent[MagickPathExtent] = "110";
7774 
7775  /*
7776  Query user for percent brightness change.
7777  */
7778  (void) XDialogWidget(display,windows,"Apply",
7779  "Enter percent change in color brightness (0-200):",brightness_percent);
7780  if (*brightness_percent == '\0')
7781  break;
7782  /*
7783  Vary the color brightness.
7784  */
7785  XSetCursorState(display,windows,MagickTrue);
7786  XCheckRefreshWindows(display,windows);
7787  (void) CopyMagickString(modulate_factors,brightness_percent,
7788  MagickPathExtent);
7789  (void) ModulateImage(*image,modulate_factors,exception);
7790  XSetCursorState(display,windows,MagickFalse);
7791  if (windows->image.orphan != MagickFalse)
7792  break;
7793  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7794  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7795  break;
7796  }
7797  case GammaCommand:
7798  {
7799  static char
7800  factor[MagickPathExtent] = "1.6";
7801 
7802  /*
7803  Query user for gamma value.
7804  */
7805  (void) XDialogWidget(display,windows,"Gamma",
7806  "Enter gamma value (e.g. 1.2):",factor);
7807  if (*factor == '\0')
7808  break;
7809  /*
7810  Gamma correct image.
7811  */
7812  XSetCursorState(display,windows,MagickTrue);
7813  XCheckRefreshWindows(display,windows);
7814  (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7815  XSetCursorState(display,windows,MagickFalse);
7816  if (windows->image.orphan != MagickFalse)
7817  break;
7818  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7819  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7820  break;
7821  }
7822  case SpiffCommand:
7823  {
7824  /*
7825  Sharpen the image contrast.
7826  */
7827  XSetCursorState(display,windows,MagickTrue);
7828  XCheckRefreshWindows(display,windows);
7829  (void) ContrastImage(*image,MagickTrue,exception);
7830  XSetCursorState(display,windows,MagickFalse);
7831  if (windows->image.orphan != MagickFalse)
7832  break;
7833  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7834  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7835  break;
7836  }
7837  case DullCommand:
7838  {
7839  /*
7840  Dull the image contrast.
7841  */
7842  XSetCursorState(display,windows,MagickTrue);
7843  XCheckRefreshWindows(display,windows);
7844  (void) ContrastImage(*image,MagickFalse,exception);
7845  XSetCursorState(display,windows,MagickFalse);
7846  if (windows->image.orphan != MagickFalse)
7847  break;
7848  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850  break;
7851  }
7852  case ContrastStretchCommand:
7853  {
7854  double
7855  black_point,
7856  white_point;
7857 
7858  static char
7859  levels[MagickPathExtent] = "1%";
7860 
7861  /*
7862  Query user for gamma value.
7863  */
7864  (void) XDialogWidget(display,windows,"Contrast Stretch",
7865  "Enter black and white points:",levels);
7866  if (*levels == '\0')
7867  break;
7868  /*
7869  Contrast stretch image.
7870  */
7871  XSetCursorState(display,windows,MagickTrue);
7872  XCheckRefreshWindows(display,windows);
7873  flags=ParseGeometry(levels,&geometry_info);
7874  black_point=geometry_info.rho;
7875  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7876  if ((flags & PercentValue) != 0)
7877  {
7878  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7879  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7880  }
7881  white_point=(double) (*image)->columns*(*image)->rows-white_point;
7882  (void) ContrastStretchImage(*image,black_point,white_point,
7883  exception);
7884  XSetCursorState(display,windows,MagickFalse);
7885  if (windows->image.orphan != MagickFalse)
7886  break;
7887  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7888  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7889  break;
7890  }
7891  case SigmoidalContrastCommand:
7892  {
7893  GeometryInfo
7894  geometry_info;
7895 
7896  MagickStatusType
7897  flags;
7898 
7899  static char
7900  levels[MagickPathExtent] = "3x50%";
7901 
7902  /*
7903  Query user for gamma value.
7904  */
7905  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7906  "Enter contrast and midpoint:",levels);
7907  if (*levels == '\0')
7908  break;
7909  /*
7910  Contrast stretch image.
7911  */
7912  XSetCursorState(display,windows,MagickTrue);
7913  XCheckRefreshWindows(display,windows);
7914  flags=ParseGeometry(levels,&geometry_info);
7915  if ((flags & SigmaValue) == 0)
7916  geometry_info.sigma=1.0*(double) QuantumRange/2.0;
7917  if ((flags & PercentValue) != 0)
7918  geometry_info.sigma=1.0*(double) QuantumRange*geometry_info.sigma/100.0;
7919  (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7920  geometry_info.sigma,exception);
7921  XSetCursorState(display,windows,MagickFalse);
7922  if (windows->image.orphan != MagickFalse)
7923  break;
7924  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7925  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7926  break;
7927  }
7928  case NormalizeCommand:
7929  {
7930  /*
7931  Perform histogram normalization on the image.
7932  */
7933  XSetCursorState(display,windows,MagickTrue);
7934  XCheckRefreshWindows(display,windows);
7935  (void) NormalizeImage(*image,exception);
7936  XSetCursorState(display,windows,MagickFalse);
7937  if (windows->image.orphan != MagickFalse)
7938  break;
7939  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7940  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7941  break;
7942  }
7943  case EqualizeCommand:
7944  {
7945  /*
7946  Perform histogram equalization on the image.
7947  */
7948  XSetCursorState(display,windows,MagickTrue);
7949  XCheckRefreshWindows(display,windows);
7950  (void) EqualizeImage(*image,exception);
7951  XSetCursorState(display,windows,MagickFalse);
7952  if (windows->image.orphan != MagickFalse)
7953  break;
7954  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956  break;
7957  }
7958  case NegateCommand:
7959  {
7960  /*
7961  Negate colors in image.
7962  */
7963  XSetCursorState(display,windows,MagickTrue);
7964  XCheckRefreshWindows(display,windows);
7965  (void) NegateImage(*image,MagickFalse,exception);
7966  XSetCursorState(display,windows,MagickFalse);
7967  if (windows->image.orphan != MagickFalse)
7968  break;
7969  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971  break;
7972  }
7973  case GrayscaleCommand:
7974  {
7975  /*
7976  Convert image to grayscale.
7977  */
7978  XSetCursorState(display,windows,MagickTrue);
7979  XCheckRefreshWindows(display,windows);
7980  (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7981  GrayscaleType : GrayscaleAlphaType,exception);
7982  XSetCursorState(display,windows,MagickFalse);
7983  if (windows->image.orphan != MagickFalse)
7984  break;
7985  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7986  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7987  break;
7988  }
7989  case MapCommand:
7990  {
7991  Image
7992  *affinity_image;
7993 
7994  static char
7995  filename[MagickPathExtent] = "\0";
7996 
7997  /*
7998  Request image file name from user.
7999  */
8000  XFileBrowserWidget(display,windows,"Map",filename);
8001  if (*filename == '\0')
8002  break;
8003  /*
8004  Map image.
8005  */
8006  XSetCursorState(display,windows,MagickTrue);
8007  XCheckRefreshWindows(display,windows);
8008  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8009  affinity_image=ReadImage(image_info,exception);
8010  if (affinity_image != (Image *) NULL)
8011  {
8012  (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8013  affinity_image=DestroyImage(affinity_image);
8014  }
8015  CatchException(exception);
8016  XSetCursorState(display,windows,MagickFalse);
8017  if (windows->image.orphan != MagickFalse)
8018  break;
8019  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8020  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8021  break;
8022  }
8023  case QuantizeCommand:
8024  {
8025  int
8026  status;
8027 
8028  static char
8029  colors[MagickPathExtent] = "256";
8030 
8031  /*
8032  Query user for maximum number of colors.
8033  */
8034  status=XDialogWidget(display,windows,"Quantize",
8035  "Maximum number of colors:",colors);
8036  if (*colors == '\0')
8037  break;
8038  /*
8039  Color reduce the image.
8040  */
8041  XSetCursorState(display,windows,MagickTrue);
8042  XCheckRefreshWindows(display,windows);
8043  quantize_info.number_colors=StringToUnsignedLong(colors);
8044  quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8045  NoDitherMethod;
8046  (void) QuantizeImage(&quantize_info,*image,exception);
8047  XSetCursorState(display,windows,MagickFalse);
8048  if (windows->image.orphan != MagickFalse)
8049  break;
8050  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8051  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8052  break;
8053  }
8054  case DespeckleCommand:
8055  {
8056  Image
8057  *despeckle_image;
8058 
8059  /*
8060  Despeckle image.
8061  */
8062  XSetCursorState(display,windows,MagickTrue);
8063  XCheckRefreshWindows(display,windows);
8064  despeckle_image=DespeckleImage(*image,exception);
8065  if (despeckle_image != (Image *) NULL)
8066  {
8067  *image=DestroyImage(*image);
8068  *image=despeckle_image;
8069  }
8070  CatchException(exception);
8071  XSetCursorState(display,windows,MagickFalse);
8072  if (windows->image.orphan != MagickFalse)
8073  break;
8074  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8075  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8076  break;
8077  }
8078  case EmbossCommand:
8079  {
8080  Image
8081  *emboss_image;
8082 
8083  static char
8084  radius[MagickPathExtent] = "0.0x1.0";
8085 
8086  /*
8087  Query user for emboss radius.
8088  */
8089  (void) XDialogWidget(display,windows,"Emboss",
8090  "Enter the emboss radius and standard deviation:",radius);
8091  if (*radius == '\0')
8092  break;
8093  /*
8094  Reduce noise in the image.
8095  */
8096  XSetCursorState(display,windows,MagickTrue);
8097  XCheckRefreshWindows(display,windows);
8098  flags=ParseGeometry(radius,&geometry_info);
8099  if ((flags & SigmaValue) == 0)
8100  geometry_info.sigma=1.0;
8101  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8102  exception);
8103  if (emboss_image != (Image *) NULL)
8104  {
8105  *image=DestroyImage(*image);
8106  *image=emboss_image;
8107  }
8108  CatchException(exception);
8109  XSetCursorState(display,windows,MagickFalse);
8110  if (windows->image.orphan != MagickFalse)
8111  break;
8112  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8113  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8114  break;
8115  }
8116  case ReduceNoiseCommand:
8117  {
8118  Image
8119  *noise_image;
8120 
8121  static char
8122  radius[MagickPathExtent] = "0";
8123 
8124  /*
8125  Query user for noise radius.
8126  */
8127  (void) XDialogWidget(display,windows,"Reduce Noise",
8128  "Enter the noise radius:",radius);
8129  if (*radius == '\0')
8130  break;
8131  /*
8132  Reduce noise in the image.
8133  */
8134  XSetCursorState(display,windows,MagickTrue);
8135  XCheckRefreshWindows(display,windows);
8136  flags=ParseGeometry(radius,&geometry_info);
8137  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8138  geometry_info.rho,(size_t) geometry_info.rho,exception);
8139  if (noise_image != (Image *) NULL)
8140  {
8141  *image=DestroyImage(*image);
8142  *image=noise_image;
8143  }
8144  CatchException(exception);
8145  XSetCursorState(display,windows,MagickFalse);
8146  if (windows->image.orphan != MagickFalse)
8147  break;
8148  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8149  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8150  break;
8151  }
8152  case AddNoiseCommand:
8153  {
8154  char
8155  **noises;
8156 
8157  Image
8158  *noise_image;
8159 
8160  static char
8161  noise_type[MagickPathExtent] = "Gaussian";
8162 
8163  /*
8164  Add noise to the image.
8165  */
8166  noises=GetCommandOptions(MagickNoiseOptions);
8167  if (noises == (char **) NULL)
8168  break;
8169  XListBrowserWidget(display,windows,&windows->widget,
8170  (const char **) noises,"Add Noise",
8171  "Select a type of noise to add to your image:",noise_type);
8172  noises=DestroyStringList(noises);
8173  if (*noise_type == '\0')
8174  break;
8175  XSetCursorState(display,windows,MagickTrue);
8176  XCheckRefreshWindows(display,windows);
8177  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8178  MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8179  if (noise_image != (Image *) NULL)
8180  {
8181  *image=DestroyImage(*image);
8182  *image=noise_image;
8183  }
8184  CatchException(exception);
8185  XSetCursorState(display,windows,MagickFalse);
8186  if (windows->image.orphan != MagickFalse)
8187  break;
8188  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8189  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8190  break;
8191  }
8192  case SharpenCommand:
8193  {
8194  Image
8195  *sharp_image;
8196 
8197  static char
8198  radius[MagickPathExtent] = "0.0x1.0";
8199 
8200  /*
8201  Query user for sharpen radius.
8202  */
8203  (void) XDialogWidget(display,windows,"Sharpen",
8204  "Enter the sharpen radius and standard deviation:",radius);
8205  if (*radius == '\0')
8206  break;
8207  /*
8208  Sharpen image scanlines.
8209  */
8210  XSetCursorState(display,windows,MagickTrue);
8211  XCheckRefreshWindows(display,windows);
8212  flags=ParseGeometry(radius,&geometry_info);
8213  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8214  exception);
8215  if (sharp_image != (Image *) NULL)
8216  {
8217  *image=DestroyImage(*image);
8218  *image=sharp_image;
8219  }
8220  CatchException(exception);
8221  XSetCursorState(display,windows,MagickFalse);
8222  if (windows->image.orphan != MagickFalse)
8223  break;
8224  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8225  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8226  break;
8227  }
8228  case BlurCommand:
8229  {
8230  Image
8231  *blur_image;
8232 
8233  static char
8234  radius[MagickPathExtent] = "0.0x1.0";
8235 
8236  /*
8237  Query user for blur radius.
8238  */
8239  (void) XDialogWidget(display,windows,"Blur",
8240  "Enter the blur radius and standard deviation:",radius);
8241  if (*radius == '\0')
8242  break;
8243  /*
8244  Blur an image.
8245  */
8246  XSetCursorState(display,windows,MagickTrue);
8247  XCheckRefreshWindows(display,windows);
8248  flags=ParseGeometry(radius,&geometry_info);
8249  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8250  exception);
8251  if (blur_image != (Image *) NULL)
8252  {
8253  *image=DestroyImage(*image);
8254  *image=blur_image;
8255  }
8256  CatchException(exception);
8257  XSetCursorState(display,windows,MagickFalse);
8258  if (windows->image.orphan != MagickFalse)
8259  break;
8260  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8261  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8262  break;
8263  }
8264  case ThresholdCommand:
8265  {
8266  double
8267  threshold;
8268 
8269  static char
8270  factor[MagickPathExtent] = "128";
8271 
8272  /*
8273  Query user for threshold value.
8274  */
8275  (void) XDialogWidget(display,windows,"Threshold",
8276  "Enter threshold value:",factor);
8277  if (*factor == '\0')
8278  break;
8279  /*
8280  Gamma correct image.
8281  */
8282  XSetCursorState(display,windows,MagickTrue);
8283  XCheckRefreshWindows(display,windows);
8284  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8285  (void) BilevelImage(*image,threshold,exception);
8286  XSetCursorState(display,windows,MagickFalse);
8287  if (windows->image.orphan != MagickFalse)
8288  break;
8289  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291  break;
8292  }
8293  case EdgeDetectCommand:
8294  {
8295  Image
8296  *edge_image;
8297 
8298  static char
8299  radius[MagickPathExtent] = "0";
8300 
8301  /*
8302  Query user for edge factor.
8303  */
8304  (void) XDialogWidget(display,windows,"Detect Edges",
8305  "Enter the edge detect radius:",radius);
8306  if (*radius == '\0')
8307  break;
8308  /*
8309  Detect edge in image.
8310  */
8311  XSetCursorState(display,windows,MagickTrue);
8312  XCheckRefreshWindows(display,windows);
8313  flags=ParseGeometry(radius,&geometry_info);
8314  edge_image=EdgeImage(*image,geometry_info.rho,exception);
8315  if (edge_image != (Image *) NULL)
8316  {
8317  *image=DestroyImage(*image);
8318  *image=edge_image;
8319  }
8320  CatchException(exception);
8321  XSetCursorState(display,windows,MagickFalse);
8322  if (windows->image.orphan != MagickFalse)
8323  break;
8324  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8325  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8326  break;
8327  }
8328  case SpreadCommand:
8329  {
8330  Image
8331  *spread_image;
8332 
8333  static char
8334  amount[MagickPathExtent] = "2";
8335 
8336  /*
8337  Query user for spread amount.
8338  */
8339  (void) XDialogWidget(display,windows,"Spread",
8340  "Enter the displacement amount:",amount);
8341  if (*amount == '\0')
8342  break;
8343  /*
8344  Displace image pixels by a random amount.
8345  */
8346  XSetCursorState(display,windows,MagickTrue);
8347  XCheckRefreshWindows(display,windows);
8348  flags=ParseGeometry(amount,&geometry_info);
8349  spread_image=EdgeImage(*image,geometry_info.rho,exception);
8350  if (spread_image != (Image *) NULL)
8351  {
8352  *image=DestroyImage(*image);
8353  *image=spread_image;
8354  }
8355  CatchException(exception);
8356  XSetCursorState(display,windows,MagickFalse);
8357  if (windows->image.orphan != MagickFalse)
8358  break;
8359  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8360  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8361  break;
8362  }
8363  case ShadeCommand:
8364  {
8365  Image
8366  *shade_image;
8367 
8368  int
8369  status;
8370 
8371  static char
8372  geometry[MagickPathExtent] = "30x30";
8373 
8374  /*
8375  Query user for the shade geometry.
8376  */
8377  status=XDialogWidget(display,windows,"Shade",
8378  "Enter the azimuth and elevation of the light source:",geometry);
8379  if (*geometry == '\0')
8380  break;
8381  /*
8382  Shade image pixels.
8383  */
8384  XSetCursorState(display,windows,MagickTrue);
8385  XCheckRefreshWindows(display,windows);
8386  flags=ParseGeometry(geometry,&geometry_info);
8387  if ((flags & SigmaValue) == 0)
8388  geometry_info.sigma=1.0;
8389  shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8390  geometry_info.rho,geometry_info.sigma,exception);
8391  if (shade_image != (Image *) NULL)
8392  {
8393  *image=DestroyImage(*image);
8394  *image=shade_image;
8395  }
8396  CatchException(exception);
8397  XSetCursorState(display,windows,MagickFalse);
8398  if (windows->image.orphan != MagickFalse)
8399  break;
8400  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8401  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8402  break;
8403  }
8404  case RaiseCommand:
8405  {
8406  static char
8407  bevel_width[MagickPathExtent] = "10";
8408 
8409  /*
8410  Query user for bevel width.
8411  */
8412  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8413  if (*bevel_width == '\0')
8414  break;
8415  /*
8416  Raise an image.
8417  */
8418  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8419  exception);
8420  XSetCursorState(display,windows,MagickTrue);
8421  XCheckRefreshWindows(display,windows);
8422  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8423  exception);
8424  (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8425  XSetCursorState(display,windows,MagickFalse);
8426  if (windows->image.orphan != MagickFalse)
8427  break;
8428  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8429  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8430  break;
8431  }
8432  case SegmentCommand:
8433  {
8434  static char
8435  threshold[MagickPathExtent] = "1.0x1.5";
8436 
8437  /*
8438  Query user for smoothing threshold.
8439  */
8440  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8441  threshold);
8442  if (*threshold == '\0')
8443  break;
8444  /*
8445  Segment an image.
8446  */
8447  XSetCursorState(display,windows,MagickTrue);
8448  XCheckRefreshWindows(display,windows);
8449  flags=ParseGeometry(threshold,&geometry_info);
8450  if ((flags & SigmaValue) == 0)
8451  geometry_info.sigma=1.0;
8452  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8453  geometry_info.sigma,exception);
8454  XSetCursorState(display,windows,MagickFalse);
8455  if (windows->image.orphan != MagickFalse)
8456  break;
8457  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8458  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459  break;
8460  }
8461  case SepiaToneCommand:
8462  {
8463  double
8464  threshold;
8465 
8466  Image
8467  *sepia_image;
8468 
8469  static char
8470  factor[MagickPathExtent] = "80%";
8471 
8472  /*
8473  Query user for sepia-tone factor.
8474  */
8475  (void) XDialogWidget(display,windows,"Sepia Tone",
8476  "Enter the sepia tone factor (0 - 99.9%):",factor);
8477  if (*factor == '\0')
8478  break;
8479  /*
8480  Sepia tone image pixels.
8481  */
8482  XSetCursorState(display,windows,MagickTrue);
8483  XCheckRefreshWindows(display,windows);
8484  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8485  sepia_image=SepiaToneImage(*image,threshold,exception);
8486  if (sepia_image != (Image *) NULL)
8487  {
8488  *image=DestroyImage(*image);
8489  *image=sepia_image;
8490  }
8491  CatchException(exception);
8492  XSetCursorState(display,windows,MagickFalse);
8493  if (windows->image.orphan != MagickFalse)
8494  break;
8495  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8496  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8497  break;
8498  }
8499  case SolarizeCommand:
8500  {
8501  double
8502  threshold;
8503 
8504  static char
8505  factor[MagickPathExtent] = "60%";
8506 
8507  /*
8508  Query user for solarize factor.
8509  */
8510  (void) XDialogWidget(display,windows,"Solarize",
8511  "Enter the solarize factor (0 - 99.9%):",factor);
8512  if (*factor == '\0')
8513  break;
8514  /*
8515  Solarize image pixels.
8516  */
8517  XSetCursorState(display,windows,MagickTrue);
8518  XCheckRefreshWindows(display,windows);
8519  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8520  (void) SolarizeImage(*image,threshold,exception);
8521  XSetCursorState(display,windows,MagickFalse);
8522  if (windows->image.orphan != MagickFalse)
8523  break;
8524  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8525  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526  break;
8527  }
8528  case SwirlCommand:
8529  {
8530  Image
8531  *swirl_image;
8532 
8533  static char
8534  degrees[MagickPathExtent] = "60";
8535 
8536  /*
8537  Query user for swirl angle.
8538  */
8539  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8540  degrees);
8541  if (*degrees == '\0')
8542  break;
8543  /*
8544  Swirl image pixels about the center.
8545  */
8546  XSetCursorState(display,windows,MagickTrue);
8547  XCheckRefreshWindows(display,windows);
8548  flags=ParseGeometry(degrees,&geometry_info);
8549  swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8550  exception);
8551  if (swirl_image != (Image *) NULL)
8552  {
8553  *image=DestroyImage(*image);
8554  *image=swirl_image;
8555  }
8556  CatchException(exception);
8557  XSetCursorState(display,windows,MagickFalse);
8558  if (windows->image.orphan != MagickFalse)
8559  break;
8560  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8561  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8562  break;
8563  }
8564  case ImplodeCommand:
8565  {
8566  Image
8567  *implode_image;
8568 
8569  static char
8570  factor[MagickPathExtent] = "0.3";
8571 
8572  /*
8573  Query user for implode factor.
8574  */
8575  (void) XDialogWidget(display,windows,"Implode",
8576  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8577  if (*factor == '\0')
8578  break;
8579  /*
8580  Implode image pixels about the center.
8581  */
8582  XSetCursorState(display,windows,MagickTrue);
8583  XCheckRefreshWindows(display,windows);
8584  flags=ParseGeometry(factor,&geometry_info);
8585  implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8586  exception);
8587  if (implode_image != (Image *) NULL)
8588  {
8589  *image=DestroyImage(*image);
8590  *image=implode_image;
8591  }
8592  CatchException(exception);
8593  XSetCursorState(display,windows,MagickFalse);
8594  if (windows->image.orphan != MagickFalse)
8595  break;
8596  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8597  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8598  break;
8599  }
8600  case VignetteCommand:
8601  {
8602  Image
8603  *vignette_image;
8604 
8605  static char
8606  geometry[MagickPathExtent] = "0x20";
8607 
8608  /*
8609  Query user for the vignette geometry.
8610  */
8611  (void) XDialogWidget(display,windows,"Vignette",
8612  "Enter the radius, sigma, and x and y offsets:",geometry);
8613  if (*geometry == '\0')
8614  break;
8615  /*
8616  Soften the edges of the image in vignette style
8617  */
8618  XSetCursorState(display,windows,MagickTrue);
8619  XCheckRefreshWindows(display,windows);
8620  flags=ParseGeometry(geometry,&geometry_info);
8621  if ((flags & SigmaValue) == 0)
8622  geometry_info.sigma=1.0;
8623  if ((flags & XiValue) == 0)
8624  geometry_info.xi=0.1*(*image)->columns;
8625  if ((flags & PsiValue) == 0)
8626  geometry_info.psi=0.1*(*image)->rows;
8627  vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8628  ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8629  exception);
8630  if (vignette_image != (Image *) NULL)
8631  {
8632  *image=DestroyImage(*image);
8633  *image=vignette_image;
8634  }
8635  CatchException(exception);
8636  XSetCursorState(display,windows,MagickFalse);
8637  if (windows->image.orphan != MagickFalse)
8638  break;
8639  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8640  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8641  break;
8642  }
8643  case WaveCommand:
8644  {
8645  Image
8646  *wave_image;
8647 
8648  static char
8649  geometry[MagickPathExtent] = "25x150";
8650 
8651  /*
8652  Query user for the wave geometry.
8653  */
8654  (void) XDialogWidget(display,windows,"Wave",
8655  "Enter the amplitude and length of the wave:",geometry);
8656  if (*geometry == '\0')
8657  break;
8658  /*
8659  Alter an image along a sine wave.
8660  */
8661  XSetCursorState(display,windows,MagickTrue);
8662  XCheckRefreshWindows(display,windows);
8663  flags=ParseGeometry(geometry,&geometry_info);
8664  if ((flags & SigmaValue) == 0)
8665  geometry_info.sigma=1.0;
8666  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8667  (*image)->interpolate,exception);
8668  if (wave_image != (Image *) NULL)
8669  {
8670  *image=DestroyImage(*image);
8671  *image=wave_image;
8672  }
8673  CatchException(exception);
8674  XSetCursorState(display,windows,MagickFalse);
8675  if (windows->image.orphan != MagickFalse)
8676  break;
8677  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8678  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8679  break;
8680  }
8681  case OilPaintCommand:
8682  {
8683  Image
8684  *paint_image;
8685 
8686  static char
8687  radius[MagickPathExtent] = "0";
8688 
8689  /*
8690  Query user for circular neighborhood radius.
8691  */
8692  (void) XDialogWidget(display,windows,"Oil Paint",
8693  "Enter the mask radius:",radius);
8694  if (*radius == '\0')
8695  break;
8696  /*
8697  OilPaint image scanlines.
8698  */
8699  XSetCursorState(display,windows,MagickTrue);
8700  XCheckRefreshWindows(display,windows);
8701  flags=ParseGeometry(radius,&geometry_info);
8702  paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8703  exception);
8704  if (paint_image != (Image *) NULL)
8705  {
8706  *image=DestroyImage(*image);
8707  *image=paint_image;
8708  }
8709  CatchException(exception);
8710  XSetCursorState(display,windows,MagickFalse);
8711  if (windows->image.orphan != MagickFalse)
8712  break;
8713  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8714  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8715  break;
8716  }
8717  case CharcoalDrawCommand:
8718  {
8719  Image
8720  *charcoal_image;
8721 
8722  static char
8723  radius[MagickPathExtent] = "0x1";
8724 
8725  /*
8726  Query user for charcoal radius.
8727  */
8728  (void) XDialogWidget(display,windows,"Charcoal Draw",
8729  "Enter the charcoal radius and sigma:",radius);
8730  if (*radius == '\0')
8731  break;
8732  /*
8733  Charcoal the image.
8734  */
8735  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8736  exception);
8737  XSetCursorState(display,windows,MagickTrue);
8738  XCheckRefreshWindows(display,windows);
8739  flags=ParseGeometry(radius,&geometry_info);
8740  if ((flags & SigmaValue) == 0)
8741  geometry_info.sigma=geometry_info.rho;
8742  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8743  exception);
8744  if (charcoal_image != (Image *) NULL)
8745  {
8746  *image=DestroyImage(*image);
8747  *image=charcoal_image;
8748  }
8749  CatchException(exception);
8750  XSetCursorState(display,windows,MagickFalse);
8751  if (windows->image.orphan != MagickFalse)
8752  break;
8753  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8754  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8755  break;
8756  }
8757  case AnnotateCommand:
8758  {
8759  /*
8760  Annotate the image with text.
8761  */
8762  status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8763  if (status == MagickFalse)
8764  {
8765  XNoticeWidget(display,windows,"Unable to annotate X image",
8766  (*image)->filename);
8767  break;
8768  }
8769  break;
8770  }
8771  case DrawCommand:
8772  {
8773  /*
8774  Draw image.
8775  */
8776  status=XDrawEditImage(display,resource_info,windows,image,exception);
8777  if (status == MagickFalse)
8778  {
8779  XNoticeWidget(display,windows,"Unable to draw on the X image",
8780  (*image)->filename);
8781  break;
8782  }
8783  break;
8784  }
8785  case ColorCommand:
8786  {
8787  /*
8788  Color edit.
8789  */
8790  status=XColorEditImage(display,resource_info,windows,image,exception);
8791  if (status == MagickFalse)
8792  {
8793  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8794  (*image)->filename);
8795  break;
8796  }
8797  break;
8798  }
8799  case MatteCommand:
8800  {
8801  /*
8802  Matte edit.
8803  */
8804  status=XMatteEditImage(display,resource_info,windows,image,exception);
8805  if (status == MagickFalse)
8806  {
8807  XNoticeWidget(display,windows,"Unable to matte edit X image",
8808  (*image)->filename);
8809  break;
8810  }
8811  break;
8812  }
8813  case CompositeCommand:
8814  {
8815  /*
8816  Composite image.
8817  */
8818  status=XCompositeImage(display,resource_info,windows,*image,
8819  exception);
8820  if (status == MagickFalse)
8821  {
8822  XNoticeWidget(display,windows,"Unable to composite X image",
8823  (*image)->filename);
8824  break;
8825  }
8826  break;
8827  }
8828  case AddBorderCommand:
8829  {
8830  Image
8831  *border_image;
8832 
8833  static char
8834  geometry[MagickPathExtent] = "6x6";
8835 
8836  /*
8837  Query user for border color and geometry.
8838  */
8839  XColorBrowserWidget(display,windows,"Select",color);
8840  if (*color == '\0')
8841  break;
8842  (void) XDialogWidget(display,windows,"Add Border",
8843  "Enter border geometry:",geometry);
8844  if (*geometry == '\0')
8845  break;
8846  /*
8847  Add a border to the image.
8848  */
8849  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8850  exception);
8851  XSetCursorState(display,windows,MagickTrue);
8852  XCheckRefreshWindows(display,windows);
8853  (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8854  exception);
8855  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8856  exception);
8857  border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8858  exception);
8859  if (border_image != (Image *) NULL)
8860  {
8861  *image=DestroyImage(*image);
8862  *image=border_image;
8863  }
8864  CatchException(exception);
8865  XSetCursorState(display,windows,MagickFalse);
8866  if (windows->image.orphan != MagickFalse)
8867  break;
8868  windows->image.window_changes.width=(int) (*image)->columns;
8869  windows->image.window_changes.height=(int) (*image)->rows;
8870  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8871  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8872  break;
8873  }
8874  case AddFrameCommand:
8875  {
8876  FrameInfo
8877  frame_info;
8878 
8879  Image
8880  *frame_image;
8881 
8882  static char
8883  geometry[MagickPathExtent] = "6x6";
8884 
8885  /*
8886  Query user for frame color and geometry.
8887  */
8888  XColorBrowserWidget(display,windows,"Select",color);
8889  if (*color == '\0')
8890  break;
8891  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8892  geometry);
8893  if (*geometry == '\0')
8894  break;
8895  /*
8896  Surround image with an ornamental border.
8897  */
8898  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8899  exception);
8900  XSetCursorState(display,windows,MagickTrue);
8901  XCheckRefreshWindows(display,windows);
8902  (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8903  exception);
8904  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8905  exception);
8906  frame_info.width=page_geometry.width;
8907  frame_info.height=page_geometry.height;
8908  frame_info.outer_bevel=page_geometry.x;
8909  frame_info.inner_bevel=page_geometry.y;
8910  frame_info.x=(ssize_t) frame_info.width;
8911  frame_info.y=(ssize_t) frame_info.height;
8912  frame_info.width=(*image)->columns+2*frame_info.width;
8913  frame_info.height=(*image)->rows+2*frame_info.height;
8914  frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8915  if (frame_image != (Image *) NULL)
8916  {
8917  *image=DestroyImage(*image);
8918  *image=frame_image;
8919  }
8920  CatchException(exception);
8921  XSetCursorState(display,windows,MagickFalse);
8922  if (windows->image.orphan != MagickFalse)
8923  break;
8924  windows->image.window_changes.width=(int) (*image)->columns;
8925  windows->image.window_changes.height=(int) (*image)->rows;
8926  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8927  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8928  break;
8929  }
8930  case CommentCommand:
8931  {
8932  const char
8933  *value;
8934 
8935  FILE
8936  *file;
8937 
8938  int
8939  unique_file;
8940 
8941  /*
8942  Edit image comment.
8943  */
8944  unique_file=AcquireUniqueFileResource(image_info->filename);
8945  if (unique_file == -1)
8946  {
8947  XNoticeWidget(display,windows,"Unable to edit image comment",
8948  image_info->filename);
8949  break;
8950  }
8951  value=GetImageProperty(*image,"comment",exception);
8952  if (value == (char *) NULL)
8953  unique_file=close(unique_file)-1;
8954  else
8955  {
8956  const char
8957  *p;
8958 
8959  file=fdopen(unique_file,"w");
8960  if (file == (FILE *) NULL)
8961  {
8962  XNoticeWidget(display,windows,"Unable to edit image comment",
8963  image_info->filename);
8964  break;
8965  }
8966  for (p=value; *p != '\0'; p++)
8967  (void) fputc((int) *p,file);
8968  (void) fputc('\n',file);
8969  (void) fclose(file);
8970  }
8971  XSetCursorState(display,windows,MagickTrue);
8972  XCheckRefreshWindows(display,windows);
8973  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8974  exception);
8975  if (status == MagickFalse)
8976  XNoticeWidget(display,windows,"Unable to edit image comment",
8977  (char *) NULL);
8978  else
8979  {
8980  char
8981  *comment;
8982 
8983  comment=FileToString(image_info->filename,~0UL,exception);
8984  if (comment != (char *) NULL)
8985  {
8986  (void) SetImageProperty(*image,"comment",comment,exception);
8987  (*image)->taint=MagickTrue;
8988  }
8989  }
8990  (void) RelinquishUniqueFileResource(image_info->filename);
8991  XSetCursorState(display,windows,MagickFalse);
8992  break;
8993  }
8994  case LaunchCommand:
8995  {
8996  /*
8997  Launch program.
8998  */
8999  XSetCursorState(display,windows,MagickTrue);
9000  XCheckRefreshWindows(display,windows);
9001  (void) AcquireUniqueFilename(filename);
9002  (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9003  filename);
9004  status=WriteImage(image_info,*image,exception);
9005  if (status == MagickFalse)
9006  XNoticeWidget(display,windows,"Unable to launch image editor",
9007  (char *) NULL);
9008  else
9009  {
9010  nexus=ReadImage(resource_info->image_info,exception);
9011  CatchException(exception);
9012  XClientMessage(display,windows->image.id,windows->im_protocols,
9013  windows->im_next_image,CurrentTime);
9014  }
9015  (void) RelinquishUniqueFileResource(filename);
9016  XSetCursorState(display,windows,MagickFalse);
9017  break;
9018  }
9019  case RegionOfInterestCommand:
9020  {
9021  /*
9022  Apply an image processing technique to a region of interest.
9023  */
9024  (void) XROIImage(display,resource_info,windows,image,exception);
9025  break;
9026  }
9027  case InfoCommand:
9028  break;
9029  case ZoomCommand:
9030  {
9031  /*
9032  Zoom image.
9033  */
9034  if (windows->magnify.mapped != MagickFalse)
9035  (void) XRaiseWindow(display,windows->magnify.id);
9036  else
9037  {
9038  /*
9039  Make magnify image.
9040  */
9041  XSetCursorState(display,windows,MagickTrue);
9042  (void) XMapRaised(display,windows->magnify.id);
9043  XSetCursorState(display,windows,MagickFalse);
9044  }
9045  break;
9046  }
9047  case ShowPreviewCommand:
9048  {
9049  char
9050  **previews;
9051 
9052  Image
9053  *preview_image;
9054 
9055  PreviewType
9056  preview;
9057 
9058  static char
9059  preview_type[MagickPathExtent] = "Gamma";
9060 
9061  /*
9062  Select preview type from menu.
9063  */
9064  previews=GetCommandOptions(MagickPreviewOptions);
9065  if (previews == (char **) NULL)
9066  break;
9067  XListBrowserWidget(display,windows,&windows->widget,
9068  (const char **) previews,"Preview",
9069  "Select an enhancement, effect, or F/X:",preview_type);
9070  previews=DestroyStringList(previews);
9071  if (*preview_type == '\0')
9072  break;
9073  /*
9074  Show image preview.
9075  */
9076  XSetCursorState(display,windows,MagickTrue);
9077  XCheckRefreshWindows(display,windows);
9078  preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9079  MagickFalse,preview_type);
9080  (void) FormatImageProperty(*image,"group","%.20g",(double)
9081  windows->image.id);
9082  (void) DeleteImageProperty(*image,"label");
9083  (void) SetImageProperty(*image,"label","Preview",exception);
9084  preview_image=PreviewImage(*image,preview,exception);
9085  if (preview_image == (Image *) NULL)
9086  break;
9087  (void) AcquireUniqueFilename(filename);
9088  (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9089  "show:%s",filename);
9090  status=WriteImage(image_info,preview_image,exception);
9091  (void) RelinquishUniqueFileResource(filename);
9092  preview_image=DestroyImage(preview_image);
9093  if (status == MagickFalse)
9094  XNoticeWidget(display,windows,"Unable to show image preview",
9095  (*image)->filename);
9096  XDelay(display,1500);
9097  XSetCursorState(display,windows,MagickFalse);
9098  break;
9099  }
9100  case ShowHistogramCommand:
9101  {
9102  Image
9103  *histogram_image;
9104 
9105  /*
9106  Show image histogram.
9107  */
9108  XSetCursorState(display,windows,MagickTrue);
9109  XCheckRefreshWindows(display,windows);
9110  (void) DeleteImageProperty(*image,"label");
9111  (void) FormatImageProperty(*image,"group","%.20g",(double)
9112  windows->image.id);
9113  (void) SetImageProperty(*image,"label","Histogram",exception);
9114  (void) AcquireUniqueFilename(filename);
9115  (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9116  "histogram:%s",filename);
9117  status=WriteImage(image_info,*image,exception);
9118  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9119  histogram_image=ReadImage(image_info,exception);
9120  (void) RelinquishUniqueFileResource(filename);
9121  if (histogram_image == (Image *) NULL)
9122  break;
9123  (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9124  "show:%s",filename);
9125  status=WriteImage(image_info,histogram_image,exception);
9126  histogram_image=DestroyImage(histogram_image);
9127  if (status == MagickFalse)
9128  XNoticeWidget(display,windows,"Unable to show histogram",
9129  (*image)->filename);
9130  XDelay(display,1500);
9131  XSetCursorState(display,windows,MagickFalse);
9132  break;
9133  }
9134  case ShowMatteCommand:
9135  {
9136  Image
9137  *matte_image;
9138 
9139  if ((*image)->alpha_trait == UndefinedPixelTrait)
9140  {
9141  XNoticeWidget(display,windows,
9142  "Image does not have any matte information",(*image)->filename);
9143  break;
9144  }
9145  /*
9146  Show image matte.
9147  */
9148  XSetCursorState(display,windows,MagickTrue);
9149  XCheckRefreshWindows(display,windows);
9150  (void) FormatImageProperty(*image,"group","%.20g",(double)
9151  windows->image.id);
9152  (void) DeleteImageProperty(*image,"label");
9153  (void) SetImageProperty(*image,"label","Matte",exception);
9154  (void) AcquireUniqueFilename(filename);
9155  (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9156  filename);
9157  status=WriteImage(image_info,*image,exception);
9158  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9159  matte_image=ReadImage(image_info,exception);
9160  (void) RelinquishUniqueFileResource(filename);
9161  if (matte_image == (Image *) NULL)
9162  break;
9163  (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9164  "show:%s",filename);
9165  status=WriteImage(image_info,matte_image,exception);
9166  matte_image=DestroyImage(matte_image);
9167  if (status == MagickFalse)
9168  XNoticeWidget(display,windows,"Unable to show matte",
9169  (*image)->filename);
9170  XDelay(display,1500);
9171  XSetCursorState(display,windows,MagickFalse);
9172  break;
9173  }
9174  case BackgroundCommand:
9175  {
9176  /*
9177  Background image.
9178  */
9179  status=XBackgroundImage(display,resource_info,windows,image,exception);
9180  if (status == MagickFalse)
9181  break;
9182  nexus=CloneImage(*image,0,0,MagickTrue,exception);
9183  if (nexus != (Image *) NULL)
9184  XClientMessage(display,windows->image.id,windows->im_protocols,
9185  windows->im_next_image,CurrentTime);
9186  break;
9187  }
9188  case SlideShowCommand:
9189  {
9190  static char
9191  delay[MagickPathExtent] = "5";
9192 
9193  /*
9194  Display next image after pausing.
9195  */
9196  (void) XDialogWidget(display,windows,"Slide Show",
9197  "Pause how many 1/100ths of a second between images:",delay);
9198  if (*delay == '\0')
9199  break;
9200  resource_info->delay=StringToUnsignedLong(delay);
9201  XClientMessage(display,windows->image.id,windows->im_protocols,
9202  windows->im_next_image,CurrentTime);
9203  break;
9204  }
9205  case PreferencesCommand:
9206  {
9207  /*
9208  Set user preferences.
9209  */
9210  status=XPreferencesWidget(display,resource_info,windows);
9211  if (status == MagickFalse)
9212  break;
9213  nexus=CloneImage(*image,0,0,MagickTrue,exception);
9214  if (nexus != (Image *) NULL)
9215  XClientMessage(display,windows->image.id,windows->im_protocols,
9216  windows->im_next_image,CurrentTime);
9217  break;
9218  }
9219  case HelpCommand:
9220  {
9221  /*
9222  User requested help.
9223  */
9224  XTextViewHelp(display,resource_info,windows,MagickFalse,
9225  "Help Viewer - Display",DisplayHelp);
9226  break;
9227  }
9228  case BrowseDocumentationCommand:
9229  {
9230  Atom
9231  mozilla_atom;
9232 
9233  Window
9234  mozilla_window,
9235  root_window;
9236 
9237  /*
9238  Browse the ImageMagick documentation.
9239  */
9240  root_window=XRootWindow(display,XDefaultScreen(display));
9241  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9242  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9243  if (mozilla_window != (Window) NULL)
9244  {
9245  char
9246  command[MagickPathExtent];
9247 
9248  /*
9249  Display documentation using Netscape remote control.
9250  */
9251  (void) FormatLocaleString(command,MagickPathExtent,
9252  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9253  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9254  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9255  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9256  XSetCursorState(display,windows,MagickFalse);
9257  break;
9258  }
9259  XSetCursorState(display,windows,MagickTrue);
9260  XCheckRefreshWindows(display,windows);
9261  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9262  exception);
9263  if (status == MagickFalse)
9264  XNoticeWidget(display,windows,"Unable to browse documentation",
9265  (char *) NULL);
9266  XDelay(display,1500);
9267  XSetCursorState(display,windows,MagickFalse);
9268  break;
9269  }
9270  case VersionCommand:
9271  {
9272  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9273  GetMagickCopyright());
9274  break;
9275  }
9276  case SaveToUndoBufferCommand:
9277  break;
9278  default:
9279  {
9280  (void) XBell(display,0);
9281  break;
9282  }
9283  }
9284  image_info=DestroyImageInfo(image_info);
9285  return(nexus);
9286 }
9287 
9288 /*
9289 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9290 % %
9291 % %
9292 % %
9293 + X M a g n i f y I m a g e %
9294 % %
9295 % %
9296 % %
9297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9298 %
9299 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9300 % The magnified portion is displayed in a separate window.
9301 %
9302 % The format of the XMagnifyImage method is:
9303 %
9304 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9305 % ExceptionInfo *exception)
9306 %
9307 % A description of each parameter follows:
9308 %
9309 % o display: Specifies a connection to an X server; returned from
9310 % XOpenDisplay.
9311 %
9312 % o windows: Specifies a pointer to a XWindows structure.
9313 %
9314 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9315 % the entire image is refreshed.
9316 %
9317 % o exception: return any errors or warnings in this structure.
9318 %
9319 */
9320 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9321  ExceptionInfo *exception)
9322 {
9323  char
9324  text[MagickPathExtent];
9325 
9326  int
9327  x,
9328  y;
9329 
9330  size_t
9331  state;
9332 
9333  /*
9334  Update magnified image until the mouse button is released.
9335  */
9336  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9337  state=DefaultState;
9338  x=event->xbutton.x;
9339  y=event->xbutton.y;
9340  windows->magnify.x=(int) windows->image.x+x;
9341  windows->magnify.y=(int) windows->image.y+y;
9342  do
9343  {
9344  /*
9345  Map and unmap Info widget as text cursor crosses its boundaries.
9346  */
9347  if (windows->info.mapped != MagickFalse)
9348  {
9349  if ((x < (windows->info.x+(int) windows->info.width)) &&
9350  (y < (windows->info.y+(int) windows->info.height)))
9351  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9352  }
9353  else
9354  if ((x > (windows->info.x+(int) windows->info.width)) ||
9355  (y > (windows->info.y+(int) windows->info.height)))
9356  (void) XMapWindow(display,windows->info.id);
9357  if (windows->info.mapped != MagickFalse)
9358  {
9359  /*
9360  Display pointer position.
9361  */
9362  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9363  windows->magnify.x,windows->magnify.y);
9364  XInfoWidget(display,windows,text);
9365  }
9366  /*
9367  Wait for next event.
9368  */
9369  XScreenEvent(display,windows,event,exception);
9370  switch (event->type)
9371  {
9372  case ButtonPress:
9373  break;
9374  case ButtonRelease:
9375  {
9376  /*
9377  User has finished magnifying image.
9378  */
9379  x=event->xbutton.x;
9380  y=event->xbutton.y;
9381  state|=ExitState;
9382  break;
9383  }
9384  case Expose:
9385  break;
9386  case MotionNotify:
9387  {
9388  x=event->xmotion.x;
9389  y=event->xmotion.y;
9390  break;
9391  }
9392  default:
9393  break;
9394  }
9395  /*
9396  Check boundary conditions.
9397  */
9398  if (x < 0)
9399  x=0;
9400  else
9401  if (x >= (int) windows->image.width)
9402  x=(int) windows->image.width-1;
9403  if (y < 0)
9404  y=0;
9405  else
9406  if (y >= (int) windows->image.height)
9407  y=(int) windows->image.height-1;
9408  } while ((state & ExitState) == 0);
9409  /*
9410  Display magnified image.
9411  */
9412  XSetCursorState(display,windows,MagickFalse);
9413 }
9414 
9415 /*
9416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9417 % %
9418 % %
9419 % %
9420 + X M a g n i f y W i n d o w C o m m a n d %
9421 % %
9422 % %
9423 % %
9424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9425 %
9426 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9427 % pixel as specified by the key symbol.
9428 %
9429 % The format of the XMagnifyWindowCommand method is:
9430 %
9431 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9432 % const MagickStatusType state,const KeySym key_symbol,
9433 % ExceptionInfo *exception)
9434 %
9435 % A description of each parameter follows:
9436 %
9437 % o display: Specifies a connection to an X server; returned from
9438 % XOpenDisplay.
9439 %
9440 % o windows: Specifies a pointer to a XWindows structure.
9441 %
9442 % o state: key mask.
9443 %
9444 % o key_symbol: Specifies a KeySym which indicates which side of the image
9445 % to trim.
9446 %
9447 % o exception: return any errors or warnings in this structure.
9448 %
9449 */
9450 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9451  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9452 {
9453  unsigned int
9454  quantum;
9455 
9456  /*
9457  User specified a magnify factor or position.
9458  */
9459  quantum=1;
9460  if ((state & Mod1Mask) != 0)
9461  quantum=10;
9462  switch ((int) key_symbol)
9463  {
9464  case QuitCommand:
9465  {
9466  (void) XWithdrawWindow(display,windows->magnify.id,
9467  windows->magnify.screen);
9468  break;
9469  }
9470  case XK_Home:
9471  case XK_KP_Home:
9472  {
9473  windows->magnify.x=(int) windows->image.width/2;
9474  windows->magnify.y=(int) windows->image.height/2;
9475  break;
9476  }
9477  case XK_Left:
9478  case XK_KP_Left:
9479  {
9480  if (windows->magnify.x > 0)
9481  windows->magnify.x-=(int) quantum;
9482  break;
9483  }
9484  case XK_Up:
9485  case XK_KP_Up:
9486  {
9487  if (windows->magnify.y > 0)
9488  windows->magnify.y-=(int) quantum;
9489  break;
9490  }
9491  case XK_Right:
9492  case XK_KP_Right:
9493  {
9494  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9495  windows->magnify.x+=(int) quantum;
9496  break;
9497  }
9498  case XK_Down:
9499  case XK_KP_Down:
9500  {
9501  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9502  windows->magnify.y+=(int) quantum;
9503  break;
9504  }
9505  case XK_0:
9506  case XK_1:
9507  case XK_2:
9508  case XK_3:
9509  case XK_4:
9510  case XK_5:
9511  case XK_6:
9512  case XK_7:
9513  case XK_8:
9514  case XK_9:
9515  {
9516  windows->magnify.data=(key_symbol-XK_0);
9517  break;
9518  }
9519  case XK_KP_0:
9520  case XK_KP_1:
9521  case XK_KP_2:
9522  case XK_KP_3:
9523  case XK_KP_4:
9524  case XK_KP_5:
9525  case XK_KP_6:
9526  case XK_KP_7:
9527  case XK_KP_8:
9528  case XK_KP_9:
9529  {
9530  windows->magnify.data=(key_symbol-XK_KP_0);
9531  break;
9532  }
9533  default:
9534  break;
9535  }
9536  XMakeMagnifyImage(display,windows,exception);
9537 }
9538 
9539 /*
9540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9541 % %
9542 % %
9543 % %
9544 + X M a k e P a n I m a g e %
9545 % %
9546 % %
9547 % %
9548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9549 %
9550 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9551 % icon window.
9552 %
9553 % The format of the XMakePanImage method is:
9554 %
9555 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9556 % XWindows *windows,Image *image,ExceptionInfo *exception)
9557 %
9558 % A description of each parameter follows:
9559 %
9560 % o display: Specifies a connection to an X server; returned from
9561 % XOpenDisplay.
9562 %
9563 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9564 %
9565 % o windows: Specifies a pointer to a XWindows structure.
9566 %
9567 % o image: the image.
9568 %
9569 % o exception: return any errors or warnings in this structure.
9570 %
9571 */
9572 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9573  XWindows *windows,Image *image,ExceptionInfo *exception)
9574 {
9575  MagickStatusType
9576  status;
9577 
9578  /*
9579  Create and display image for panning icon.
9580  */
9581  XSetCursorState(display,windows,MagickTrue);
9582  XCheckRefreshWindows(display,windows);
9583  windows->pan.x=(int) windows->image.x;
9584  windows->pan.y=(int) windows->image.y;
9585  status=XMakeImage(display,resource_info,&windows->pan,image,
9586  windows->pan.width,windows->pan.height,exception);
9587  if (status == MagickFalse)
9588  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
9589  image->filename);
9590  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9591  windows->pan.pixmap);
9592  (void) XClearWindow(display,windows->pan.id);
9593  XDrawPanRectangle(display,windows);
9594  XSetCursorState(display,windows,MagickFalse);
9595 }
9596 
9597 /*
9598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9599 % %
9600 % %
9601 % %
9602 + X M a t t a E d i t I m a g e %
9603 % %
9604 % %
9605 % %
9606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9607 %
9608 % XMatteEditImage() allows the user to interactively change the Matte channel
9609 % of an image. If the image is PseudoClass it is promoted to DirectClass
9610 % before the matte information is stored.
9611 %
9612 % The format of the XMatteEditImage method is:
9613 %
9614 % MagickBooleanType XMatteEditImage(Display *display,
9615 % XResourceInfo *resource_info,XWindows *windows,Image **image,
9616 % ExceptionInfo *exception)
9617 %
9618 % A description of each parameter follows:
9619 %
9620 % o display: Specifies a connection to an X server; returned from
9621 % XOpenDisplay.
9622 %
9623 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9624 %
9625 % o windows: Specifies a pointer to a XWindows structure.
9626 %
9627 % o image: the image; returned from ReadImage.
9628 %
9629 % o exception: return any errors or warnings in this structure.
9630 %
9631 */
9632 static MagickBooleanType XMatteEditImage(Display *display,
9633  XResourceInfo *resource_info,XWindows *windows,Image **image,
9634  ExceptionInfo *exception)
9635 {
9636  const char
9637  *const MatteEditMenu[] =
9638  {
9639  "Method",
9640  "Border Color",
9641  "Fuzz",
9642  "Matte Value",
9643  "Undo",
9644  "Help",
9645  "Dismiss",
9646  (char *) NULL
9647  };
9648 
9649  static char
9650  matte[MagickPathExtent] = "0";
9651 
9652  static const ModeType
9653  MatteEditCommands[] =
9654  {
9655  MatteEditMethod,
9656  MatteEditBorderCommand,
9657  MatteEditFuzzCommand,
9658  MatteEditValueCommand,
9659  MatteEditUndoCommand,
9660  MatteEditHelpCommand,
9661  MatteEditDismissCommand
9662  };
9663 
9664  static PaintMethod
9665  method = PointMethod;
9666 
9667  static XColor
9668  border_color = { 0, 0, 0, 0, 0, 0 };
9669 
9670  char
9671  command[MagickPathExtent],
9672  text[MagickPathExtent];
9673 
9674  Cursor
9675  cursor;
9676 
9677  int
9678  entry,
9679  id,
9680  x,
9681  x_offset,
9682  y,
9683  y_offset;
9684 
9685  int
9686  i;
9687 
9688  Quantum
9689  *q;
9690 
9691  unsigned int
9692  height,
9693  width;
9694 
9695  size_t
9696  state;
9697 
9698  XEvent
9699  event;
9700 
9701  /*
9702  Map Command widget.
9703  */
9704  (void) CloneString(&windows->command.name,"Matte Edit");
9705  windows->command.data=4;
9706  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9707  (void) XMapRaised(display,windows->command.id);
9708  XClientMessage(display,windows->image.id,windows->im_protocols,
9709  windows->im_update_widget,CurrentTime);
9710  /*
9711  Make cursor.
9712  */
9713  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9714  resource_info->background_color,resource_info->foreground_color);
9715  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9716  /*
9717  Track pointer until button 1 is pressed.
9718  */
9719  XQueryPosition(display,windows->image.id,&x,&y);
9720  (void) XSelectInput(display,windows->image.id,
9721  windows->image.attributes.event_mask | PointerMotionMask);
9722  state=DefaultState;
9723  do
9724  {
9725  if (windows->info.mapped != MagickFalse)
9726  {
9727  /*
9728  Display pointer position.
9729  */
9730  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9731  x+windows->image.x,y+windows->image.y);
9732  XInfoWidget(display,windows,text);
9733  }
9734  /*
9735  Wait for next event.
9736  */
9737  XScreenEvent(display,windows,&event,exception);
9738  if (event.xany.window == windows->command.id)
9739  {
9740  /*
9741  Select a command from the Command widget.
9742  */
9743  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9744  if (id < 0)
9745  {
9746  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9747  continue;
9748  }
9749  switch (MatteEditCommands[id])
9750  {
9751  case MatteEditMethod:
9752  {
9753  char
9754  **methods;
9755 
9756  /*
9757  Select a method from the pop-up menu.
9758  */
9759  methods=GetCommandOptions(MagickMethodOptions);
9760  if (methods == (char **) NULL)
9761  break;
9762  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9763  (const char **) methods,command);
9764  if (entry >= 0)
9765  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9766  MagickFalse,methods[entry]);
9767  methods=DestroyStringList(methods);
9768  break;
9769  }
9770  case MatteEditBorderCommand:
9771  {
9772  const char
9773  *ColorMenu[MaxNumberPens];
9774 
9775  int
9776  pen_number;
9777 
9778  /*
9779  Initialize menu selections.
9780  */
9781  for (i=0; i < (int) (MaxNumberPens-2); i++)
9782  ColorMenu[i]=resource_info->pen_colors[i];
9783  ColorMenu[MaxNumberPens-2]="Browser...";
9784  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9785  /*
9786  Select a pen color from the pop-up menu.
9787  */
9788  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9789  (const char **) ColorMenu,command);
9790  if (pen_number < 0)
9791  break;
9792  if (pen_number == (MaxNumberPens-2))
9793  {
9794  static char
9795  color_name[MagickPathExtent] = "gray";
9796 
9797  /*
9798  Select a pen color from a dialog.
9799  */
9800  resource_info->pen_colors[pen_number]=color_name;
9801  XColorBrowserWidget(display,windows,"Select",color_name);
9802  if (*color_name == '\0')
9803  break;
9804  }
9805  /*
9806  Set border color.
9807  */
9808  (void) XParseColor(display,windows->map_info->colormap,
9809  resource_info->pen_colors[pen_number],&border_color);
9810  break;
9811  }
9812  case MatteEditFuzzCommand:
9813  {
9814  const char
9815  *const FuzzMenu[] =
9816  {
9817  "0%",
9818  "2%",
9819  "5%",
9820  "10%",
9821  "15%",
9822  "Dialog...",
9823  (char *) NULL,
9824  };
9825 
9826  static char
9827  fuzz[MagickPathExtent];
9828 
9829  /*
9830  Select a command from the pop-up menu.
9831  */
9832  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9833  command);
9834  if (entry < 0)
9835  break;
9836  if (entry != 5)
9837  {
9838  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9839  QuantumRange+1.0);
9840  break;
9841  }
9842  (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9843  (void) XDialogWidget(display,windows,"Ok",
9844  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9845  if (*fuzz == '\0')
9846  break;
9847  (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9848  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9849  1.0);
9850  break;
9851  }
9852  case MatteEditValueCommand:
9853  {
9854  const char
9855  *const MatteMenu[] =
9856  {
9857  "Opaque",
9858  "Transparent",
9859  "Dialog...",
9860  (char *) NULL,
9861  };
9862 
9863  static char
9864  message[MagickPathExtent];
9865 
9866  /*
9867  Select a command from the pop-up menu.
9868  */
9869  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9870  command);
9871  if (entry < 0)
9872  break;
9873  if (entry != 2)
9874  {
9875  (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9876  (double) OpaqueAlpha);
9877  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9878  (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9879  (double) TransparentAlpha);
9880  break;
9881  }
9882  (void) FormatLocaleString(message,MagickPathExtent,
9883  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9884  (void) XDialogWidget(display,windows,"Matte",message,matte);
9885  if (*matte == '\0')
9886  break;
9887  break;
9888  }
9889  case MatteEditUndoCommand:
9890  {
9891  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9892  image,exception);
9893  break;
9894  }
9895  case MatteEditHelpCommand:
9896  {
9897  XTextViewHelp(display,resource_info,windows,MagickFalse,
9898  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9899  break;
9900  }
9901  case MatteEditDismissCommand:
9902  {
9903  /*
9904  Prematurely exit.
9905  */
9906  state|=EscapeState;
9907  state|=ExitState;
9908  break;
9909  }
9910  default:
9911  break;
9912  }
9913  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9914  continue;
9915  }
9916  switch (event.type)
9917  {
9918  case ButtonPress:
9919  {
9920  if (event.xbutton.button != Button1)
9921  break;
9922  if ((event.xbutton.window != windows->image.id) &&
9923  (event.xbutton.window != windows->magnify.id))
9924  break;
9925  /*
9926  Update matte data.
9927  */
9928  x=event.xbutton.x;
9929  y=event.xbutton.y;
9930  (void) XMagickCommand(display,resource_info,windows,
9931  SaveToUndoBufferCommand,image,exception);
9932  state|=UpdateConfigurationState;
9933  break;
9934  }
9935  case ButtonRelease:
9936  {
9937  if (event.xbutton.button != Button1)
9938  break;
9939  if ((event.xbutton.window != windows->image.id) &&
9940  (event.xbutton.window != windows->magnify.id))
9941  break;
9942  /*
9943  Update colormap information.
9944  */
9945  x=event.xbutton.x;
9946  y=event.xbutton.y;
9947  XConfigureImageColormap(display,resource_info,windows,*image,exception);
9948  (void) XConfigureImage(display,resource_info,windows,*image,exception);
9949  XInfoWidget(display,windows,text);
9950  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9951  state&=(unsigned int) (~UpdateConfigurationState);
9952  break;
9953  }
9954  case Expose:
9955  break;
9956  case KeyPress:
9957  {
9958  char
9959  command[MagickPathExtent];
9960 
9961  KeySym
9962  key_symbol;
9963 
9964  if (event.xkey.window == windows->magnify.id)
9965  {
9966  Window
9967  window;
9968 
9969  window=windows->magnify.id;
9970  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9971  }
9972  if (event.xkey.window != windows->image.id)
9973  break;
9974  /*
9975  Respond to a user key press.
9976  */
9977  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9978  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9979  switch ((int) key_symbol)
9980  {
9981  case XK_Escape:
9982  case XK_F20:
9983  {
9984  /*
9985  Prematurely exit.
9986  */
9987  state|=ExitState;
9988  break;
9989  }
9990  case XK_F1:
9991  case XK_Help:
9992  {
9993  XTextViewHelp(display,resource_info,windows,MagickFalse,
9994  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9995  break;
9996  }
9997  default:
9998  {
9999  (void) XBell(display,0);
10000  break;
10001  }
10002  }
10003  break;
10004  }
10005  case MotionNotify:
10006  {
10007  /*
10008  Map and unmap Info widget as cursor crosses its boundaries.
10009  */
10010  x=event.xmotion.x;
10011  y=event.xmotion.y;
10012  if (windows->info.mapped != MagickFalse)
10013  {
10014  if ((x < (windows->info.x+(int) windows->info.width)) &&
10015  (y < (windows->info.y+(int) windows->info.height)))
10016  (void) XWithdrawWindow(display,windows->info.id,
10017  windows->info.screen);
10018  }
10019  else
10020  if ((x > (windows->info.x+(int) windows->info.width)) ||
10021  (y > (windows->info.y+(int) windows->info.height)))
10022  (void) XMapWindow(display,windows->info.id);
10023  break;
10024  }
10025  default:
10026  break;
10027  }
10028  if (event.xany.window == windows->magnify.id)
10029  {
10030  x=windows->magnify.x-windows->image.x;
10031  y=windows->magnify.y-windows->image.y;
10032  }
10033  x_offset=x;
10034  y_offset=y;
10035  if ((state & UpdateConfigurationState) != 0)
10036  {
10037  CacheView
10038  *image_view;
10039 
10040  int
10041  x,
10042  y;
10043 
10044  /*
10045  Matte edit is relative to image configuration.
10046  */
10047  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10048  MagickTrue);
10049  XPutPixel(windows->image.ximage,x_offset,y_offset,
10050  windows->pixel_info->background_color.pixel);
10051  width=(unsigned int) (*image)->columns;
10052  height=(unsigned int) (*image)->rows;
10053  x=0;
10054  y=0;
10055  if (windows->image.crop_geometry != (char *) NULL)
10056  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10057  &height);
10058  x_offset=((int) width*(windows->image.x+x_offset)/
10059  windows->image.ximage->width+x);
10060  y_offset=((int) height*(windows->image.y+y_offset)/
10061  windows->image.ximage->height+y);
10062  if ((x_offset < 0) || (y_offset < 0))
10063  continue;
10064  if ((x_offset >= (int) (*image)->columns) ||
10065  (y_offset >= (int) (*image)->rows))
10066  continue;
10067  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10068  return(MagickFalse);
10069  if ((*image)->alpha_trait == UndefinedPixelTrait)
10070  (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10071  image_view=AcquireAuthenticCacheView(*image,exception);
10072  switch (method)
10073  {
10074  case PointMethod:
10075  default:
10076  {
10077  /*
10078  Update matte information using point algorithm.
10079  */
10080  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10081  (ssize_t) y_offset,1,1,exception);
10082  if (q == (Quantum *) NULL)
10083  break;
10084  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10085  (void) SyncCacheViewAuthenticPixels(image_view,exception);
10086  break;
10087  }
10088  case ReplaceMethod:
10089  {
10090  PixelInfo
10091  pixel,
10092  target;
10093 
10094  /*
10095  Update matte information using replace algorithm.
10096  */
10097  (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10098  x_offset,(ssize_t) y_offset,&target,exception);
10099  for (y=0; y < (int) (*image)->rows; y++)
10100  {
10101  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10102  (*image)->columns,1,exception);
10103  if (q == (Quantum *) NULL)
10104  break;
10105  for (x=0; x < (int) (*image)->columns; x++)
10106  {
10107  GetPixelInfoPixel(*image,q,&pixel);
10108  if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10109  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10110  q+=(ptrdiff_t) GetPixelChannels(*image);
10111  }
10112  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10113  break;
10114  }
10115  break;
10116  }
10117  case FloodfillMethod:
10118  case FillToBorderMethod:
10119  {
10120  ChannelType
10121  channel_mask;
10122 
10123  DrawInfo
10124  *draw_info;
10125 
10126  PixelInfo
10127  target;
10128 
10129  /*
10130  Update matte information using floodfill algorithm.
10131  */
10132  (void) GetOneVirtualPixelInfo(*image,
10133  GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10134  y_offset,&target,exception);
10135  if (method == FillToBorderMethod)
10136  {
10137  target.red=(double) ScaleShortToQuantum(
10138  border_color.red);
10139  target.green=(double) ScaleShortToQuantum(
10140  border_color.green);
10141  target.blue=(double) ScaleShortToQuantum(
10142  border_color.blue);
10143  }
10144  draw_info=CloneDrawInfo(resource_info->image_info,
10145  (DrawInfo *) NULL);
10146  draw_info->fill.alpha=(double) ClampToQuantum(
10147  StringToDouble(matte,(char **) NULL));
10148  channel_mask=SetImageChannelMask(*image,AlphaChannel);
10149  (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10150  x_offset,(ssize_t) y_offset,
10151  method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10152  (void) SetPixelChannelMask(*image,channel_mask);
10153  draw_info=DestroyDrawInfo(draw_info);
10154  break;
10155  }
10156  case ResetMethod:
10157  {
10158  /*
10159  Update matte information using reset algorithm.
10160  */
10161  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10162  return(MagickFalse);
10163  for (y=0; y < (int) (*image)->rows; y++)
10164  {
10165  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10166  (*image)->columns,1,exception);
10167  if (q == (Quantum *) NULL)
10168  break;
10169  for (x=0; x < (int) (*image)->columns; x++)
10170  {
10171  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10172  q+=(ptrdiff_t) GetPixelChannels(*image);
10173  }
10174  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10175  break;
10176  }
10177  if (StringToLong(matte) == (long) OpaqueAlpha)
10178  (*image)->alpha_trait=UndefinedPixelTrait;
10179  break;
10180  }
10181  }
10182  image_view=DestroyCacheView(image_view);
10183  state&=(unsigned int) (~UpdateConfigurationState);
10184  }
10185  } while ((state & ExitState) == 0);
10186  (void) XSelectInput(display,windows->image.id,
10187  windows->image.attributes.event_mask);
10188  XSetCursorState(display,windows,MagickFalse);
10189  (void) XFreeCursor(display,cursor);
10190  return(MagickTrue);
10191 }
10192 
10193 /*
10194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10195 % %
10196 % %
10197 % %
10198 + X O p e n I m a g e %
10199 % %
10200 % %
10201 % %
10202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10203 %
10204 % XOpenImage() loads an image from a file.
10205 %
10206 % The format of the XOpenImage method is:
10207 %
10208 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10209 % XWindows *windows,const unsigned int command)
10210 %
10211 % A description of each parameter follows:
10212 %
10213 % o display: Specifies a connection to an X server; returned from
10214 % XOpenDisplay.
10215 %
10216 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10217 %
10218 % o windows: Specifies a pointer to a XWindows structure.
10219 %
10220 % o command: A value other than zero indicates that the file is selected
10221 % from the command line argument list.
10222 %
10223 */
10224 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10225  XWindows *windows,const MagickBooleanType command)
10226 {
10227  const MagickInfo
10228  *magick_info;
10229 
10231  *exception;
10232 
10233  Image
10234  *nexus;
10235 
10236  ImageInfo
10237  *image_info;
10238 
10239  static char
10240  filename[MagickPathExtent] = "\0";
10241 
10242  /*
10243  Request file name from user.
10244  */
10245  if (command == MagickFalse)
10246  XFileBrowserWidget(display,windows,"Open",filename);
10247  else
10248  {
10249  char
10250  **filelist,
10251  **files;
10252 
10253  int
10254  count,
10255  status;
10256 
10257  int
10258  i,
10259  j;
10260 
10261  /*
10262  Select next image from the command line.
10263  */
10264  status=XGetCommand(display,windows->image.id,&files,&count);
10265  if (status == 0)
10266  {
10267  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10268  return((Image *) NULL);
10269  }
10270  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10271  if (filelist == (char **) NULL)
10272  {
10273  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10274  "...");
10275  (void) XFreeStringList(files);
10276  return((Image *) NULL);
10277  }
10278  j=0;
10279  for (i=1; i < count; i++)
10280  if (*files[i] != '-')
10281  filelist[j++]=files[i];
10282  filelist[j]=(char *) NULL;
10283  XListBrowserWidget(display,windows,&windows->widget,
10284  (const char **) filelist,"Load","Select Image to Load:",filename);
10285  filelist=(char **) RelinquishMagickMemory(filelist);
10286  (void) XFreeStringList(files);
10287  }
10288  if (*filename == '\0')
10289  return((Image *) NULL);
10290  image_info=CloneImageInfo(resource_info->image_info);
10291  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10292  (void *) NULL);
10293  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10294  exception=AcquireExceptionInfo();
10295  (void) SetImageInfo(image_info,0,exception);
10296  if (LocaleCompare(image_info->magick,"X") == 0)
10297  {
10298  char
10299  seconds[MagickPathExtent];
10300 
10301  /*
10302  User may want to delay the X server screen grab.
10303  */
10304  (void) CopyMagickString(seconds,"0",MagickPathExtent);
10305  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10306  seconds);
10307  if (*seconds == '\0')
10308  return((Image *) NULL);
10309  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10310  }
10311  magick_info=GetMagickInfo(image_info->magick,exception);
10312  if ((magick_info != (const MagickInfo *) NULL) &&
10313  GetMagickRawSupport(magick_info) == MagickTrue)
10314  {
10315  char
10316  geometry[MagickPathExtent];
10317 
10318  /*
10319  Request image size from the user.
10320  */
10321  (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10322  if (image_info->size != (char *) NULL)
10323  (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10324  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10325  geometry);
10326  (void) CloneString(&image_info->size,geometry);
10327  }
10328  /*
10329  Load the image.
10330  */
10331  XSetCursorState(display,windows,MagickTrue);
10332  XCheckRefreshWindows(display,windows);
10333  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10334  nexus=ReadImage(image_info,exception);
10335  CatchException(exception);
10336  XSetCursorState(display,windows,MagickFalse);
10337  if (nexus != (Image *) NULL)
10338  XClientMessage(display,windows->image.id,windows->im_protocols,
10339  windows->im_next_image,CurrentTime);
10340  else
10341  {
10342  char
10343  *text,
10344  **textlist;
10345 
10346  /*
10347  Unknown image format.
10348  */
10349  text=FileToString(filename,~0UL,exception);
10350  if (text == (char *) NULL)
10351  return((Image *) NULL);
10352  textlist=StringToList(text);
10353  if (textlist != (char **) NULL)
10354  {
10355  char
10356  title[MagickPathExtent];
10357 
10358  int
10359  i;
10360 
10361  (void) FormatLocaleString(title,MagickPathExtent,
10362  "Unknown format: %s",filename);
10363  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10364  (const char **) textlist);
10365  for (i=0; textlist[i] != (char *) NULL; i++)
10366  textlist[i]=DestroyString(textlist[i]);
10367  textlist=(char **) RelinquishMagickMemory(textlist);
10368  }
10369  text=DestroyString(text);
10370  }
10371  exception=DestroyExceptionInfo(exception);
10372  image_info=DestroyImageInfo(image_info);
10373  return(nexus);
10374 }
10375 
10376 /*
10377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10378 % %
10379 % %
10380 % %
10381 + X P a n I m a g e %
10382 % %
10383 % %
10384 % %
10385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10386 %
10387 % XPanImage() pans the image until the mouse button is released.
10388 %
10389 % The format of the XPanImage method is:
10390 %
10391 % void XPanImage(Display *display,XWindows *windows,XEvent *event,
10392 % ExceptionInfo *exception)
10393 %
10394 % A description of each parameter follows:
10395 %
10396 % o display: Specifies a connection to an X server; returned from
10397 % XOpenDisplay.
10398 %
10399 % o windows: Specifies a pointer to a XWindows structure.
10400 %
10401 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10402 % the entire image is refreshed.
10403 %
10404 % o exception: return any errors or warnings in this structure.
10405 %
10406 */
10407 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10408  ExceptionInfo *exception)
10409 {
10410  char
10411  text[MagickPathExtent];
10412 
10413  Cursor
10414  cursor;
10415 
10416  double
10417  x_factor,
10418  y_factor;
10419 
10421  pan_info;
10422 
10423  size_t
10424  state;
10425 
10426  /*
10427  Define cursor.
10428  */
10429  if ((windows->image.ximage->width > (int) windows->image.width) &&
10430  (windows->image.ximage->height > (int) windows->image.height))
10431  cursor=XCreateFontCursor(display,XC_fleur);
10432  else
10433  if (windows->image.ximage->width > (int) windows->image.width)
10434  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10435  else
10436  if (windows->image.ximage->height > (int) windows->image.height)
10437  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10438  else
10439  cursor=XCreateFontCursor(display,XC_arrow);
10440  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10441  /*
10442  Pan image as pointer moves until the mouse button is released.
10443  */
10444  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10445  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10446  pan_info.width=windows->pan.width*windows->image.width/
10447  (unsigned int) windows->image.ximage->width;
10448  pan_info.height=windows->pan.height*windows->image.height/
10449  (unsigned int) windows->image.ximage->height;
10450  pan_info.x=0;
10451  pan_info.y=0;
10452  state=UpdateConfigurationState;
10453  do
10454  {
10455  switch (event->type)
10456  {
10457  case ButtonPress:
10458  {
10459  /*
10460  User choose an initial pan location.
10461  */
10462  pan_info.x=(ssize_t) event->xbutton.x;
10463  pan_info.y=(ssize_t) event->xbutton.y;
10464  state|=UpdateConfigurationState;
10465  break;
10466  }
10467  case ButtonRelease:
10468  {
10469  /*
10470  User has finished panning the image.
10471  */
10472  pan_info.x=(ssize_t) event->xbutton.x;
10473  pan_info.y=(ssize_t) event->xbutton.y;
10474  state|=UpdateConfigurationState | ExitState;
10475  break;
10476  }
10477  case MotionNotify:
10478  {
10479  pan_info.x=(ssize_t) event->xmotion.x;
10480  pan_info.y=(ssize_t) event->xmotion.y;
10481  state|=UpdateConfigurationState;
10482  }
10483  default:
10484  break;
10485  }
10486  if ((state & UpdateConfigurationState) != 0)
10487  {
10488  /*
10489  Check boundary conditions.
10490  */
10491  if (pan_info.x < (ssize_t) (pan_info.width/2))
10492  pan_info.x=0;
10493  else
10494  pan_info.x=(x_factor*(pan_info.x-((int) pan_info.width/2)));
10495  if (pan_info.x < 0)
10496  pan_info.x=0;
10497  else
10498  if ((int) (pan_info.x+windows->image.width) >
10499  windows->image.ximage->width)
10500  pan_info.x=windows->image.ximage->width-(int) 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=(y_factor*(pan_info.y-((int) 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=windows->image.ximage->height-(int)
10511  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&=(unsigned int) (~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 */
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 
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 (resource_info->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 (resource_info->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 (resource_info->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 < (windows->info.x+(int) windows->info.width)) &&
10857  (y < (windows->info.y+(int) windows->info.height)))
10858  (void) XWithdrawWindow(display,windows->info.id,
10859  windows->info.screen);
10860  }
10861  else
10862  if ((x > (windows->info.x+(int) windows->info.width)) ||
10863  (y > (windows->info.y+(int) 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 (resource_info->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 */
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 */
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 DisplayCommand
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  ThresholdCommand,
11230  EdgeDetectCommand,
11231  SpreadCommand,
11232  ShadeCommand,
11233  RaiseCommand,
11234  SegmentCommand
11235  },
11236  FXCommands[] =
11237  {
11238  SolarizeCommand,
11239  SepiaToneCommand,
11240  SwirlCommand,
11241  ImplodeCommand,
11242  VignetteCommand,
11243  WaveCommand,
11244  OilPaintCommand,
11245  CharcoalDrawCommand
11246  },
11247  MiscellanyCommands[] =
11248  {
11249  InfoCommand,
11250  ZoomCommand,
11251  ShowPreviewCommand,
11252  ShowHistogramCommand,
11253  ShowMatteCommand
11254  },
11255  ROICommands[] =
11256  {
11257  ROIHelpCommand,
11258  ROIDismissCommand
11259  };
11260 
11261  static const DisplayCommand
11262  *Commands[ApplyMenus] =
11263  {
11264  FileCommands,
11265  EditCommands,
11266  TransformCommands,
11267  EnhanceCommands,
11268  EffectsCommands,
11269  FXCommands,
11270  MiscellanyCommands
11271  };
11272 
11273  char
11274  command[MagickPathExtent],
11275  text[MagickPathExtent];
11276 
11277  DisplayCommand
11278  display_command;
11279 
11280  Cursor
11281  cursor;
11282 
11283  Image
11284  *roi_image;
11285 
11286  int
11287  entry,
11288  id,
11289  x,
11290  y;
11291 
11292  double
11293  scale_factor;
11294 
11295  MagickProgressMonitor
11296  progress_monitor;
11297 
11299  crop_info,
11300  highlight_info,
11301  roi_info;
11302 
11303  unsigned int
11304  height,
11305  width;
11306 
11307  size_t
11308  state;
11309 
11310  XEvent
11311  event;
11312 
11313  /*
11314  Map Command widget.
11315  */
11316  (void) CloneString(&windows->command.name,"ROI");
11317  windows->command.data=0;
11318  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11319  (void) XMapRaised(display,windows->command.id);
11320  XClientMessage(display,windows->image.id,windows->im_protocols,
11321  windows->im_update_widget,CurrentTime);
11322  /*
11323  Track pointer until button 1 is pressed.
11324  */
11325  XQueryPosition(display,windows->image.id,&x,&y);
11326  (void) XSelectInput(display,windows->image.id,
11327  windows->image.attributes.event_mask | PointerMotionMask);
11328  roi_info.x=(ssize_t) windows->image.x+x;
11329  roi_info.y=(ssize_t) windows->image.y+y;
11330  roi_info.width=0;
11331  roi_info.height=0;
11332  cursor=XCreateFontCursor(display,XC_fleur);
11333  state=DefaultState;
11334  do
11335  {
11336  if (windows->info.mapped != MagickFalse)
11337  {
11338  /*
11339  Display pointer position.
11340  */
11341  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11342  (long) roi_info.x,(long) roi_info.y);
11343  XInfoWidget(display,windows,text);
11344  }
11345  /*
11346  Wait for next event.
11347  */
11348  XScreenEvent(display,windows,&event,exception);
11349  if (event.xany.window == windows->command.id)
11350  {
11351  /*
11352  Select a command from the Command widget.
11353  */
11354  id=XCommandWidget(display,windows,ROIMenu,&event);
11355  if (id < 0)
11356  continue;
11357  switch (ROICommands[id])
11358  {
11359  case ROIHelpCommand:
11360  {
11361  XTextViewHelp(display,resource_info,windows,MagickFalse,
11362  "Help Viewer - Region of Interest",ImageROIHelp);
11363  break;
11364  }
11365  case ROIDismissCommand:
11366  {
11367  /*
11368  Prematurely exit.
11369  */
11370  state|=EscapeState;
11371  state|=ExitState;
11372  break;
11373  }
11374  default:
11375  break;
11376  }
11377  continue;
11378  }
11379  switch (event.type)
11380  {
11381  case ButtonPress:
11382  {
11383  if (event.xbutton.button != Button1)
11384  break;
11385  if (event.xbutton.window != windows->image.id)
11386  break;
11387  /*
11388  Note first corner of region of interest rectangle-- exit loop.
11389  */
11390  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11391  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11392  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11393  state|=ExitState;
11394  break;
11395  }
11396  case ButtonRelease:
11397  break;
11398  case Expose:
11399  break;
11400  case KeyPress:
11401  {
11402  KeySym
11403  key_symbol;
11404 
11405  if (event.xkey.window != windows->image.id)
11406  break;
11407  /*
11408  Respond to a user key press.
11409  */
11410  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11411  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11412  switch ((int) key_symbol)
11413  {
11414  case XK_Escape:
11415  case XK_F20:
11416  {
11417  /*
11418  Prematurely exit.
11419  */
11420  state|=EscapeState;
11421  state|=ExitState;
11422  break;
11423  }
11424  case XK_F1:
11425  case XK_Help:
11426  {
11427  XTextViewHelp(display,resource_info,windows,MagickFalse,
11428  "Help Viewer - Region of Interest",ImageROIHelp);
11429  break;
11430  }
11431  default:
11432  {
11433  (void) XBell(display,0);
11434  break;
11435  }
11436  }
11437  break;
11438  }
11439  case MotionNotify:
11440  {
11441  /*
11442  Map and unmap Info widget as text cursor crosses its boundaries.
11443  */
11444  x=event.xmotion.x;
11445  y=event.xmotion.y;
11446  if (windows->info.mapped != MagickFalse)
11447  {
11448  if ((x < (windows->info.x+(int) windows->info.width)) &&
11449  (y < (windows->info.y+(int) windows->info.height)))
11450  (void) XWithdrawWindow(display,windows->info.id,
11451  windows->info.screen);
11452  }
11453  else
11454  if ((x > (windows->info.x+(int) windows->info.width)) ||
11455  (y > (windows->info.y+(int) windows->info.height)))
11456  (void) XMapWindow(display,windows->info.id);
11457  roi_info.x=(ssize_t) windows->image.x+x;
11458  roi_info.y=(ssize_t) windows->image.y+y;
11459  break;
11460  }
11461  default:
11462  break;
11463  }
11464  } while ((state & ExitState) == 0);
11465  (void) XSelectInput(display,windows->image.id,
11466  windows->image.attributes.event_mask);
11467  if ((state & EscapeState) != 0)
11468  {
11469  /*
11470  User want to exit without region of interest.
11471  */
11472  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11473  (void) XFreeCursor(display,cursor);
11474  return(MagickTrue);
11475  }
11476  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11477  do
11478  {
11479  /*
11480  Size rectangle as pointer moves until the mouse button is released.
11481  */
11482  x=(int) roi_info.x;
11483  y=(int) roi_info.y;
11484  roi_info.width=0;
11485  roi_info.height=0;
11486  state=DefaultState;
11487  do
11488  {
11489  highlight_info=roi_info;
11490  highlight_info.x=roi_info.x-windows->image.x;
11491  highlight_info.y=roi_info.y-windows->image.y;
11492  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11493  {
11494  /*
11495  Display info and draw region of interest rectangle.
11496  */
11497  if (windows->info.mapped == MagickFalse)
11498  (void) XMapWindow(display,windows->info.id);
11499  (void) FormatLocaleString(text,MagickPathExtent,
11500  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11501  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11502  XInfoWidget(display,windows,text);
11503  XHighlightRectangle(display,windows->image.id,
11504  windows->image.highlight_context,&highlight_info);
11505  }
11506  else
11507  if (windows->info.mapped != MagickFalse)
11508  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11509  /*
11510  Wait for next event.
11511  */
11512  XScreenEvent(display,windows,&event,exception);
11513  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11514  XHighlightRectangle(display,windows->image.id,
11515  windows->image.highlight_context,&highlight_info);
11516  switch (event.type)
11517  {
11518  case ButtonPress:
11519  {
11520  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11521  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11522  break;
11523  }
11524  case ButtonRelease:
11525  {
11526  /*
11527  User has committed to region of interest rectangle.
11528  */
11529  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11530  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11531  XSetCursorState(display,windows,MagickFalse);
11532  state|=ExitState;
11533  if (LocaleCompare(windows->command.name,"Apply") == 0)
11534  break;
11535  (void) CloneString(&windows->command.name,"Apply");
11536  windows->command.data=ApplyMenus;
11537  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11538  break;
11539  }
11540  case Expose:
11541  break;
11542  case MotionNotify:
11543  {
11544  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11545  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11546  }
11547  default:
11548  break;
11549  }
11550  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11551  ((state & ExitState) != 0))
11552  {
11553  /*
11554  Check boundary conditions.
11555  */
11556  if (roi_info.x < 0)
11557  roi_info.x=0;
11558  else
11559  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11560  roi_info.x=(ssize_t) windows->image.ximage->width;
11561  if ((int) roi_info.x < x)
11562  roi_info.width=(unsigned int) (x-roi_info.x);
11563  else
11564  {
11565  roi_info.width=(unsigned int) (roi_info.x-x);
11566  roi_info.x=(ssize_t) x;
11567  }
11568  if (roi_info.y < 0)
11569  roi_info.y=0;
11570  else
11571  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11572  roi_info.y=(ssize_t) windows->image.ximage->height;
11573  if ((int) roi_info.y < y)
11574  roi_info.height=(unsigned int) (y-roi_info.y);
11575  else
11576  {
11577  roi_info.height=(unsigned int) (roi_info.y-y);
11578  roi_info.y=(ssize_t) y;
11579  }
11580  }
11581  } while ((state & ExitState) == 0);
11582  /*
11583  Wait for user to grab a corner of the rectangle or press return.
11584  */
11585  state=DefaultState;
11586  display_command=NullCommand;
11587  crop_info.x=0;
11588  crop_info.y=0;
11589  (void) XMapWindow(display,windows->info.id);
11590  do
11591  {
11592  if (windows->info.mapped != MagickFalse)
11593  {
11594  /*
11595  Display pointer position.
11596  */
11597  (void) FormatLocaleString(text,MagickPathExtent,
11598  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11599  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11600  XInfoWidget(display,windows,text);
11601  }
11602  highlight_info=roi_info;
11603  highlight_info.x=roi_info.x-windows->image.x;
11604  highlight_info.y=roi_info.y-windows->image.y;
11605  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11606  {
11607  state|=EscapeState;
11608  state|=ExitState;
11609  break;
11610  }
11611  if ((state & UpdateRegionState) != 0)
11612  {
11613  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11614  switch (display_command)
11615  {
11616  case UndoCommand:
11617  case RedoCommand:
11618  {
11619  (void) XMagickCommand(display,resource_info,windows,
11620  display_command,image,exception);
11621  break;
11622  }
11623  default:
11624  {
11625  /*
11626  Region of interest is relative to image configuration.
11627  */
11628  progress_monitor=SetImageProgressMonitor(*image,
11629  (MagickProgressMonitor) NULL,(*image)->client_data);
11630  crop_info=roi_info;
11631  width=(unsigned int) (*image)->columns;
11632  height=(unsigned int) (*image)->rows;
11633  x=0;
11634  y=0;
11635  if (windows->image.crop_geometry != (char *) NULL)
11636  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11637  &width,&height);
11638  scale_factor=(double) width/windows->image.ximage->width;
11639  crop_info.x+=x;
11640  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11641  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11642  scale_factor=(double)
11643  height/windows->image.ximage->height;
11644  crop_info.y+=y;
11645  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11646  crop_info.height=(unsigned int)
11647  (scale_factor*crop_info.height+0.5);
11648  roi_image=CropImage(*image,&crop_info,exception);
11649  (void) SetImageProgressMonitor(*image,progress_monitor,
11650  (*image)->client_data);
11651  if (roi_image == (Image *) NULL)
11652  continue;
11653  /*
11654  Apply image processing technique to the region of interest.
11655  */
11656  windows->image.orphan=MagickTrue;
11657  (void) XMagickCommand(display,resource_info,windows,
11658  display_command,&roi_image,exception);
11659  progress_monitor=SetImageProgressMonitor(*image,
11660  (MagickProgressMonitor) NULL,(*image)->client_data);
11661  (void) XMagickCommand(display,resource_info,windows,
11662  SaveToUndoBufferCommand,image,exception);
11663  windows->image.orphan=MagickFalse;
11664  (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11665  MagickTrue,crop_info.x,crop_info.y,exception);
11666  roi_image=DestroyImage(roi_image);
11667  (void) SetImageProgressMonitor(*image,progress_monitor,
11668  (*image)->client_data);
11669  break;
11670  }
11671  }
11672  if (display_command != InfoCommand)
11673  {
11674  XConfigureImageColormap(display,resource_info,windows,*image,
11675  exception);
11676  (void) XConfigureImage(display,resource_info,windows,*image,
11677  exception);
11678  }
11679  XCheckRefreshWindows(display,windows);
11680  XInfoWidget(display,windows,text);
11681  (void) XSetFunction(display,windows->image.highlight_context,
11682  GXinvert);
11683  state&=(unsigned int) (~UpdateRegionState);
11684  }
11685  XHighlightRectangle(display,windows->image.id,
11686  windows->image.highlight_context,&highlight_info);
11687  XScreenEvent(display,windows,&event,exception);
11688  if (event.xany.window == windows->command.id)
11689  {
11690  /*
11691  Select a command from the Command widget.
11692  */
11693  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11694  display_command=NullCommand;
11695  id=XCommandWidget(display,windows,ApplyMenu,&event);
11696  if (id >= 0)
11697  {
11698  (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11699  display_command=ApplyCommands[id];
11700  if (id < ApplyMenus)
11701  {
11702  /*
11703  Select a command from a pop-up menu.
11704  */
11705  entry=XMenuWidget(display,windows,ApplyMenu[id],
11706  (const char **) Menus[id],command);
11707  if (entry >= 0)
11708  {
11709  (void) CopyMagickString(command,Menus[id][entry],
11710  MagickPathExtent);
11711  display_command=Commands[id][entry];
11712  }
11713  }
11714  }
11715  (void) XSetFunction(display,windows->image.highlight_context,
11716  GXinvert);
11717  XHighlightRectangle(display,windows->image.id,
11718  windows->image.highlight_context,&highlight_info);
11719  if (display_command == HelpCommand)
11720  {
11721  (void) XSetFunction(display,windows->image.highlight_context,
11722  GXcopy);
11723  XTextViewHelp(display,resource_info,windows,MagickFalse,
11724  "Help Viewer - Region of Interest",ImageROIHelp);
11725  (void) XSetFunction(display,windows->image.highlight_context,
11726  GXinvert);
11727  continue;
11728  }
11729  if (display_command == QuitCommand)
11730  {
11731  /*
11732  exit.
11733  */
11734  state|=EscapeState;
11735  state|=ExitState;
11736  continue;
11737  }
11738  if (display_command != NullCommand)
11739  state|=UpdateRegionState;
11740  continue;
11741  }
11742  XHighlightRectangle(display,windows->image.id,
11743  windows->image.highlight_context,&highlight_info);
11744  switch (event.type)
11745  {
11746  case ButtonPress:
11747  {
11748  x=windows->image.x;
11749  y=windows->image.y;
11750  if (event.xbutton.button != Button1)
11751  break;
11752  if (event.xbutton.window != windows->image.id)
11753  break;
11754  x=windows->image.x+event.xbutton.x;
11755  y=windows->image.y+event.xbutton.y;
11756  if ((x < (int) (roi_info.x+RoiDelta)) &&
11757  (x > (int) (roi_info.x-RoiDelta)) &&
11758  (y < (int) (roi_info.y+RoiDelta)) &&
11759  (y > (int) (roi_info.y-RoiDelta)))
11760  {
11761  roi_info.x=roi_info.x+(int) roi_info.width;
11762  roi_info.y=roi_info.y+(int) roi_info.height;
11763  state|=UpdateConfigurationState;
11764  break;
11765  }
11766  if ((x < (int) (roi_info.x+RoiDelta)) &&
11767  (x > (int) (roi_info.x-RoiDelta)) &&
11768  (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11769  (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11770  {
11771  roi_info.x=roi_info.x+(int) roi_info.width;
11772  state|=UpdateConfigurationState;
11773  break;
11774  }
11775  if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11776  (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11777  (y < (int) (roi_info.y+RoiDelta)) &&
11778  (y > (int) (roi_info.y-RoiDelta)))
11779  {
11780  roi_info.y=roi_info.y+(int) roi_info.height;
11781  state|=UpdateConfigurationState;
11782  break;
11783  }
11784  if ((x < (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11785  (x > (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11786  (y < (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11787  (y > (roi_info.y+(int) roi_info.height-RoiDelta)))
11788  {
11789  state|=UpdateConfigurationState;
11790  break;
11791  }
11792  magick_fallthrough;
11793  }
11794  case ButtonRelease:
11795  {
11796  if (event.xbutton.window == windows->pan.id)
11797  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11798  (highlight_info.y != crop_info.y-windows->image.y))
11799  XHighlightRectangle(display,windows->image.id,
11800  windows->image.highlight_context,&highlight_info);
11801  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11802  event.xbutton.time);
11803  break;
11804  }
11805  case Expose:
11806  {
11807  if (event.xexpose.window == windows->image.id)
11808  if (event.xexpose.count == 0)
11809  {
11810  event.xexpose.x=(int) highlight_info.x;
11811  event.xexpose.y=(int) highlight_info.y;
11812  event.xexpose.width=(int) highlight_info.width;
11813  event.xexpose.height=(int) highlight_info.height;
11814  XRefreshWindow(display,&windows->image,&event);
11815  }
11816  if (event.xexpose.window == windows->info.id)
11817  if (event.xexpose.count == 0)
11818  XInfoWidget(display,windows,text);
11819  break;
11820  }
11821  case KeyPress:
11822  {
11823  KeySym
11824  key_symbol;
11825 
11826  if (event.xkey.window != windows->image.id)
11827  break;
11828  /*
11829  Respond to a user key press.
11830  */
11831  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11832  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11833  switch ((int) key_symbol)
11834  {
11835  case XK_Shift_L:
11836  case XK_Shift_R:
11837  break;
11838  case XK_Escape:
11839  case XK_F20:
11840  {
11841  state|=EscapeState;
11842  magick_fallthrough;
11843  }
11844  case XK_Return:
11845  {
11846  state|=ExitState;
11847  break;
11848  }
11849  case XK_Home:
11850  case XK_KP_Home:
11851  {
11852  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11853  roi_info.y=(ssize_t) (windows->image.height/2L-
11854  roi_info.height/2L);
11855  break;
11856  }
11857  case XK_Left:
11858  case XK_KP_Left:
11859  {
11860  roi_info.x--;
11861  break;
11862  }
11863  case XK_Up:
11864  case XK_KP_Up:
11865  case XK_Next:
11866  {
11867  roi_info.y--;
11868  break;
11869  }
11870  case XK_Right:
11871  case XK_KP_Right:
11872  {
11873  roi_info.x++;
11874  break;
11875  }
11876  case XK_Prior:
11877  case XK_Down:
11878  case XK_KP_Down:
11879  {
11880  roi_info.y++;
11881  break;
11882  }
11883  case XK_F1:
11884  case XK_Help:
11885  {
11886  (void) XSetFunction(display,windows->image.highlight_context,
11887  GXcopy);
11888  XTextViewHelp(display,resource_info,windows,MagickFalse,
11889  "Help Viewer - Region of Interest",ImageROIHelp);
11890  (void) XSetFunction(display,windows->image.highlight_context,
11891  GXinvert);
11892  break;
11893  }
11894  default:
11895  {
11896  display_command=XImageWindowCommand(display,resource_info,windows,
11897  event.xkey.state,key_symbol,image,exception);
11898  if (display_command != NullCommand)
11899  state|=UpdateRegionState;
11900  break;
11901  }
11902  }
11903  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11904  event.xkey.time);
11905  break;
11906  }
11907  case KeyRelease:
11908  break;
11909  case MotionNotify:
11910  {
11911  if (event.xbutton.window != windows->image.id)
11912  break;
11913  /*
11914  Map and unmap Info widget as text cursor crosses its boundaries.
11915  */
11916  x=event.xmotion.x;
11917  y=event.xmotion.y;
11918  if (windows->info.mapped != MagickFalse)
11919  {
11920  if ((x < (windows->info.x+(int) windows->info.width)) &&
11921  (y < (windows->info.y+(int) windows->info.height)))
11922  (void) XWithdrawWindow(display,windows->info.id,
11923  windows->info.screen);
11924  }
11925  else
11926  if ((x > (windows->info.x+(int) windows->info.width)) ||
11927  (y > (windows->info.y+(int) windows->info.height)))
11928  (void) XMapWindow(display,windows->info.id);
11929  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11930  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11931  break;
11932  }
11933  case SelectionRequest:
11934  {
11935  XSelectionEvent
11936  notify;
11937 
11938  XSelectionRequestEvent
11939  *request;
11940 
11941  /*
11942  Set primary selection.
11943  */
11944  (void) FormatLocaleString(text,MagickPathExtent,
11945  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11946  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11947  request=(&(event.xselectionrequest));
11948  (void) XChangeProperty(request->display,request->requestor,
11949  request->property,request->target,8,PropModeReplace,
11950  (unsigned char *) text,(int) strlen(text));
11951  notify.type=SelectionNotify;
11952  notify.display=request->display;
11953  notify.requestor=request->requestor;
11954  notify.selection=request->selection;
11955  notify.target=request->target;
11956  notify.time=request->time;
11957  if (request->property == None)
11958  notify.property=request->target;
11959  else
11960  notify.property=request->property;
11961  (void) XSendEvent(request->display,request->requestor,False,0,
11962  (XEvent *) &notify);
11963  }
11964  default:
11965  break;
11966  }
11967  if ((state & UpdateConfigurationState) != 0)
11968  {
11969  (void) XPutBackEvent(display,&event);
11970  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11971  break;
11972  }
11973  } while ((state & ExitState) == 0);
11974  } while ((state & ExitState) == 0);
11975  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11976  XSetCursorState(display,windows,MagickFalse);
11977  if ((state & EscapeState) != 0)
11978  return(MagickTrue);
11979  return(MagickTrue);
11980 }
11981 
11982 /*
11983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11984 % %
11985 % %
11986 % %
11987 + X R o t a t e I m a g e %
11988 % %
11989 % %
11990 % %
11991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11992 %
11993 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11994 % rotation angle is computed from the slope of a line drawn by the user.
11995 %
11996 % The format of the XRotateImage method is:
11997 %
11998 % MagickBooleanType XRotateImage(Display *display,
11999 % XResourceInfo *resource_info,XWindows *windows,double degrees,
12000 % Image **image,ExceptionInfo *exception)
12001 %
12002 % A description of each parameter follows:
12003 %
12004 % o display: Specifies a connection to an X server; returned from
12005 % XOpenDisplay.
12006 %
12007 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12008 %
12009 % o windows: Specifies a pointer to a XWindows structure.
12010 %
12011 % o degrees: Specifies the number of degrees to rotate the image.
12012 %
12013 % o image: the image.
12014 %
12015 % o exception: return any errors or warnings in this structure.
12016 %
12017 */
12018 static MagickBooleanType XRotateImage(Display *display,
12019  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12020  ExceptionInfo *exception)
12021 {
12022  const char
12023  *const RotateMenu[] =
12024  {
12025  "Pixel Color",
12026  "Direction",
12027  "Help",
12028  "Dismiss",
12029  (char *) NULL
12030  };
12031 
12032  static ModeType
12033  direction = HorizontalRotateCommand;
12034 
12035  static const ModeType
12036  DirectionCommands[] =
12037  {
12038  HorizontalRotateCommand,
12039  VerticalRotateCommand
12040  },
12041  RotateCommands[] =
12042  {
12043  RotateColorCommand,
12044  RotateDirectionCommand,
12045  RotateHelpCommand,
12046  RotateDismissCommand
12047  };
12048 
12049  static unsigned int
12050  pen_id = 0;
12051 
12052  char
12053  command[MagickPathExtent],
12054  text[MagickPathExtent];
12055 
12056  Image
12057  *rotate_image;
12058 
12059  int
12060  id,
12061  x,
12062  y;
12063 
12064  double
12065  normalized_degrees;
12066 
12067  int
12068  i;
12069 
12070  unsigned int
12071  height,
12072  rotations,
12073  width;
12074 
12075  if (degrees == 0.0)
12076  {
12077  unsigned int
12078  distance;
12079 
12080  size_t
12081  state;
12082 
12083  XEvent
12084  event;
12085 
12086  XSegment
12087  rotate_info;
12088 
12089  /*
12090  Map Command widget.
12091  */
12092  (void) CloneString(&windows->command.name,"Rotate");
12093  windows->command.data=2;
12094  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12095  (void) XMapRaised(display,windows->command.id);
12096  XClientMessage(display,windows->image.id,windows->im_protocols,
12097  windows->im_update_widget,CurrentTime);
12098  /*
12099  Wait for first button press.
12100  */
12101  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12102  XQueryPosition(display,windows->image.id,&x,&y);
12103  rotate_info.x1=x;
12104  rotate_info.y1=y;
12105  rotate_info.x2=x;
12106  rotate_info.y2=y;
12107  state=DefaultState;
12108  do
12109  {
12110  XHighlightLine(display,windows->image.id,
12111  windows->image.highlight_context,&rotate_info);
12112  /*
12113  Wait for next event.
12114  */
12115  XScreenEvent(display,windows,&event,exception);
12116  XHighlightLine(display,windows->image.id,
12117  windows->image.highlight_context,&rotate_info);
12118  if (event.xany.window == windows->command.id)
12119  {
12120  /*
12121  Select a command from the Command widget.
12122  */
12123  id=XCommandWidget(display,windows,RotateMenu,&event);
12124  if (id < 0)
12125  continue;
12126  (void) XSetFunction(display,windows->image.highlight_context,
12127  GXcopy);
12128  switch (RotateCommands[id])
12129  {
12130  case RotateColorCommand:
12131  {
12132  const char
12133  *ColorMenu[MaxNumberPens];
12134 
12135  int
12136  pen_number;
12137 
12138  XColor
12139  color;
12140 
12141  /*
12142  Initialize menu selections.
12143  */
12144  for (i=0; i < (int) (MaxNumberPens-2); i++)
12145  ColorMenu[i]=resource_info->pen_colors[i];
12146  ColorMenu[MaxNumberPens-2]="Browser...";
12147  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12148  /*
12149  Select a pen color from the pop-up menu.
12150  */
12151  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12152  (const char **) ColorMenu,command);
12153  if (pen_number < 0)
12154  break;
12155  if (pen_number == (MaxNumberPens-2))
12156  {
12157  static char
12158  color_name[MagickPathExtent] = "gray";
12159 
12160  /*
12161  Select a pen color from a dialog.
12162  */
12163  resource_info->pen_colors[pen_number]=color_name;
12164  XColorBrowserWidget(display,windows,"Select",color_name);
12165  if (*color_name == '\0')
12166  break;
12167  }
12168  /*
12169  Set pen color.
12170  */
12171  (void) XParseColor(display,windows->map_info->colormap,
12172  resource_info->pen_colors[pen_number],&color);
12173  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12174  (unsigned int) MaxColors,&color);
12175  windows->pixel_info->pen_colors[pen_number]=color;
12176  pen_id=(unsigned int) pen_number;
12177  break;
12178  }
12179  case RotateDirectionCommand:
12180  {
12181  const char
12182  *Directions[] =
12183  {
12184  "horizontal",
12185  "vertical",
12186  (char *) NULL,
12187  };
12188 
12189  /*
12190  Select a command from the pop-up menu.
12191  */
12192  id=XMenuWidget(display,windows,RotateMenu[id],
12193  Directions,command);
12194  if (id >= 0)
12195  direction=DirectionCommands[id];
12196  break;
12197  }
12198  case RotateHelpCommand:
12199  {
12200  XTextViewHelp(display,resource_info,windows,MagickFalse,
12201  "Help Viewer - Image Rotation",ImageRotateHelp);
12202  break;
12203  }
12204  case RotateDismissCommand:
12205  {
12206  /*
12207  Prematurely exit.
12208  */
12209  state|=EscapeState;
12210  state|=ExitState;
12211  break;
12212  }
12213  default:
12214  break;
12215  }
12216  (void) XSetFunction(display,windows->image.highlight_context,
12217  GXinvert);
12218  continue;
12219  }
12220  switch (event.type)
12221  {
12222  case ButtonPress:
12223  {
12224  if (event.xbutton.button != Button1)
12225  break;
12226  if (event.xbutton.window != windows->image.id)
12227  break;
12228  /*
12229  exit loop.
12230  */
12231  (void) XSetFunction(display,windows->image.highlight_context,
12232  GXcopy);
12233  rotate_info.x1=event.xbutton.x;
12234  rotate_info.y1=event.xbutton.y;
12235  state|=ExitState;
12236  break;
12237  }
12238  case ButtonRelease:
12239  break;
12240  case Expose:
12241  break;
12242  case KeyPress:
12243  {
12244  char
12245  command[MagickPathExtent];
12246 
12247  KeySym
12248  key_symbol;
12249 
12250  if (event.xkey.window != windows->image.id)
12251  break;
12252  /*
12253  Respond to a user key press.
12254  */
12255  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12256  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12257  switch ((int) key_symbol)
12258  {
12259  case XK_Escape:
12260  case XK_F20:
12261  {
12262  /*
12263  Prematurely exit.
12264  */
12265  state|=EscapeState;
12266  state|=ExitState;
12267  break;
12268  }
12269  case XK_F1:
12270  case XK_Help:
12271  {
12272  (void) XSetFunction(display,windows->image.highlight_context,
12273  GXcopy);
12274  XTextViewHelp(display,resource_info,windows,MagickFalse,
12275  "Help Viewer - Image Rotation",ImageRotateHelp);
12276  (void) XSetFunction(display,windows->image.highlight_context,
12277  GXinvert);
12278  break;
12279  }
12280  default:
12281  {
12282  (void) XBell(display,0);
12283  break;
12284  }
12285  }
12286  break;
12287  }
12288  case MotionNotify:
12289  {
12290  rotate_info.x1=event.xmotion.x;
12291  rotate_info.y1=event.xmotion.y;
12292  }
12293  }
12294  rotate_info.x2=rotate_info.x1;
12295  rotate_info.y2=rotate_info.y1;
12296  if (direction == HorizontalRotateCommand)
12297  rotate_info.x2+=32;
12298  else
12299  rotate_info.y2-=32;
12300  } while ((state & ExitState) == 0);
12301  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12302  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12303  if ((state & EscapeState) != 0)
12304  return(MagickTrue);
12305  /*
12306  Draw line as pointer moves until the mouse button is released.
12307  */
12308  distance=0;
12309  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12310  state=DefaultState;
12311  do
12312  {
12313  if (distance > 9)
12314  {
12315  /*
12316  Display info and draw rotation line.
12317  */
12318  if (windows->info.mapped == MagickFalse)
12319  (void) XMapWindow(display,windows->info.id);
12320  (void) FormatLocaleString(text,MagickPathExtent," %g",
12321  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12322  XInfoWidget(display,windows,text);
12323  XHighlightLine(display,windows->image.id,
12324  windows->image.highlight_context,&rotate_info);
12325  }
12326  else
12327  if (windows->info.mapped != MagickFalse)
12328  (void) XWithdrawWindow(display,windows->info.id,
12329  windows->info.screen);
12330  /*
12331  Wait for next event.
12332  */
12333  XScreenEvent(display,windows,&event,exception);
12334  if (distance > 9)
12335  XHighlightLine(display,windows->image.id,
12336  windows->image.highlight_context,&rotate_info);
12337  switch (event.type)
12338  {
12339  case ButtonPress:
12340  break;
12341  case ButtonRelease:
12342  {
12343  /*
12344  User has committed to rotation line.
12345  */
12346  rotate_info.x2=event.xbutton.x;
12347  rotate_info.y2=event.xbutton.y;
12348  state|=ExitState;
12349  break;
12350  }
12351  case Expose:
12352  break;
12353  case MotionNotify:
12354  {
12355  rotate_info.x2=event.xmotion.x;
12356  rotate_info.y2=event.xmotion.y;
12357  }
12358  default:
12359  break;
12360  }
12361  /*
12362  Check boundary conditions.
12363  */
12364  if (rotate_info.x2 < 0)
12365  rotate_info.x2=0;
12366  else
12367  if (rotate_info.x2 > (int) windows->image.width)
12368  rotate_info.x2=(short) windows->image.width;
12369  if (rotate_info.y2 < 0)
12370  rotate_info.y2=0;
12371  else
12372  if (rotate_info.y2 > (int) windows->image.height)
12373  rotate_info.y2=(short) windows->image.height;
12374  /*
12375  Compute rotation angle from the slope of the line.
12376  */
12377  degrees=0.0;
12378  distance=(unsigned int)
12379  (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12380  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12381  if (distance > 9)
12382  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12383  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12384  } while ((state & ExitState) == 0);
12385  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12386  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12387  if (distance <= 9)
12388  return(MagickTrue);
12389  }
12390  if (direction == VerticalRotateCommand)
12391  degrees-=90.0;
12392  if (degrees == 0.0)
12393  return(MagickTrue);
12394  /*
12395  Rotate image.
12396  */
12397  normalized_degrees=degrees;
12398  while (normalized_degrees < -45.0)
12399  normalized_degrees+=360.0;
12400  for (rotations=0; normalized_degrees > 45.0; rotations++)
12401  normalized_degrees-=90.0;
12402  if (normalized_degrees != 0.0)
12403  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12404  exception);
12405  XSetCursorState(display,windows,MagickTrue);
12406  XCheckRefreshWindows(display,windows);
12407  (*image)->background_color.red=(double) ScaleShortToQuantum(
12408  windows->pixel_info->pen_colors[pen_id].red);
12409  (*image)->background_color.green=(double) ScaleShortToQuantum(
12410  windows->pixel_info->pen_colors[pen_id].green);
12411  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12412  windows->pixel_info->pen_colors[pen_id].blue);
12413  rotate_image=RotateImage(*image,degrees,exception);
12414  XSetCursorState(display,windows,MagickFalse);
12415  if (rotate_image == (Image *) NULL)
12416  return(MagickFalse);
12417  *image=DestroyImage(*image);
12418  *image=rotate_image;
12419  if (windows->image.crop_geometry != (char *) NULL)
12420  {
12421  /*
12422  Rotate crop geometry.
12423  */
12424  width=(unsigned int) (*image)->columns;
12425  height=(unsigned int) (*image)->rows;
12426  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12427  switch (rotations % 4)
12428  {
12429  default:
12430  case 0:
12431  break;
12432  case 1:
12433  {
12434  /*
12435  Rotate 90 degrees.
12436  */
12437  (void) FormatLocaleString(windows->image.crop_geometry,
12438  MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12439  (int) height-y,x);
12440  break;
12441  }
12442  case 2:
12443  {
12444  /*
12445  Rotate 180 degrees.
12446  */
12447  (void) FormatLocaleString(windows->image.crop_geometry,
12448  MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12449  height-y);
12450  break;
12451  }
12452  case 3:
12453  {
12454  /*
12455  Rotate 270 degrees.
12456  */
12457  (void) FormatLocaleString(windows->image.crop_geometry,
12458  MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12459  (int) width-x);
12460  break;
12461  }
12462  }
12463  }
12464  if (windows->image.orphan != MagickFalse)
12465  return(MagickTrue);
12466  if (normalized_degrees != 0.0)
12467  {
12468  /*
12469  Update image colormap.
12470  */
12471  windows->image.window_changes.width=(int) (*image)->columns;
12472  windows->image.window_changes.height=(int) (*image)->rows;
12473  if (windows->image.crop_geometry != (char *) NULL)
12474  {
12475  /*
12476  Obtain dimensions of image from crop geometry.
12477  */
12478  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12479  &width,&height);
12480  windows->image.window_changes.width=(int) width;
12481  windows->image.window_changes.height=(int) height;
12482  }
12483  XConfigureImageColormap(display,resource_info,windows,*image,exception);
12484  }
12485  else
12486  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12487  {
12488  windows->image.window_changes.width=windows->image.ximage->height;
12489  windows->image.window_changes.height=windows->image.ximage->width;
12490  }
12491  /*
12492  Update image configuration.
12493  */
12494  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12495  return(MagickTrue);
12496 }
12497 
12498 /*
12499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12500 % %
12501 % %
12502 % %
12503 + X S a v e I m a g e %
12504 % %
12505 % %
12506 % %
12507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508 %
12509 % XSaveImage() saves an image to a file.
12510 %
12511 % The format of the XSaveImage method is:
12512 %
12513 % MagickBooleanType XSaveImage(Display *display,
12514 % XResourceInfo *resource_info,XWindows *windows,Image *image,
12515 % ExceptionInfo *exception)
12516 %
12517 % A description of each parameter follows:
12518 %
12519 % o display: Specifies a connection to an X server; returned from
12520 % XOpenDisplay.
12521 %
12522 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12523 %
12524 % o windows: Specifies a pointer to a XWindows structure.
12525 %
12526 % o image: the image.
12527 %
12528 % o exception: return any errors or warnings in this structure.
12529 %
12530 */
12531 static MagickBooleanType XSaveImage(Display *display,
12532  XResourceInfo *resource_info,XWindows *windows,Image *image,
12533  ExceptionInfo *exception)
12534 {
12535  char
12536  filename[MagickPathExtent],
12537  geometry[MagickPathExtent];
12538 
12539  Image
12540  *save_image;
12541 
12542  ImageInfo
12543  *image_info;
12544 
12545  MagickStatusType
12546  status;
12547 
12548  /*
12549  Request file name from user.
12550  */
12551  if (resource_info->write_filename != (char *) NULL)
12552  (void) CopyMagickString(filename,resource_info->write_filename,
12553  MagickPathExtent);
12554  else
12555  {
12556  char
12557  path[MagickPathExtent];
12558 
12559  int
12560  status;
12561 
12562  GetPathComponent(image->filename,HeadPath,path);
12563  GetPathComponent(image->filename,TailPath,filename);
12564  if (*path != '\0')
12565  {
12566  status=chdir(path);
12567  if (status == -1)
12568  (void) ThrowMagickException(exception,GetMagickModule(),
12569  FileOpenError,"UnableToOpenFile","%s",path);
12570  }
12571  }
12572  XFileBrowserWidget(display,windows,"Save",filename);
12573  if (*filename == '\0')
12574  return(MagickTrue);
12575  if (IsPathAccessible(filename) != MagickFalse)
12576  {
12577  int
12578  status;
12579 
12580  /*
12581  File exists-- seek user's permission before overwriting.
12582  */
12583  status=XConfirmWidget(display,windows,"Overwrite",filename);
12584  if (status <= 0)
12585  return(MagickTrue);
12586  }
12587  image_info=CloneImageInfo(resource_info->image_info);
12588  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12589  (void) SetImageInfo(image_info,1,exception);
12590  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12591  (LocaleCompare(image_info->magick,"JPG") == 0))
12592  {
12593  char
12594  quality[MagickPathExtent];
12595 
12596  int
12597  status;
12598 
12599  /*
12600  Request JPEG quality from user.
12601  */
12602  (void) FormatLocaleString(quality,MagickPathExtent,"%.20g",(double)
12603  image->quality);
12604  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12605  quality);
12606  if (*quality == '\0')
12607  return(MagickTrue);
12608  image->quality=StringToUnsignedLong(quality);
12609  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12610  }
12611  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12612  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12613  (LocaleCompare(image_info->magick,"PS") == 0) ||
12614  (LocaleCompare(image_info->magick,"PS2") == 0))
12615  {
12616  char
12617  geometry[MagickPathExtent];
12618 
12619  const char
12620  *const PageSizes[] =
12621  {
12622  "Letter",
12623  "Tabloid",
12624  "Ledger",
12625  "Legal",
12626  "Statement",
12627  "Executive",
12628  "A3",
12629  "A4",
12630  "A5",
12631  "B4",
12632  "B5",
12633  "Folio",
12634  "Quarto",
12635  "10x14",
12636  (char *) NULL
12637  };
12638 
12639  /*
12640  Request page geometry from user.
12641  */
12642  (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12643  if (LocaleCompare(image_info->magick,"PDF") == 0)
12644  (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12645  if (image_info->page != (char *) NULL)
12646  (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12647  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12648  "Select page geometry:",geometry);
12649  if (*geometry != '\0')
12650  image_info->page=GetPageGeometry(geometry);
12651  }
12652  /*
12653  Apply image transforms.
12654  */
12655  XSetCursorState(display,windows,MagickTrue);
12656  XCheckRefreshWindows(display,windows);
12657  save_image=CloneImage(image,0,0,MagickTrue,exception);
12658  if (save_image == (Image *) NULL)
12659  return(MagickFalse);
12660  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12661  windows->image.ximage->width,windows->image.ximage->height);
12662  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12663  exception);
12664  /*
12665  Write image.
12666  */
12667  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12668  status=WriteImage(image_info,save_image,exception);
12669  if (status != MagickFalse)
12670  image->taint=MagickFalse;
12671  save_image=DestroyImage(save_image);
12672  image_info=DestroyImageInfo(image_info);
12673  XSetCursorState(display,windows,MagickFalse);
12674  return(status != 0 ? MagickTrue : MagickFalse);
12675 }
12676 
12677 /*
12678 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12679 % %
12680 % %
12681 % %
12682 + X S c r e e n E v e n t %
12683 % %
12684 % %
12685 % %
12686 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12687 %
12688 % XScreenEvent() handles global events associated with the Pan and Magnify
12689 % windows.
12690 %
12691 % The format of the XScreenEvent function is:
12692 %
12693 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12694 % ExceptionInfo *exception)
12695 %
12696 % A description of each parameter follows:
12697 %
12698 % o display: Specifies a pointer to the Display structure; returned from
12699 % XOpenDisplay.
12700 %
12701 % o windows: Specifies a pointer to a XWindows structure.
12702 %
12703 % o event: Specifies a pointer to a X11 XEvent structure.
12704 %
12705 % o exception: return any errors or warnings in this structure.
12706 %
12707 */
12708 
12709 #if defined(__cplusplus) || defined(c_plusplus)
12710 extern "C" {
12711 #endif
12712 
12713 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12714 {
12715  XWindows
12716  *windows;
12717 
12718  windows=(XWindows *) data;
12719  if ((event->type == ClientMessage) &&
12720  (event->xclient.window == windows->image.id))
12721  return(MagickFalse);
12722  return(MagickTrue);
12723 }
12724 
12725 #if defined(__cplusplus) || defined(c_plusplus)
12726 }
12727 #endif
12728 
12729 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12730  ExceptionInfo *exception)
12731 {
12732  int
12733  x,
12734  y;
12735 
12736  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12737  if (event->xany.window == windows->command.id)
12738  return;
12739  switch (event->type)
12740  {
12741  case ButtonPress:
12742  case ButtonRelease:
12743  {
12744  if ((event->xbutton.button == Button3) &&
12745  (event->xbutton.state & Mod1Mask))
12746  {
12747  /*
12748  Convert Alt-Button3 to Button2.
12749  */
12750  event->xbutton.button=Button2;
12751  event->xbutton.state&=(unsigned int) (~Mod1Mask);
12752  }
12753  if (event->xbutton.window == windows->backdrop.id)
12754  {
12755  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12756  event->xbutton.time);
12757  break;
12758  }
12759  if (event->xbutton.window == windows->pan.id)
12760  {
12761  XPanImage(display,windows,event,exception);
12762  break;
12763  }
12764  if (event->xbutton.window == windows->image.id)
12765  if (event->xbutton.button == Button2)
12766  {
12767  /*
12768  Update magnified image.
12769  */
12770  x=event->xbutton.x;
12771  y=event->xbutton.y;
12772  if (x < 0)
12773  x=0;
12774  else
12775  if (x >= (int) windows->image.width)
12776  x=(int) (windows->image.width-1);
12777  windows->magnify.x=(int) windows->image.x+x;
12778  if (y < 0)
12779  y=0;
12780  else
12781  if (y >= (int) windows->image.height)
12782  y=(int) (windows->image.height-1);
12783  windows->magnify.y=windows->image.y+y;
12784  if (windows->magnify.mapped == MagickFalse)
12785  (void) XMapRaised(display,windows->magnify.id);
12786  XMakeMagnifyImage(display,windows,exception);
12787  if (event->type == ButtonRelease)
12788  (void) XWithdrawWindow(display,windows->info.id,
12789  windows->info.screen);
12790  break;
12791  }
12792  break;
12793  }
12794  case ClientMessage:
12795  {
12796  /*
12797  If client window delete message, exit.
12798  */
12799  if (event->xclient.message_type != windows->wm_protocols)
12800  break;
12801  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12802  break;
12803  if (event->xclient.window == windows->magnify.id)
12804  {
12805  (void) XWithdrawWindow(display,windows->magnify.id,
12806  windows->magnify.screen);
12807  break;
12808  }
12809  break;
12810  }
12811  case ConfigureNotify:
12812  {
12813  if (event->xconfigure.window == windows->magnify.id)
12814  {
12815  unsigned int
12816  magnify;
12817 
12818  /*
12819  Magnify window has a new configuration.
12820  */
12821  windows->magnify.width=(unsigned int) event->xconfigure.width;
12822  windows->magnify.height=(unsigned int) event->xconfigure.height;
12823  if (windows->magnify.mapped == MagickFalse)
12824  break;
12825  magnify=1;
12826  while ((int) magnify <= event->xconfigure.width)
12827  magnify<<=1;
12828  while ((int) magnify <= event->xconfigure.height)
12829  magnify<<=1;
12830  magnify>>=1;
12831  if (((int) magnify != event->xconfigure.width) ||
12832  ((int) magnify != event->xconfigure.height))
12833  {
12834  XWindowChanges
12835  window_changes;
12836 
12837  window_changes.width=(int) magnify;
12838  window_changes.height=(int) magnify;
12839  (void) XReconfigureWMWindow(display,windows->magnify.id,
12840  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12841  &window_changes);
12842  break;
12843  }
12844  XMakeMagnifyImage(display,windows,exception);
12845  break;
12846  }
12847  break;
12848  }
12849  case Expose:
12850  {
12851  if (event->xexpose.window == windows->image.id)
12852  {
12853  XRefreshWindow(display,&windows->image,event);
12854  break;
12855  }
12856  if (event->xexpose.window == windows->pan.id)
12857  if (event->xexpose.count == 0)
12858  {
12859  XDrawPanRectangle(display,windows);
12860  break;
12861  }
12862  if (event->xexpose.window == windows->magnify.id)
12863  if (event->xexpose.count == 0)
12864  {
12865  XMakeMagnifyImage(display,windows,exception);
12866  break;
12867  }
12868  break;
12869  }
12870  case KeyPress:
12871  {
12872  char
12873  command[MagickPathExtent];
12874 
12875  KeySym
12876  key_symbol;
12877 
12878  if (event->xkey.window != windows->magnify.id)
12879  break;
12880  /*
12881  Respond to a user key press.
12882  */
12883  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12884  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12885  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12886  exception);
12887  break;
12888  }
12889  case MapNotify:
12890  {
12891  if (event->xmap.window == windows->magnify.id)
12892  {
12893  windows->magnify.mapped=MagickTrue;
12894  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12895  break;
12896  }
12897  if (event->xmap.window == windows->info.id)
12898  {
12899  windows->info.mapped=MagickTrue;
12900  break;
12901  }
12902  break;
12903  }
12904  case MotionNotify:
12905  {
12906  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12907  if (event->xmotion.window == windows->image.id)
12908  if (windows->magnify.mapped != MagickFalse)
12909  {
12910  /*
12911  Update magnified image.
12912  */
12913  x=event->xmotion.x;
12914  y=event->xmotion.y;
12915  if (x < 0)
12916  x=0;
12917  else
12918  if (x >= (int) windows->image.width)
12919  x=(int) (windows->image.width-1);
12920  windows->magnify.x=(int) windows->image.x+x;
12921  if (y < 0)
12922  y=0;
12923  else
12924  if (y >= (int) windows->image.height)
12925  y=(int) (windows->image.height-1);
12926  windows->magnify.y=windows->image.y+y;
12927  XMakeMagnifyImage(display,windows,exception);
12928  }
12929  break;
12930  }
12931  case UnmapNotify:
12932  {
12933  if (event->xunmap.window == windows->magnify.id)
12934  {
12935  windows->magnify.mapped=MagickFalse;
12936  break;
12937  }
12938  if (event->xunmap.window == windows->info.id)
12939  {
12940  windows->info.mapped=MagickFalse;
12941  break;
12942  }
12943  break;
12944  }
12945  default:
12946  break;
12947  }
12948 }
12949 
12950 /*
12951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12952 % %
12953 % %
12954 % %
12955 + X S e t C r o p G e o m e t r y %
12956 % %
12957 % %
12958 % %
12959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12960 %
12961 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12962 % and translates it to a cropping geometry relative to the image.
12963 %
12964 % The format of the XSetCropGeometry method is:
12965 %
12966 % void XSetCropGeometry(Display *display,XWindows *windows,
12967 % RectangleInfo *crop_info,Image *image)
12968 %
12969 % A description of each parameter follows:
12970 %
12971 % o display: Specifies a connection to an X server; returned from
12972 % XOpenDisplay.
12973 %
12974 % o windows: Specifies a pointer to a XWindows structure.
12975 %
12976 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12977 % Image window to crop.
12978 %
12979 % o image: the image.
12980 %
12981 */
12982 static void XSetCropGeometry(Display *display,XWindows *windows,
12983  RectangleInfo *crop_info,Image *image)
12984 {
12985  char
12986  text[MagickPathExtent];
12987 
12988  int
12989  x,
12990  y;
12991 
12992  double
12993  scale_factor;
12994 
12995  unsigned int
12996  height,
12997  width;
12998 
12999  if (windows->info.mapped != MagickFalse)
13000  {
13001  /*
13002  Display info on cropping rectangle.
13003  */
13004  (void) FormatLocaleString(text,MagickPathExtent," %.20gx%.20g%+.20g%+.20g",
13005  (double) crop_info->width,(double) crop_info->height,(double)
13006  crop_info->x,(double) crop_info->y);
13007  XInfoWidget(display,windows,text);
13008  }
13009  /*
13010  Cropping geometry is relative to any previous crop geometry.
13011  */
13012  x=0;
13013  y=0;
13014  width=(unsigned int) image->columns;
13015  height=(unsigned int) image->rows;
13016  if (windows->image.crop_geometry != (char *) NULL)
13017  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13018  else
13019  windows->image.crop_geometry=AcquireString((char *) NULL);
13020  /*
13021  Define the crop geometry string from the cropping rectangle.
13022  */
13023  scale_factor=(double) width/windows->image.ximage->width;
13024  if (crop_info->x > 0)
13025  x+=(int) (scale_factor*crop_info->x+0.5);
13026  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13027  if (width == 0)
13028  width=1;
13029  scale_factor=(double) height/windows->image.ximage->height;
13030  if (crop_info->y > 0)
13031  y+=(int) (scale_factor*crop_info->y+0.5);
13032  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13033  if (height == 0)
13034  height=1;
13035  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13036  "%ux%u%+d%+d",width,height,x,y);
13037 }
13038 
13039 /*
13040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13041 % %
13042 % %
13043 % %
13044 + X T i l e I m a g e %
13045 % %
13046 % %
13047 % %
13048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13049 %
13050 % XTileImage() loads or deletes a selected tile from a visual image directory.
13051 % The load or delete command is chosen from a menu.
13052 %
13053 % The format of the XTileImage method is:
13054 %
13055 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
13056 % XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13057 %
13058 % A description of each parameter follows:
13059 %
13060 % o tile_image: XTileImage reads or deletes the tile image
13061 % and returns it. A null image is returned if an error occurs.
13062 %
13063 % o display: Specifies a connection to an X server; returned from
13064 % XOpenDisplay.
13065 %
13066 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13067 %
13068 % o windows: Specifies a pointer to a XWindows structure.
13069 %
13070 % o image: the image; returned from ReadImage.
13071 %
13072 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
13073 % the entire image is refreshed.
13074 %
13075 % o exception: return any errors or warnings in this structure.
13076 %
13077 */
13078 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13079  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13080 {
13081  const char
13082  *const VerbMenu[] =
13083  {
13084  "Load",
13085  "Next",
13086  "Former",
13087  "Delete",
13088  "Update",
13089  (char *) NULL,
13090  };
13091 
13092  static const ModeType
13093  TileCommands[] =
13094  {
13095  TileLoadCommand,
13096  TileNextCommand,
13097  TileFormerCommand,
13098  TileDeleteCommand,
13099  TileUpdateCommand
13100  };
13101 
13102  char
13103  command[MagickPathExtent],
13104  filename[MagickPathExtent];
13105 
13106  Image
13107  *tile_image;
13108 
13109  int
13110  id,
13111  status,
13112  tile,
13113  x,
13114  y;
13115 
13116  double
13117  scale_factor;
13118 
13119  char
13120  *p,
13121  *q;
13122 
13123  int
13124  i;
13125 
13126  unsigned int
13127  height,
13128  width;
13129 
13130  /*
13131  Tile image is relative to montage image configuration.
13132  */
13133  x=0;
13134  y=0;
13135  width=(unsigned int) image->columns;
13136  height=(unsigned int) image->rows;
13137  if (windows->image.crop_geometry != (char *) NULL)
13138  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13139  scale_factor=(double) width/windows->image.ximage->width;
13140  event->xbutton.x+=windows->image.x;
13141  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13142  scale_factor=(double) height/windows->image.ximage->height;
13143  event->xbutton.y+=windows->image.y;
13144  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13145  /*
13146  Determine size and location of each tile in the visual image directory.
13147  */
13148  width=(unsigned int) image->columns;
13149  height=(unsigned int) image->rows;
13150  x=0;
13151  y=0;
13152  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13153  tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13154  width)+(event->xbutton.x-x)/(int) width;
13155  if (tile < 0)
13156  {
13157  /*
13158  Button press is outside any tile.
13159  */
13160  (void) XBell(display,0);
13161  return((Image *) NULL);
13162  }
13163  /*
13164  Determine file name from the tile directory.
13165  */
13166  p=image->directory;
13167  for (i=tile; (i != 0) && (*p != '\0'); )
13168  {
13169  if (*p == '\xff')
13170  i--;
13171  p++;
13172  }
13173  if (*p == '\0')
13174  {
13175  /*
13176  Button press is outside any tile.
13177  */
13178  (void) XBell(display,0);
13179  return((Image *) NULL);
13180  }
13181  /*
13182  Select a command from the pop-up menu.
13183  */
13184  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13185  if (id < 0)
13186  return((Image *) NULL);
13187  q=p;
13188  while ((*q != '\xff') && (*q != '\0'))
13189  q++;
13190  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13191  /*
13192  Perform command for the selected tile.
13193  */
13194  XSetCursorState(display,windows,MagickTrue);
13195  XCheckRefreshWindows(display,windows);
13196  tile_image=NewImageList();
13197  switch (TileCommands[id])
13198  {
13199  case TileLoadCommand:
13200  {
13201  /*
13202  Load tile image.
13203  */
13204  XCheckRefreshWindows(display,windows);
13205  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13206  MagickPathExtent);
13207  (void) CopyMagickString(resource_info->image_info->filename,filename,
13208  MagickPathExtent);
13209  tile_image=ReadImage(resource_info->image_info,exception);
13210  CatchException(exception);
13211  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13212  break;
13213  }
13214  case TileNextCommand:
13215  {
13216  /*
13217  Display next image.
13218  */
13219  XClientMessage(display,windows->image.id,windows->im_protocols,
13220  windows->im_next_image,CurrentTime);
13221  break;
13222  }
13223  case TileFormerCommand:
13224  {
13225  /*
13226  Display former image.
13227  */
13228  XClientMessage(display,windows->image.id,windows->im_protocols,
13229  windows->im_former_image,CurrentTime);
13230  break;
13231  }
13232  case TileDeleteCommand:
13233  {
13234  /*
13235  Delete tile image.
13236  */
13237  if (IsPathAccessible(filename) == MagickFalse)
13238  {
13239  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13240  break;
13241  }
13242  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13243  if (status <= 0)
13244  break;
13245  status=ShredFile(filename) == MagickFalse ? 0 : 1;
13246  status|=remove_utf8(filename);
13247  if (status != MagickFalse)
13248  {
13249  XNoticeWidget(display,windows,"Unable to delete image file:",
13250  filename);
13251  break;
13252  }
13253  magick_fallthrough;
13254  }
13255  case TileUpdateCommand:
13256  {
13257  int
13258  x_offset,
13259  y_offset;
13260 
13261  PixelInfo
13262  pixel;
13263 
13264  int
13265  j;
13266 
13267  Quantum
13268  *s;
13269 
13270  /*
13271  Ensure all the images exist.
13272  */
13273  tile=0;
13274  GetPixelInfo(image,&pixel);
13275  for (p=image->directory; *p != '\0'; p++)
13276  {
13277  CacheView
13278  *image_view;
13279 
13280  q=p;
13281  while ((*q != '\xff') && (*q != '\0'))
13282  q++;
13283  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13284  p=q;
13285  if (IsPathAccessible(filename) != MagickFalse)
13286  {
13287  tile++;
13288  continue;
13289  }
13290  /*
13291  Overwrite tile with background color.
13292  */
13293  x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13294  x);
13295  y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13296  y);
13297  image_view=AcquireAuthenticCacheView(image,exception);
13298  (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13299  for (i=0; i < (int) height; i++)
13300  {
13301  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13302  y_offset+i,width,1,exception);
13303  if (s == (Quantum *) NULL)
13304  break;
13305  for (j=0; j < (int) width; j++)
13306  {
13307  SetPixelViaPixelInfo(image,&pixel,s);
13308  s+=(ptrdiff_t) GetPixelChannels(image);
13309  }
13310  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13311  break;
13312  }
13313  image_view=DestroyCacheView(image_view);
13314  tile++;
13315  }
13316  windows->image.window_changes.width=(int) image->columns;
13317  windows->image.window_changes.height=(int) image->rows;
13318  XConfigureImageColormap(display,resource_info,windows,image,exception);
13319  (void) XConfigureImage(display,resource_info,windows,image,exception);
13320  break;
13321  }
13322  default:
13323  break;
13324  }
13325  XSetCursorState(display,windows,MagickFalse);
13326  return(tile_image);
13327 }
13328 
13329 /*
13330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13331 % %
13332 % %
13333 % %
13334 + X T r a n s l a t e I m a g e %
13335 % %
13336 % %
13337 % %
13338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13339 %
13340 % XTranslateImage() translates the image within an Image window by one pixel
13341 % as specified by the key symbol. If the image has a montage string the
13342 % translation is respect to the width and height contained within the string.
13343 %
13344 % The format of the XTranslateImage method is:
13345 %
13346 % void XTranslateImage(Display *display,XWindows *windows,
13347 % Image *image,const KeySym key_symbol)
13348 %
13349 % A description of each parameter follows:
13350 %
13351 % o display: Specifies a connection to an X server; returned from
13352 % XOpenDisplay.
13353 %
13354 % o windows: Specifies a pointer to a XWindows structure.
13355 %
13356 % o image: the image.
13357 %
13358 % o key_symbol: Specifies a KeySym which indicates which side of the image
13359 % to trim.
13360 %
13361 */
13362 static void XTranslateImage(Display *display,XWindows *windows,
13363  Image *image,const KeySym key_symbol)
13364 {
13365  char
13366  text[MagickPathExtent];
13367 
13368  int
13369  x,
13370  y;
13371 
13372  unsigned int
13373  x_offset,
13374  y_offset;
13375 
13376  /*
13377  User specified a pan position offset.
13378  */
13379  x_offset=windows->image.width;
13380  y_offset=windows->image.height;
13381  if (image->montage != (char *) NULL)
13382  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13383  switch ((int) key_symbol)
13384  {
13385  case XK_Home:
13386  case XK_KP_Home:
13387  {
13388  windows->image.x=(int) windows->image.width/2;
13389  windows->image.y=(int) windows->image.height/2;
13390  break;
13391  }
13392  case XK_Left:
13393  case XK_KP_Left:
13394  {
13395  windows->image.x-=(int) x_offset;
13396  break;
13397  }
13398  case XK_Next:
13399  case XK_Up:
13400  case XK_KP_Up:
13401  {
13402  windows->image.y-=(int) y_offset;
13403  break;
13404  }
13405  case XK_Right:
13406  case XK_KP_Right:
13407  {
13408  windows->image.x+=(int) x_offset;
13409  break;
13410  }
13411  case XK_Prior:
13412  case XK_Down:
13413  case XK_KP_Down:
13414  {
13415  windows->image.y+=(int) y_offset;
13416  break;
13417  }
13418  default:
13419  return;
13420  }
13421  /*
13422  Check boundary conditions.
13423  */
13424  if (windows->image.x < 0)
13425  windows->image.x=0;
13426  else
13427  if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13428  windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13429  if (windows->image.y < 0)
13430  windows->image.y=0;
13431  else
13432  if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13433  windows->image.y=windows->image.ximage->height-(int)
13434  windows->image.height;
13435  /*
13436  Refresh Image window.
13437  */
13438  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13439  windows->image.width,windows->image.height,windows->image.x,
13440  windows->image.y);
13441  XInfoWidget(display,windows,text);
13442  XCheckRefreshWindows(display,windows);
13443  XDrawPanRectangle(display,windows);
13444  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13445  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13446 }
13447 
13448 /*
13449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13450 % %
13451 % %
13452 % %
13453 + X T r i m I m a g e %
13454 % %
13455 % %
13456 % %
13457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13458 %
13459 % XTrimImage() trims the edges from the Image window.
13460 %
13461 % The format of the XTrimImage method is:
13462 %
13463 % MagickBooleanType XTrimImage(Display *display,
13464 % XResourceInfo *resource_info,XWindows *windows,Image *image,
13465 % ExceptionInfo *exception)
13466 %
13467 % A description of each parameter follows:
13468 %
13469 % o display: Specifies a connection to an X server; returned from
13470 % XOpenDisplay.
13471 %
13472 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13473 %
13474 % o windows: Specifies a pointer to a XWindows structure.
13475 %
13476 % o image: the image.
13477 %
13478 % o exception: return any errors or warnings in this structure.
13479 %
13480 */
13481 static MagickBooleanType XTrimImage(Display *display,
13482  XResourceInfo *resource_info,XWindows *windows,Image *image,
13483  ExceptionInfo *exception)
13484 {
13486  trim_info;
13487 
13488  int
13489  x,
13490  y;
13491 
13492  size_t
13493  background,
13494  pixel;
13495 
13496  /*
13497  Trim edges from image.
13498  */
13499  XSetCursorState(display,windows,MagickTrue);
13500  XCheckRefreshWindows(display,windows);
13501  /*
13502  Crop the left edge.
13503  */
13504  background=XGetPixel(windows->image.ximage,0,0);
13505  trim_info.width=(size_t) windows->image.ximage->width;
13506  for (x=0; x < windows->image.ximage->width; x++)
13507  {
13508  for (y=0; y < windows->image.ximage->height; y++)
13509  {
13510  pixel=XGetPixel(windows->image.ximage,x,y);
13511  if (pixel != background)
13512  break;
13513  }
13514  if (y < windows->image.ximage->height)
13515  break;
13516  }
13517  trim_info.x=(ssize_t) x;
13518  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13519  {
13520  XSetCursorState(display,windows,MagickFalse);
13521  return(MagickFalse);
13522  }
13523  /*
13524  Crop the right edge.
13525  */
13526  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13527  for (x=windows->image.ximage->width-1; x != 0; x--)
13528  {
13529  for (y=0; y < windows->image.ximage->height; y++)
13530  {
13531  pixel=XGetPixel(windows->image.ximage,x,y);
13532  if (pixel != background)
13533  break;
13534  }
13535  if (y < windows->image.ximage->height)
13536  break;
13537  }
13538  trim_info.width=(size_t) (x-trim_info.x+1);
13539  /*
13540  Crop the top edge.
13541  */
13542  background=XGetPixel(windows->image.ximage,0,0);
13543  trim_info.height=(size_t) windows->image.ximage->height;
13544  for (y=0; y < windows->image.ximage->height; y++)
13545  {
13546  for (x=0; x < windows->image.ximage->width; x++)
13547  {
13548  pixel=XGetPixel(windows->image.ximage,x,y);
13549  if (pixel != background)
13550  break;
13551  }
13552  if (x < windows->image.ximage->width)
13553  break;
13554  }
13555  trim_info.y=(ssize_t) y;
13556  /*
13557  Crop the bottom edge.
13558  */
13559  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13560  for (y=windows->image.ximage->height-1; y != 0; y--)
13561  {
13562  for (x=0; x < windows->image.ximage->width; x++)
13563  {
13564  pixel=XGetPixel(windows->image.ximage,x,y);
13565  if (pixel != background)
13566  break;
13567  }
13568  if (x < windows->image.ximage->width)
13569  break;
13570  }
13571  trim_info.height=(size_t) (y-trim_info.y+1);
13572  if (((unsigned int) trim_info.width != windows->image.width) ||
13573  ((unsigned int) trim_info.height != windows->image.height))
13574  {
13575  /*
13576  Reconfigure Image window as defined by the trimming rectangle.
13577  */
13578  XSetCropGeometry(display,windows,&trim_info,image);
13579  windows->image.window_changes.width=(int) trim_info.width;
13580  windows->image.window_changes.height=(int) trim_info.height;
13581  (void) XConfigureImage(display,resource_info,windows,image,exception);
13582  }
13583  XSetCursorState(display,windows,MagickFalse);
13584  return(MagickTrue);
13585 }
13586 
13587 /*
13588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13589 % %
13590 % %
13591 % %
13592 + X V i s u a l D i r e c t o r y I m a g e %
13593 % %
13594 % %
13595 % %
13596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13597 %
13598 % XVisualDirectoryImage() creates a Visual Image Directory.
13599 %
13600 % The format of the XVisualDirectoryImage method is:
13601 %
13602 % Image *XVisualDirectoryImage(Display *display,
13603 % XResourceInfo *resource_info,XWindows *windows,
13604 % ExceptionInfo *exception)
13605 %
13606 % A description of each parameter follows:
13607 %
13608 % o display: Specifies a connection to an X server; returned from
13609 % XOpenDisplay.
13610 %
13611 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13612 %
13613 % o windows: Specifies a pointer to a XWindows structure.
13614 %
13615 % o exception: return any errors or warnings in this structure.
13616 %
13617 */
13618 static Image *XVisualDirectoryImage(Display *display,
13619  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13620 {
13621 #define TileImageTag "Scale/Image"
13622 #define XClientName "montage"
13623 
13624  char
13625  **filelist;
13626 
13627  Image
13628  *images,
13629  *montage_image,
13630  *next_image,
13631  *thumbnail_image;
13632 
13633  ImageInfo
13634  *read_info;
13635 
13636  int
13637  number_files;
13638 
13639  MagickBooleanType
13640  backdrop;
13641 
13642  MagickStatusType
13643  status;
13644 
13645  MontageInfo
13646  *montage_info;
13647 
13649  geometry;
13650 
13651  int
13652  i;
13653 
13654  static char
13655  filename[MagickPathExtent] = "\0",
13656  filenames[MagickPathExtent] = "*";
13657 
13658  XResourceInfo
13659  background_resources;
13660 
13661  /*
13662  Request file name from user.
13663  */
13664  XFileBrowserWidget(display,windows,"Directory",filenames);
13665  if (*filenames == '\0')
13666  return((Image *) NULL);
13667  /*
13668  Expand the filenames.
13669  */
13670  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13671  if (filelist == (char **) NULL)
13672  {
13673  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13674  filenames);
13675  return((Image *) NULL);
13676  }
13677  number_files=1;
13678  filelist[0]=filenames;
13679  status=ExpandFilenames(&number_files,&filelist);
13680  if ((status == MagickFalse) || (number_files == 0))
13681  {
13682  if (number_files == 0)
13683  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13684  else
13685  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13686  filenames);
13687  return((Image *) NULL);
13688  }
13689  /*
13690  Set image background resources.
13691  */
13692  background_resources=(*resource_info);
13693  background_resources.window_id=AcquireString("");
13694  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13695  "0x%lx",windows->image.id);
13696  background_resources.backdrop=MagickTrue;
13697  /*
13698  Read each image and convert them to a tile.
13699  */
13700  backdrop=((windows->visual_info->klass == TrueColor) ||
13701  (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13702  read_info=CloneImageInfo(resource_info->image_info);
13703  (void) SetImageOption(read_info,"jpeg:size","120x120");
13704  (void) CloneString(&read_info->size,DefaultTileGeometry);
13705  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13706  (void *) NULL);
13707  images=NewImageList();
13708  XSetCursorState(display,windows,MagickTrue);
13709  XCheckRefreshWindows(display,windows);
13710  for (i=0; i < (int) number_files; i++)
13711  {
13712  (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13713  filelist[i]=DestroyString(filelist[i]);
13714  *read_info->magick='\0';
13715  next_image=ReadImage(read_info,exception);
13716  CatchException(exception);
13717  if (next_image != (Image *) NULL)
13718  {
13719  (void) DeleteImageProperty(next_image,"label");
13720  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13721  read_info,next_image,DefaultTileLabel,exception),exception);
13722  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13723  exception);
13724  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13725  geometry.height,exception);
13726  if (thumbnail_image != (Image *) NULL)
13727  {
13728  next_image=DestroyImage(next_image);
13729  next_image=thumbnail_image;
13730  }
13731  if (backdrop)
13732  {
13733  (void) XDisplayBackgroundImage(display,&background_resources,
13734  next_image,exception);
13735  XSetCursorState(display,windows,MagickTrue);
13736  }
13737  AppendImageToList(&images,next_image);
13738  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13739  {
13740  MagickBooleanType
13741  proceed;
13742 
13743  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13744  (MagickSizeType) number_files);
13745  if (proceed == MagickFalse)
13746  break;
13747  }
13748  }
13749  }
13750  filelist=(char **) RelinquishMagickMemory(filelist);
13751  if (images == (Image *) NULL)
13752  {
13753  read_info=DestroyImageInfo(read_info);
13754  XSetCursorState(display,windows,MagickFalse);
13755  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13756  return((Image *) NULL);
13757  }
13758  /*
13759  Create the Visual Image Directory.
13760  */
13761  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13762  montage_info->pointsize=10;
13763  if (resource_info->font != (char *) NULL)
13764  (void) CloneString(&montage_info->font,resource_info->font);
13765  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13766  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13767  images),exception);
13768  images=DestroyImageList(images);
13769  montage_info=DestroyMontageInfo(montage_info);
13770  read_info=DestroyImageInfo(read_info);
13771  XSetCursorState(display,windows,MagickFalse);
13772  if (montage_image == (Image *) NULL)
13773  return(montage_image);
13774  XClientMessage(display,windows->image.id,windows->im_protocols,
13775  windows->im_next_image,CurrentTime);
13776  return(montage_image);
13777 }
13778 
13779 /*
13780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13781 % %
13782 % %
13783 % %
13784 % X D i s p l a y B a c k g r o u n d I m a g e %
13785 % %
13786 % %
13787 % %
13788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13789 %
13790 % XDisplayBackgroundImage() displays an image in the background of a window.
13791 %
13792 % The format of the XDisplayBackgroundImage method is:
13793 %
13794 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13795 % XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13796 %
13797 % A description of each parameter follows:
13798 %
13799 % o display: Specifies a connection to an X server; returned from
13800 % XOpenDisplay.
13801 %
13802 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13803 %
13804 % o image: the image.
13805 %
13806 % o exception: return any errors or warnings in this structure.
13807 %
13808 */
13809 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13810  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13811 {
13812  char
13813  geometry[MagickPathExtent],
13814  visual_type[MagickPathExtent];
13815 
13816  int
13817  height,
13818  status,
13819  width;
13820 
13822  geometry_info;
13823 
13824  static XPixelInfo
13825  pixel;
13826 
13827  static XStandardColormap
13828  *map_info;
13829 
13830  static XVisualInfo
13831  *visual_info = (XVisualInfo *) NULL;
13832 
13833  static XWindowInfo
13834  window_info;
13835 
13836  size_t
13837  delay;
13838 
13839  Window
13840  root_window;
13841 
13842  XGCValues
13843  context_values;
13844 
13845  XResourceInfo
13846  resources;
13847 
13848  XWindowAttributes
13849  window_attributes;
13850 
13851  /*
13852  Determine target window.
13853  */
13854  assert(image != (Image *) NULL);
13855  assert(image->signature == MagickCoreSignature);
13856  if (IsEventLogging() != MagickFalse)
13857  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13858  resources=(*resource_info);
13859  window_info.id=(Window) NULL;
13860  root_window=XRootWindow(display,XDefaultScreen(display));
13861  if (LocaleCompare(resources.window_id,"root") == 0)
13862  window_info.id=root_window;
13863  else
13864  {
13865  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13866  window_info.id=XWindowByID(display,root_window,
13867  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13868  if (window_info.id == (Window) NULL)
13869  window_info.id=XWindowByName(display,root_window,resources.window_id);
13870  }
13871  if (window_info.id == (Window) NULL)
13872  {
13873  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13874  resources.window_id);
13875  return(MagickFalse);
13876  }
13877  /*
13878  Determine window visual id.
13879  */
13880  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13881  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13882  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13883  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13884  if (status != 0)
13885  (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13886  XVisualIDFromVisual(window_attributes.visual));
13887  if (visual_info == (XVisualInfo *) NULL)
13888  {
13889  /*
13890  Allocate standard colormap.
13891  */
13892  map_info=XAllocStandardColormap();
13893  if (map_info == (XStandardColormap *) NULL)
13894  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13895  image->filename);
13896  map_info->colormap=(Colormap) NULL;
13897  pixel.pixels=(unsigned long *) NULL;
13898  /*
13899  Initialize visual info.
13900  */
13901  resources.map_type=(char *) NULL;
13902  resources.visual_type=visual_type;
13903  visual_info=XBestVisualInfo(display,map_info,&resources);
13904  if (visual_info == (XVisualInfo *) NULL)
13905  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13906  resources.visual_type);
13907  /*
13908  Initialize window info.
13909  */
13910  window_info.ximage=(XImage *) NULL;
13911  window_info.matte_image=(XImage *) NULL;
13912  window_info.pixmap=(Pixmap) NULL;
13913  window_info.matte_pixmap=(Pixmap) NULL;
13914  }
13915  /*
13916  Free previous root colors.
13917  */
13918  if (window_info.id == root_window)
13919  (void) XDestroyWindowColors(display,root_window);
13920  /*
13921  Initialize Standard Colormap.
13922  */
13923  resources.colormap=SharedColormap;
13924  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13925  exception);
13926  /*
13927  Graphic context superclass.
13928  */
13929  context_values.background=pixel.foreground_color.pixel;
13930  context_values.foreground=pixel.background_color.pixel;
13931  pixel.annotate_context=XCreateGC(display,window_info.id,
13932  (size_t) (GCBackground | GCForeground),&context_values);
13933  if (pixel.annotate_context == (GC) NULL)
13934  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13935  image->filename);
13936  /*
13937  Initialize Image window attributes.
13938  */
13939  window_info.name=AcquireString("\0");
13940  window_info.icon_name=AcquireString("\0");
13941  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13942  &resources,&window_info);
13943  /*
13944  Create the X image.
13945  */
13946  window_info.width=(unsigned int) image->columns;
13947  window_info.height=(unsigned int) image->rows;
13948  if ((image->columns != window_info.width) ||
13949  (image->rows != window_info.height))
13950  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13951  image->filename);
13952  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13953  window_attributes.width,window_attributes.height);
13954  geometry_info.width=window_info.width;
13955  geometry_info.height=window_info.height;
13956  geometry_info.x=(ssize_t) window_info.x;
13957  geometry_info.y=(ssize_t) window_info.y;
13958  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13959  &geometry_info.width,&geometry_info.height);
13960  window_info.width=(unsigned int) geometry_info.width;
13961  window_info.height=(unsigned int) geometry_info.height;
13962  window_info.x=(int) geometry_info.x;
13963  window_info.y=(int) geometry_info.y;
13964  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13965  window_info.height,exception) == MagickFalse ? 0 : 1;
13966  if (status == MagickFalse)
13967  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13968  image->filename);
13969  window_info.x=0;
13970  window_info.y=0;
13971  if (resource_info->debug != MagickFalse)
13972  {
13973  (void) LogMagickEvent(X11Event,GetMagickModule(),
13974  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13975  (double) image->columns,(double) image->rows);
13976  if (image->colors != 0)
13977  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13978  image->colors);
13979  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13980  }
13981  /*
13982  Adjust image dimensions as specified by backdrop or geometry options.
13983  */
13984  width=(int) window_info.width;
13985  height=(int) window_info.height;
13986  if (resources.backdrop != MagickFalse)
13987  {
13988  /*
13989  Center image on window.
13990  */
13991  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13992  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13993  width=window_attributes.width;
13994  height=window_attributes.height;
13995  }
13996  if ((resources.image_geometry != (char *) NULL) &&
13997  (*resources.image_geometry != '\0'))
13998  {
13999  char
14000  default_geometry[MagickPathExtent];
14001 
14002  int
14003  flags,
14004  gravity;
14005 
14006  XSizeHints
14007  *size_hints;
14008 
14009  /*
14010  User specified geometry.
14011  */
14012  size_hints=XAllocSizeHints();
14013  if (size_hints == (XSizeHints *) NULL)
14014  ThrowXWindowFatalException(ResourceLimitFatalError,
14015  "MemoryAllocationFailed",image->filename);
14016  size_hints->flags=0L;
14017  (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14018  width,height);
14019  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14020  default_geometry,window_info.border_width,size_hints,&window_info.x,
14021  &window_info.y,&width,&height,&gravity);
14022  if (flags & (XValue | YValue))
14023  {
14024  width=window_attributes.width;
14025  height=window_attributes.height;
14026  }
14027  (void) XFree((void *) size_hints);
14028  }
14029  /*
14030  Create the X pixmap.
14031  */
14032  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14033  (unsigned int) height,window_info.depth);
14034  if (window_info.pixmap == (Pixmap) NULL)
14035  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14036  image->filename);
14037  /*
14038  Display pixmap on the window.
14039  */
14040  if (((unsigned int) width > window_info.width) ||
14041  ((unsigned int) height > window_info.height))
14042  (void) XFillRectangle(display,window_info.pixmap,
14043  window_info.annotate_context,0,0,(unsigned int) width,
14044  (unsigned int) height);
14045  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14046  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14047  window_info.width,(unsigned int) window_info.height);
14048  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14049  (void) XClearWindow(display,window_info.id);
14050  delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14051  XDelay(display,delay == 0UL ? 10UL : delay);
14052  (void) XSync(display,MagickFalse);
14053  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14054 }
14055 
14056 /*
14057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14058 % %
14059 % %
14060 % %
14061 + X D i s p l a y I m a g e %
14062 % %
14063 % %
14064 % %
14065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14066 %
14067 % XDisplayImage() displays an image via X11. A new image is created and
14068 % returned if the user interactively transforms the displayed image.
14069 %
14070 % The format of the XDisplayImage method is:
14071 %
14072 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14073 % char **argv,int argc,Image **image,size_t *state,
14074 % ExceptionInfo *exception)
14075 %
14076 % A description of each parameter follows:
14077 %
14078 % o nexus: Method XDisplayImage returns an image when the
14079 % user chooses 'Open Image' from the command menu or picks a tile
14080 % from the image directory. Otherwise a null image is returned.
14081 %
14082 % o display: Specifies a connection to an X server; returned from
14083 % XOpenDisplay.
14084 %
14085 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14086 %
14087 % o argv: Specifies the application's argument list.
14088 %
14089 % o argc: Specifies the number of arguments.
14090 %
14091 % o image: Specifies an address to an address of an Image structure;
14092 %
14093 % o exception: return any errors or warnings in this structure.
14094 %
14095 */
14096 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14097  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14098 {
14099 #define MagnifySize 256 /* must be a power of 2 */
14100 #define MagickMenus 10
14101 #define MagickTitle "Commands"
14102 
14103  const char
14104  *const CommandMenu[] =
14105  {
14106  "File",
14107  "Edit",
14108  "View",
14109  "Transform",
14110  "Enhance",
14111  "Effects",
14112  "F/X",
14113  "Image Edit",
14114  "Miscellany",
14115  "Help",
14116  (char *) NULL
14117  },
14118  *const FileMenu[] =
14119  {
14120  "Open...",
14121  "Next",
14122  "Former",
14123  "Select...",
14124  "Save...",
14125  "Print...",
14126  "Delete...",
14127  "New...",
14128  "Visual Directory...",
14129  "Quit",
14130  (char *) NULL
14131  },
14132  *const EditMenu[] =
14133  {
14134  "Undo",
14135  "Redo",
14136  "Cut",
14137  "Copy",
14138  "Paste",
14139  (char *) NULL
14140  },
14141  *const ViewMenu[] =
14142  {
14143  "Half Size",
14144  "Original Size",
14145  "Double Size",
14146  "Resize...",
14147  "Apply",
14148  "Refresh",
14149  "Restore",
14150  (char *) NULL
14151  },
14152  *const TransformMenu[] =
14153  {
14154  "Crop",
14155  "Chop",
14156  "Flop",
14157  "Flip",
14158  "Rotate Right",
14159  "Rotate Left",
14160  "Rotate...",
14161  "Shear...",
14162  "Roll...",
14163  "Trim Edges",
14164  (char *) NULL
14165  },
14166  *const EnhanceMenu[] =
14167  {
14168  "Hue...",
14169  "Saturation...",
14170  "Brightness...",
14171  "Gamma...",
14172  "Spiff",
14173  "Dull",
14174  "Contrast Stretch...",
14175  "Sigmoidal Contrast...",
14176  "Normalize",
14177  "Equalize",
14178  "Negate",
14179  "Grayscale",
14180  "Map...",
14181  "Quantize...",
14182  (char *) NULL
14183  },
14184  *const EffectsMenu[] =
14185  {
14186  "Despeckle",
14187  "Emboss",
14188  "Reduce Noise",
14189  "Add Noise...",
14190  "Sharpen...",
14191  "Blur...",
14192  "Threshold...",
14193  "Edge Detect...",
14194  "Spread...",
14195  "Shade...",
14196  "Raise...",
14197  "Segment...",
14198  (char *) NULL
14199  },
14200  *const FXMenu[] =
14201  {
14202  "Solarize...",
14203  "Sepia Tone...",
14204  "Swirl...",
14205  "Implode...",
14206  "Vignette...",
14207  "Wave...",
14208  "Oil Paint...",
14209  "Charcoal Draw...",
14210  (char *) NULL
14211  },
14212  *const ImageEditMenu[] =
14213  {
14214  "Annotate...",
14215  "Draw...",
14216  "Color...",
14217  "Matte...",
14218  "Composite...",
14219  "Add Border...",
14220  "Add Frame...",
14221  "Comment...",
14222  "Launch...",
14223  "Region of Interest...",
14224  (char *) NULL
14225  },
14226  *const MiscellanyMenu[] =
14227  {
14228  "Image Info",
14229  "Zoom Image",
14230  "Show Preview...",
14231  "Show Histogram",
14232  "Show Matte",
14233  "Background...",
14234  "Slide Show...",
14235  "Preferences...",
14236  (char *) NULL
14237  },
14238  *const HelpMenu[] =
14239  {
14240  "Overview",
14241  "Browse Documentation",
14242  "About Display",
14243  (char *) NULL
14244  },
14245  *const ShortCutsMenu[] =
14246  {
14247  "Next",
14248  "Former",
14249  "Open...",
14250  "Save...",
14251  "Print...",
14252  "Undo",
14253  "Restore",
14254  "Image Info",
14255  "Quit",
14256  (char *) NULL
14257  },
14258  *const VirtualMenu[] =
14259  {
14260  "Image Info",
14261  "Print",
14262  "Next",
14263  "Quit",
14264  (char *) NULL
14265  };
14266 
14267  const char
14268  *const *Menus[MagickMenus] =
14269  {
14270  FileMenu,
14271  EditMenu,
14272  ViewMenu,
14273  TransformMenu,
14274  EnhanceMenu,
14275  EffectsMenu,
14276  FXMenu,
14277  ImageEditMenu,
14278  MiscellanyMenu,
14279  HelpMenu
14280  };
14281 
14282  static DisplayCommand
14283  CommandMenus[] =
14284  {
14285  NullCommand,
14286  NullCommand,
14287  NullCommand,
14288  NullCommand,
14289  NullCommand,
14290  NullCommand,
14291  NullCommand,
14292  NullCommand,
14293  NullCommand,
14294  NullCommand,
14295  },
14296  FileCommands[] =
14297  {
14298  OpenCommand,
14299  NextCommand,
14300  FormerCommand,
14301  SelectCommand,
14302  SaveCommand,
14303  PrintCommand,
14304  DeleteCommand,
14305  NewCommand,
14306  VisualDirectoryCommand,
14307  QuitCommand
14308  },
14309  EditCommands[] =
14310  {
14311  UndoCommand,
14312  RedoCommand,
14313  CutCommand,
14314  CopyCommand,
14315  PasteCommand
14316  },
14317  ViewCommands[] =
14318  {
14319  HalfSizeCommand,
14320  OriginalSizeCommand,
14321  DoubleSizeCommand,
14322  ResizeCommand,
14323  ApplyCommand,
14324  RefreshCommand,
14325  RestoreCommand
14326  },
14327  TransformCommands[] =
14328  {
14329  CropCommand,
14330  ChopCommand,
14331  FlopCommand,
14332  FlipCommand,
14333  RotateRightCommand,
14334  RotateLeftCommand,
14335  RotateCommand,
14336  ShearCommand,
14337  RollCommand,
14338  TrimCommand
14339  },
14340  EnhanceCommands[] =
14341  {
14342  HueCommand,
14343  SaturationCommand,
14344  BrightnessCommand,
14345  GammaCommand,
14346  SpiffCommand,
14347  DullCommand,
14348  ContrastStretchCommand,
14349  SigmoidalContrastCommand,
14350  NormalizeCommand,
14351  EqualizeCommand,
14352  NegateCommand,
14353  GrayscaleCommand,
14354  MapCommand,
14355  QuantizeCommand
14356  },
14357  EffectsCommands[] =
14358  {
14359  DespeckleCommand,
14360  EmbossCommand,
14361  ReduceNoiseCommand,
14362  AddNoiseCommand,
14363  SharpenCommand,
14364  BlurCommand,
14365  ThresholdCommand,
14366  EdgeDetectCommand,
14367  SpreadCommand,
14368  ShadeCommand,
14369  RaiseCommand,
14370  SegmentCommand
14371  },
14372  FXCommands[] =
14373  {
14374  SolarizeCommand,
14375  SepiaToneCommand,
14376  SwirlCommand,
14377  ImplodeCommand,
14378  VignetteCommand,
14379  WaveCommand,
14380  OilPaintCommand,
14381  CharcoalDrawCommand
14382  },
14383  ImageEditCommands[] =
14384  {
14385  AnnotateCommand,
14386  DrawCommand,
14387  ColorCommand,
14388  MatteCommand,
14389  CompositeCommand,
14390  AddBorderCommand,
14391  AddFrameCommand,
14392  CommentCommand,
14393  LaunchCommand,
14394  RegionOfInterestCommand
14395  },
14396  MiscellanyCommands[] =
14397  {
14398  InfoCommand,
14399  ZoomCommand,
14400  ShowPreviewCommand,
14401  ShowHistogramCommand,
14402  ShowMatteCommand,
14403  BackgroundCommand,
14404  SlideShowCommand,
14405  PreferencesCommand
14406  },
14407  HelpCommands[] =
14408  {
14409  HelpCommand,
14410  BrowseDocumentationCommand,
14411  VersionCommand
14412  },
14413  ShortCutsCommands[] =
14414  {
14415  NextCommand,
14416  FormerCommand,
14417  OpenCommand,
14418  SaveCommand,
14419  PrintCommand,
14420  UndoCommand,
14421  RestoreCommand,
14422  InfoCommand,
14423  QuitCommand
14424  },
14425  VirtualCommands[] =
14426  {
14427  InfoCommand,
14428  PrintCommand,
14429  NextCommand,
14430  QuitCommand
14431  };
14432 
14433  static DisplayCommand
14434  *Commands[MagickMenus] =
14435  {
14436  FileCommands,
14437  EditCommands,
14438  ViewCommands,
14439  TransformCommands,
14440  EnhanceCommands,
14441  EffectsCommands,
14442  FXCommands,
14443  ImageEditCommands,
14444  MiscellanyCommands,
14445  HelpCommands
14446  };
14447 
14448  char
14449  command[MagickPathExtent],
14450  *directory,
14451  geometry[MagickPathExtent],
14452  resource_name[MagickPathExtent];
14453 
14454  DisplayCommand
14455  display_command;
14456 
14457  Image
14458  *display_image,
14459  *nexus;
14460 
14461  int
14462  entry,
14463  id;
14464 
14465  KeySym
14466  key_symbol;
14467 
14468  MagickStatusType
14469  context_mask,
14470  status;
14471 
14473  geometry_info;
14474 
14475  int
14476  i;
14477 
14478  static char
14479  working_directory[MagickPathExtent];
14480 
14481  static XPoint
14482  vid_info;
14483 
14484  static XWindowInfo
14485  *magick_windows[MaxXWindows];
14486 
14487  static unsigned int
14488  number_windows;
14489 
14490  struct stat
14491  attributes;
14492 
14493  time_t
14494  timer,
14495  timestamp,
14496  update_time;
14497 
14498  unsigned int
14499  height,
14500  width;
14501 
14502  size_t
14503  delay;
14504 
14505  WarningHandler
14506  warning_handler;
14507 
14508  Window
14509  root_window;
14510 
14511  XClassHint
14512  *class_hints;
14513 
14514  XEvent
14515  event;
14516 
14517  XFontStruct
14518  *font_info;
14519 
14520  XGCValues
14521  context_values;
14522 
14523  XPixelInfo
14524  *icon_pixel,
14525  *pixel;
14526 
14527  XResourceInfo
14528  *icon_resources;
14529 
14530  XStandardColormap
14531  *icon_map,
14532  *map_info;
14533 
14534  XVisualInfo
14535  *icon_visual,
14536  *visual_info;
14537 
14538  XWindowChanges
14539  window_changes;
14540 
14541  XWindows
14542  *windows;
14543 
14544  XWMHints
14545  *manager_hints;
14546 
14547  assert(image != (Image **) NULL);
14548  assert((*image)->signature == MagickCoreSignature);
14549  if (IsEventLogging() != MagickFalse)
14550  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14551  display_image=(*image);
14552  warning_handler=(WarningHandler) NULL;
14553  windows=XSetWindows((XWindows *) ~0);
14554  if (windows != (XWindows *) NULL)
14555  {
14556  int
14557  status;
14558 
14559  if (*working_directory == '\0')
14560  (void) CopyMagickString(working_directory,".",MagickPathExtent);
14561  status=chdir(working_directory);
14562  if (status == -1)
14563  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14564  "UnableToOpenFile","%s",working_directory);
14565  warning_handler=resource_info->display_warnings ?
14566  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14567  warning_handler=resource_info->display_warnings ?
14568  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14569  }
14570  else
14571  {
14572  /*
14573  Allocate windows structure.
14574  */
14575  resource_info->colors=display_image->colors;
14576  windows=XSetWindows(XInitializeWindows(display,resource_info));
14577  if (windows == (XWindows *) NULL)
14578  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14579  (*image)->filename);
14580  /*
14581  Initialize window id's.
14582  */
14583  number_windows=0;
14584  magick_windows[number_windows++]=(&windows->icon);
14585  magick_windows[number_windows++]=(&windows->backdrop);
14586  magick_windows[number_windows++]=(&windows->image);
14587  magick_windows[number_windows++]=(&windows->info);
14588  magick_windows[number_windows++]=(&windows->command);
14589  magick_windows[number_windows++]=(&windows->widget);
14590  magick_windows[number_windows++]=(&windows->popup);
14591  magick_windows[number_windows++]=(&windows->magnify);
14592  magick_windows[number_windows++]=(&windows->pan);
14593  for (i=0; i < (int) number_windows; i++)
14594  magick_windows[i]->id=(Window) NULL;
14595  vid_info.x=0;
14596  vid_info.y=0;
14597  }
14598  /*
14599  Initialize font info.
14600  */
14601  if (windows->font_info != (XFontStruct *) NULL)
14602  (void) XFreeFont(display,windows->font_info);
14603  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14604  if (windows->font_info == (XFontStruct *) NULL)
14605  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14606  resource_info->font);
14607  /*
14608  Initialize Standard Colormap.
14609  */
14610  map_info=windows->map_info;
14611  icon_map=windows->icon_map;
14612  visual_info=windows->visual_info;
14613  icon_visual=windows->icon_visual;
14614  pixel=windows->pixel_info;
14615  icon_pixel=windows->icon_pixel;
14616  font_info=windows->font_info;
14617  icon_resources=windows->icon_resources;
14618  class_hints=windows->class_hints;
14619  manager_hints=windows->manager_hints;
14620  root_window=XRootWindow(display,visual_info->screen);
14621  nexus=NewImageList();
14622  if (resource_info->debug != MagickFalse)
14623  {
14624  (void) LogMagickEvent(X11Event,GetMagickModule(),
14625  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14626  (double) display_image->scene,(double) display_image->columns,
14627  (double) display_image->rows);
14628  if (display_image->colors != 0)
14629  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14630  display_image->colors);
14631  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14632  display_image->magick);
14633  }
14634  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14635  map_info,pixel,exception);
14636  display_image->taint=MagickFalse;
14637  /*
14638  Initialize graphic context.
14639  */
14640  windows->context.id=(Window) NULL;
14641  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14642  resource_info,&windows->context);
14643  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14644  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14645  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14646  class_hints->res_class[0]);
14647  manager_hints->flags=InputHint | StateHint;
14648  manager_hints->input=MagickFalse;
14649  manager_hints->initial_state=WithdrawnState;
14650  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14651  &windows->context);
14652  if (resource_info->debug != MagickFalse)
14653  (void) LogMagickEvent(X11Event,GetMagickModule(),
14654  "Window id: 0x%lx (context)",windows->context.id);
14655  context_values.background=pixel->background_color.pixel;
14656  context_values.font=font_info->fid;
14657  context_values.foreground=pixel->foreground_color.pixel;
14658  context_values.graphics_exposures=MagickFalse;
14659  context_mask=(MagickStatusType)
14660  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14661  if (pixel->annotate_context != (GC) NULL)
14662  (void) XFreeGC(display,pixel->annotate_context);
14663  pixel->annotate_context=XCreateGC(display,windows->context.id,
14664  context_mask,&context_values);
14665  if (pixel->annotate_context == (GC) NULL)
14666  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14667  display_image->filename);
14668  context_values.background=pixel->depth_color.pixel;
14669  if (pixel->widget_context != (GC) NULL)
14670  (void) XFreeGC(display,pixel->widget_context);
14671  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14672  &context_values);
14673  if (pixel->widget_context == (GC) NULL)
14674  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14675  display_image->filename);
14676  context_values.background=pixel->foreground_color.pixel;
14677  context_values.foreground=pixel->background_color.pixel;
14678  context_values.plane_mask=context_values.background ^
14679  context_values.foreground;
14680  if (pixel->highlight_context != (GC) NULL)
14681  (void) XFreeGC(display,pixel->highlight_context);
14682  pixel->highlight_context=XCreateGC(display,windows->context.id,
14683  (size_t) (context_mask | GCPlaneMask),&context_values);
14684  if (pixel->highlight_context == (GC) NULL)
14685  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14686  display_image->filename);
14687  (void) XDestroyWindow(display,windows->context.id);
14688  /*
14689  Initialize icon window.
14690  */
14691  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14692  icon_resources,&windows->icon);
14693  windows->icon.geometry=resource_info->icon_geometry;
14694  XBestIconSize(display,&windows->icon,display_image);
14695  windows->icon.attributes.colormap=XDefaultColormap(display,
14696  icon_visual->screen);
14697  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14698  manager_hints->flags=InputHint | StateHint;
14699  manager_hints->input=MagickFalse;
14700  manager_hints->initial_state=IconicState;
14701  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14702  &windows->icon);
14703  if (resource_info->debug != MagickFalse)
14704  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14705  windows->icon.id);
14706  /*
14707  Initialize graphic context for icon window.
14708  */
14709  if (icon_pixel->annotate_context != (GC) NULL)
14710  (void) XFreeGC(display,icon_pixel->annotate_context);
14711  context_values.background=icon_pixel->background_color.pixel;
14712  context_values.foreground=icon_pixel->foreground_color.pixel;
14713  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14714  (size_t) (GCBackground | GCForeground),&context_values);
14715  if (icon_pixel->annotate_context == (GC) NULL)
14716  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14717  display_image->filename);
14718  windows->icon.annotate_context=icon_pixel->annotate_context;
14719  /*
14720  Initialize Image window.
14721  */
14722  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14723  &windows->image);
14724  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14725  if (resource_info->use_shared_memory == MagickFalse)
14726  windows->image.shared_memory=MagickFalse;
14727  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14728  {
14729  char
14730  *title;
14731 
14732  title=InterpretImageProperties(resource_info->image_info,display_image,
14733  resource_info->title,exception);
14734  (void) CloneString(&windows->image.name,title);
14735  (void) CloneString(&windows->image.icon_name,title);
14736  title=DestroyString(title);
14737  }
14738  else
14739  {
14740  char
14741  filename[MagickPathExtent],
14742  window_name[MagickPathExtent];
14743 
14744  /*
14745  Window name is the base of the filename.
14746  */
14747  GetPathComponent(display_image->magick_filename,TailPath,filename);
14748  if (display_image->scene == 0)
14749  (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14750  MagickPackageName,filename);
14751  else
14752  (void) FormatLocaleString(window_name,MagickPathExtent,
14753  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14754  (double) display_image->scene,(double) GetImageListLength(
14755  display_image));
14756  (void) CloneString(&windows->image.name,window_name);
14757  (void) CloneString(&windows->image.icon_name,filename);
14758  }
14759  if (resource_info->immutable)
14760  windows->image.immutable=MagickTrue;
14761  windows->image.use_pixmap=resource_info->use_pixmap;
14762  windows->image.geometry=resource_info->image_geometry;
14763  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14764  XDisplayWidth(display,visual_info->screen),
14765  XDisplayHeight(display,visual_info->screen));
14766  geometry_info.width=display_image->columns;
14767  geometry_info.height=display_image->rows;
14768  geometry_info.x=0;
14769  geometry_info.y=0;
14770  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14771  &geometry_info.width,&geometry_info.height);
14772  windows->image.width=(unsigned int) geometry_info.width;
14773  windows->image.height=(unsigned int) geometry_info.height;
14774  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14775  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14776  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14777  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14778  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14779  resource_info,&windows->backdrop);
14780  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14781  {
14782  /*
14783  Initialize backdrop window.
14784  */
14785  windows->backdrop.x=0;
14786  windows->backdrop.y=0;
14787  (void) CloneString(&windows->backdrop.name,"Backdrop");
14788  windows->backdrop.flags=(size_t) (USSize | USPosition);
14789  windows->backdrop.width=(unsigned int)
14790  XDisplayWidth(display,visual_info->screen);
14791  windows->backdrop.height=(unsigned int)
14792  XDisplayHeight(display,visual_info->screen);
14793  windows->backdrop.border_width=0;
14794  windows->backdrop.immutable=MagickTrue;
14795  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14796  ButtonReleaseMask;
14797  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14798  StructureNotifyMask;
14799  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14800  manager_hints->icon_window=windows->icon.id;
14801  manager_hints->input=MagickTrue;
14802  manager_hints->initial_state=resource_info->iconic ? IconicState :
14803  NormalState;
14804  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14805  &windows->backdrop);
14806  if (resource_info->debug != MagickFalse)
14807  (void) LogMagickEvent(X11Event,GetMagickModule(),
14808  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14809  (void) XMapWindow(display,windows->backdrop.id);
14810  (void) XClearWindow(display,windows->backdrop.id);
14811  if (windows->image.id != (Window) NULL)
14812  {
14813  (void) XDestroyWindow(display,windows->image.id);
14814  windows->image.id=(Window) NULL;
14815  }
14816  /*
14817  Position image in the center the backdrop.
14818  */
14819  windows->image.flags|=USPosition;
14820  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14821  ((int) windows->image.width/2);
14822  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14823  ((int) windows->image.height/2);
14824  }
14825  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14826  manager_hints->icon_window=windows->icon.id;
14827  manager_hints->input=MagickTrue;
14828  manager_hints->initial_state=resource_info->iconic ? IconicState :
14829  NormalState;
14830  if (windows->group_leader.id != (Window) NULL)
14831  {
14832  /*
14833  Follow the leader.
14834  */
14835  manager_hints->flags|=WindowGroupHint;
14836  manager_hints->window_group=windows->group_leader.id;
14837  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14838  if (resource_info->debug != MagickFalse)
14839  (void) LogMagickEvent(X11Event,GetMagickModule(),
14840  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14841  }
14842  XMakeWindow(display,
14843  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14844  argv,argc,class_hints,manager_hints,&windows->image);
14845  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14846  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14847  if (windows->group_leader.id != (Window) NULL)
14848  (void) XSetTransientForHint(display,windows->image.id,
14849  windows->group_leader.id);
14850  if (resource_info->debug != MagickFalse)
14851  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14852  windows->image.id);
14853  /*
14854  Initialize Info widget.
14855  */
14856  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14857  &windows->info);
14858  (void) CloneString(&windows->info.name,"Info");
14859  (void) CloneString(&windows->info.icon_name,"Info");
14860  windows->info.border_width=1;
14861  windows->info.x=2;
14862  windows->info.y=2;
14863  windows->info.flags|=PPosition;
14864  windows->info.attributes.win_gravity=UnmapGravity;
14865  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14866  StructureNotifyMask;
14867  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14868  manager_hints->input=MagickFalse;
14869  manager_hints->initial_state=NormalState;
14870  manager_hints->window_group=windows->image.id;
14871  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14872  &windows->info);
14873  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14874  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14875  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14876  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14877  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14878  if (windows->image.mapped != MagickFalse)
14879  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14880  if (resource_info->debug != MagickFalse)
14881  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14882  windows->info.id);
14883  /*
14884  Initialize Command widget.
14885  */
14886  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887  resource_info,&windows->command);
14888  windows->command.data=MagickMenus;
14889  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14890  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14891  resource_info->client_name);
14892  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14893  resource_name,"geometry",(char *) NULL);
14894  (void) CloneString(&windows->command.name,MagickTitle);
14895  windows->command.border_width=0;
14896  windows->command.flags|=PPosition;
14897  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14898  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14899  OwnerGrabButtonMask | StructureNotifyMask;
14900  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14901  manager_hints->input=MagickTrue;
14902  manager_hints->initial_state=NormalState;
14903  manager_hints->window_group=windows->image.id;
14904  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14905  &windows->command);
14906  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14907  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14908  HighlightHeight);
14909  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14910  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14911  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14912  if (windows->command.mapped != MagickFalse)
14913  (void) XMapRaised(display,windows->command.id);
14914  if (resource_info->debug != MagickFalse)
14915  (void) LogMagickEvent(X11Event,GetMagickModule(),
14916  "Window id: 0x%lx (command)",windows->command.id);
14917  /*
14918  Initialize Widget window.
14919  */
14920  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14921  resource_info,&windows->widget);
14922  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14923  resource_info->client_name);
14924  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14925  resource_name,"geometry",(char *) NULL);
14926  windows->widget.border_width=0;
14927  windows->widget.flags|=PPosition;
14928  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14929  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14930  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14931  StructureNotifyMask;
14932  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14933  manager_hints->input=MagickTrue;
14934  manager_hints->initial_state=NormalState;
14935  manager_hints->window_group=windows->image.id;
14936  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14937  &windows->widget);
14938  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14939  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14940  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14941  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14942  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14943  if (resource_info->debug != MagickFalse)
14944  (void) LogMagickEvent(X11Event,GetMagickModule(),
14945  "Window id: 0x%lx (widget)",windows->widget.id);
14946  /*
14947  Initialize popup window.
14948  */
14949  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14950  resource_info,&windows->popup);
14951  windows->popup.border_width=0;
14952  windows->popup.flags|=PPosition;
14953  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14954  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14955  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14956  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14957  manager_hints->input=MagickTrue;
14958  manager_hints->initial_state=NormalState;
14959  manager_hints->window_group=windows->image.id;
14960  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14961  &windows->popup);
14962  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14963  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14964  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14965  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14966  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14967  if (resource_info->debug != MagickFalse)
14968  (void) LogMagickEvent(X11Event,GetMagickModule(),
14969  "Window id: 0x%lx (pop up)",windows->popup.id);
14970  /*
14971  Initialize Magnify window and cursor.
14972  */
14973  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14974  resource_info,&windows->magnify);
14975  if (resource_info->use_shared_memory == MagickFalse)
14976  windows->magnify.shared_memory=MagickFalse;
14977  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14978  resource_info->client_name);
14979  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14980  resource_name,"geometry",(char *) NULL);
14981  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14982  "Magnify %uX",resource_info->magnify);
14983  if (windows->magnify.cursor != (Cursor) NULL)
14984  (void) XFreeCursor(display,windows->magnify.cursor);
14985  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14986  map_info->colormap,resource_info->background_color,
14987  resource_info->foreground_color);
14988  if (windows->magnify.cursor == (Cursor) NULL)
14989  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14990  display_image->filename);
14991  windows->magnify.width=MagnifySize;
14992  windows->magnify.height=MagnifySize;
14993  windows->magnify.flags|=PPosition;
14994  windows->magnify.min_width=MagnifySize;
14995  windows->magnify.min_height=MagnifySize;
14996  windows->magnify.width_inc=MagnifySize;
14997  windows->magnify.height_inc=MagnifySize;
14998  windows->magnify.data=resource_info->magnify;
14999  windows->magnify.attributes.cursor=windows->magnify.cursor;
15000  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
15001  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
15002  StructureNotifyMask;
15003  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15004  manager_hints->input=MagickTrue;
15005  manager_hints->initial_state=NormalState;
15006  manager_hints->window_group=windows->image.id;
15007  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15008  &windows->magnify);
15009  if (resource_info->debug != MagickFalse)
15010  (void) LogMagickEvent(X11Event,GetMagickModule(),
15011  "Window id: 0x%lx (magnify)",windows->magnify.id);
15012  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15013  /*
15014  Initialize panning window.
15015  */
15016  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15017  resource_info,&windows->pan);
15018  (void) CloneString(&windows->pan.name,"Pan Icon");
15019  windows->pan.width=windows->icon.width;
15020  windows->pan.height=windows->icon.height;
15021  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15022  resource_info->client_name);
15023  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15024  resource_name,"geometry",(char *) NULL);
15025  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15026  &windows->pan.width,&windows->pan.height);
15027  windows->pan.flags|=PPosition;
15028  windows->pan.immutable=MagickTrue;
15029  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15030  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15031  StructureNotifyMask;
15032  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15033  manager_hints->input=MagickFalse;
15034  manager_hints->initial_state=NormalState;
15035  manager_hints->window_group=windows->image.id;
15036  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15037  &windows->pan);
15038  if (resource_info->debug != MagickFalse)
15039  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15040  windows->pan.id);
15041  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15042  if (windows->info.mapped != MagickFalse)
15043  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15044  if ((windows->image.mapped == MagickFalse) ||
15045  (windows->backdrop.id != (Window) NULL))
15046  (void) XMapWindow(display,windows->image.id);
15047  /*
15048  Set our progress monitor and warning handlers.
15049  */
15050  if (warning_handler == (WarningHandler) NULL)
15051  {
15052  warning_handler=resource_info->display_warnings ?
15053  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15054  warning_handler=resource_info->display_warnings ?
15055  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15056  }
15057  /*
15058  Initialize Image and Magnify X images.
15059  */
15060  windows->image.x=0;
15061  windows->image.y=0;
15062  windows->magnify.shape=MagickFalse;
15063  width=(unsigned int) display_image->columns;
15064  height=(unsigned int) display_image->rows;
15065  if ((display_image->columns != width) || (display_image->rows != height))
15066  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15067  display_image->filename);
15068  status=XMakeImage(display,resource_info,&windows->image,display_image,
15069  width,height,exception);
15070  if (status == MagickFalse)
15071  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15072  display_image->filename);
15073  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15074  windows->magnify.width,windows->magnify.height,exception);
15075  if (status == MagickFalse)
15076  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15077  display_image->filename);
15078  if (windows->magnify.mapped != MagickFalse)
15079  (void) XMapRaised(display,windows->magnify.id);
15080  if (windows->pan.mapped != MagickFalse)
15081  (void) XMapRaised(display,windows->pan.id);
15082  windows->image.window_changes.width=(int) display_image->columns;
15083  windows->image.window_changes.height=(int) display_image->rows;
15084  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15085  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15086  (void) XSync(display,MagickFalse);
15087  /*
15088  Respond to events.
15089  */
15090  delay=display_image->delay/(size_t)
15091  MagickMax(display_image->ticks_per_second,1L);
15092  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15093  update_time=0;
15094  if (resource_info->update != MagickFalse)
15095  {
15096  MagickBooleanType
15097  status;
15098 
15099  /*
15100  Determine when file data was last modified.
15101  */
15102  status=GetPathAttributes(display_image->filename,&attributes);
15103  if (status != MagickFalse)
15104  update_time=attributes.st_mtime;
15105  }
15106  *state&=(unsigned int) (~FormerImageState);
15107  *state&=(unsigned int) (~MontageImageState);
15108  *state&=(unsigned int) (~NextImageState);
15109  do
15110  {
15111  /*
15112  Handle a window event.
15113  */
15114  if (windows->image.mapped != MagickFalse)
15115  if ((display_image->delay != 0) || (resource_info->update != 0))
15116  {
15117  if (timer < GetMagickTime())
15118  {
15119  if (resource_info->update == MagickFalse)
15120  *state|=NextImageState | ExitState;
15121  else
15122  {
15123  MagickBooleanType
15124  status;
15125 
15126  /*
15127  Determine if image file was modified.
15128  */
15129  status=GetPathAttributes(display_image->filename,&attributes);
15130  if (status != MagickFalse)
15131  if (update_time != attributes.st_mtime)
15132  {
15133  /*
15134  Redisplay image.
15135  */
15136  (void) FormatLocaleString(
15137  resource_info->image_info->filename,MagickPathExtent,
15138  "%s:%s",display_image->magick,
15139  display_image->filename);
15140  nexus=ReadImage(resource_info->image_info,exception);
15141  if (nexus != (Image *) NULL)
15142  *state|=NextImageState | ExitState;
15143  }
15144  delay=display_image->delay/(size_t) MagickMax(
15145  display_image->ticks_per_second,1L);
15146  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15147  }
15148  }
15149  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15150  {
15151  /*
15152  Do not block if delay > 0.
15153  */
15154  XDelay(display,SuspendTime << 2);
15155  continue;
15156  }
15157  }
15158  timestamp=GetMagickTime();
15159  (void) XNextEvent(display,&event);
15160  if ((windows->image.stasis == MagickFalse) ||
15161  (windows->magnify.stasis == MagickFalse))
15162  {
15163  if ((GetMagickTime()-timestamp) > 0)
15164  {
15165  windows->image.stasis=MagickTrue;
15166  windows->magnify.stasis=MagickTrue;
15167  }
15168  }
15169  if (event.xany.window == windows->command.id)
15170  {
15171  /*
15172  Select a command from the Command widget.
15173  */
15174  id=XCommandWidget(display,windows,CommandMenu,&event);
15175  if (id < 0)
15176  continue;
15177  (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15178  display_command=CommandMenus[id];
15179  if (id < MagickMenus)
15180  {
15181  /*
15182  Select a command from a pop-up menu.
15183  */
15184  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15185  command);
15186  if (entry < 0)
15187  continue;
15188  (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15189  display_command=Commands[id][entry];
15190  }
15191  if (display_command != NullCommand)
15192  nexus=XMagickCommand(display,resource_info,windows,display_command,
15193  &display_image,exception);
15194  continue;
15195  }
15196  switch (event.type)
15197  {
15198  case ButtonPress:
15199  {
15200  if (resource_info->debug != MagickFalse)
15201  (void) LogMagickEvent(X11Event,GetMagickModule(),
15202  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15203  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15204  if ((event.xbutton.button == Button3) &&
15205  (event.xbutton.state & Mod1Mask))
15206  {
15207  /*
15208  Convert Alt-Button3 to Button2.
15209  */
15210  event.xbutton.button=Button2;
15211  event.xbutton.state&=(unsigned int) (~Mod1Mask);
15212  }
15213  if (event.xbutton.window == windows->backdrop.id)
15214  {
15215  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15216  event.xbutton.time);
15217  break;
15218  }
15219  if (event.xbutton.window == windows->image.id)
15220  {
15221  switch (event.xbutton.button)
15222  {
15223  case Button1:
15224  {
15225  if (resource_info->immutable)
15226  {
15227  /*
15228  Select a command from the Virtual menu.
15229  */
15230  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15231  command);
15232  if (entry >= 0)
15233  nexus=XMagickCommand(display,resource_info,windows,
15234  VirtualCommands[entry],&display_image,exception);
15235  break;
15236  }
15237  /*
15238  Map/unmap Command widget.
15239  */
15240  if (windows->command.mapped != MagickFalse)
15241  (void) XWithdrawWindow(display,windows->command.id,
15242  windows->command.screen);
15243  else
15244  {
15245  (void) XCommandWidget(display,windows,CommandMenu,
15246  (XEvent *) NULL);
15247  (void) XMapRaised(display,windows->command.id);
15248  }
15249  break;
15250  }
15251  case Button2:
15252  {
15253  /*
15254  User pressed the image magnify button.
15255  */
15256  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15257  &display_image,exception);
15258  XMagnifyImage(display,windows,&event,exception);
15259  break;
15260  }
15261  case Button3:
15262  {
15263  if (resource_info->immutable)
15264  {
15265  /*
15266  Select a command from the Virtual menu.
15267  */
15268  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15269  command);
15270  if (entry >= 0)
15271  nexus=XMagickCommand(display,resource_info,windows,
15272  VirtualCommands[entry],&display_image,exception);
15273  break;
15274  }
15275  if (display_image->montage != (char *) NULL)
15276  {
15277  /*
15278  Open or delete a tile from a visual image directory.
15279  */
15280  nexus=XTileImage(display,resource_info,windows,
15281  display_image,&event,exception);
15282  if (nexus != (Image *) NULL)
15283  *state|=MontageImageState | NextImageState | ExitState;
15284  vid_info.x=(short int) windows->image.x;
15285  vid_info.y=(short int) windows->image.y;
15286  break;
15287  }
15288  /*
15289  Select a command from the Short Cuts menu.
15290  */
15291  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15292  command);
15293  if (entry >= 0)
15294  nexus=XMagickCommand(display,resource_info,windows,
15295  ShortCutsCommands[entry],&display_image,exception);
15296  break;
15297  }
15298  case Button4:
15299  {
15300  /*
15301  Wheel up.
15302  */
15303  XTranslateImage(display,windows,*image,XK_Up);
15304  break;
15305  }
15306  case Button5:
15307  {
15308  /*
15309  Wheel down.
15310  */
15311  XTranslateImage(display,windows,*image,XK_Down);
15312  break;
15313  }
15314  default:
15315  break;
15316  }
15317  break;
15318  }
15319  if (event.xbutton.window == windows->magnify.id)
15320  {
15321  const char
15322  *const MagnifyMenu[] =
15323  {
15324  "2",
15325  "4",
15326  "5",
15327  "6",
15328  "7",
15329  "8",
15330  "9",
15331  "3",
15332  (char *) NULL,
15333  };
15334 
15335  int
15336  factor;
15337 
15338  static KeySym
15339  MagnifyCommands[] =
15340  {
15341  XK_2,
15342  XK_4,
15343  XK_5,
15344  XK_6,
15345  XK_7,
15346  XK_8,
15347  XK_9,
15348  XK_3
15349  };
15350 
15351  /*
15352  Select a magnify factor from the pop-up menu.
15353  */
15354  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15355  if (factor >= 0)
15356  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15357  exception);
15358  break;
15359  }
15360  if (event.xbutton.window == windows->pan.id)
15361  {
15362  switch (event.xbutton.button)
15363  {
15364  case Button4:
15365  {
15366  /*
15367  Wheel up.
15368  */
15369  XTranslateImage(display,windows,*image,XK_Up);
15370  break;
15371  }
15372  case Button5:
15373  {
15374  /*
15375  Wheel down.
15376  */
15377  XTranslateImage(display,windows,*image,XK_Down);
15378  break;
15379  }
15380  default:
15381  {
15382  XPanImage(display,windows,&event,exception);
15383  break;
15384  }
15385  }
15386  break;
15387  }
15388  delay=display_image->delay/(size_t)
15389  MagickMax(display_image->ticks_per_second,1L);
15390  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15391  break;
15392  }
15393  case ButtonRelease:
15394  {
15395  if (resource_info->debug != MagickFalse)
15396  (void) LogMagickEvent(X11Event,GetMagickModule(),
15397  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15398  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15399  break;
15400  }
15401  case ClientMessage:
15402  {
15403  if (resource_info->debug != MagickFalse)
15404  (void) LogMagickEvent(X11Event,GetMagickModule(),
15405  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15406  event.xclient.message_type,event.xclient.format,(unsigned long)
15407  event.xclient.data.l[0]);
15408  if (event.xclient.message_type == windows->im_protocols)
15409  {
15410  if (*event.xclient.data.l == (long) windows->im_update_widget)
15411  {
15412  (void) CloneString(&windows->command.name,MagickTitle);
15413  windows->command.data=MagickMenus;
15414  (void) XCommandWidget(display,windows,CommandMenu,
15415  (XEvent *) NULL);
15416  break;
15417  }
15418  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15419  {
15420  /*
15421  Update graphic context and window colormap.
15422  */
15423  for (i=0; i < (int) number_windows; i++)
15424  {
15425  if (magick_windows[i]->id == windows->icon.id)
15426  continue;
15427  context_values.background=pixel->background_color.pixel;
15428  context_values.foreground=pixel->foreground_color.pixel;
15429  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15430  context_mask,&context_values);
15431  (void) XChangeGC(display,magick_windows[i]->widget_context,
15432  context_mask,&context_values);
15433  context_values.background=pixel->foreground_color.pixel;
15434  context_values.foreground=pixel->background_color.pixel;
15435  context_values.plane_mask=context_values.background ^
15436  context_values.foreground;
15437  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15438  (size_t) (context_mask | GCPlaneMask),
15439  &context_values);
15440  magick_windows[i]->attributes.background_pixel=
15441  pixel->background_color.pixel;
15442  magick_windows[i]->attributes.border_pixel=
15443  pixel->border_color.pixel;
15444  magick_windows[i]->attributes.colormap=map_info->colormap;
15445  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15446  (unsigned long) magick_windows[i]->mask,
15447  &magick_windows[i]->attributes);
15448  }
15449  if (windows->pan.mapped != MagickFalse)
15450  {
15451  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15452  windows->pan.pixmap);
15453  (void) XClearWindow(display,windows->pan.id);
15454  XDrawPanRectangle(display,windows);
15455  }
15456  if (windows->backdrop.id != (Window) NULL)
15457  (void) XInstallColormap(display,map_info->colormap);
15458  break;
15459  }
15460  if (*event.xclient.data.l == (long) windows->im_former_image)
15461  {
15462  *state|=FormerImageState | ExitState;
15463  break;
15464  }
15465  if (*event.xclient.data.l == (long) windows->im_next_image)
15466  {
15467  *state|=NextImageState | ExitState;
15468  break;
15469  }
15470  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15471  {
15472  *state|=RetainColorsState;
15473  break;
15474  }
15475  if (*event.xclient.data.l == (long) windows->im_exit)
15476  {
15477  *state|=ExitState;
15478  break;
15479  }
15480  break;
15481  }
15482  if (event.xclient.message_type == windows->dnd_protocols)
15483  {
15484  Atom
15485  selection,
15486  type;
15487 
15488  int
15489  format,
15490  status;
15491 
15492  unsigned char
15493  *data;
15494 
15495  unsigned long
15496  after,
15497  length;
15498 
15499  /*
15500  Display image named by the Drag-and-Drop selection.
15501  */
15502  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15503  break;
15504  selection=XInternAtom(display,"DndSelection",MagickFalse);
15505  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15506  MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15507  &length,&after,&data);
15508  if ((status != Success) || (length == 0))
15509  break;
15510  if (*event.xclient.data.l == 2)
15511  {
15512  /*
15513  Offix DND.
15514  */
15515  (void) CopyMagickString(resource_info->image_info->filename,
15516  (char *) data,MagickPathExtent);
15517  }
15518  else
15519  {
15520  /*
15521  XDND.
15522  */
15523  if (strncmp((char *) data, "file:", 5) != 0)
15524  {
15525  (void) XFree((void *) data);
15526  break;
15527  }
15528  (void) CopyMagickString(resource_info->image_info->filename,
15529  ((char *) data)+5,MagickPathExtent);
15530  }
15531  nexus=ReadImage(resource_info->image_info,exception);
15532  CatchException(exception);
15533  if (nexus != (Image *) NULL)
15534  *state|=NextImageState | ExitState;
15535  (void) XFree((void *) data);
15536  break;
15537  }
15538  /*
15539  If client window delete message, exit.
15540  */
15541  if (event.xclient.message_type != windows->wm_protocols)
15542  break;
15543  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15544  break;
15545  (void) XWithdrawWindow(display,event.xclient.window,
15546  visual_info->screen);
15547  if (event.xclient.window == windows->image.id)
15548  {
15549  *state|=ExitState;
15550  break;
15551  }
15552  if (event.xclient.window == windows->pan.id)
15553  {
15554  /*
15555  Restore original image size when pan window is deleted.
15556  */
15557  windows->image.window_changes.width=windows->image.ximage->width;
15558  windows->image.window_changes.height=windows->image.ximage->height;
15559  (void) XConfigureImage(display,resource_info,windows,
15560  display_image,exception);
15561  }
15562  break;
15563  }
15564  case ConfigureNotify:
15565  {
15566  if (resource_info->debug != MagickFalse)
15567  (void) LogMagickEvent(X11Event,GetMagickModule(),
15568  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15569  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15570  event.xconfigure.y,event.xconfigure.send_event);
15571  if (event.xconfigure.window == windows->image.id)
15572  {
15573  /*
15574  Image window has a new configuration.
15575  */
15576  if (event.xconfigure.send_event != 0)
15577  {
15578  XWindowChanges
15579  window_changes;
15580 
15581  /*
15582  Position the transient windows relative of the Image window.
15583  */
15584  if (windows->command.geometry == (char *) NULL)
15585  if (windows->command.mapped == MagickFalse)
15586  {
15587  windows->command.x=event.xconfigure.x-(int)
15588  windows->command.width-25;
15589  windows->command.y=event.xconfigure.y;
15590  XConstrainWindowPosition(display,&windows->command);
15591  window_changes.x=windows->command.x;
15592  window_changes.y=windows->command.y;
15593  (void) XReconfigureWMWindow(display,windows->command.id,
15594  windows->command.screen,(unsigned int) (CWX | CWY),
15595  &window_changes);
15596  }
15597  if (windows->widget.geometry == (char *) NULL)
15598  if (windows->widget.mapped == MagickFalse)
15599  {
15600  windows->widget.x=event.xconfigure.x+
15601  event.xconfigure.width/10;
15602  windows->widget.y=event.xconfigure.y+
15603  event.xconfigure.height/10;
15604  XConstrainWindowPosition(display,&windows->widget);
15605  window_changes.x=windows->widget.x;
15606  window_changes.y=windows->widget.y;
15607  (void) XReconfigureWMWindow(display,windows->widget.id,
15608  windows->widget.screen,(unsigned int) (CWX | CWY),
15609  &window_changes);
15610  }
15611  if (windows->magnify.geometry == (char *) NULL)
15612  if (windows->magnify.mapped == MagickFalse)
15613  {
15614  windows->magnify.x=event.xconfigure.x+
15615  event.xconfigure.width+25;
15616  windows->magnify.y=event.xconfigure.y;
15617  XConstrainWindowPosition(display,&windows->magnify);
15618  window_changes.x=windows->magnify.x;
15619  window_changes.y=windows->magnify.y;
15620  (void) XReconfigureWMWindow(display,windows->magnify.id,
15621  windows->magnify.screen,(unsigned int) (CWX | CWY),
15622  &window_changes);
15623  }
15624  if (windows->pan.geometry == (char *) NULL)
15625  if (windows->pan.mapped == MagickFalse)
15626  {
15627  windows->pan.x=event.xconfigure.x+(int)
15628  event.xconfigure.width+25;
15629  windows->pan.y=event.xconfigure.y+(int)
15630  windows->magnify.height+50;
15631  XConstrainWindowPosition(display,&windows->pan);
15632  window_changes.x=windows->pan.x;
15633  window_changes.y=windows->pan.y;
15634  (void) XReconfigureWMWindow(display,windows->pan.id,
15635  windows->pan.screen,(unsigned int) (CWX | CWY),
15636  &window_changes);
15637  }
15638  }
15639  if ((event.xconfigure.width == (int) windows->image.width) &&
15640  (event.xconfigure.height == (int) windows->image.height))
15641  break;
15642  windows->image.width=(unsigned int) event.xconfigure.width;
15643  windows->image.height=(unsigned int) event.xconfigure.height;
15644  windows->image.x=0;
15645  windows->image.y=0;
15646  if (display_image->montage != (char *) NULL)
15647  {
15648  windows->image.x=vid_info.x;
15649  windows->image.y=vid_info.y;
15650  }
15651  if (windows->image.mapped != MagickFalse &&
15652  windows->image.stasis != MagickFalse)
15653  {
15654  /*
15655  Update image window configuration.
15656  */
15657  windows->image.window_changes.width=event.xconfigure.width;
15658  windows->image.window_changes.height=event.xconfigure.height;
15659  (void) XConfigureImage(display,resource_info,windows,
15660  display_image,exception);
15661  }
15662  /*
15663  Update pan window configuration.
15664  */
15665  if ((event.xconfigure.width < windows->image.ximage->width) ||
15666  (event.xconfigure.height < windows->image.ximage->height))
15667  {
15668  (void) XMapRaised(display,windows->pan.id);
15669  XDrawPanRectangle(display,windows);
15670  }
15671  else
15672  if (windows->pan.mapped != MagickFalse)
15673  (void) XWithdrawWindow(display,windows->pan.id,
15674  windows->pan.screen);
15675  break;
15676  }
15677  if (event.xconfigure.window == windows->magnify.id)
15678  {
15679  unsigned int
15680  magnify;
15681 
15682  /*
15683  Magnify window has a new configuration.
15684  */
15685  windows->magnify.width=(unsigned int) event.xconfigure.width;
15686  windows->magnify.height=(unsigned int) event.xconfigure.height;
15687  if (windows->magnify.mapped == MagickFalse)
15688  break;
15689  magnify=1;
15690  while ((int) magnify <= event.xconfigure.width)
15691  magnify<<=1;
15692  while ((int) magnify <= event.xconfigure.height)
15693  magnify<<=1;
15694  magnify>>=1;
15695  if (((int) magnify != event.xconfigure.width) ||
15696  ((int) magnify != event.xconfigure.height))
15697  {
15698  window_changes.width=(int) magnify;
15699  window_changes.height=(int) magnify;
15700  (void) XReconfigureWMWindow(display,windows->magnify.id,
15701  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15702  &window_changes);
15703  break;
15704  }
15705  if (windows->magnify.mapped != MagickFalse &&
15706  windows->magnify.stasis != MagickFalse)
15707  {
15708  status=XMakeImage(display,resource_info,&windows->magnify,
15709  display_image,windows->magnify.width,windows->magnify.height,
15710  exception);
15711  XMakeMagnifyImage(display,windows,exception);
15712  }
15713  break;
15714  }
15715  if (windows->magnify.mapped != MagickFalse &&
15716  (event.xconfigure.window == windows->pan.id))
15717  {
15718  /*
15719  Pan icon window has a new configuration.
15720  */
15721  if (event.xconfigure.send_event != 0)
15722  {
15723  windows->pan.x=event.xconfigure.x;
15724  windows->pan.y=event.xconfigure.y;
15725  }
15726  windows->pan.width=(unsigned int) event.xconfigure.width;
15727  windows->pan.height=(unsigned int) event.xconfigure.height;
15728  break;
15729  }
15730  if (event.xconfigure.window == windows->icon.id)
15731  {
15732  /*
15733  Icon window has a new configuration.
15734  */
15735  windows->icon.width=(unsigned int) event.xconfigure.width;
15736  windows->icon.height=(unsigned int) event.xconfigure.height;
15737  break;
15738  }
15739  break;
15740  }
15741  case DestroyNotify:
15742  {
15743  /*
15744  Group leader has exited.
15745  */
15746  if (resource_info->debug != MagickFalse)
15747  (void) LogMagickEvent(X11Event,GetMagickModule(),
15748  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15749  if (event.xdestroywindow.window == windows->group_leader.id)
15750  {
15751  *state|=ExitState;
15752  break;
15753  }
15754  break;
15755  }
15756  case EnterNotify:
15757  {
15758  /*
15759  Selectively install colormap.
15760  */
15761  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15762  if (event.xcrossing.mode != NotifyUngrab)
15763  XInstallColormap(display,map_info->colormap);
15764  break;
15765  }
15766  case Expose:
15767  {
15768  if (resource_info->debug != MagickFalse)
15769  (void) LogMagickEvent(X11Event,GetMagickModule(),
15770  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15771  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15772  event.xexpose.y);
15773  /*
15774  Refresh windows that are now exposed.
15775  */
15776  if ((event.xexpose.window == windows->image.id) &&
15777  windows->image.mapped != MagickFalse)
15778  {
15779  XRefreshWindow(display,&windows->image,&event);
15780  delay=display_image->delay/(size_t) MagickMax(
15781  display_image->ticks_per_second,1L);
15782  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15783  break;
15784  }
15785  if ((event.xexpose.window == windows->magnify.id) &&
15786  windows->magnify.mapped != MagickFalse)
15787  {
15788  XMakeMagnifyImage(display,windows,exception);
15789  break;
15790  }
15791  if (event.xexpose.window == windows->pan.id)
15792  {
15793  XDrawPanRectangle(display,windows);
15794  break;
15795  }
15796  if (event.xexpose.window == windows->icon.id)
15797  {
15798  XRefreshWindow(display,&windows->icon,&event);
15799  break;
15800  }
15801  break;
15802  }
15803  case KeyPress:
15804  {
15805  int
15806  length;
15807 
15808  /*
15809  Respond to a user key press.
15810  */
15811  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15812  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15813  *(command+length)='\0';
15814  if (resource_info->debug != MagickFalse)
15815  (void) LogMagickEvent(X11Event,GetMagickModule(),
15816  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15817  key_symbol,command);
15818  if (event.xkey.window == windows->image.id)
15819  {
15820  display_command=XImageWindowCommand(display,resource_info,windows,
15821  event.xkey.state,key_symbol,&display_image,exception);
15822  if (display_command != NullCommand)
15823  nexus=XMagickCommand(display,resource_info,windows,
15824  display_command,&display_image,exception);
15825  }
15826  if (event.xkey.window == windows->magnify.id)
15827  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15828  exception);
15829  if (event.xkey.window == windows->pan.id)
15830  {
15831  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15832  (void) XWithdrawWindow(display,windows->pan.id,
15833  windows->pan.screen);
15834  else
15835  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15836  XTextViewHelp(display,resource_info,windows,MagickFalse,
15837  "Help Viewer - Image Pan",ImagePanHelp);
15838  else
15839  XTranslateImage(display,windows,*image,key_symbol);
15840  }
15841  delay=display_image->delay/(size_t) MagickMax(
15842  display_image->ticks_per_second,1L);
15843  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15844  break;
15845  }
15846  case KeyRelease:
15847  {
15848  /*
15849  Respond to a user key release.
15850  */
15851  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15852  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15853  if (resource_info->debug != MagickFalse)
15854  (void) LogMagickEvent(X11Event,GetMagickModule(),
15855  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15856  break;
15857  }
15858  case LeaveNotify:
15859  {
15860  /*
15861  Selectively uninstall colormap.
15862  */
15863  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15864  if (event.xcrossing.mode != NotifyUngrab)
15865  XUninstallColormap(display,map_info->colormap);
15866  break;
15867  }
15868  case MapNotify:
15869  {
15870  if (resource_info->debug != MagickFalse)
15871  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15872  event.xmap.window);
15873  if (event.xmap.window == windows->backdrop.id)
15874  {
15875  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15876  CurrentTime);
15877  windows->backdrop.mapped=MagickTrue;
15878  break;
15879  }
15880  if (event.xmap.window == windows->image.id)
15881  {
15882  if (windows->backdrop.id != (Window) NULL)
15883  (void) XInstallColormap(display,map_info->colormap);
15884  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15885  {
15886  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15887  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15888  }
15889  if (((int) windows->image.width < windows->image.ximage->width) ||
15890  ((int) windows->image.height < windows->image.ximage->height))
15891  (void) XMapRaised(display,windows->pan.id);
15892  windows->image.mapped=MagickTrue;
15893  break;
15894  }
15895  if (event.xmap.window == windows->magnify.id)
15896  {
15897  XMakeMagnifyImage(display,windows,exception);
15898  windows->magnify.mapped=MagickTrue;
15899  (void) XWithdrawWindow(display,windows->info.id,
15900  windows->info.screen);
15901  break;
15902  }
15903  if (event.xmap.window == windows->pan.id)
15904  {
15905  XMakePanImage(display,resource_info,windows,display_image,
15906  exception);
15907  windows->pan.mapped=MagickTrue;
15908  break;
15909  }
15910  if (event.xmap.window == windows->info.id)
15911  {
15912  windows->info.mapped=MagickTrue;
15913  break;
15914  }
15915  if (event.xmap.window == windows->icon.id)
15916  {
15917  MagickBooleanType
15918  taint;
15919 
15920  /*
15921  Create an icon image.
15922  */
15923  taint=display_image->taint;
15924  XMakeStandardColormap(display,icon_visual,icon_resources,
15925  display_image,icon_map,icon_pixel,exception);
15926  (void) XMakeImage(display,icon_resources,&windows->icon,
15927  display_image,windows->icon.width,windows->icon.height,
15928  exception);
15929  display_image->taint=taint;
15930  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15931  windows->icon.pixmap);
15932  (void) XClearWindow(display,windows->icon.id);
15933  (void) XWithdrawWindow(display,windows->info.id,
15934  windows->info.screen);
15935  windows->icon.mapped=MagickTrue;
15936  break;
15937  }
15938  if (event.xmap.window == windows->command.id)
15939  {
15940  windows->command.mapped=MagickTrue;
15941  break;
15942  }
15943  if (event.xmap.window == windows->popup.id)
15944  {
15945  windows->popup.mapped=MagickTrue;
15946  break;
15947  }
15948  if (event.xmap.window == windows->widget.id)
15949  {
15950  windows->widget.mapped=MagickTrue;
15951  break;
15952  }
15953  break;
15954  }
15955  case MappingNotify:
15956  {
15957  (void) XRefreshKeyboardMapping(&event.xmapping);
15958  break;
15959  }
15960  case NoExpose:
15961  break;
15962  case PropertyNotify:
15963  {
15964  Atom
15965  type;
15966 
15967  int
15968  format,
15969  status;
15970 
15971  unsigned char
15972  *data;
15973 
15974  unsigned long
15975  after,
15976  length;
15977 
15978  if (resource_info->debug != MagickFalse)
15979  (void) LogMagickEvent(X11Event,GetMagickModule(),
15980  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15981  event.xproperty.atom,event.xproperty.state);
15982  if (event.xproperty.atom != windows->im_remote_command)
15983  break;
15984  /*
15985  Display image named by the remote command protocol.
15986  */
15987  status=XGetWindowProperty(display,event.xproperty.window,
15988  event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15989  AnyPropertyType,&type,&format,&length,&after,&data);
15990  if ((status != Success) || (length == 0))
15991  break;
15992  if (LocaleCompare((char *) data,"-quit") == 0)
15993  {
15994  XClientMessage(display,windows->image.id,windows->im_protocols,
15995  windows->im_exit,CurrentTime);
15996  (void) XFree((void *) data);
15997  break;
15998  }
15999  (void) CopyMagickString(resource_info->image_info->filename,
16000  (char *) data,MagickPathExtent);
16001  (void) XFree((void *) data);
16002  nexus=ReadImage(resource_info->image_info,exception);
16003  CatchException(exception);
16004  if (nexus != (Image *) NULL)
16005  *state|=NextImageState | ExitState;
16006  break;
16007  }
16008  case ReparentNotify:
16009  {
16010  if (resource_info->debug != MagickFalse)
16011  (void) LogMagickEvent(X11Event,GetMagickModule(),
16012  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16013  event.xreparent.window);
16014  break;
16015  }
16016  case UnmapNotify:
16017  {
16018  if (resource_info->debug != MagickFalse)
16019  (void) LogMagickEvent(X11Event,GetMagickModule(),
16020  "Unmap Notify: 0x%lx",event.xunmap.window);
16021  if (event.xunmap.window == windows->backdrop.id)
16022  {
16023  windows->backdrop.mapped=MagickFalse;
16024  break;
16025  }
16026  if (event.xunmap.window == windows->image.id)
16027  {
16028  windows->image.mapped=MagickFalse;
16029  break;
16030  }
16031  if (event.xunmap.window == windows->magnify.id)
16032  {
16033  windows->magnify.mapped=MagickFalse;
16034  break;
16035  }
16036  if (event.xunmap.window == windows->pan.id)
16037  {
16038  windows->pan.mapped=MagickFalse;
16039  break;
16040  }
16041  if (event.xunmap.window == windows->info.id)
16042  {
16043  windows->info.mapped=MagickFalse;
16044  break;
16045  }
16046  if (event.xunmap.window == windows->icon.id)
16047  {
16048  if (map_info->colormap == icon_map->colormap)
16049  XConfigureImageColormap(display,resource_info,windows,
16050  display_image,exception);
16051  (void) XFreeStandardColormap(display,icon_visual,icon_map,
16052  icon_pixel);
16053  windows->icon.mapped=MagickFalse;
16054  break;
16055  }
16056  if (event.xunmap.window == windows->command.id)
16057  {
16058  windows->command.mapped=MagickFalse;
16059  break;
16060  }
16061  if (event.xunmap.window == windows->popup.id)
16062  {
16063  if (windows->backdrop.id != (Window) NULL)
16064  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16065  CurrentTime);
16066  windows->popup.mapped=MagickFalse;
16067  break;
16068  }
16069  if (event.xunmap.window == windows->widget.id)
16070  {
16071  if (windows->backdrop.id != (Window) NULL)
16072  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16073  CurrentTime);
16074  windows->widget.mapped=MagickFalse;
16075  break;
16076  }
16077  break;
16078  }
16079  default:
16080  {
16081  if (resource_info->debug != MagickFalse)
16082  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16083  event.type);
16084  break;
16085  }
16086  }
16087  } while (!(*state & ExitState));
16088  if ((*state & ExitState) == 0)
16089  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16090  &display_image,exception);
16091  else
16092  if (resource_info->confirm_edit != MagickFalse)
16093  {
16094  /*
16095  Query user if image has changed.
16096  */
16097  if ((resource_info->immutable == MagickFalse) &&
16098  display_image->taint != MagickFalse)
16099  {
16100  int
16101  status;
16102 
16103  status=XConfirmWidget(display,windows,"Your image changed.",
16104  "Do you want to save it");
16105  if (status == 0)
16106  *state&=(unsigned int) (~ExitState);
16107  else
16108  if (status > 0)
16109  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16110  &display_image,exception);
16111  }
16112  }
16113  if ((windows->visual_info->klass == GrayScale) ||
16114  (windows->visual_info->klass == PseudoColor) ||
16115  (windows->visual_info->klass == DirectColor))
16116  {
16117  /*
16118  Withdraw pan and Magnify window.
16119  */
16120  if (windows->info.mapped != MagickFalse)
16121  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16122  if (windows->magnify.mapped != MagickFalse)
16123  (void) XWithdrawWindow(display,windows->magnify.id,
16124  windows->magnify.screen);
16125  if (windows->command.mapped != MagickFalse)
16126  (void) XWithdrawWindow(display,windows->command.id,
16127  windows->command.screen);
16128  }
16129  if (windows->pan.mapped != MagickFalse)
16130  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16131  if (resource_info->backdrop == MagickFalse)
16132  if (windows->backdrop.mapped)
16133  {
16134  (void) XWithdrawWindow(display,windows->backdrop.id,
16135  windows->backdrop.screen);
16136  (void) XDestroyWindow(display,windows->backdrop.id);
16137  windows->backdrop.id=(Window) NULL;
16138  (void) XWithdrawWindow(display,windows->image.id,
16139  windows->image.screen);
16140  (void) XDestroyWindow(display,windows->image.id);
16141  windows->image.id=(Window) NULL;
16142  }
16143  XSetCursorState(display,windows,MagickTrue);
16144  XCheckRefreshWindows(display,windows);
16145  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16146  *state&=(unsigned int) (~ExitState);
16147  if (*state & ExitState)
16148  {
16149  /*
16150  Free Standard Colormap.
16151  */
16152  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16153  if (resource_info->map_type == (char *) NULL)
16154  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16155  /*
16156  Free X resources.
16157  */
16158  if (resource_info->copy_image != (Image *) NULL)
16159  {
16160  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16161  resource_info->copy_image=NewImageList();
16162  }
16163  DestroyXResources();
16164  }
16165  (void) XSync(display,MagickFalse);
16166  /*
16167  Restore our progress monitor and warning handlers.
16168  */
16169  (void) SetErrorHandler(warning_handler);
16170  (void) SetWarningHandler(warning_handler);
16171  /*
16172  Change to home directory.
16173  */
16174  directory=getcwd(working_directory,MagickPathExtent);
16175  (void) directory;
16176  {
16177  int
16178  status;
16179 
16180  if (*resource_info->home_directory == '\0')
16181  (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16182  status=chdir(resource_info->home_directory);
16183  if (status == -1)
16184  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16185  "UnableToOpenFile","%s",resource_info->home_directory);
16186  }
16187  *image=display_image;
16188  return(nexus);
16189 }
16190 #else
16191 
16192 /*
16193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16194 % %
16195 % %
16196 % %
16197 + D i s p l a y I m a g e s %
16198 % %
16199 % %
16200 % %
16201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16202 %
16203 % DisplayImages() displays an image sequence to any X window screen. It
16204 % returns a value other than 0 if successful. Check the exception member
16205 % of image to determine the reason for any failure.
16206 %
16207 % The format of the DisplayImages method is:
16208 %
16209 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16210 % Image *images,ExceptionInfo *exception)
16211 %
16212 % A description of each parameter follows:
16213 %
16214 % o image_info: the image info.
16215 %
16216 % o image: the image.
16217 %
16218 % o exception: return any errors or warnings in this structure.
16219 %
16220 */
16221 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16222  Image *image,ExceptionInfo *exception)
16223 {
16224  assert(image_info != (const ImageInfo *) NULL);
16225  assert(image_info->signature == MagickCoreSignature);
16226  assert(image != (Image *) NULL);
16227  assert(image->signature == MagickCoreSignature);
16228  (void) image_info;
16229  if (IsEventLogging() != MagickFalse)
16230  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16231  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16232  "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16233  return(MagickFalse);
16234 }
16235 
16236 /*
16237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16238 % %
16239 % %
16240 % %
16241 + R e m o t e D i s p l a y C o m m a n d %
16242 % %
16243 % %
16244 % %
16245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16246 %
16247 % RemoteDisplayCommand() encourages a remote display program to display the
16248 % specified image filename.
16249 %
16250 % The format of the RemoteDisplayCommand method is:
16251 %
16252 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16253 % const char *window,const char *filename,ExceptionInfo *exception)
16254 %
16255 % A description of each parameter follows:
16256 %
16257 % o image_info: the image info.
16258 %
16259 % o window: Specifies the name or id of an X window.
16260 %
16261 % o filename: the name of the image filename to display.
16262 %
16263 % o exception: return any errors or warnings in this structure.
16264 %
16265 */
16266 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16267  const char *window,const char *filename,ExceptionInfo *exception)
16268 {
16269  assert(image_info != (const ImageInfo *) NULL);
16270  assert(image_info->signature == MagickCoreSignature);
16271  assert(filename != (char *) NULL);
16272  if (IsEventLogging() != MagickFalse)
16273  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16274  (void) window;
16275  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16276  "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16277  return(MagickFalse);
16278 }
16279 #endif
_FrameInfo
Definition: decorate.h:27
_MontageInfo
Definition: montage.h:33
_RectangleInfo
Definition: geometry.h:129
_MagickInfo
Definition: magick.h:66
_GeometryInfo
Definition: geometry.h:105
_CacheView
Definition: cache-view.c:65
_Image
Definition: image.h:131
_PixelInfo
Definition: pixel.h:181
_QuantizeInfo
Definition: quantize.h:35
_ImageInfo
Definition: image.h:358
_ExceptionInfo
Definition: exception.h:101
_DrawInfo
Definition: draw.h:209