MagickCore  7.1.2-29
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/license/ %
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  " %.17gx%.17g%+.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=>%.17gx%.17g",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  " %.17gx%.17g%+.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  " %.17gx%.17g%+.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 < (int) (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5052  (y > (int) (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 < (int) (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5059  (x > (int) (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 < (int) (crop_info.x+(int) crop_info.width+RoiDelta)) &&
5068  (x > (int) (crop_info.x+(int) crop_info.width-RoiDelta)) &&
5069  (y < (int) (crop_info.y+(int) crop_info.height+RoiDelta)) &&
5070  (y > (int) (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  "%.17gx%.17g%+.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  " %.17gx%.17g%+.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  " %.17gx%.17g%+.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+1] = "";
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  size_t
6708  length;
6709 
6710  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6711  {
6712  *delta='\0';
6713  resource_info->quantum=1;
6714  }
6715  last_symbol=key_symbol;
6716  length=strlen(delta);
6717  if (length < MagickPathExtent)
6718  {
6719  delta[length]=Digits[key_symbol-XK_0];
6720  delta[length+1]='\0';
6721  }
6722  resource_info->quantum=StringToLong(delta);
6723  return(NullCommand);
6724  }
6725  last_symbol=key_symbol;
6726  if (resource_info->immutable)
6727  {
6728  /*
6729  Virtual image window has a restricted command set.
6730  */
6731  switch (key_symbol)
6732  {
6733  case XK_question:
6734  return(InfoCommand);
6735  case XK_p:
6736  case XK_Print:
6737  return(PrintCommand);
6738  case XK_space:
6739  return(NextCommand);
6740  case XK_q:
6741  case XK_Escape:
6742  return(QuitCommand);
6743  default:
6744  break;
6745  }
6746  return(NullCommand);
6747  }
6748  switch ((int) key_symbol)
6749  {
6750  case XK_o:
6751  {
6752  if ((state & ControlMask) == 0)
6753  break;
6754  return(OpenCommand);
6755  }
6756  case XK_space:
6757  return(NextCommand);
6758  case XK_BackSpace:
6759  return(FormerCommand);
6760  case XK_s:
6761  {
6762  if ((state & Mod1Mask) != 0)
6763  return(SwirlCommand);
6764  if ((state & ControlMask) == 0)
6765  return(ShearCommand);
6766  return(SaveCommand);
6767  }
6768  case XK_p:
6769  case XK_Print:
6770  {
6771  if ((state & Mod1Mask) != 0)
6772  return(OilPaintCommand);
6773  if ((state & Mod4Mask) != 0)
6774  return(ColorCommand);
6775  if ((state & ControlMask) == 0)
6776  return(NullCommand);
6777  return(PrintCommand);
6778  }
6779  case XK_d:
6780  {
6781  if ((state & Mod4Mask) != 0)
6782  return(DrawCommand);
6783  if ((state & ControlMask) == 0)
6784  return(NullCommand);
6785  return(DeleteCommand);
6786  }
6787  case XK_Select:
6788  {
6789  if ((state & ControlMask) == 0)
6790  return(NullCommand);
6791  return(SelectCommand);
6792  }
6793  case XK_n:
6794  {
6795  if ((state & ControlMask) == 0)
6796  return(NullCommand);
6797  return(NewCommand);
6798  }
6799  case XK_q:
6800  case XK_Escape:
6801  return(QuitCommand);
6802  case XK_z:
6803  case XK_Undo:
6804  {
6805  if ((state & ControlMask) == 0)
6806  return(NullCommand);
6807  return(UndoCommand);
6808  }
6809  case XK_r:
6810  case XK_Redo:
6811  {
6812  if ((state & ControlMask) == 0)
6813  return(RollCommand);
6814  return(RedoCommand);
6815  }
6816  case XK_x:
6817  {
6818  if ((state & ControlMask) == 0)
6819  return(NullCommand);
6820  return(CutCommand);
6821  }
6822  case XK_c:
6823  {
6824  if ((state & Mod1Mask) != 0)
6825  return(CharcoalDrawCommand);
6826  if ((state & ControlMask) == 0)
6827  return(CropCommand);
6828  return(CopyCommand);
6829  }
6830  case XK_v:
6831  case XK_Insert:
6832  {
6833  if ((state & Mod4Mask) != 0)
6834  return(CompositeCommand);
6835  if ((state & ControlMask) == 0)
6836  return(FlipCommand);
6837  return(PasteCommand);
6838  }
6839  case XK_less:
6840  return(HalfSizeCommand);
6841  case XK_minus:
6842  return(OriginalSizeCommand);
6843  case XK_greater:
6844  return(DoubleSizeCommand);
6845  case XK_percent:
6846  return(ResizeCommand);
6847  case XK_at:
6848  return(RefreshCommand);
6849  case XK_bracketleft:
6850  return(ChopCommand);
6851  case XK_h:
6852  return(FlopCommand);
6853  case XK_slash:
6854  return(RotateRightCommand);
6855  case XK_backslash:
6856  return(RotateLeftCommand);
6857  case XK_asterisk:
6858  return(RotateCommand);
6859  case XK_t:
6860  return(TrimCommand);
6861  case XK_H:
6862  return(HueCommand);
6863  case XK_S:
6864  return(SaturationCommand);
6865  case XK_L:
6866  return(BrightnessCommand);
6867  case XK_G:
6868  return(GammaCommand);
6869  case XK_C:
6870  return(SpiffCommand);
6871  case XK_Z:
6872  return(DullCommand);
6873  case XK_N:
6874  return(NormalizeCommand);
6875  case XK_equal:
6876  return(EqualizeCommand);
6877  case XK_asciitilde:
6878  return(NegateCommand);
6879  case XK_period:
6880  return(GrayscaleCommand);
6881  case XK_numbersign:
6882  return(QuantizeCommand);
6883  case XK_F2:
6884  return(DespeckleCommand);
6885  case XK_F3:
6886  return(EmbossCommand);
6887  case XK_F4:
6888  return(ReduceNoiseCommand);
6889  case XK_F5:
6890  return(AddNoiseCommand);
6891  case XK_F6:
6892  return(SharpenCommand);
6893  case XK_F7:
6894  return(BlurCommand);
6895  case XK_F8:
6896  return(ThresholdCommand);
6897  case XK_F9:
6898  return(EdgeDetectCommand);
6899  case XK_F10:
6900  return(SpreadCommand);
6901  case XK_F11:
6902  return(ShadeCommand);
6903  case XK_F12:
6904  return(RaiseCommand);
6905  case XK_F13:
6906  return(SegmentCommand);
6907  case XK_i:
6908  {
6909  if ((state & Mod1Mask) == 0)
6910  return(NullCommand);
6911  return(ImplodeCommand);
6912  }
6913  case XK_w:
6914  {
6915  if ((state & Mod1Mask) == 0)
6916  return(NullCommand);
6917  return(WaveCommand);
6918  }
6919  case XK_m:
6920  {
6921  if ((state & Mod4Mask) == 0)
6922  return(NullCommand);
6923  return(MatteCommand);
6924  }
6925  case XK_b:
6926  {
6927  if ((state & Mod4Mask) == 0)
6928  return(NullCommand);
6929  return(AddBorderCommand);
6930  }
6931  case XK_f:
6932  {
6933  if ((state & Mod4Mask) == 0)
6934  return(NullCommand);
6935  return(AddFrameCommand);
6936  }
6937  case XK_exclam:
6938  {
6939  if ((state & Mod4Mask) == 0)
6940  return(NullCommand);
6941  return(CommentCommand);
6942  }
6943  case XK_a:
6944  {
6945  if ((state & Mod1Mask) != 0)
6946  return(ApplyCommand);
6947  if ((state & Mod4Mask) != 0)
6948  return(AnnotateCommand);
6949  if ((state & ControlMask) == 0)
6950  return(NullCommand);
6951  return(RegionOfInterestCommand);
6952  }
6953  case XK_question:
6954  return(InfoCommand);
6955  case XK_plus:
6956  return(ZoomCommand);
6957  case XK_P:
6958  {
6959  if ((state & ShiftMask) == 0)
6960  return(NullCommand);
6961  return(ShowPreviewCommand);
6962  }
6963  case XK_Execute:
6964  return(LaunchCommand);
6965  case XK_F1:
6966  return(HelpCommand);
6967  case XK_Find:
6968  return(BrowseDocumentationCommand);
6969  case XK_Menu:
6970  {
6971  (void) XMapRaised(display,windows->command.id);
6972  return(NullCommand);
6973  }
6974  case XK_Next:
6975  case XK_Prior:
6976  case XK_Home:
6977  case XK_KP_Home:
6978  {
6979  XTranslateImage(display,windows,*image,key_symbol);
6980  return(NullCommand);
6981  }
6982  case XK_Up:
6983  case XK_KP_Up:
6984  case XK_Down:
6985  case XK_KP_Down:
6986  case XK_Left:
6987  case XK_KP_Left:
6988  case XK_Right:
6989  case XK_KP_Right:
6990  {
6991  if ((state & Mod1Mask) != 0)
6992  {
6994  crop_info;
6995 
6996  /*
6997  Trim one pixel from edge of image.
6998  */
6999  crop_info.x=0;
7000  crop_info.y=0;
7001  crop_info.width=(size_t) windows->image.ximage->width;
7002  crop_info.height=(size_t) windows->image.ximage->height;
7003  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7004  {
7005  if (resource_info->quantum >= (int) crop_info.height)
7006  resource_info->quantum=(int) crop_info.height-1;
7007  crop_info.height-=(size_t) resource_info->quantum;
7008  }
7009  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7010  {
7011  if (resource_info->quantum >= (int) (crop_info.height-(ssize_t) crop_info.y))
7012  resource_info->quantum=(int) (crop_info.height-(ssize_t) crop_info.y-1);
7013  crop_info.y+=resource_info->quantum;
7014  crop_info.height-=(size_t) resource_info->quantum;
7015  }
7016  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7017  {
7018  if (resource_info->quantum >= (int) crop_info.width)
7019  resource_info->quantum=(int) crop_info.width-1;
7020  crop_info.width-=(size_t) resource_info->quantum;
7021  }
7022  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7023  {
7024  if (resource_info->quantum >= (int) (crop_info.width-(ssize_t) crop_info.x))
7025  resource_info->quantum=(int) (crop_info.width-(ssize_t) crop_info.x-1);
7026  crop_info.x+=resource_info->quantum;
7027  crop_info.width-=(size_t) resource_info->quantum;
7028  }
7029  if ((windows->image.x+(int) windows->image.width) > (int) crop_info.width)
7030  windows->image.x=(int) (crop_info.width-windows->image.width);
7031  if ((windows->image.y+(int) windows->image.height) > (int) crop_info.height)
7032  windows->image.y=(int) (crop_info.height-windows->image.height);
7033  XSetCropGeometry(display,windows,&crop_info,*image);
7034  windows->image.window_changes.width=(int) crop_info.width;
7035  windows->image.window_changes.height=(int) crop_info.height;
7036  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7037  (void) XConfigureImage(display,resource_info,windows,*image,
7038  exception);
7039  return(NullCommand);
7040  }
7041  XTranslateImage(display,windows,*image,key_symbol);
7042  return(NullCommand);
7043  }
7044  default:
7045  return(NullCommand);
7046  }
7047  return(NullCommand);
7048 }
7049 ␌
7050 /*
7051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7052 % %
7053 % %
7054 % %
7055 + X M a g i c k C o m m a n d %
7056 % %
7057 % %
7058 % %
7059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7060 %
7061 % XMagickCommand() makes a transform to the image or Image window as
7062 % specified by a user menu button or keyboard command.
7063 %
7064 % The format of the XMagickCommand method is:
7065 %
7066 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7067 % XWindows *windows,const DisplayCommand command,Image **image,
7068 % ExceptionInfo *exception)
7069 %
7070 % A description of each parameter follows:
7071 %
7072 % o display: Specifies a connection to an X server; returned from
7073 % XOpenDisplay.
7074 %
7075 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7076 %
7077 % o windows: Specifies a pointer to a XWindows structure.
7078 %
7079 % o command: Specifies a command to perform.
7080 %
7081 % o image: the image; XMagickCommand may transform the image and return a
7082 % new image pointer.
7083 %
7084 % o exception: return any errors or warnings in this structure.
7085 %
7086 */
7087 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7088  XWindows *windows,const DisplayCommand command,Image **image,
7089  ExceptionInfo *exception)
7090 {
7091  char
7092  filename[MagickPathExtent],
7093  geometry[MagickPathExtent],
7094  modulate_factors[MagickPathExtent];
7095 
7096  GeometryInfo
7097  geometry_info;
7098 
7099  Image
7100  *nexus;
7101 
7102  ImageInfo
7103  *image_info;
7104 
7105  int
7106  x,
7107  y;
7108 
7109  MagickStatusType
7110  flags,
7111  status;
7112 
7113  QuantizeInfo
7114  quantize_info;
7115 
7117  page_geometry;
7118 
7119  int
7120  i;
7121 
7122  static char
7123  color[MagickPathExtent] = "gray";
7124 
7125  unsigned int
7126  height,
7127  width;
7128 
7129  /*
7130  Process user command.
7131  */
7132  XCheckRefreshWindows(display,windows);
7133  XImageCache(display,resource_info,windows,command,image,exception);
7134  nexus=NewImageList();
7135  windows->image.window_changes.width=windows->image.ximage->width;
7136  windows->image.window_changes.height=windows->image.ximage->height;
7137  image_info=CloneImageInfo(resource_info->image_info);
7138  SetGeometryInfo(&geometry_info);
7139  GetQuantizeInfo(&quantize_info);
7140  switch (command)
7141  {
7142  case OpenCommand:
7143  {
7144  /*
7145  Load image.
7146  */
7147  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7148  break;
7149  }
7150  case NextCommand:
7151  {
7152  /*
7153  Display next image.
7154  */
7155  for (i=0; i < resource_info->quantum; i++)
7156  XClientMessage(display,windows->image.id,windows->im_protocols,
7157  windows->im_next_image,CurrentTime);
7158  break;
7159  }
7160  case FormerCommand:
7161  {
7162  /*
7163  Display former image.
7164  */
7165  for (i=0; i < resource_info->quantum; i++)
7166  XClientMessage(display,windows->image.id,windows->im_protocols,
7167  windows->im_former_image,CurrentTime);
7168  break;
7169  }
7170  case SelectCommand:
7171  {
7172  int
7173  status;
7174 
7175  /*
7176  Select image.
7177  */
7178  if (*resource_info->home_directory == '\0')
7179  (void) CopyMagickString(resource_info->home_directory,".",
7180  MagickPathExtent);
7181  status=chdir(resource_info->home_directory);
7182  if (status == -1)
7183  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7184  "UnableToOpenFile","%s",resource_info->home_directory);
7185  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7186  break;
7187  }
7188  case SaveCommand:
7189  {
7190  /*
7191  Save image.
7192  */
7193  status=XSaveImage(display,resource_info,windows,*image,exception);
7194  if (status == MagickFalse)
7195  {
7196  char
7197  message[MagickPathExtent];
7198 
7199  (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7200  exception->reason != (char *) NULL ? exception->reason : "",
7201  exception->description != (char *) NULL ? exception->description :
7202  "");
7203  XNoticeWidget(display,windows,"Unable to save file:",message);
7204  break;
7205  }
7206  break;
7207  }
7208  case PrintCommand:
7209  {
7210  /*
7211  Print image.
7212  */
7213  status=XPrintImage(display,resource_info,windows,*image,exception);
7214  if (status == MagickFalse)
7215  {
7216  char
7217  message[MagickPathExtent];
7218 
7219  (void) FormatLocaleString(message,MagickPathExtent,"%s:%s",
7220  exception->reason != (char *) NULL ? exception->reason : "",
7221  exception->description != (char *) NULL ? exception->description :
7222  "");
7223  XNoticeWidget(display,windows,"Unable to print file:",message);
7224  break;
7225  }
7226  break;
7227  }
7228  case DeleteCommand:
7229  {
7230  static char
7231  filename[MagickPathExtent] = "\0";
7232 
7233  /*
7234  Delete image file.
7235  */
7236  XFileBrowserWidget(display,windows,"Delete",filename);
7237  if (*filename == '\0')
7238  break;
7239  status=ShredFile(filename);
7240  if (remove_utf8(filename) < 0)
7241  status=MagickTrue;
7242  if (status != MagickFalse)
7243  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7244  break;
7245  }
7246  case NewCommand:
7247  {
7248  int
7249  status;
7250 
7251  static char
7252  color[MagickPathExtent] = "gray",
7253  geometry[MagickPathExtent] = "640x480";
7254 
7255  static const char
7256  *format = "gradient";
7257 
7258  /*
7259  Query user for canvas geometry.
7260  */
7261  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7262  geometry);
7263  if (*geometry == '\0')
7264  break;
7265  if (status == 0)
7266  format="xc";
7267  XColorBrowserWidget(display,windows,"Select",color);
7268  if (*color == '\0')
7269  break;
7270  /*
7271  Create canvas.
7272  */
7273  (void) FormatLocaleString(image_info->filename,MagickPathExtent,
7274  "%s:%s",format,color);
7275  (void) CloneString(&image_info->size,geometry);
7276  nexus=ReadImage(image_info,exception);
7277  CatchException(exception);
7278  XClientMessage(display,windows->image.id,windows->im_protocols,
7279  windows->im_next_image,CurrentTime);
7280  break;
7281  }
7282  case VisualDirectoryCommand:
7283  {
7284  /*
7285  Visual Image directory.
7286  */
7287  nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7288  break;
7289  }
7290  case QuitCommand:
7291  {
7292  /*
7293  exit program.
7294  */
7295  if (resource_info->confirm_exit == MagickFalse)
7296  XClientMessage(display,windows->image.id,windows->im_protocols,
7297  windows->im_exit,CurrentTime);
7298  else
7299  {
7300  int
7301  status;
7302 
7303  /*
7304  Confirm program exit.
7305  */
7306  status=XConfirmWidget(display,windows,"Do you really want to exit",
7307  resource_info->client_name);
7308  if (status > 0)
7309  XClientMessage(display,windows->image.id,windows->im_protocols,
7310  windows->im_exit,CurrentTime);
7311  }
7312  break;
7313  }
7314  case CutCommand:
7315  {
7316  /*
7317  Cut image.
7318  */
7319  (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7320  break;
7321  }
7322  case CopyCommand:
7323  {
7324  /*
7325  Copy image.
7326  */
7327  (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7328  exception);
7329  break;
7330  }
7331  case PasteCommand:
7332  {
7333  /*
7334  Paste image.
7335  */
7336  status=XPasteImage(display,resource_info,windows,*image,exception);
7337  if (status == MagickFalse)
7338  {
7339  XNoticeWidget(display,windows,"Unable to paste X image",
7340  (*image)->filename);
7341  break;
7342  }
7343  break;
7344  }
7345  case HalfSizeCommand:
7346  {
7347  /*
7348  Half image size.
7349  */
7350  windows->image.window_changes.width=windows->image.ximage->width/2;
7351  windows->image.window_changes.height=windows->image.ximage->height/2;
7352  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7353  break;
7354  }
7355  case OriginalSizeCommand:
7356  {
7357  /*
7358  Original image size.
7359  */
7360  windows->image.window_changes.width=(int) (*image)->columns;
7361  windows->image.window_changes.height=(int) (*image)->rows;
7362  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7363  break;
7364  }
7365  case DoubleSizeCommand:
7366  {
7367  /*
7368  Double the image size.
7369  */
7370  windows->image.window_changes.width=windows->image.ximage->width << 1;
7371  windows->image.window_changes.height=windows->image.ximage->height << 1;
7372  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7373  break;
7374  }
7375  case ResizeCommand:
7376  {
7377  int
7378  status;
7379 
7380  size_t
7381  height,
7382  width;
7383 
7384  ssize_t
7385  x,
7386  y;
7387 
7388  /*
7389  Resize image.
7390  */
7391  width=(size_t) windows->image.ximage->width;
7392  height=(size_t) windows->image.ximage->height;
7393  x=0;
7394  y=0;
7395  (void) FormatLocaleString(geometry,MagickPathExtent,"%.17gx%.17g+0+0",
7396  (double) width,(double) height);
7397  status=XDialogWidget(display,windows,"Resize",
7398  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7399  if (*geometry == '\0')
7400  break;
7401  if (status == 0)
7402  (void) ConcatenateMagickString(geometry,"!",MagickPathExtent);
7403  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7404  windows->image.window_changes.width=(int) width;
7405  windows->image.window_changes.height=(int) height;
7406  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7407  break;
7408  }
7409  case ApplyCommand:
7410  {
7411  char
7412  image_geometry[MagickPathExtent];
7413 
7414  if ((windows->image.crop_geometry == (char *) NULL) &&
7415  ((int) (*image)->columns == windows->image.ximage->width) &&
7416  ((int) (*image)->rows == windows->image.ximage->height))
7417  break;
7418  /*
7419  Apply size transforms to image.
7420  */
7421  XSetCursorState(display,windows,MagickTrue);
7422  XCheckRefreshWindows(display,windows);
7423  /*
7424  Crop and/or scale displayed image.
7425  */
7426  (void) FormatLocaleString(image_geometry,MagickPathExtent,"%dx%d!",
7427  windows->image.ximage->width,windows->image.ximage->height);
7428  (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7429  exception);
7430  if (windows->image.crop_geometry != (char *) NULL)
7431  windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7432  windows->image.crop_geometry);
7433  windows->image.x=0;
7434  windows->image.y=0;
7435  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7436  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7437  break;
7438  }
7439  case RefreshCommand:
7440  {
7441  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7442  break;
7443  }
7444  case RestoreCommand:
7445  {
7446  /*
7447  Restore Image window to its original size.
7448  */
7449  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7450  (windows->image.height == (unsigned int) (*image)->rows) &&
7451  (windows->image.crop_geometry == (char *) NULL))
7452  {
7453  (void) XBell(display,0);
7454  break;
7455  }
7456  windows->image.window_changes.width=(int) (*image)->columns;
7457  windows->image.window_changes.height=(int) (*image)->rows;
7458  if (windows->image.crop_geometry != (char *) NULL)
7459  {
7460  windows->image.crop_geometry=(char *)
7461  RelinquishMagickMemory(windows->image.crop_geometry);
7462  windows->image.crop_geometry=(char *) NULL;
7463  windows->image.x=0;
7464  windows->image.y=0;
7465  }
7466  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7467  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7468  break;
7469  }
7470  case CropCommand:
7471  {
7472  /*
7473  Crop image.
7474  */
7475  (void) XCropImage(display,resource_info,windows,*image,CropMode,
7476  exception);
7477  break;
7478  }
7479  case ChopCommand:
7480  {
7481  /*
7482  Chop image.
7483  */
7484  status=XChopImage(display,resource_info,windows,image,exception);
7485  if (status == MagickFalse)
7486  {
7487  XNoticeWidget(display,windows,"Unable to cut X image",
7488  (*image)->filename);
7489  break;
7490  }
7491  break;
7492  }
7493  case FlopCommand:
7494  {
7495  Image
7496  *flop_image;
7497 
7498  /*
7499  Flop image scanlines.
7500  */
7501  XSetCursorState(display,windows,MagickTrue);
7502  XCheckRefreshWindows(display,windows);
7503  flop_image=FlopImage(*image,exception);
7504  if (flop_image != (Image *) NULL)
7505  {
7506  *image=DestroyImage(*image);
7507  *image=flop_image;
7508  }
7509  CatchException(exception);
7510  XSetCursorState(display,windows,MagickFalse);
7511  if (windows->image.crop_geometry != (char *) NULL)
7512  {
7513  /*
7514  Flop crop geometry.
7515  */
7516  width=(unsigned int) (*image)->columns;
7517  height=(unsigned int) (*image)->rows;
7518  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7519  &width,&height);
7520  (void) FormatLocaleString(windows->image.crop_geometry,
7521  MagickPathExtent,"%ux%u%+d%+d",width,height,(int) (*image)->columns-
7522  (int) width-x,y);
7523  }
7524  if (windows->image.orphan != MagickFalse)
7525  break;
7526  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527  break;
7528  }
7529  case FlipCommand:
7530  {
7531  Image
7532  *flip_image;
7533 
7534  /*
7535  Flip image scanlines.
7536  */
7537  XSetCursorState(display,windows,MagickTrue);
7538  XCheckRefreshWindows(display,windows);
7539  flip_image=FlipImage(*image,exception);
7540  if (flip_image != (Image *) NULL)
7541  {
7542  *image=DestroyImage(*image);
7543  *image=flip_image;
7544  }
7545  CatchException(exception);
7546  XSetCursorState(display,windows,MagickFalse);
7547  if (windows->image.crop_geometry != (char *) NULL)
7548  {
7549  /*
7550  Flip crop geometry.
7551  */
7552  width=(unsigned int) (*image)->columns;
7553  height=(unsigned int) (*image)->rows;
7554  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555  &width,&height);
7556  (void) FormatLocaleString(windows->image.crop_geometry,
7557  MagickPathExtent,"%ux%u%+d%+d",width,height,x,(int) (*image)->rows-
7558  (int) height-y);
7559  }
7560  if (windows->image.orphan != MagickFalse)
7561  break;
7562  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7563  break;
7564  }
7565  case RotateRightCommand:
7566  {
7567  /*
7568  Rotate image 90 degrees clockwise.
7569  */
7570  status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7571  if (status == MagickFalse)
7572  {
7573  XNoticeWidget(display,windows,"Unable to rotate X image",
7574  (*image)->filename);
7575  break;
7576  }
7577  break;
7578  }
7579  case RotateLeftCommand:
7580  {
7581  /*
7582  Rotate image 90 degrees counter-clockwise.
7583  */
7584  status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7585  if (status == MagickFalse)
7586  {
7587  XNoticeWidget(display,windows,"Unable to rotate X image",
7588  (*image)->filename);
7589  break;
7590  }
7591  break;
7592  }
7593  case RotateCommand:
7594  {
7595  /*
7596  Rotate image.
7597  */
7598  status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7599  if (status == MagickFalse)
7600  {
7601  XNoticeWidget(display,windows,"Unable to rotate X image",
7602  (*image)->filename);
7603  break;
7604  }
7605  break;
7606  }
7607  case ShearCommand:
7608  {
7609  Image
7610  *shear_image;
7611 
7612  static char
7613  geometry[MagickPathExtent] = "45.0x45.0";
7614 
7615  /*
7616  Query user for shear color and geometry.
7617  */
7618  XColorBrowserWidget(display,windows,"Select",color);
7619  if (*color == '\0')
7620  break;
7621  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7622  geometry);
7623  if (*geometry == '\0')
7624  break;
7625  /*
7626  Shear image.
7627  */
7628  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7629  exception);
7630  XSetCursorState(display,windows,MagickTrue);
7631  XCheckRefreshWindows(display,windows);
7632  (void) QueryColorCompliance(color,AllCompliance,
7633  &(*image)->background_color,exception);
7634  flags=ParseGeometry(geometry,&geometry_info);
7635  if ((flags & SigmaValue) == 0)
7636  geometry_info.sigma=geometry_info.rho;
7637  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7638  exception);
7639  if (shear_image != (Image *) NULL)
7640  {
7641  *image=DestroyImage(*image);
7642  *image=shear_image;
7643  }
7644  CatchException(exception);
7645  XSetCursorState(display,windows,MagickFalse);
7646  if (windows->image.orphan != MagickFalse)
7647  break;
7648  windows->image.window_changes.width=(int) (*image)->columns;
7649  windows->image.window_changes.height=(int) (*image)->rows;
7650  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7651  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7652  break;
7653  }
7654  case RollCommand:
7655  {
7656  Image
7657  *roll_image;
7658 
7659  static char
7660  geometry[MagickPathExtent] = "+2+2";
7661 
7662  /*
7663  Query user for the roll geometry.
7664  */
7665  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7666  geometry);
7667  if (*geometry == '\0')
7668  break;
7669  /*
7670  Roll image.
7671  */
7672  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7673  exception);
7674  XSetCursorState(display,windows,MagickTrue);
7675  XCheckRefreshWindows(display,windows);
7676  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7677  exception);
7678  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7679  exception);
7680  if (roll_image != (Image *) NULL)
7681  {
7682  *image=DestroyImage(*image);
7683  *image=roll_image;
7684  }
7685  CatchException(exception);
7686  XSetCursorState(display,windows,MagickFalse);
7687  if (windows->image.orphan != MagickFalse)
7688  break;
7689  windows->image.window_changes.width=(int) (*image)->columns;
7690  windows->image.window_changes.height=(int) (*image)->rows;
7691  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7692  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7693  break;
7694  }
7695  case TrimCommand:
7696  {
7697  static char
7698  fuzz[MagickPathExtent];
7699 
7700  /*
7701  Query user for the fuzz factor.
7702  */
7703  (void) FormatLocaleString(fuzz,MagickPathExtent,"%g%%",100.0*
7704  (*image)->fuzz/((double) QuantumRange+1.0));
7705  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7706  if (*fuzz == '\0')
7707  break;
7708  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7709  /*
7710  Trim image.
7711  */
7712  status=XTrimImage(display,resource_info,windows,*image,exception);
7713  if (status == MagickFalse)
7714  {
7715  XNoticeWidget(display,windows,"Unable to trim X image",
7716  (*image)->filename);
7717  break;
7718  }
7719  break;
7720  }
7721  case HueCommand:
7722  {
7723  static char
7724  hue_percent[MagickPathExtent] = "110";
7725 
7726  /*
7727  Query user for percent hue change.
7728  */
7729  (void) XDialogWidget(display,windows,"Apply",
7730  "Enter percent change in image hue (0-200):",hue_percent);
7731  if (*hue_percent == '\0')
7732  break;
7733  /*
7734  Vary the image hue.
7735  */
7736  XSetCursorState(display,windows,MagickTrue);
7737  XCheckRefreshWindows(display,windows);
7738  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MagickPathExtent);
7739  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7740  MagickPathExtent);
7741  (void) ModulateImage(*image,modulate_factors,exception);
7742  XSetCursorState(display,windows,MagickFalse);
7743  if (windows->image.orphan != MagickFalse)
7744  break;
7745  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7746  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7747  break;
7748  }
7749  case SaturationCommand:
7750  {
7751  static char
7752  saturation_percent[MagickPathExtent] = "110";
7753 
7754  /*
7755  Query user for percent saturation change.
7756  */
7757  (void) XDialogWidget(display,windows,"Apply",
7758  "Enter percent change in color saturation (0-200):",saturation_percent);
7759  if (*saturation_percent == '\0')
7760  break;
7761  /*
7762  Vary color saturation.
7763  */
7764  XSetCursorState(display,windows,MagickTrue);
7765  XCheckRefreshWindows(display,windows);
7766  (void) CopyMagickString(modulate_factors,"100.0/",MagickPathExtent);
7767  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7768  MagickPathExtent);
7769  (void) ModulateImage(*image,modulate_factors,exception);
7770  XSetCursorState(display,windows,MagickFalse);
7771  if (windows->image.orphan != MagickFalse)
7772  break;
7773  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7774  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7775  break;
7776  }
7777  case BrightnessCommand:
7778  {
7779  static char
7780  brightness_percent[MagickPathExtent] = "110";
7781 
7782  /*
7783  Query user for percent brightness change.
7784  */
7785  (void) XDialogWidget(display,windows,"Apply",
7786  "Enter percent change in color brightness (0-200):",brightness_percent);
7787  if (*brightness_percent == '\0')
7788  break;
7789  /*
7790  Vary the color brightness.
7791  */
7792  XSetCursorState(display,windows,MagickTrue);
7793  XCheckRefreshWindows(display,windows);
7794  (void) CopyMagickString(modulate_factors,brightness_percent,
7795  MagickPathExtent);
7796  (void) ModulateImage(*image,modulate_factors,exception);
7797  XSetCursorState(display,windows,MagickFalse);
7798  if (windows->image.orphan != MagickFalse)
7799  break;
7800  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7801  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7802  break;
7803  }
7804  case GammaCommand:
7805  {
7806  static char
7807  factor[MagickPathExtent] = "1.6";
7808 
7809  /*
7810  Query user for gamma value.
7811  */
7812  (void) XDialogWidget(display,windows,"Gamma",
7813  "Enter gamma value (e.g. 1.2):",factor);
7814  if (*factor == '\0')
7815  break;
7816  /*
7817  Gamma correct image.
7818  */
7819  XSetCursorState(display,windows,MagickTrue);
7820  XCheckRefreshWindows(display,windows);
7821  (void) GammaImage(*image,strtod(factor,(char **) NULL),exception);
7822  XSetCursorState(display,windows,MagickFalse);
7823  if (windows->image.orphan != MagickFalse)
7824  break;
7825  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7826  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7827  break;
7828  }
7829  case SpiffCommand:
7830  {
7831  /*
7832  Sharpen the image contrast.
7833  */
7834  XSetCursorState(display,windows,MagickTrue);
7835  XCheckRefreshWindows(display,windows);
7836  (void) ContrastImage(*image,MagickTrue,exception);
7837  XSetCursorState(display,windows,MagickFalse);
7838  if (windows->image.orphan != MagickFalse)
7839  break;
7840  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7841  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7842  break;
7843  }
7844  case DullCommand:
7845  {
7846  /*
7847  Dull the image contrast.
7848  */
7849  XSetCursorState(display,windows,MagickTrue);
7850  XCheckRefreshWindows(display,windows);
7851  (void) ContrastImage(*image,MagickFalse,exception);
7852  XSetCursorState(display,windows,MagickFalse);
7853  if (windows->image.orphan != MagickFalse)
7854  break;
7855  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7856  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7857  break;
7858  }
7859  case ContrastStretchCommand:
7860  {
7861  double
7862  black_point,
7863  white_point;
7864 
7865  static char
7866  levels[MagickPathExtent] = "1%";
7867 
7868  /*
7869  Query user for gamma value.
7870  */
7871  (void) XDialogWidget(display,windows,"Contrast Stretch",
7872  "Enter black and white points:",levels);
7873  if (*levels == '\0')
7874  break;
7875  /*
7876  Contrast stretch image.
7877  */
7878  XSetCursorState(display,windows,MagickTrue);
7879  XCheckRefreshWindows(display,windows);
7880  flags=ParseGeometry(levels,&geometry_info);
7881  black_point=geometry_info.rho;
7882  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7883  if ((flags & PercentValue) != 0)
7884  {
7885  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7887  }
7888  white_point=(double) (*image)->columns*(*image)->rows-white_point;
7889  (void) ContrastStretchImage(*image,black_point,white_point,
7890  exception);
7891  XSetCursorState(display,windows,MagickFalse);
7892  if (windows->image.orphan != MagickFalse)
7893  break;
7894  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7895  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7896  break;
7897  }
7898  case SigmoidalContrastCommand:
7899  {
7900  GeometryInfo
7901  geometry_info;
7902 
7903  MagickStatusType
7904  flags;
7905 
7906  static char
7907  levels[MagickPathExtent] = "3x50%";
7908 
7909  /*
7910  Query user for gamma value.
7911  */
7912  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7913  "Enter contrast and midpoint:",levels);
7914  if (*levels == '\0')
7915  break;
7916  /*
7917  Contrast stretch image.
7918  */
7919  XSetCursorState(display,windows,MagickTrue);
7920  XCheckRefreshWindows(display,windows);
7921  flags=ParseGeometry(levels,&geometry_info);
7922  if ((flags & SigmaValue) == 0)
7923  geometry_info.sigma=1.0*(double) QuantumRange/2.0;
7924  if ((flags & PercentValue) != 0)
7925  geometry_info.sigma=1.0*(double) QuantumRange*geometry_info.sigma/100.0;
7926  (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7927  geometry_info.sigma,exception);
7928  XSetCursorState(display,windows,MagickFalse);
7929  if (windows->image.orphan != MagickFalse)
7930  break;
7931  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7932  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7933  break;
7934  }
7935  case NormalizeCommand:
7936  {
7937  /*
7938  Perform histogram normalization on the image.
7939  */
7940  XSetCursorState(display,windows,MagickTrue);
7941  XCheckRefreshWindows(display,windows);
7942  (void) NormalizeImage(*image,exception);
7943  XSetCursorState(display,windows,MagickFalse);
7944  if (windows->image.orphan != MagickFalse)
7945  break;
7946  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7947  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7948  break;
7949  }
7950  case EqualizeCommand:
7951  {
7952  /*
7953  Perform histogram equalization on the image.
7954  */
7955  XSetCursorState(display,windows,MagickTrue);
7956  XCheckRefreshWindows(display,windows);
7957  (void) EqualizeImage(*image,exception);
7958  XSetCursorState(display,windows,MagickFalse);
7959  if (windows->image.orphan != MagickFalse)
7960  break;
7961  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7962  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7963  break;
7964  }
7965  case NegateCommand:
7966  {
7967  /*
7968  Negate colors in image.
7969  */
7970  XSetCursorState(display,windows,MagickTrue);
7971  XCheckRefreshWindows(display,windows);
7972  (void) NegateImage(*image,MagickFalse,exception);
7973  XSetCursorState(display,windows,MagickFalse);
7974  if (windows->image.orphan != MagickFalse)
7975  break;
7976  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7977  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7978  break;
7979  }
7980  case GrayscaleCommand:
7981  {
7982  /*
7983  Convert image to grayscale.
7984  */
7985  XSetCursorState(display,windows,MagickTrue);
7986  XCheckRefreshWindows(display,windows);
7987  (void) SetImageType(*image,(*image)->alpha_trait == UndefinedPixelTrait ?
7988  GrayscaleType : GrayscaleAlphaType,exception);
7989  XSetCursorState(display,windows,MagickFalse);
7990  if (windows->image.orphan != MagickFalse)
7991  break;
7992  XConfigureImageColormap(display,resource_info,windows,*image,exception);
7993  (void) XConfigureImage(display,resource_info,windows,*image,exception);
7994  break;
7995  }
7996  case MapCommand:
7997  {
7998  Image
7999  *affinity_image;
8000 
8001  static char
8002  filename[MagickPathExtent] = "\0";
8003 
8004  /*
8005  Request image file name from user.
8006  */
8007  XFileBrowserWidget(display,windows,"Map",filename);
8008  if (*filename == '\0')
8009  break;
8010  /*
8011  Map image.
8012  */
8013  XSetCursorState(display,windows,MagickTrue);
8014  XCheckRefreshWindows(display,windows);
8015  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
8016  affinity_image=ReadImage(image_info,exception);
8017  if (affinity_image != (Image *) NULL)
8018  {
8019  (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8020  affinity_image=DestroyImage(affinity_image);
8021  }
8022  CatchException(exception);
8023  XSetCursorState(display,windows,MagickFalse);
8024  if (windows->image.orphan != MagickFalse)
8025  break;
8026  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8027  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8028  break;
8029  }
8030  case QuantizeCommand:
8031  {
8032  int
8033  status;
8034 
8035  static char
8036  colors[MagickPathExtent] = "256";
8037 
8038  /*
8039  Query user for maximum number of colors.
8040  */
8041  status=XDialogWidget(display,windows,"Quantize",
8042  "Maximum number of colors:",colors);
8043  if (*colors == '\0')
8044  break;
8045  /*
8046  Color reduce the image.
8047  */
8048  XSetCursorState(display,windows,MagickTrue);
8049  XCheckRefreshWindows(display,windows);
8050  quantize_info.number_colors=StringToUnsignedLong(colors);
8051  quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8052  NoDitherMethod;
8053  (void) QuantizeImage(&quantize_info,*image,exception);
8054  XSetCursorState(display,windows,MagickFalse);
8055  if (windows->image.orphan != MagickFalse)
8056  break;
8057  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8058  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8059  break;
8060  }
8061  case DespeckleCommand:
8062  {
8063  Image
8064  *despeckle_image;
8065 
8066  /*
8067  Despeckle image.
8068  */
8069  XSetCursorState(display,windows,MagickTrue);
8070  XCheckRefreshWindows(display,windows);
8071  despeckle_image=DespeckleImage(*image,exception);
8072  if (despeckle_image != (Image *) NULL)
8073  {
8074  *image=DestroyImage(*image);
8075  *image=despeckle_image;
8076  }
8077  CatchException(exception);
8078  XSetCursorState(display,windows,MagickFalse);
8079  if (windows->image.orphan != MagickFalse)
8080  break;
8081  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8082  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8083  break;
8084  }
8085  case EmbossCommand:
8086  {
8087  Image
8088  *emboss_image;
8089 
8090  static char
8091  radius[MagickPathExtent] = "0.0x1.0";
8092 
8093  /*
8094  Query user for emboss radius.
8095  */
8096  (void) XDialogWidget(display,windows,"Emboss",
8097  "Enter the emboss radius and standard deviation:",radius);
8098  if (*radius == '\0')
8099  break;
8100  /*
8101  Reduce noise in the image.
8102  */
8103  XSetCursorState(display,windows,MagickTrue);
8104  XCheckRefreshWindows(display,windows);
8105  flags=ParseGeometry(radius,&geometry_info);
8106  if ((flags & SigmaValue) == 0)
8107  geometry_info.sigma=1.0;
8108  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8109  exception);
8110  if (emboss_image != (Image *) NULL)
8111  {
8112  *image=DestroyImage(*image);
8113  *image=emboss_image;
8114  }
8115  CatchException(exception);
8116  XSetCursorState(display,windows,MagickFalse);
8117  if (windows->image.orphan != MagickFalse)
8118  break;
8119  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8120  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8121  break;
8122  }
8123  case ReduceNoiseCommand:
8124  {
8125  Image
8126  *noise_image;
8127 
8128  static char
8129  radius[MagickPathExtent] = "0";
8130 
8131  /*
8132  Query user for noise radius.
8133  */
8134  (void) XDialogWidget(display,windows,"Reduce Noise",
8135  "Enter the noise radius:",radius);
8136  if (*radius == '\0')
8137  break;
8138  /*
8139  Reduce noise in the image.
8140  */
8141  XSetCursorState(display,windows,MagickTrue);
8142  XCheckRefreshWindows(display,windows);
8143  flags=ParseGeometry(radius,&geometry_info);
8144  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8145  geometry_info.rho,(size_t) geometry_info.rho,exception);
8146  if (noise_image != (Image *) NULL)
8147  {
8148  *image=DestroyImage(*image);
8149  *image=noise_image;
8150  }
8151  CatchException(exception);
8152  XSetCursorState(display,windows,MagickFalse);
8153  if (windows->image.orphan != MagickFalse)
8154  break;
8155  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8156  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8157  break;
8158  }
8159  case AddNoiseCommand:
8160  {
8161  char
8162  **noises;
8163 
8164  Image
8165  *noise_image;
8166 
8167  static char
8168  noise_type[MagickPathExtent] = "Gaussian";
8169 
8170  /*
8171  Add noise to the image.
8172  */
8173  noises=GetCommandOptions(MagickNoiseOptions);
8174  if (noises == (char **) NULL)
8175  break;
8176  XListBrowserWidget(display,windows,&windows->widget,
8177  (const char **) noises,"Add Noise",
8178  "Select a type of noise to add to your image:",noise_type);
8179  noises=DestroyStringList(noises);
8180  if (*noise_type == '\0')
8181  break;
8182  XSetCursorState(display,windows,MagickTrue);
8183  XCheckRefreshWindows(display,windows);
8184  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8185  MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8186  if (noise_image != (Image *) NULL)
8187  {
8188  *image=DestroyImage(*image);
8189  *image=noise_image;
8190  }
8191  CatchException(exception);
8192  XSetCursorState(display,windows,MagickFalse);
8193  if (windows->image.orphan != MagickFalse)
8194  break;
8195  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8196  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8197  break;
8198  }
8199  case SharpenCommand:
8200  {
8201  Image
8202  *sharp_image;
8203 
8204  static char
8205  radius[MagickPathExtent] = "0.0x1.0";
8206 
8207  /*
8208  Query user for sharpen radius.
8209  */
8210  (void) XDialogWidget(display,windows,"Sharpen",
8211  "Enter the sharpen radius and standard deviation:",radius);
8212  if (*radius == '\0')
8213  break;
8214  /*
8215  Sharpen image scanlines.
8216  */
8217  XSetCursorState(display,windows,MagickTrue);
8218  XCheckRefreshWindows(display,windows);
8219  flags=ParseGeometry(radius,&geometry_info);
8220  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8221  exception);
8222  if (sharp_image != (Image *) NULL)
8223  {
8224  *image=DestroyImage(*image);
8225  *image=sharp_image;
8226  }
8227  CatchException(exception);
8228  XSetCursorState(display,windows,MagickFalse);
8229  if (windows->image.orphan != MagickFalse)
8230  break;
8231  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8232  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8233  break;
8234  }
8235  case BlurCommand:
8236  {
8237  Image
8238  *blur_image;
8239 
8240  static char
8241  radius[MagickPathExtent] = "0.0x1.0";
8242 
8243  /*
8244  Query user for blur radius.
8245  */
8246  (void) XDialogWidget(display,windows,"Blur",
8247  "Enter the blur radius and standard deviation:",radius);
8248  if (*radius == '\0')
8249  break;
8250  /*
8251  Blur an image.
8252  */
8253  XSetCursorState(display,windows,MagickTrue);
8254  XCheckRefreshWindows(display,windows);
8255  flags=ParseGeometry(radius,&geometry_info);
8256  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8257  exception);
8258  if (blur_image != (Image *) NULL)
8259  {
8260  *image=DestroyImage(*image);
8261  *image=blur_image;
8262  }
8263  CatchException(exception);
8264  XSetCursorState(display,windows,MagickFalse);
8265  if (windows->image.orphan != MagickFalse)
8266  break;
8267  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8268  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8269  break;
8270  }
8271  case ThresholdCommand:
8272  {
8273  double
8274  threshold;
8275 
8276  static char
8277  factor[MagickPathExtent] = "128";
8278 
8279  /*
8280  Query user for threshold value.
8281  */
8282  (void) XDialogWidget(display,windows,"Threshold",
8283  "Enter threshold value:",factor);
8284  if (*factor == '\0')
8285  break;
8286  /*
8287  Gamma correct image.
8288  */
8289  XSetCursorState(display,windows,MagickTrue);
8290  XCheckRefreshWindows(display,windows);
8291  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8292  (void) BilevelImage(*image,threshold,exception);
8293  XSetCursorState(display,windows,MagickFalse);
8294  if (windows->image.orphan != MagickFalse)
8295  break;
8296  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8297  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8298  break;
8299  }
8300  case EdgeDetectCommand:
8301  {
8302  Image
8303  *edge_image;
8304 
8305  static char
8306  radius[MagickPathExtent] = "0";
8307 
8308  /*
8309  Query user for edge factor.
8310  */
8311  (void) XDialogWidget(display,windows,"Detect Edges",
8312  "Enter the edge detect radius:",radius);
8313  if (*radius == '\0')
8314  break;
8315  /*
8316  Detect edge in image.
8317  */
8318  XSetCursorState(display,windows,MagickTrue);
8319  XCheckRefreshWindows(display,windows);
8320  flags=ParseGeometry(radius,&geometry_info);
8321  edge_image=EdgeImage(*image,geometry_info.rho,exception);
8322  if (edge_image != (Image *) NULL)
8323  {
8324  *image=DestroyImage(*image);
8325  *image=edge_image;
8326  }
8327  CatchException(exception);
8328  XSetCursorState(display,windows,MagickFalse);
8329  if (windows->image.orphan != MagickFalse)
8330  break;
8331  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8332  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8333  break;
8334  }
8335  case SpreadCommand:
8336  {
8337  Image
8338  *spread_image;
8339 
8340  static char
8341  amount[MagickPathExtent] = "2";
8342 
8343  /*
8344  Query user for spread amount.
8345  */
8346  (void) XDialogWidget(display,windows,"Spread",
8347  "Enter the displacement amount:",amount);
8348  if (*amount == '\0')
8349  break;
8350  /*
8351  Displace image pixels by a random amount.
8352  */
8353  XSetCursorState(display,windows,MagickTrue);
8354  XCheckRefreshWindows(display,windows);
8355  flags=ParseGeometry(amount,&geometry_info);
8356  spread_image=EdgeImage(*image,geometry_info.rho,exception);
8357  if (spread_image != (Image *) NULL)
8358  {
8359  *image=DestroyImage(*image);
8360  *image=spread_image;
8361  }
8362  CatchException(exception);
8363  XSetCursorState(display,windows,MagickFalse);
8364  if (windows->image.orphan != MagickFalse)
8365  break;
8366  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8367  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8368  break;
8369  }
8370  case ShadeCommand:
8371  {
8372  Image
8373  *shade_image;
8374 
8375  int
8376  status;
8377 
8378  static char
8379  geometry[MagickPathExtent] = "30x30";
8380 
8381  /*
8382  Query user for the shade geometry.
8383  */
8384  status=XDialogWidget(display,windows,"Shade",
8385  "Enter the azimuth and elevation of the light source:",geometry);
8386  if (*geometry == '\0')
8387  break;
8388  /*
8389  Shade image pixels.
8390  */
8391  XSetCursorState(display,windows,MagickTrue);
8392  XCheckRefreshWindows(display,windows);
8393  flags=ParseGeometry(geometry,&geometry_info);
8394  if ((flags & SigmaValue) == 0)
8395  geometry_info.sigma=1.0;
8396  shade_image=ShadeImage(*image,status != 0 ? MagickTrue : MagickFalse,
8397  geometry_info.rho,geometry_info.sigma,exception);
8398  if (shade_image != (Image *) NULL)
8399  {
8400  *image=DestroyImage(*image);
8401  *image=shade_image;
8402  }
8403  CatchException(exception);
8404  XSetCursorState(display,windows,MagickFalse);
8405  if (windows->image.orphan != MagickFalse)
8406  break;
8407  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8408  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8409  break;
8410  }
8411  case RaiseCommand:
8412  {
8413  static char
8414  bevel_width[MagickPathExtent] = "10";
8415 
8416  /*
8417  Query user for bevel width.
8418  */
8419  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8420  if (*bevel_width == '\0')
8421  break;
8422  /*
8423  Raise an image.
8424  */
8425  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8426  exception);
8427  XSetCursorState(display,windows,MagickTrue);
8428  XCheckRefreshWindows(display,windows);
8429  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8430  exception);
8431  (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8432  XSetCursorState(display,windows,MagickFalse);
8433  if (windows->image.orphan != MagickFalse)
8434  break;
8435  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8436  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8437  break;
8438  }
8439  case SegmentCommand:
8440  {
8441  static char
8442  threshold[MagickPathExtent] = "1.0x1.5";
8443 
8444  /*
8445  Query user for smoothing threshold.
8446  */
8447  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8448  threshold);
8449  if (*threshold == '\0')
8450  break;
8451  /*
8452  Segment an image.
8453  */
8454  XSetCursorState(display,windows,MagickTrue);
8455  XCheckRefreshWindows(display,windows);
8456  flags=ParseGeometry(threshold,&geometry_info);
8457  if ((flags & SigmaValue) == 0)
8458  geometry_info.sigma=1.0;
8459  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8460  geometry_info.sigma,exception);
8461  XSetCursorState(display,windows,MagickFalse);
8462  if (windows->image.orphan != MagickFalse)
8463  break;
8464  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8465  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8466  break;
8467  }
8468  case SepiaToneCommand:
8469  {
8470  double
8471  threshold;
8472 
8473  Image
8474  *sepia_image;
8475 
8476  static char
8477  factor[MagickPathExtent] = "80%";
8478 
8479  /*
8480  Query user for sepia-tone factor.
8481  */
8482  (void) XDialogWidget(display,windows,"Sepia Tone",
8483  "Enter the sepia tone factor (0 - 99.9%):",factor);
8484  if (*factor == '\0')
8485  break;
8486  /*
8487  Sepia tone image pixels.
8488  */
8489  XSetCursorState(display,windows,MagickTrue);
8490  XCheckRefreshWindows(display,windows);
8491  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8492  sepia_image=SepiaToneImage(*image,threshold,exception);
8493  if (sepia_image != (Image *) NULL)
8494  {
8495  *image=DestroyImage(*image);
8496  *image=sepia_image;
8497  }
8498  CatchException(exception);
8499  XSetCursorState(display,windows,MagickFalse);
8500  if (windows->image.orphan != MagickFalse)
8501  break;
8502  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8503  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8504  break;
8505  }
8506  case SolarizeCommand:
8507  {
8508  double
8509  threshold;
8510 
8511  static char
8512  factor[MagickPathExtent] = "60%";
8513 
8514  /*
8515  Query user for solarize factor.
8516  */
8517  (void) XDialogWidget(display,windows,"Solarize",
8518  "Enter the solarize factor (0 - 99.9%):",factor);
8519  if (*factor == '\0')
8520  break;
8521  /*
8522  Solarize image pixels.
8523  */
8524  XSetCursorState(display,windows,MagickTrue);
8525  XCheckRefreshWindows(display,windows);
8526  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8527  (void) SolarizeImage(*image,threshold,exception);
8528  XSetCursorState(display,windows,MagickFalse);
8529  if (windows->image.orphan != MagickFalse)
8530  break;
8531  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8532  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8533  break;
8534  }
8535  case SwirlCommand:
8536  {
8537  Image
8538  *swirl_image;
8539 
8540  static char
8541  degrees[MagickPathExtent] = "60";
8542 
8543  /*
8544  Query user for swirl angle.
8545  */
8546  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8547  degrees);
8548  if (*degrees == '\0')
8549  break;
8550  /*
8551  Swirl image pixels about the center.
8552  */
8553  XSetCursorState(display,windows,MagickTrue);
8554  XCheckRefreshWindows(display,windows);
8555  flags=ParseGeometry(degrees,&geometry_info);
8556  swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8557  exception);
8558  if (swirl_image != (Image *) NULL)
8559  {
8560  *image=DestroyImage(*image);
8561  *image=swirl_image;
8562  }
8563  CatchException(exception);
8564  XSetCursorState(display,windows,MagickFalse);
8565  if (windows->image.orphan != MagickFalse)
8566  break;
8567  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8568  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8569  break;
8570  }
8571  case ImplodeCommand:
8572  {
8573  Image
8574  *implode_image;
8575 
8576  static char
8577  factor[MagickPathExtent] = "0.3";
8578 
8579  /*
8580  Query user for implode factor.
8581  */
8582  (void) XDialogWidget(display,windows,"Implode",
8583  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8584  if (*factor == '\0')
8585  break;
8586  /*
8587  Implode image pixels about the center.
8588  */
8589  XSetCursorState(display,windows,MagickTrue);
8590  XCheckRefreshWindows(display,windows);
8591  flags=ParseGeometry(factor,&geometry_info);
8592  implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8593  exception);
8594  if (implode_image != (Image *) NULL)
8595  {
8596  *image=DestroyImage(*image);
8597  *image=implode_image;
8598  }
8599  CatchException(exception);
8600  XSetCursorState(display,windows,MagickFalse);
8601  if (windows->image.orphan != MagickFalse)
8602  break;
8603  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8604  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8605  break;
8606  }
8607  case VignetteCommand:
8608  {
8609  Image
8610  *vignette_image;
8611 
8612  static char
8613  geometry[MagickPathExtent] = "0x20";
8614 
8615  /*
8616  Query user for the vignette geometry.
8617  */
8618  (void) XDialogWidget(display,windows,"Vignette",
8619  "Enter the radius, sigma, and x and y offsets:",geometry);
8620  if (*geometry == '\0')
8621  break;
8622  /*
8623  Soften the edges of the image in vignette style
8624  */
8625  XSetCursorState(display,windows,MagickTrue);
8626  XCheckRefreshWindows(display,windows);
8627  flags=ParseGeometry(geometry,&geometry_info);
8628  if ((flags & SigmaValue) == 0)
8629  geometry_info.sigma=1.0;
8630  if ((flags & XiValue) == 0)
8631  geometry_info.xi=0.1*(*image)->columns;
8632  if ((flags & PsiValue) == 0)
8633  geometry_info.psi=0.1*(*image)->rows;
8634  vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8635  ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8636  exception);
8637  if (vignette_image != (Image *) NULL)
8638  {
8639  *image=DestroyImage(*image);
8640  *image=vignette_image;
8641  }
8642  CatchException(exception);
8643  XSetCursorState(display,windows,MagickFalse);
8644  if (windows->image.orphan != MagickFalse)
8645  break;
8646  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8647  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8648  break;
8649  }
8650  case WaveCommand:
8651  {
8652  Image
8653  *wave_image;
8654 
8655  static char
8656  geometry[MagickPathExtent] = "25x150";
8657 
8658  /*
8659  Query user for the wave geometry.
8660  */
8661  (void) XDialogWidget(display,windows,"Wave",
8662  "Enter the amplitude and length of the wave:",geometry);
8663  if (*geometry == '\0')
8664  break;
8665  /*
8666  Alter an image along a sine wave.
8667  */
8668  XSetCursorState(display,windows,MagickTrue);
8669  XCheckRefreshWindows(display,windows);
8670  flags=ParseGeometry(geometry,&geometry_info);
8671  if ((flags & SigmaValue) == 0)
8672  geometry_info.sigma=1.0;
8673  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8674  (*image)->interpolate,exception);
8675  if (wave_image != (Image *) NULL)
8676  {
8677  *image=DestroyImage(*image);
8678  *image=wave_image;
8679  }
8680  CatchException(exception);
8681  XSetCursorState(display,windows,MagickFalse);
8682  if (windows->image.orphan != MagickFalse)
8683  break;
8684  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8685  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8686  break;
8687  }
8688  case OilPaintCommand:
8689  {
8690  Image
8691  *paint_image;
8692 
8693  static char
8694  radius[MagickPathExtent] = "0";
8695 
8696  /*
8697  Query user for circular neighborhood radius.
8698  */
8699  (void) XDialogWidget(display,windows,"Oil Paint",
8700  "Enter the mask radius:",radius);
8701  if (*radius == '\0')
8702  break;
8703  /*
8704  OilPaint image scanlines.
8705  */
8706  XSetCursorState(display,windows,MagickTrue);
8707  XCheckRefreshWindows(display,windows);
8708  flags=ParseGeometry(radius,&geometry_info);
8709  paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8710  exception);
8711  if (paint_image != (Image *) NULL)
8712  {
8713  *image=DestroyImage(*image);
8714  *image=paint_image;
8715  }
8716  CatchException(exception);
8717  XSetCursorState(display,windows,MagickFalse);
8718  if (windows->image.orphan != MagickFalse)
8719  break;
8720  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8721  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8722  break;
8723  }
8724  case CharcoalDrawCommand:
8725  {
8726  Image
8727  *charcoal_image;
8728 
8729  static char
8730  radius[MagickPathExtent] = "0x1";
8731 
8732  /*
8733  Query user for charcoal radius.
8734  */
8735  (void) XDialogWidget(display,windows,"Charcoal Draw",
8736  "Enter the charcoal radius and sigma:",radius);
8737  if (*radius == '\0')
8738  break;
8739  /*
8740  Charcoal the image.
8741  */
8742  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8743  exception);
8744  XSetCursorState(display,windows,MagickTrue);
8745  XCheckRefreshWindows(display,windows);
8746  flags=ParseGeometry(radius,&geometry_info);
8747  if ((flags & SigmaValue) == 0)
8748  geometry_info.sigma=geometry_info.rho;
8749  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8750  exception);
8751  if (charcoal_image != (Image *) NULL)
8752  {
8753  *image=DestroyImage(*image);
8754  *image=charcoal_image;
8755  }
8756  CatchException(exception);
8757  XSetCursorState(display,windows,MagickFalse);
8758  if (windows->image.orphan != MagickFalse)
8759  break;
8760  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8761  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8762  break;
8763  }
8764  case AnnotateCommand:
8765  {
8766  /*
8767  Annotate the image with text.
8768  */
8769  status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8770  if (status == MagickFalse)
8771  {
8772  XNoticeWidget(display,windows,"Unable to annotate X image",
8773  (*image)->filename);
8774  break;
8775  }
8776  break;
8777  }
8778  case DrawCommand:
8779  {
8780  /*
8781  Draw image.
8782  */
8783  status=XDrawEditImage(display,resource_info,windows,image,exception);
8784  if (status == MagickFalse)
8785  {
8786  XNoticeWidget(display,windows,"Unable to draw on the X image",
8787  (*image)->filename);
8788  break;
8789  }
8790  break;
8791  }
8792  case ColorCommand:
8793  {
8794  /*
8795  Color edit.
8796  */
8797  status=XColorEditImage(display,resource_info,windows,image,exception);
8798  if (status == MagickFalse)
8799  {
8800  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8801  (*image)->filename);
8802  break;
8803  }
8804  break;
8805  }
8806  case MatteCommand:
8807  {
8808  /*
8809  Matte edit.
8810  */
8811  status=XMatteEditImage(display,resource_info,windows,image,exception);
8812  if (status == MagickFalse)
8813  {
8814  XNoticeWidget(display,windows,"Unable to matte edit X image",
8815  (*image)->filename);
8816  break;
8817  }
8818  break;
8819  }
8820  case CompositeCommand:
8821  {
8822  /*
8823  Composite image.
8824  */
8825  status=XCompositeImage(display,resource_info,windows,*image,
8826  exception);
8827  if (status == MagickFalse)
8828  {
8829  XNoticeWidget(display,windows,"Unable to composite X image",
8830  (*image)->filename);
8831  break;
8832  }
8833  break;
8834  }
8835  case AddBorderCommand:
8836  {
8837  Image
8838  *border_image;
8839 
8840  static char
8841  geometry[MagickPathExtent] = "6x6";
8842 
8843  /*
8844  Query user for border color and geometry.
8845  */
8846  XColorBrowserWidget(display,windows,"Select",color);
8847  if (*color == '\0')
8848  break;
8849  (void) XDialogWidget(display,windows,"Add Border",
8850  "Enter border geometry:",geometry);
8851  if (*geometry == '\0')
8852  break;
8853  /*
8854  Add a border to the image.
8855  */
8856  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8857  exception);
8858  XSetCursorState(display,windows,MagickTrue);
8859  XCheckRefreshWindows(display,windows);
8860  (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8861  exception);
8862  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8863  exception);
8864  border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8865  exception);
8866  if (border_image != (Image *) NULL)
8867  {
8868  *image=DestroyImage(*image);
8869  *image=border_image;
8870  }
8871  CatchException(exception);
8872  XSetCursorState(display,windows,MagickFalse);
8873  if (windows->image.orphan != MagickFalse)
8874  break;
8875  windows->image.window_changes.width=(int) (*image)->columns;
8876  windows->image.window_changes.height=(int) (*image)->rows;
8877  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8878  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8879  break;
8880  }
8881  case AddFrameCommand:
8882  {
8883  FrameInfo
8884  frame_info;
8885 
8886  Image
8887  *frame_image;
8888 
8889  static char
8890  geometry[MagickPathExtent] = "6x6";
8891 
8892  /*
8893  Query user for frame color and geometry.
8894  */
8895  XColorBrowserWidget(display,windows,"Select",color);
8896  if (*color == '\0')
8897  break;
8898  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8899  geometry);
8900  if (*geometry == '\0')
8901  break;
8902  /*
8903  Surround image with an ornamental border.
8904  */
8905  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8906  exception);
8907  XSetCursorState(display,windows,MagickTrue);
8908  XCheckRefreshWindows(display,windows);
8909  (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8910  exception);
8911  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8912  exception);
8913  frame_info.width=page_geometry.width;
8914  frame_info.height=page_geometry.height;
8915  frame_info.outer_bevel=page_geometry.x;
8916  frame_info.inner_bevel=page_geometry.y;
8917  frame_info.x=(ssize_t) frame_info.width;
8918  frame_info.y=(ssize_t) frame_info.height;
8919  frame_info.width=(*image)->columns+2*frame_info.width;
8920  frame_info.height=(*image)->rows+2*frame_info.height;
8921  frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8922  if (frame_image != (Image *) NULL)
8923  {
8924  *image=DestroyImage(*image);
8925  *image=frame_image;
8926  }
8927  CatchException(exception);
8928  XSetCursorState(display,windows,MagickFalse);
8929  if (windows->image.orphan != MagickFalse)
8930  break;
8931  windows->image.window_changes.width=(int) (*image)->columns;
8932  windows->image.window_changes.height=(int) (*image)->rows;
8933  XConfigureImageColormap(display,resource_info,windows,*image,exception);
8934  (void) XConfigureImage(display,resource_info,windows,*image,exception);
8935  break;
8936  }
8937  case CommentCommand:
8938  {
8939  const char
8940  *value;
8941 
8942  FILE
8943  *file;
8944 
8945  int
8946  unique_file;
8947 
8948  /*
8949  Edit image comment.
8950  */
8951  unique_file=AcquireUniqueFileResource(image_info->filename);
8952  if (unique_file == -1)
8953  {
8954  XNoticeWidget(display,windows,"Unable to edit image comment",
8955  image_info->filename);
8956  break;
8957  }
8958  value=GetImageProperty(*image,"comment",exception);
8959  if (value == (char *) NULL)
8960  unique_file=close_utf8(unique_file)-1;
8961  else
8962  {
8963  const char
8964  *p;
8965 
8966  file=fdopen(unique_file,"w");
8967  if (file == (FILE *) NULL)
8968  {
8969  XNoticeWidget(display,windows,"Unable to edit image comment",
8970  image_info->filename);
8971  break;
8972  }
8973  for (p=value; *p != '\0'; p++)
8974  (void) fputc((int) *p,file);
8975  (void) fputc('\n',file);
8976  (void) fclose(file);
8977  }
8978  XSetCursorState(display,windows,MagickTrue);
8979  XCheckRefreshWindows(display,windows);
8980  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8981  exception);
8982  if (status == MagickFalse)
8983  XNoticeWidget(display,windows,"Unable to edit image comment",
8984  (char *) NULL);
8985  else
8986  {
8987  char
8988  *comment;
8989 
8990  comment=FileToString(image_info->filename,~0UL,exception);
8991  if (comment != (char *) NULL)
8992  {
8993  (void) SetImageProperty(*image,"comment",comment,exception);
8994  (*image)->taint=MagickTrue;
8995  }
8996  }
8997  (void) RelinquishUniqueFileResource(image_info->filename);
8998  XSetCursorState(display,windows,MagickFalse);
8999  break;
9000  }
9001  case LaunchCommand:
9002  {
9003  /*
9004  Launch program.
9005  */
9006  XSetCursorState(display,windows,MagickTrue);
9007  XCheckRefreshWindows(display,windows);
9008  (void) AcquireUniqueFilename(filename);
9009  (void) FormatLocaleString((*image)->filename,MagickPathExtent,"launch:%s",
9010  filename);
9011  status=WriteImage(image_info,*image,exception);
9012  if (status == MagickFalse)
9013  XNoticeWidget(display,windows,"Unable to launch image editor",
9014  (char *) NULL);
9015  else
9016  {
9017  nexus=ReadImage(resource_info->image_info,exception);
9018  CatchException(exception);
9019  XClientMessage(display,windows->image.id,windows->im_protocols,
9020  windows->im_next_image,CurrentTime);
9021  }
9022  (void) RelinquishUniqueFileResource(filename);
9023  XSetCursorState(display,windows,MagickFalse);
9024  break;
9025  }
9026  case RegionOfInterestCommand:
9027  {
9028  /*
9029  Apply an image processing technique to a region of interest.
9030  */
9031  (void) XROIImage(display,resource_info,windows,image,exception);
9032  break;
9033  }
9034  case InfoCommand:
9035  break;
9036  case ZoomCommand:
9037  {
9038  /*
9039  Zoom image.
9040  */
9041  if (windows->magnify.mapped != MagickFalse)
9042  (void) XRaiseWindow(display,windows->magnify.id);
9043  else
9044  {
9045  /*
9046  Make magnify image.
9047  */
9048  XSetCursorState(display,windows,MagickTrue);
9049  (void) XMapRaised(display,windows->magnify.id);
9050  XSetCursorState(display,windows,MagickFalse);
9051  }
9052  break;
9053  }
9054  case ShowPreviewCommand:
9055  {
9056  char
9057  **previews;
9058 
9059  Image
9060  *preview_image;
9061 
9062  PreviewType
9063  preview;
9064 
9065  static char
9066  preview_type[MagickPathExtent] = "Gamma";
9067 
9068  /*
9069  Select preview type from menu.
9070  */
9071  previews=GetCommandOptions(MagickPreviewOptions);
9072  if (previews == (char **) NULL)
9073  break;
9074  XListBrowserWidget(display,windows,&windows->widget,
9075  (const char **) previews,"Preview",
9076  "Select an enhancement, effect, or F/X:",preview_type);
9077  previews=DestroyStringList(previews);
9078  if (*preview_type == '\0')
9079  break;
9080  /*
9081  Show image preview.
9082  */
9083  XSetCursorState(display,windows,MagickTrue);
9084  XCheckRefreshWindows(display,windows);
9085  preview=(PreviewType) ParseCommandOption(MagickPreviewOptions,
9086  MagickFalse,preview_type);
9087  (void) FormatImageProperty(*image,"group","%.17g",(double)
9088  windows->image.id);
9089  (void) DeleteImageProperty(*image,"label");
9090  (void) SetImageProperty(*image,"label","Preview",exception);
9091  preview_image=PreviewImage(*image,preview,exception);
9092  if (preview_image == (Image *) NULL)
9093  break;
9094  (void) AcquireUniqueFilename(filename);
9095  (void) FormatLocaleString(preview_image->filename,MagickPathExtent,
9096  "show:%s",filename);
9097  status=WriteImage(image_info,preview_image,exception);
9098  (void) RelinquishUniqueFileResource(filename);
9099  preview_image=DestroyImage(preview_image);
9100  if (status == MagickFalse)
9101  XNoticeWidget(display,windows,"Unable to show image preview",
9102  (*image)->filename);
9103  XDelay(display,1500);
9104  XSetCursorState(display,windows,MagickFalse);
9105  break;
9106  }
9107  case ShowHistogramCommand:
9108  {
9109  Image
9110  *histogram_image;
9111 
9112  /*
9113  Show image histogram.
9114  */
9115  XSetCursorState(display,windows,MagickTrue);
9116  XCheckRefreshWindows(display,windows);
9117  (void) DeleteImageProperty(*image,"label");
9118  (void) FormatImageProperty(*image,"group","%.17g",(double)
9119  windows->image.id);
9120  (void) SetImageProperty(*image,"label","Histogram",exception);
9121  (void) AcquireUniqueFilename(filename);
9122  (void) FormatLocaleString((*image)->filename,MagickPathExtent,
9123  "histogram:%s",filename);
9124  status=WriteImage(image_info,*image,exception);
9125  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9126  histogram_image=ReadImage(image_info,exception);
9127  (void) RelinquishUniqueFileResource(filename);
9128  if (histogram_image == (Image *) NULL)
9129  break;
9130  (void) FormatLocaleString(histogram_image->filename,MagickPathExtent,
9131  "show:%s",filename);
9132  status=WriteImage(image_info,histogram_image,exception);
9133  histogram_image=DestroyImage(histogram_image);
9134  if (status == MagickFalse)
9135  XNoticeWidget(display,windows,"Unable to show histogram",
9136  (*image)->filename);
9137  XDelay(display,1500);
9138  XSetCursorState(display,windows,MagickFalse);
9139  break;
9140  }
9141  case ShowMatteCommand:
9142  {
9143  Image
9144  *matte_image;
9145 
9146  if ((*image)->alpha_trait == UndefinedPixelTrait)
9147  {
9148  XNoticeWidget(display,windows,
9149  "Image does not have any matte information",(*image)->filename);
9150  break;
9151  }
9152  /*
9153  Show image matte.
9154  */
9155  XSetCursorState(display,windows,MagickTrue);
9156  XCheckRefreshWindows(display,windows);
9157  (void) FormatImageProperty(*image,"group","%.17g",(double)
9158  windows->image.id);
9159  (void) DeleteImageProperty(*image,"label");
9160  (void) SetImageProperty(*image,"label","Matte",exception);
9161  (void) AcquireUniqueFilename(filename);
9162  (void) FormatLocaleString((*image)->filename,MagickPathExtent,"matte:%s",
9163  filename);
9164  status=WriteImage(image_info,*image,exception);
9165  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
9166  matte_image=ReadImage(image_info,exception);
9167  (void) RelinquishUniqueFileResource(filename);
9168  if (matte_image == (Image *) NULL)
9169  break;
9170  (void) FormatLocaleString(matte_image->filename,MagickPathExtent,
9171  "show:%s",filename);
9172  status=WriteImage(image_info,matte_image,exception);
9173  matte_image=DestroyImage(matte_image);
9174  if (status == MagickFalse)
9175  XNoticeWidget(display,windows,"Unable to show matte",
9176  (*image)->filename);
9177  XDelay(display,1500);
9178  XSetCursorState(display,windows,MagickFalse);
9179  break;
9180  }
9181  case BackgroundCommand:
9182  {
9183  /*
9184  Background image.
9185  */
9186  status=XBackgroundImage(display,resource_info,windows,image,exception);
9187  if (status == MagickFalse)
9188  break;
9189  nexus=CloneImage(*image,0,0,MagickTrue,exception);
9190  if (nexus != (Image *) NULL)
9191  XClientMessage(display,windows->image.id,windows->im_protocols,
9192  windows->im_next_image,CurrentTime);
9193  break;
9194  }
9195  case SlideShowCommand:
9196  {
9197  static char
9198  delay[MagickPathExtent] = "5";
9199 
9200  /*
9201  Display next image after pausing.
9202  */
9203  (void) XDialogWidget(display,windows,"Slide Show",
9204  "Pause how many 1/100ths of a second between images:",delay);
9205  if (*delay == '\0')
9206  break;
9207  resource_info->delay=StringToUnsignedLong(delay);
9208  XClientMessage(display,windows->image.id,windows->im_protocols,
9209  windows->im_next_image,CurrentTime);
9210  break;
9211  }
9212  case PreferencesCommand:
9213  {
9214  /*
9215  Set user preferences.
9216  */
9217  status=XPreferencesWidget(display,resource_info,windows);
9218  if (status == MagickFalse)
9219  break;
9220  nexus=CloneImage(*image,0,0,MagickTrue,exception);
9221  if (nexus != (Image *) NULL)
9222  XClientMessage(display,windows->image.id,windows->im_protocols,
9223  windows->im_next_image,CurrentTime);
9224  break;
9225  }
9226  case HelpCommand:
9227  {
9228  /*
9229  User requested help.
9230  */
9231  XTextViewHelp(display,resource_info,windows,MagickFalse,
9232  "Help Viewer - Display",DisplayHelp);
9233  break;
9234  }
9235  case BrowseDocumentationCommand:
9236  {
9237  Atom
9238  mozilla_atom;
9239 
9240  Window
9241  mozilla_window,
9242  root_window;
9243 
9244  /*
9245  Browse the ImageMagick documentation.
9246  */
9247  root_window=XRootWindow(display,XDefaultScreen(display));
9248  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9249  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9250  if (mozilla_window != (Window) NULL)
9251  {
9252  char
9253  command[MagickPathExtent];
9254 
9255  /*
9256  Display documentation using Netscape remote control.
9257  */
9258  (void) FormatLocaleString(command,MagickPathExtent,
9259  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9260  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9261  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9262  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9263  XSetCursorState(display,windows,MagickFalse);
9264  break;
9265  }
9266  XSetCursorState(display,windows,MagickTrue);
9267  XCheckRefreshWindows(display,windows);
9268  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9269  exception);
9270  if (status == MagickFalse)
9271  XNoticeWidget(display,windows,"Unable to browse documentation",
9272  (char *) NULL);
9273  XDelay(display,1500);
9274  XSetCursorState(display,windows,MagickFalse);
9275  break;
9276  }
9277  case VersionCommand:
9278  {
9279  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9280  GetMagickCopyright());
9281  break;
9282  }
9283  case SaveToUndoBufferCommand:
9284  break;
9285  default:
9286  {
9287  (void) XBell(display,0);
9288  break;
9289  }
9290  }
9291  image_info=DestroyImageInfo(image_info);
9292  return(nexus);
9293 }
9294 ␌
9295 /*
9296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9297 % %
9298 % %
9299 % %
9300 + X M a g n i f y I m a g e %
9301 % %
9302 % %
9303 % %
9304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9305 %
9306 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9307 % The magnified portion is displayed in a separate window.
9308 %
9309 % The format of the XMagnifyImage method is:
9310 %
9311 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9312 % ExceptionInfo *exception)
9313 %
9314 % A description of each parameter follows:
9315 %
9316 % o display: Specifies a connection to an X server; returned from
9317 % XOpenDisplay.
9318 %
9319 % o windows: Specifies a pointer to a XWindows structure.
9320 %
9321 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9322 % the entire image is refreshed.
9323 %
9324 % o exception: return any errors or warnings in this structure.
9325 %
9326 */
9327 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9328  ExceptionInfo *exception)
9329 {
9330  char
9331  text[MagickPathExtent];
9332 
9333  int
9334  x,
9335  y;
9336 
9337  size_t
9338  state;
9339 
9340  /*
9341  Update magnified image until the mouse button is released.
9342  */
9343  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9344  state=DefaultState;
9345  x=event->xbutton.x;
9346  y=event->xbutton.y;
9347  windows->magnify.x=(int) windows->image.x+x;
9348  windows->magnify.y=(int) windows->image.y+y;
9349  do
9350  {
9351  /*
9352  Map and unmap Info widget as text cursor crosses its boundaries.
9353  */
9354  if (windows->info.mapped != MagickFalse)
9355  {
9356  if ((x < (windows->info.x+(int) windows->info.width)) &&
9357  (y < (windows->info.y+(int) windows->info.height)))
9358  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9359  }
9360  else
9361  if ((x > (windows->info.x+(int) windows->info.width)) ||
9362  (y > (windows->info.y+(int) windows->info.height)))
9363  (void) XMapWindow(display,windows->info.id);
9364  if (windows->info.mapped != MagickFalse)
9365  {
9366  /*
9367  Display pointer position.
9368  */
9369  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9370  windows->magnify.x,windows->magnify.y);
9371  XInfoWidget(display,windows,text);
9372  }
9373  /*
9374  Wait for next event.
9375  */
9376  XScreenEvent(display,windows,event,exception);
9377  switch (event->type)
9378  {
9379  case ButtonPress:
9380  break;
9381  case ButtonRelease:
9382  {
9383  /*
9384  User has finished magnifying image.
9385  */
9386  x=event->xbutton.x;
9387  y=event->xbutton.y;
9388  state|=ExitState;
9389  break;
9390  }
9391  case Expose:
9392  break;
9393  case MotionNotify:
9394  {
9395  x=event->xmotion.x;
9396  y=event->xmotion.y;
9397  break;
9398  }
9399  default:
9400  break;
9401  }
9402  /*
9403  Check boundary conditions.
9404  */
9405  if (x < 0)
9406  x=0;
9407  else
9408  if (x >= (int) windows->image.width)
9409  x=(int) windows->image.width-1;
9410  if (y < 0)
9411  y=0;
9412  else
9413  if (y >= (int) windows->image.height)
9414  y=(int) windows->image.height-1;
9415  } while ((state & ExitState) == 0);
9416  /*
9417  Display magnified image.
9418  */
9419  XSetCursorState(display,windows,MagickFalse);
9420 }
9421 ␌
9422 /*
9423 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9424 % %
9425 % %
9426 % %
9427 + X M a g n i f y W i n d o w C o m m a n d %
9428 % %
9429 % %
9430 % %
9431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9432 %
9433 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9434 % pixel as specified by the key symbol.
9435 %
9436 % The format of the XMagnifyWindowCommand method is:
9437 %
9438 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9439 % const MagickStatusType state,const KeySym key_symbol,
9440 % ExceptionInfo *exception)
9441 %
9442 % A description of each parameter follows:
9443 %
9444 % o display: Specifies a connection to an X server; returned from
9445 % XOpenDisplay.
9446 %
9447 % o windows: Specifies a pointer to a XWindows structure.
9448 %
9449 % o state: key mask.
9450 %
9451 % o key_symbol: Specifies a KeySym which indicates which side of the image
9452 % to trim.
9453 %
9454 % o exception: return any errors or warnings in this structure.
9455 %
9456 */
9457 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9458  const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9459 {
9460  unsigned int
9461  quantum;
9462 
9463  /*
9464  User specified a magnify factor or position.
9465  */
9466  quantum=1;
9467  if ((state & Mod1Mask) != 0)
9468  quantum=10;
9469  switch ((int) key_symbol)
9470  {
9471  case QuitCommand:
9472  {
9473  (void) XWithdrawWindow(display,windows->magnify.id,
9474  windows->magnify.screen);
9475  break;
9476  }
9477  case XK_Home:
9478  case XK_KP_Home:
9479  {
9480  windows->magnify.x=(int) windows->image.width/2;
9481  windows->magnify.y=(int) windows->image.height/2;
9482  break;
9483  }
9484  case XK_Left:
9485  case XK_KP_Left:
9486  {
9487  if (windows->magnify.x > 0)
9488  windows->magnify.x-=(int) quantum;
9489  break;
9490  }
9491  case XK_Up:
9492  case XK_KP_Up:
9493  {
9494  if (windows->magnify.y > 0)
9495  windows->magnify.y-=(int) quantum;
9496  break;
9497  }
9498  case XK_Right:
9499  case XK_KP_Right:
9500  {
9501  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9502  windows->magnify.x+=(int) quantum;
9503  break;
9504  }
9505  case XK_Down:
9506  case XK_KP_Down:
9507  {
9508  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9509  windows->magnify.y+=(int) quantum;
9510  break;
9511  }
9512  case XK_0:
9513  case XK_1:
9514  case XK_2:
9515  case XK_3:
9516  case XK_4:
9517  case XK_5:
9518  case XK_6:
9519  case XK_7:
9520  case XK_8:
9521  case XK_9:
9522  {
9523  windows->magnify.data=(key_symbol-XK_0);
9524  break;
9525  }
9526  case XK_KP_0:
9527  case XK_KP_1:
9528  case XK_KP_2:
9529  case XK_KP_3:
9530  case XK_KP_4:
9531  case XK_KP_5:
9532  case XK_KP_6:
9533  case XK_KP_7:
9534  case XK_KP_8:
9535  case XK_KP_9:
9536  {
9537  windows->magnify.data=(key_symbol-XK_KP_0);
9538  break;
9539  }
9540  default:
9541  break;
9542  }
9543  XMakeMagnifyImage(display,windows,exception);
9544 }
9545 ␌
9546 /*
9547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9548 % %
9549 % %
9550 % %
9551 + X M a k e P a n I m a g e %
9552 % %
9553 % %
9554 % %
9555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9556 %
9557 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9558 % icon window.
9559 %
9560 % The format of the XMakePanImage method is:
9561 %
9562 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9563 % XWindows *windows,Image *image,ExceptionInfo *exception)
9564 %
9565 % A description of each parameter follows:
9566 %
9567 % o display: Specifies a connection to an X server; returned from
9568 % XOpenDisplay.
9569 %
9570 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9571 %
9572 % o windows: Specifies a pointer to a XWindows structure.
9573 %
9574 % o image: the image.
9575 %
9576 % o exception: return any errors or warnings in this structure.
9577 %
9578 */
9579 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9580  XWindows *windows,Image *image,ExceptionInfo *exception)
9581 {
9582  MagickStatusType
9583  status;
9584 
9585  /*
9586  Create and display image for panning icon.
9587  */
9588  XSetCursorState(display,windows,MagickTrue);
9589  XCheckRefreshWindows(display,windows);
9590  windows->pan.x=(int) windows->image.x;
9591  windows->pan.y=(int) windows->image.y;
9592  status=XMakeImage(display,resource_info,&windows->pan,image,
9593  windows->pan.width,windows->pan.height,exception);
9594  if (status == MagickFalse)
9595  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
9596  image->filename);
9597  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9598  windows->pan.pixmap);
9599  (void) XClearWindow(display,windows->pan.id);
9600  XDrawPanRectangle(display,windows);
9601  XSetCursorState(display,windows,MagickFalse);
9602 }
9603 ␌
9604 /*
9605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9606 % %
9607 % %
9608 % %
9609 + X M a t t a E d i t I m a g e %
9610 % %
9611 % %
9612 % %
9613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9614 %
9615 % XMatteEditImage() allows the user to interactively change the Matte channel
9616 % of an image. If the image is PseudoClass it is promoted to DirectClass
9617 % before the matte information is stored.
9618 %
9619 % The format of the XMatteEditImage method is:
9620 %
9621 % MagickBooleanType XMatteEditImage(Display *display,
9622 % XResourceInfo *resource_info,XWindows *windows,Image **image,
9623 % ExceptionInfo *exception)
9624 %
9625 % A description of each parameter follows:
9626 %
9627 % o display: Specifies a connection to an X server; returned from
9628 % XOpenDisplay.
9629 %
9630 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9631 %
9632 % o windows: Specifies a pointer to a XWindows structure.
9633 %
9634 % o image: the image; returned from ReadImage.
9635 %
9636 % o exception: return any errors or warnings in this structure.
9637 %
9638 */
9639 static MagickBooleanType XMatteEditImage(Display *display,
9640  XResourceInfo *resource_info,XWindows *windows,Image **image,
9641  ExceptionInfo *exception)
9642 {
9643  const char
9644  *const MatteEditMenu[] =
9645  {
9646  "Method",
9647  "Border Color",
9648  "Fuzz",
9649  "Matte Value",
9650  "Undo",
9651  "Help",
9652  "Dismiss",
9653  (char *) NULL
9654  };
9655 
9656  static char
9657  matte[MagickPathExtent] = "0";
9658 
9659  static const ModeType
9660  MatteEditCommands[] =
9661  {
9662  MatteEditMethod,
9663  MatteEditBorderCommand,
9664  MatteEditFuzzCommand,
9665  MatteEditValueCommand,
9666  MatteEditUndoCommand,
9667  MatteEditHelpCommand,
9668  MatteEditDismissCommand
9669  };
9670 
9671  static PaintMethod
9672  method = PointMethod;
9673 
9674  static XColor
9675  border_color = { 0, 0, 0, 0, 0, 0 };
9676 
9677  char
9678  command[MagickPathExtent],
9679  text[MagickPathExtent];
9680 
9681  Cursor
9682  cursor;
9683 
9684  int
9685  entry,
9686  id,
9687  x,
9688  x_offset,
9689  y,
9690  y_offset;
9691 
9692  int
9693  i;
9694 
9695  Quantum
9696  *q;
9697 
9698  unsigned int
9699  height,
9700  width;
9701 
9702  size_t
9703  state;
9704 
9705  XEvent
9706  event;
9707 
9708  /*
9709  Map Command widget.
9710  */
9711  (void) CloneString(&windows->command.name,"Matte Edit");
9712  windows->command.data=4;
9713  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9714  (void) XMapRaised(display,windows->command.id);
9715  XClientMessage(display,windows->image.id,windows->im_protocols,
9716  windows->im_update_widget,CurrentTime);
9717  /*
9718  Make cursor.
9719  */
9720  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9721  resource_info->background_color,resource_info->foreground_color);
9722  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9723  /*
9724  Track pointer until button 1 is pressed.
9725  */
9726  XQueryPosition(display,windows->image.id,&x,&y);
9727  (void) XSelectInput(display,windows->image.id,
9728  windows->image.attributes.event_mask | PointerMotionMask);
9729  state=DefaultState;
9730  do
9731  {
9732  if (windows->info.mapped != MagickFalse)
9733  {
9734  /*
9735  Display pointer position.
9736  */
9737  (void) FormatLocaleString(text,MagickPathExtent," %+d%+d ",
9738  x+windows->image.x,y+windows->image.y);
9739  XInfoWidget(display,windows,text);
9740  }
9741  /*
9742  Wait for next event.
9743  */
9744  XScreenEvent(display,windows,&event,exception);
9745  if (event.xany.window == windows->command.id)
9746  {
9747  /*
9748  Select a command from the Command widget.
9749  */
9750  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9751  if (id < 0)
9752  {
9753  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9754  continue;
9755  }
9756  switch (MatteEditCommands[id])
9757  {
9758  case MatteEditMethod:
9759  {
9760  char
9761  **methods;
9762 
9763  /*
9764  Select a method from the pop-up menu.
9765  */
9766  methods=GetCommandOptions(MagickMethodOptions);
9767  if (methods == (char **) NULL)
9768  break;
9769  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9770  (const char **) methods,command);
9771  if (entry >= 0)
9772  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9773  MagickFalse,methods[entry]);
9774  methods=DestroyStringList(methods);
9775  break;
9776  }
9777  case MatteEditBorderCommand:
9778  {
9779  const char
9780  *ColorMenu[MaxNumberPens];
9781 
9782  int
9783  pen_number;
9784 
9785  /*
9786  Initialize menu selections.
9787  */
9788  for (i=0; i < (int) (MaxNumberPens-2); i++)
9789  ColorMenu[i]=resource_info->pen_colors[i];
9790  ColorMenu[MaxNumberPens-2]="Browser...";
9791  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9792  /*
9793  Select a pen color from the pop-up menu.
9794  */
9795  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9796  (const char **) ColorMenu,command);
9797  if (pen_number < 0)
9798  break;
9799  if (pen_number == (MaxNumberPens-2))
9800  {
9801  static char
9802  color_name[MagickPathExtent] = "gray";
9803 
9804  /*
9805  Select a pen color from a dialog.
9806  */
9807  resource_info->pen_colors[pen_number]=color_name;
9808  XColorBrowserWidget(display,windows,"Select",color_name);
9809  if (*color_name == '\0')
9810  break;
9811  }
9812  /*
9813  Set border color.
9814  */
9815  (void) XParseColor(display,windows->map_info->colormap,
9816  resource_info->pen_colors[pen_number],&border_color);
9817  break;
9818  }
9819  case MatteEditFuzzCommand:
9820  {
9821  const char
9822  *const FuzzMenu[] =
9823  {
9824  "0%",
9825  "2%",
9826  "5%",
9827  "10%",
9828  "15%",
9829  "Dialog...",
9830  (char *) NULL,
9831  };
9832 
9833  static char
9834  fuzz[MagickPathExtent];
9835 
9836  /*
9837  Select a command from the pop-up menu.
9838  */
9839  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9840  command);
9841  if (entry < 0)
9842  break;
9843  if (entry != 5)
9844  {
9845  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9846  QuantumRange+1.0);
9847  break;
9848  }
9849  (void) CopyMagickString(fuzz,"20%",MagickPathExtent);
9850  (void) XDialogWidget(display,windows,"Ok",
9851  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9852  if (*fuzz == '\0')
9853  break;
9854  (void) ConcatenateMagickString(fuzz,"%",MagickPathExtent);
9855  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9856  1.0);
9857  break;
9858  }
9859  case MatteEditValueCommand:
9860  {
9861  const char
9862  *const MatteMenu[] =
9863  {
9864  "Opaque",
9865  "Transparent",
9866  "Dialog...",
9867  (char *) NULL,
9868  };
9869 
9870  static char
9871  message[MagickPathExtent];
9872 
9873  /*
9874  Select a command from the pop-up menu.
9875  */
9876  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9877  command);
9878  if (entry < 0)
9879  break;
9880  if (entry != 2)
9881  {
9882  (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9883  (double) OpaqueAlpha);
9884  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9885  (void) FormatLocaleString(matte,MagickPathExtent,"%g",
9886  (double) TransparentAlpha);
9887  break;
9888  }
9889  (void) FormatLocaleString(message,MagickPathExtent,
9890  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9891  (void) XDialogWidget(display,windows,"Matte",message,matte);
9892  if (*matte == '\0')
9893  break;
9894  break;
9895  }
9896  case MatteEditUndoCommand:
9897  {
9898  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9899  image,exception);
9900  break;
9901  }
9902  case MatteEditHelpCommand:
9903  {
9904  XTextViewHelp(display,resource_info,windows,MagickFalse,
9905  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9906  break;
9907  }
9908  case MatteEditDismissCommand:
9909  {
9910  /*
9911  Prematurely exit.
9912  */
9913  state|=EscapeState;
9914  state|=ExitState;
9915  break;
9916  }
9917  default:
9918  break;
9919  }
9920  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9921  continue;
9922  }
9923  switch (event.type)
9924  {
9925  case ButtonPress:
9926  {
9927  if (event.xbutton.button != Button1)
9928  break;
9929  if ((event.xbutton.window != windows->image.id) &&
9930  (event.xbutton.window != windows->magnify.id))
9931  break;
9932  /*
9933  Update matte data.
9934  */
9935  x=event.xbutton.x;
9936  y=event.xbutton.y;
9937  (void) XMagickCommand(display,resource_info,windows,
9938  SaveToUndoBufferCommand,image,exception);
9939  state|=UpdateConfigurationState;
9940  break;
9941  }
9942  case ButtonRelease:
9943  {
9944  if (event.xbutton.button != Button1)
9945  break;
9946  if ((event.xbutton.window != windows->image.id) &&
9947  (event.xbutton.window != windows->magnify.id))
9948  break;
9949  /*
9950  Update colormap information.
9951  */
9952  x=event.xbutton.x;
9953  y=event.xbutton.y;
9954  XConfigureImageColormap(display,resource_info,windows,*image,exception);
9955  (void) XConfigureImage(display,resource_info,windows,*image,exception);
9956  XInfoWidget(display,windows,text);
9957  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9958  state&=(unsigned int) (~UpdateConfigurationState);
9959  break;
9960  }
9961  case Expose:
9962  break;
9963  case KeyPress:
9964  {
9965  char
9966  command[MagickPathExtent];
9967 
9968  KeySym
9969  key_symbol;
9970 
9971  if (event.xkey.window == windows->magnify.id)
9972  {
9973  Window
9974  window;
9975 
9976  window=windows->magnify.id;
9977  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9978  }
9979  if (event.xkey.window != windows->image.id)
9980  break;
9981  /*
9982  Respond to a user key press.
9983  */
9984  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9985  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9986  switch ((int) key_symbol)
9987  {
9988  case XK_Escape:
9989  case XK_F20:
9990  {
9991  /*
9992  Prematurely exit.
9993  */
9994  state|=ExitState;
9995  break;
9996  }
9997  case XK_F1:
9998  case XK_Help:
9999  {
10000  XTextViewHelp(display,resource_info,windows,MagickFalse,
10001  "Help Viewer - Matte Edit",ImageMatteEditHelp);
10002  break;
10003  }
10004  default:
10005  {
10006  (void) XBell(display,0);
10007  break;
10008  }
10009  }
10010  break;
10011  }
10012  case MotionNotify:
10013  {
10014  /*
10015  Map and unmap Info widget as cursor crosses its boundaries.
10016  */
10017  x=event.xmotion.x;
10018  y=event.xmotion.y;
10019  if (windows->info.mapped != MagickFalse)
10020  {
10021  if ((x < (windows->info.x+(int) windows->info.width)) &&
10022  (y < (windows->info.y+(int) windows->info.height)))
10023  (void) XWithdrawWindow(display,windows->info.id,
10024  windows->info.screen);
10025  }
10026  else
10027  if ((x > (windows->info.x+(int) windows->info.width)) ||
10028  (y > (windows->info.y+(int) windows->info.height)))
10029  (void) XMapWindow(display,windows->info.id);
10030  break;
10031  }
10032  default:
10033  break;
10034  }
10035  if (event.xany.window == windows->magnify.id)
10036  {
10037  x=windows->magnify.x-windows->image.x;
10038  y=windows->magnify.y-windows->image.y;
10039  }
10040  x_offset=x;
10041  y_offset=y;
10042  if ((state & UpdateConfigurationState) != 0)
10043  {
10044  CacheView
10045  *image_view;
10046 
10047  int
10048  x,
10049  y;
10050 
10051  /*
10052  Matte edit is relative to image configuration.
10053  */
10054  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10055  MagickTrue);
10056  XPutPixel(windows->image.ximage,x_offset,y_offset,
10057  windows->pixel_info->background_color.pixel);
10058  width=(unsigned int) (*image)->columns;
10059  height=(unsigned int) (*image)->rows;
10060  x=0;
10061  y=0;
10062  if (windows->image.crop_geometry != (char *) NULL)
10063  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10064  &height);
10065  x_offset=((int) width*(windows->image.x+x_offset)/
10066  windows->image.ximage->width+x);
10067  y_offset=((int) height*(windows->image.y+y_offset)/
10068  windows->image.ximage->height+y);
10069  if ((x_offset < 0) || (y_offset < 0))
10070  continue;
10071  if ((x_offset >= (int) (*image)->columns) ||
10072  (y_offset >= (int) (*image)->rows))
10073  continue;
10074  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10075  return(MagickFalse);
10076  if ((*image)->alpha_trait == UndefinedPixelTrait)
10077  (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10078  image_view=AcquireAuthenticCacheView(*image,exception);
10079  switch (method)
10080  {
10081  case PointMethod:
10082  default:
10083  {
10084  /*
10085  Update matte information using point algorithm.
10086  */
10087  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10088  (ssize_t) y_offset,1,1,exception);
10089  if (q == (Quantum *) NULL)
10090  break;
10091  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10092  (void) SyncCacheViewAuthenticPixels(image_view,exception);
10093  break;
10094  }
10095  case ReplaceMethod:
10096  {
10097  PixelInfo
10098  pixel,
10099  target;
10100 
10101  /*
10102  Update matte information using replace algorithm.
10103  */
10104  (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10105  x_offset,(ssize_t) y_offset,&target,exception);
10106  for (y=0; y < (int) (*image)->rows; y++)
10107  {
10108  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10109  (*image)->columns,1,exception);
10110  if (q == (Quantum *) NULL)
10111  break;
10112  for (x=0; x < (int) (*image)->columns; x++)
10113  {
10114  GetPixelInfoPixel(*image,q,&pixel);
10115  if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10116  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10117  q+=(ptrdiff_t) GetPixelChannels(*image);
10118  }
10119  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10120  break;
10121  }
10122  break;
10123  }
10124  case FloodfillMethod:
10125  case FillToBorderMethod:
10126  {
10127  ChannelType
10128  channel_mask;
10129 
10130  DrawInfo
10131  *draw_info;
10132 
10133  PixelInfo
10134  target;
10135 
10136  /*
10137  Update matte information using floodfill algorithm.
10138  */
10139  (void) GetOneVirtualPixelInfo(*image,
10140  GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10141  y_offset,&target,exception);
10142  if (method == FillToBorderMethod)
10143  {
10144  target.red=(double) ScaleShortToQuantum(
10145  border_color.red);
10146  target.green=(double) ScaleShortToQuantum(
10147  border_color.green);
10148  target.blue=(double) ScaleShortToQuantum(
10149  border_color.blue);
10150  }
10151  draw_info=CloneDrawInfo(resource_info->image_info,
10152  (DrawInfo *) NULL);
10153  draw_info->fill.alpha=(double) ClampToQuantum(
10154  StringToDouble(matte,(char **) NULL));
10155  channel_mask=SetImageChannelMask(*image,AlphaChannel);
10156  (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10157  x_offset,(ssize_t) y_offset,
10158  method != FloodfillMethod ? MagickTrue : MagickFalse,exception);
10159  (void) SetPixelChannelMask(*image,channel_mask);
10160  draw_info=DestroyDrawInfo(draw_info);
10161  break;
10162  }
10163  case ResetMethod:
10164  {
10165  /*
10166  Update matte information using reset algorithm.
10167  */
10168  if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10169  return(MagickFalse);
10170  for (y=0; y < (int) (*image)->rows; y++)
10171  {
10172  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10173  (*image)->columns,1,exception);
10174  if (q == (Quantum *) NULL)
10175  break;
10176  for (x=0; x < (int) (*image)->columns; x++)
10177  {
10178  SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10179  q+=(ptrdiff_t) GetPixelChannels(*image);
10180  }
10181  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10182  break;
10183  }
10184  if (StringToLong(matte) == (long) OpaqueAlpha)
10185  (*image)->alpha_trait=UndefinedPixelTrait;
10186  break;
10187  }
10188  }
10189  image_view=DestroyCacheView(image_view);
10190  state&=(unsigned int) (~UpdateConfigurationState);
10191  }
10192  } while ((state & ExitState) == 0);
10193  (void) XSelectInput(display,windows->image.id,
10194  windows->image.attributes.event_mask);
10195  XSetCursorState(display,windows,MagickFalse);
10196  (void) XFreeCursor(display,cursor);
10197  return(MagickTrue);
10198 }
10199 ␌
10200 /*
10201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10202 % %
10203 % %
10204 % %
10205 + X O p e n I m a g e %
10206 % %
10207 % %
10208 % %
10209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10210 %
10211 % XOpenImage() loads an image from a file.
10212 %
10213 % The format of the XOpenImage method is:
10214 %
10215 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10216 % XWindows *windows,const unsigned int command)
10217 %
10218 % A description of each parameter follows:
10219 %
10220 % o display: Specifies a connection to an X server; returned from
10221 % XOpenDisplay.
10222 %
10223 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10224 %
10225 % o windows: Specifies a pointer to a XWindows structure.
10226 %
10227 % o command: A value other than zero indicates that the file is selected
10228 % from the command line argument list.
10229 %
10230 */
10231 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10232  XWindows *windows,const MagickBooleanType command)
10233 {
10234  const MagickInfo
10235  *magick_info;
10236 
10238  *exception;
10239 
10240  Image
10241  *nexus;
10242 
10243  ImageInfo
10244  *image_info;
10245 
10246  static char
10247  filename[MagickPathExtent] = "\0";
10248 
10249  /*
10250  Request file name from user.
10251  */
10252  if (command == MagickFalse)
10253  XFileBrowserWidget(display,windows,"Open",filename);
10254  else
10255  {
10256  char
10257  **filelist,
10258  **files;
10259 
10260  int
10261  count,
10262  status;
10263 
10264  int
10265  i,
10266  j;
10267 
10268  /*
10269  Select next image from the command line.
10270  */
10271  status=XGetCommand(display,windows->image.id,&files,&count);
10272  if (status == 0)
10273  {
10274  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10275  return((Image *) NULL);
10276  }
10277  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10278  if (filelist == (char **) NULL)
10279  {
10280  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10281  "...");
10282  (void) XFreeStringList(files);
10283  return((Image *) NULL);
10284  }
10285  j=0;
10286  for (i=1; i < count; i++)
10287  if (*files[i] != '-')
10288  filelist[j++]=files[i];
10289  filelist[j]=(char *) NULL;
10290  XListBrowserWidget(display,windows,&windows->widget,
10291  (const char **) filelist,"Load","Select Image to Load:",filename);
10292  filelist=(char **) RelinquishMagickMemory(filelist);
10293  (void) XFreeStringList(files);
10294  }
10295  if (*filename == '\0')
10296  return((Image *) NULL);
10297  image_info=CloneImageInfo(resource_info->image_info);
10298  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10299  (void *) NULL);
10300  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10301  exception=AcquireExceptionInfo();
10302  (void) SetImageInfo(image_info,0,exception);
10303  if (LocaleCompare(image_info->magick,"X") == 0)
10304  {
10305  char
10306  seconds[MagickPathExtent];
10307 
10308  /*
10309  User may want to delay the X server screen grab.
10310  */
10311  (void) CopyMagickString(seconds,"0",MagickPathExtent);
10312  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10313  seconds);
10314  if (*seconds == '\0')
10315  return((Image *) NULL);
10316  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10317  }
10318  magick_info=GetMagickInfo(image_info->magick,exception);
10319  if ((magick_info != (const MagickInfo *) NULL) &&
10320  GetMagickRawSupport(magick_info) == MagickTrue)
10321  {
10322  char
10323  geometry[MagickPathExtent];
10324 
10325  /*
10326  Request image size from the user.
10327  */
10328  (void) CopyMagickString(geometry,"512x512",MagickPathExtent);
10329  if (image_info->size != (char *) NULL)
10330  (void) CopyMagickString(geometry,image_info->size,MagickPathExtent);
10331  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10332  geometry);
10333  (void) CloneString(&image_info->size,geometry);
10334  }
10335  /*
10336  Load the image.
10337  */
10338  XSetCursorState(display,windows,MagickTrue);
10339  XCheckRefreshWindows(display,windows);
10340  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
10341  nexus=ReadImage(image_info,exception);
10342  CatchException(exception);
10343  XSetCursorState(display,windows,MagickFalse);
10344  if (nexus != (Image *) NULL)
10345  XClientMessage(display,windows->image.id,windows->im_protocols,
10346  windows->im_next_image,CurrentTime);
10347  else
10348  {
10349  char
10350  *text,
10351  **textlist;
10352 
10353  /*
10354  Unknown image format.
10355  */
10356  text=FileToString(filename,~0UL,exception);
10357  if (text == (char *) NULL)
10358  return((Image *) NULL);
10359  textlist=StringToList(text);
10360  if (textlist != (char **) NULL)
10361  {
10362  char
10363  title[MagickPathExtent];
10364 
10365  int
10366  i;
10367 
10368  (void) FormatLocaleString(title,MagickPathExtent,
10369  "Unknown format: %s",filename);
10370  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10371  (const char **) textlist);
10372  for (i=0; textlist[i] != (char *) NULL; i++)
10373  textlist[i]=DestroyString(textlist[i]);
10374  textlist=(char **) RelinquishMagickMemory(textlist);
10375  }
10376  text=DestroyString(text);
10377  }
10378  exception=DestroyExceptionInfo(exception);
10379  image_info=DestroyImageInfo(image_info);
10380  return(nexus);
10381 }
10382 ␌
10383 /*
10384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10385 % %
10386 % %
10387 % %
10388 + X P a n I m a g e %
10389 % %
10390 % %
10391 % %
10392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10393 %
10394 % XPanImage() pans the image until the mouse button is released.
10395 %
10396 % The format of the XPanImage method is:
10397 %
10398 % void XPanImage(Display *display,XWindows *windows,XEvent *event,
10399 % ExceptionInfo *exception)
10400 %
10401 % A description of each parameter follows:
10402 %
10403 % o display: Specifies a connection to an X server; returned from
10404 % XOpenDisplay.
10405 %
10406 % o windows: Specifies a pointer to a XWindows structure.
10407 %
10408 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10409 % the entire image is refreshed.
10410 %
10411 % o exception: return any errors or warnings in this structure.
10412 %
10413 */
10414 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10415  ExceptionInfo *exception)
10416 {
10417  char
10418  text[MagickPathExtent];
10419 
10420  Cursor
10421  cursor;
10422 
10423  double
10424  x_factor,
10425  y_factor;
10426 
10428  pan_info;
10429 
10430  size_t
10431  state;
10432 
10433  /*
10434  Define cursor.
10435  */
10436  if ((windows->image.ximage->width > (int) windows->image.width) &&
10437  (windows->image.ximage->height > (int) windows->image.height))
10438  cursor=XCreateFontCursor(display,XC_fleur);
10439  else
10440  if (windows->image.ximage->width > (int) windows->image.width)
10441  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10442  else
10443  if (windows->image.ximage->height > (int) windows->image.height)
10444  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10445  else
10446  cursor=XCreateFontCursor(display,XC_arrow);
10447  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10448  /*
10449  Pan image as pointer moves until the mouse button is released.
10450  */
10451  x_factor=(double) windows->image.ximage->width/windows->pan.width;
10452  y_factor=(double) windows->image.ximage->height/windows->pan.height;
10453  pan_info.width=windows->pan.width*windows->image.width/
10454  (unsigned int) windows->image.ximage->width;
10455  pan_info.height=windows->pan.height*windows->image.height/
10456  (unsigned int) windows->image.ximage->height;
10457  pan_info.x=0;
10458  pan_info.y=0;
10459  state=UpdateConfigurationState;
10460  do
10461  {
10462  switch (event->type)
10463  {
10464  case ButtonPress:
10465  {
10466  /*
10467  User choose an initial pan location.
10468  */
10469  pan_info.x=(ssize_t) event->xbutton.x;
10470  pan_info.y=(ssize_t) event->xbutton.y;
10471  state|=UpdateConfigurationState;
10472  break;
10473  }
10474  case ButtonRelease:
10475  {
10476  /*
10477  User has finished panning the image.
10478  */
10479  pan_info.x=(ssize_t) event->xbutton.x;
10480  pan_info.y=(ssize_t) event->xbutton.y;
10481  state|=UpdateConfigurationState | ExitState;
10482  break;
10483  }
10484  case MotionNotify:
10485  {
10486  pan_info.x=(ssize_t) event->xmotion.x;
10487  pan_info.y=(ssize_t) event->xmotion.y;
10488  state|=UpdateConfigurationState;
10489  }
10490  default:
10491  break;
10492  }
10493  if ((state & UpdateConfigurationState) != 0)
10494  {
10495  /*
10496  Check boundary conditions.
10497  */
10498  if (pan_info.x < (ssize_t) (pan_info.width/2))
10499  pan_info.x=0;
10500  else
10501  pan_info.x=(ssize_t) (x_factor*(pan_info.x-((int) pan_info.width/2)));
10502  if (pan_info.x < 0)
10503  pan_info.x=0;
10504  else
10505  if ((int) (pan_info.x+windows->image.width) >
10506  windows->image.ximage->width)
10507  pan_info.x=(ssize_t) (windows->image.ximage->width-(int) windows->image.width);
10508  if (pan_info.y < (ssize_t) (pan_info.height/2))
10509  pan_info.y=0;
10510  else
10511  pan_info.y=(ssize_t) (y_factor*(pan_info.y-((int) pan_info.height/2)));
10512  if (pan_info.y < 0)
10513  pan_info.y=0;
10514  else
10515  if ((int) (pan_info.y+windows->image.height) >
10516  windows->image.ximage->height)
10517  pan_info.y=(ssize_t) (windows->image.ximage->height-(int)
10518  windows->image.height);
10519  if ((windows->image.x != (int) pan_info.x) ||
10520  (windows->image.y != (int) pan_info.y))
10521  {
10522  /*
10523  Display image pan offset.
10524  */
10525  windows->image.x=(int) pan_info.x;
10526  windows->image.y=(int) pan_info.y;
10527  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
10528  windows->image.width,windows->image.height,windows->image.x,
10529  windows->image.y);
10530  XInfoWidget(display,windows,text);
10531  /*
10532  Refresh Image window.
10533  */
10534  XDrawPanRectangle(display,windows);
10535  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10536  }
10537  state&=(unsigned int) (~UpdateConfigurationState);
10538  }
10539  /*
10540  Wait for next event.
10541  */
10542  if ((state & ExitState) == 0)
10543  XScreenEvent(display,windows,event,exception);
10544  } while ((state & ExitState) == 0);
10545  /*
10546  Restore cursor.
10547  */
10548  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10549  (void) XFreeCursor(display,cursor);
10550  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10551 }
10552 ␌
10553 /*
10554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10555 % %
10556 % %
10557 % %
10558 + X P a s t e I m a g e %
10559 % %
10560 % %
10561 % %
10562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10563 %
10564 % XPasteImage() pastes an image previously saved with XCropImage in the X
10565 % window image at a location the user chooses with the pointer.
10566 %
10567 % The format of the XPasteImage method is:
10568 %
10569 % MagickBooleanType XPasteImage(Display *display,
10570 % XResourceInfo *resource_info,XWindows *windows,Image *image,
10571 % ExceptionInfo *exception)
10572 %
10573 % A description of each parameter follows:
10574 %
10575 % o display: Specifies a connection to an X server; returned from
10576 % XOpenDisplay.
10577 %
10578 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10579 %
10580 % o windows: Specifies a pointer to a XWindows structure.
10581 %
10582 % o image: the image; returned from ReadImage.
10583 %
10584 % o exception: return any errors or warnings in this structure.
10585 %
10586 */
10587 static MagickBooleanType XPasteImage(Display *display,
10588  XResourceInfo *resource_info,XWindows *windows,Image *image,
10589  ExceptionInfo *exception)
10590 {
10591  const char
10592  *const PasteMenu[] =
10593  {
10594  "Operator",
10595  "Help",
10596  "Dismiss",
10597  (char *) NULL
10598  };
10599 
10600  static const ModeType
10601  PasteCommands[] =
10602  {
10603  PasteOperatorsCommand,
10604  PasteHelpCommand,
10605  PasteDismissCommand
10606  };
10607 
10608  static CompositeOperator
10609  compose = CopyCompositeOp;
10610 
10611  char
10612  text[MagickPathExtent];
10613 
10614  Cursor
10615  cursor;
10616 
10617  Image
10618  *paste_image;
10619 
10620  int
10621  entry,
10622  id,
10623  x,
10624  y;
10625 
10626  double
10627  scale_factor;
10628 
10630  highlight_info,
10631  paste_info;
10632 
10633  unsigned int
10634  height,
10635  width;
10636 
10637  size_t
10638  state;
10639 
10640  XEvent
10641  event;
10642 
10643  /*
10644  Copy image.
10645  */
10646  if (resource_info->copy_image == (Image *) NULL)
10647  return(MagickFalse);
10648  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10649  if (paste_image == (Image *) NULL)
10650  return(MagickFalse);
10651  /*
10652  Map Command widget.
10653  */
10654  (void) CloneString(&windows->command.name,"Paste");
10655  windows->command.data=1;
10656  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10657  (void) XMapRaised(display,windows->command.id);
10658  XClientMessage(display,windows->image.id,windows->im_protocols,
10659  windows->im_update_widget,CurrentTime);
10660  /*
10661  Track pointer until button 1 is pressed.
10662  */
10663  XSetCursorState(display,windows,MagickFalse);
10664  XQueryPosition(display,windows->image.id,&x,&y);
10665  (void) XSelectInput(display,windows->image.id,
10666  windows->image.attributes.event_mask | PointerMotionMask);
10667  paste_info.x=(ssize_t) windows->image.x+x;
10668  paste_info.y=(ssize_t) windows->image.y+y;
10669  paste_info.width=0;
10670  paste_info.height=0;
10671  cursor=XCreateFontCursor(display,XC_ul_angle);
10672  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10673  state=DefaultState;
10674  do
10675  {
10676  if (windows->info.mapped != MagickFalse)
10677  {
10678  /*
10679  Display pointer position.
10680  */
10681  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
10682  (long) paste_info.x,(long) paste_info.y);
10683  XInfoWidget(display,windows,text);
10684  }
10685  highlight_info=paste_info;
10686  highlight_info.x=paste_info.x-windows->image.x;
10687  highlight_info.y=paste_info.y-windows->image.y;
10688  XHighlightRectangle(display,windows->image.id,
10689  windows->image.highlight_context,&highlight_info);
10690  /*
10691  Wait for next event.
10692  */
10693  XScreenEvent(display,windows,&event,exception);
10694  XHighlightRectangle(display,windows->image.id,
10695  windows->image.highlight_context,&highlight_info);
10696  if (event.xany.window == windows->command.id)
10697  {
10698  /*
10699  Select a command from the Command widget.
10700  */
10701  id=XCommandWidget(display,windows,PasteMenu,&event);
10702  if (id < 0)
10703  continue;
10704  switch (PasteCommands[id])
10705  {
10706  case PasteOperatorsCommand:
10707  {
10708  char
10709  command[MagickPathExtent],
10710  **operators;
10711 
10712  /*
10713  Select a command from the pop-up menu.
10714  */
10715  operators=GetCommandOptions(MagickComposeOptions);
10716  if (operators == (char **) NULL)
10717  break;
10718  entry=XMenuWidget(display,windows,PasteMenu[id],
10719  (const char **) operators,command);
10720  if (entry >= 0)
10721  compose=(CompositeOperator) ParseCommandOption(
10722  MagickComposeOptions,MagickFalse,operators[entry]);
10723  operators=DestroyStringList(operators);
10724  break;
10725  }
10726  case PasteHelpCommand:
10727  {
10728  XTextViewHelp(display,resource_info,windows,MagickFalse,
10729  "Help Viewer - Image Composite",ImagePasteHelp);
10730  break;
10731  }
10732  case PasteDismissCommand:
10733  {
10734  /*
10735  Prematurely exit.
10736  */
10737  state|=EscapeState;
10738  state|=ExitState;
10739  break;
10740  }
10741  default:
10742  break;
10743  }
10744  continue;
10745  }
10746  switch (event.type)
10747  {
10748  case ButtonPress:
10749  {
10750  if (resource_info->debug != MagickFalse)
10751  (void) LogMagickEvent(X11Event,GetMagickModule(),
10752  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10753  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10754  if (event.xbutton.button != Button1)
10755  break;
10756  if (event.xbutton.window != windows->image.id)
10757  break;
10758  /*
10759  Paste rectangle is relative to image configuration.
10760  */
10761  width=(unsigned int) image->columns;
10762  height=(unsigned int) image->rows;
10763  x=0;
10764  y=0;
10765  if (windows->image.crop_geometry != (char *) NULL)
10766  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10767  &width,&height);
10768  scale_factor=(double) windows->image.ximage->width/width;
10769  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10770  scale_factor=(double) windows->image.ximage->height/height;
10771  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10772  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10773  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10774  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10775  break;
10776  }
10777  case ButtonRelease:
10778  {
10779  if (resource_info->debug != MagickFalse)
10780  (void) LogMagickEvent(X11Event,GetMagickModule(),
10781  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10782  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10783  if (event.xbutton.button != Button1)
10784  break;
10785  if (event.xbutton.window != windows->image.id)
10786  break;
10787  if ((paste_info.width != 0) && (paste_info.height != 0))
10788  {
10789  /*
10790  User has selected the location of the paste image.
10791  */
10792  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10793  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10794  state|=ExitState;
10795  }
10796  break;
10797  }
10798  case Expose:
10799  break;
10800  case KeyPress:
10801  {
10802  char
10803  command[MagickPathExtent];
10804 
10805  KeySym
10806  key_symbol;
10807 
10808  int
10809  length;
10810 
10811  if (event.xkey.window != windows->image.id)
10812  break;
10813  /*
10814  Respond to a user key press.
10815  */
10816  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10817  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10818  *(command+length)='\0';
10819  if (resource_info->debug != MagickFalse)
10820  (void) LogMagickEvent(X11Event,GetMagickModule(),
10821  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10822  switch ((int) key_symbol)
10823  {
10824  case XK_Escape:
10825  case XK_F20:
10826  {
10827  /*
10828  Prematurely exit.
10829  */
10830  paste_image=DestroyImage(paste_image);
10831  state|=EscapeState;
10832  state|=ExitState;
10833  break;
10834  }
10835  case XK_F1:
10836  case XK_Help:
10837  {
10838  (void) XSetFunction(display,windows->image.highlight_context,
10839  GXcopy);
10840  XTextViewHelp(display,resource_info,windows,MagickFalse,
10841  "Help Viewer - Image Composite",ImagePasteHelp);
10842  (void) XSetFunction(display,windows->image.highlight_context,
10843  GXinvert);
10844  break;
10845  }
10846  default:
10847  {
10848  (void) XBell(display,0);
10849  break;
10850  }
10851  }
10852  break;
10853  }
10854  case MotionNotify:
10855  {
10856  /*
10857  Map and unmap Info widget as text cursor crosses its boundaries.
10858  */
10859  x=event.xmotion.x;
10860  y=event.xmotion.y;
10861  if (windows->info.mapped != MagickFalse)
10862  {
10863  if ((x < (windows->info.x+(int) windows->info.width)) &&
10864  (y < (windows->info.y+(int) windows->info.height)))
10865  (void) XWithdrawWindow(display,windows->info.id,
10866  windows->info.screen);
10867  }
10868  else
10869  if ((x > (windows->info.x+(int) windows->info.width)) ||
10870  (y > (windows->info.y+(int) windows->info.height)))
10871  (void) XMapWindow(display,windows->info.id);
10872  paste_info.x=(ssize_t) windows->image.x+x;
10873  paste_info.y=(ssize_t) windows->image.y+y;
10874  break;
10875  }
10876  default:
10877  {
10878  if (resource_info->debug != MagickFalse)
10879  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10880  event.type);
10881  break;
10882  }
10883  }
10884  } while ((state & ExitState) == 0);
10885  (void) XSelectInput(display,windows->image.id,
10886  windows->image.attributes.event_mask);
10887  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10888  XSetCursorState(display,windows,MagickFalse);
10889  (void) XFreeCursor(display,cursor);
10890  if ((state & EscapeState) != 0)
10891  return(MagickTrue);
10892  /*
10893  Image pasting is relative to image configuration.
10894  */
10895  XSetCursorState(display,windows,MagickTrue);
10896  XCheckRefreshWindows(display,windows);
10897  width=(unsigned int) image->columns;
10898  height=(unsigned int) image->rows;
10899  x=0;
10900  y=0;
10901  if (windows->image.crop_geometry != (char *) NULL)
10902  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10903  scale_factor=(double) width/windows->image.ximage->width;
10904  paste_info.x+=x;
10905  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10906  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10907  scale_factor=(double) height/windows->image.ximage->height;
10908  paste_info.y+=y;
10909  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10910  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10911  /*
10912  Paste image with X Image window.
10913  */
10914  (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10915  paste_info.y,exception);
10916  paste_image=DestroyImage(paste_image);
10917  XSetCursorState(display,windows,MagickFalse);
10918  /*
10919  Update image colormap.
10920  */
10921  XConfigureImageColormap(display,resource_info,windows,image,exception);
10922  (void) XConfigureImage(display,resource_info,windows,image,exception);
10923  return(MagickTrue);
10924 }
10925 ␌
10926 /*
10927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10928 % %
10929 % %
10930 % %
10931 + X P r i n t I m a g e %
10932 % %
10933 % %
10934 % %
10935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10936 %
10937 % XPrintImage() prints an image to a Postscript printer.
10938 %
10939 % The format of the XPrintImage method is:
10940 %
10941 % MagickBooleanType XPrintImage(Display *display,
10942 % XResourceInfo *resource_info,XWindows *windows,Image *image,
10943 % ExceptionInfo *exception)
10944 %
10945 % A description of each parameter follows:
10946 %
10947 % o display: Specifies a connection to an X server; returned from
10948 % XOpenDisplay.
10949 %
10950 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10951 %
10952 % o windows: Specifies a pointer to a XWindows structure.
10953 %
10954 % o image: the image.
10955 %
10956 % o exception: return any errors or warnings in this structure.
10957 %
10958 */
10959 static MagickBooleanType XPrintImage(Display *display,
10960  XResourceInfo *resource_info,XWindows *windows,Image *image,
10961  ExceptionInfo *exception)
10962 {
10963  char
10964  filename[MagickPathExtent],
10965  geometry[MagickPathExtent];
10966 
10967  const char
10968  *const PageSizes[] =
10969  {
10970  "Letter",
10971  "Tabloid",
10972  "Ledger",
10973  "Legal",
10974  "Statement",
10975  "Executive",
10976  "A3",
10977  "A4",
10978  "A5",
10979  "B4",
10980  "B5",
10981  "Folio",
10982  "Quarto",
10983  "10x14",
10984  (char *) NULL
10985  };
10986 
10987  Image
10988  *print_image;
10989 
10990  ImageInfo
10991  *image_info;
10992 
10993  MagickStatusType
10994  status;
10995 
10996  /*
10997  Request Postscript page geometry from user.
10998  */
10999  image_info=CloneImageInfo(resource_info->image_info);
11000  (void) FormatLocaleString(geometry,MagickPathExtent,"Letter");
11001  if (image_info->page != (char *) NULL)
11002  (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
11003  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11004  "Select Postscript Page Geometry:",geometry);
11005  if (*geometry == '\0')
11006  return(MagickTrue);
11007  image_info->page=GetPageGeometry(geometry);
11008  /*
11009  Apply image transforms.
11010  */
11011  XSetCursorState(display,windows,MagickTrue);
11012  XCheckRefreshWindows(display,windows);
11013  print_image=CloneImage(image,0,0,MagickTrue,exception);
11014  if (print_image == (Image *) NULL)
11015  return(MagickFalse);
11016  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
11017  windows->image.ximage->width,windows->image.ximage->height);
11018  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11019  exception);
11020  /*
11021  Print image.
11022  */
11023  (void) AcquireUniqueFilename(filename);
11024  (void) FormatLocaleString(print_image->filename,MagickPathExtent,"print:%s",
11025  filename);
11026  status=WriteImage(image_info,print_image,exception);
11027  (void) RelinquishUniqueFileResource(filename);
11028  print_image=DestroyImage(print_image);
11029  image_info=DestroyImageInfo(image_info);
11030  XSetCursorState(display,windows,MagickFalse);
11031  return(status != 0 ? MagickTrue : MagickFalse);
11032 }
11033 ␌
11034 /*
11035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11036 % %
11037 % %
11038 % %
11039 + X R O I I m a g e %
11040 % %
11041 % %
11042 % %
11043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11044 %
11045 % XROIImage() applies an image processing technique to a region of interest.
11046 %
11047 % The format of the XROIImage method is:
11048 %
11049 % MagickBooleanType XROIImage(Display *display,
11050 % XResourceInfo *resource_info,XWindows *windows,Image **image,
11051 % ExceptionInfo *exception)
11052 %
11053 % A description of each parameter follows:
11054 %
11055 % o display: Specifies a connection to an X server; returned from
11056 % XOpenDisplay.
11057 %
11058 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11059 %
11060 % o windows: Specifies a pointer to a XWindows structure.
11061 %
11062 % o image: the image; returned from ReadImage.
11063 %
11064 % o exception: return any errors or warnings in this structure.
11065 %
11066 */
11067 static MagickBooleanType XROIImage(Display *display,
11068  XResourceInfo *resource_info,XWindows *windows,Image **image,
11069  ExceptionInfo *exception)
11070 {
11071 #define ApplyMenus 7
11072 
11073  const char
11074  *const ROIMenu[] =
11075  {
11076  "Help",
11077  "Dismiss",
11078  (char *) NULL
11079  },
11080  *const ApplyMenu[] =
11081  {
11082  "File",
11083  "Edit",
11084  "Transform",
11085  "Enhance",
11086  "Effects",
11087  "F/X",
11088  "Miscellany",
11089  "Help",
11090  "Dismiss",
11091  (char *) NULL
11092  },
11093  *const FileMenu[] =
11094  {
11095  "Save...",
11096  "Print...",
11097  (char *) NULL
11098  },
11099  *const EditMenu[] =
11100  {
11101  "Undo",
11102  "Redo",
11103  (char *) NULL
11104  },
11105  *const TransformMenu[] =
11106  {
11107  "Flop",
11108  "Flip",
11109  "Rotate Right",
11110  "Rotate Left",
11111  (char *) NULL
11112  },
11113  *const EnhanceMenu[] =
11114  {
11115  "Hue...",
11116  "Saturation...",
11117  "Brightness...",
11118  "Gamma...",
11119  "Spiff",
11120  "Dull",
11121  "Contrast Stretch...",
11122  "Sigmoidal Contrast...",
11123  "Normalize",
11124  "Equalize",
11125  "Negate",
11126  "Grayscale",
11127  "Map...",
11128  "Quantize...",
11129  (char *) NULL
11130  },
11131  *const EffectsMenu[] =
11132  {
11133  "Despeckle",
11134  "Emboss",
11135  "Reduce Noise",
11136  "Add Noise",
11137  "Sharpen...",
11138  "Blur...",
11139  "Threshold...",
11140  "Edge Detect...",
11141  "Spread...",
11142  "Shade...",
11143  "Raise...",
11144  "Segment...",
11145  (char *) NULL
11146  },
11147  *const FXMenu[] =
11148  {
11149  "Solarize...",
11150  "Sepia Tone...",
11151  "Swirl...",
11152  "Implode...",
11153  "Vignette...",
11154  "Wave...",
11155  "Oil Paint...",
11156  "Charcoal Draw...",
11157  (char *) NULL
11158  },
11159  *const MiscellanyMenu[] =
11160  {
11161  "Image Info",
11162  "Zoom Image",
11163  "Show Preview...",
11164  "Show Histogram",
11165  "Show Matte",
11166  (char *) NULL
11167  };
11168 
11169  const char
11170  *const *Menus[ApplyMenus] =
11171  {
11172  FileMenu,
11173  EditMenu,
11174  TransformMenu,
11175  EnhanceMenu,
11176  EffectsMenu,
11177  FXMenu,
11178  MiscellanyMenu
11179  };
11180 
11181  static const DisplayCommand
11182  ApplyCommands[] =
11183  {
11184  NullCommand,
11185  NullCommand,
11186  NullCommand,
11187  NullCommand,
11188  NullCommand,
11189  NullCommand,
11190  NullCommand,
11191  HelpCommand,
11192  QuitCommand
11193  },
11194  FileCommands[] =
11195  {
11196  SaveCommand,
11197  PrintCommand
11198  },
11199  EditCommands[] =
11200  {
11201  UndoCommand,
11202  RedoCommand
11203  },
11204  TransformCommands[] =
11205  {
11206  FlopCommand,
11207  FlipCommand,
11208  RotateRightCommand,
11209  RotateLeftCommand
11210  },
11211  EnhanceCommands[] =
11212  {
11213  HueCommand,
11214  SaturationCommand,
11215  BrightnessCommand,
11216  GammaCommand,
11217  SpiffCommand,
11218  DullCommand,
11219  ContrastStretchCommand,
11220  SigmoidalContrastCommand,
11221  NormalizeCommand,
11222  EqualizeCommand,
11223  NegateCommand,
11224  GrayscaleCommand,
11225  MapCommand,
11226  QuantizeCommand
11227  },
11228  EffectsCommands[] =
11229  {
11230  DespeckleCommand,
11231  EmbossCommand,
11232  ReduceNoiseCommand,
11233  AddNoiseCommand,
11234  SharpenCommand,
11235  BlurCommand,
11236  ThresholdCommand,
11237  EdgeDetectCommand,
11238  SpreadCommand,
11239  ShadeCommand,
11240  RaiseCommand,
11241  SegmentCommand
11242  },
11243  FXCommands[] =
11244  {
11245  SolarizeCommand,
11246  SepiaToneCommand,
11247  SwirlCommand,
11248  ImplodeCommand,
11249  VignetteCommand,
11250  WaveCommand,
11251  OilPaintCommand,
11252  CharcoalDrawCommand
11253  },
11254  MiscellanyCommands[] =
11255  {
11256  InfoCommand,
11257  ZoomCommand,
11258  ShowPreviewCommand,
11259  ShowHistogramCommand,
11260  ShowMatteCommand
11261  },
11262  ROICommands[] =
11263  {
11264  ROIHelpCommand,
11265  ROIDismissCommand
11266  };
11267 
11268  static const DisplayCommand
11269  *Commands[ApplyMenus] =
11270  {
11271  FileCommands,
11272  EditCommands,
11273  TransformCommands,
11274  EnhanceCommands,
11275  EffectsCommands,
11276  FXCommands,
11277  MiscellanyCommands
11278  };
11279 
11280  char
11281  command[MagickPathExtent],
11282  text[MagickPathExtent];
11283 
11284  DisplayCommand
11285  display_command;
11286 
11287  Cursor
11288  cursor;
11289 
11290  Image
11291  *roi_image;
11292 
11293  int
11294  entry,
11295  id,
11296  x,
11297  y;
11298 
11299  double
11300  scale_factor;
11301 
11302  MagickProgressMonitor
11303  progress_monitor;
11304 
11306  crop_info,
11307  highlight_info,
11308  roi_info;
11309 
11310  unsigned int
11311  height,
11312  width;
11313 
11314  size_t
11315  state;
11316 
11317  XEvent
11318  event;
11319 
11320  /*
11321  Map Command widget.
11322  */
11323  (void) CloneString(&windows->command.name,"ROI");
11324  windows->command.data=0;
11325  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11326  (void) XMapRaised(display,windows->command.id);
11327  XClientMessage(display,windows->image.id,windows->im_protocols,
11328  windows->im_update_widget,CurrentTime);
11329  /*
11330  Track pointer until button 1 is pressed.
11331  */
11332  XQueryPosition(display,windows->image.id,&x,&y);
11333  (void) XSelectInput(display,windows->image.id,
11334  windows->image.attributes.event_mask | PointerMotionMask);
11335  roi_info.x=(ssize_t) windows->image.x+x;
11336  roi_info.y=(ssize_t) windows->image.y+y;
11337  roi_info.width=0;
11338  roi_info.height=0;
11339  cursor=XCreateFontCursor(display,XC_fleur);
11340  state=DefaultState;
11341  do
11342  {
11343  if (windows->info.mapped != MagickFalse)
11344  {
11345  /*
11346  Display pointer position.
11347  */
11348  (void) FormatLocaleString(text,MagickPathExtent," %+ld%+ld ",
11349  (long) roi_info.x,(long) roi_info.y);
11350  XInfoWidget(display,windows,text);
11351  }
11352  /*
11353  Wait for next event.
11354  */
11355  XScreenEvent(display,windows,&event,exception);
11356  if (event.xany.window == windows->command.id)
11357  {
11358  /*
11359  Select a command from the Command widget.
11360  */
11361  id=XCommandWidget(display,windows,ROIMenu,&event);
11362  if (id < 0)
11363  continue;
11364  switch (ROICommands[id])
11365  {
11366  case ROIHelpCommand:
11367  {
11368  XTextViewHelp(display,resource_info,windows,MagickFalse,
11369  "Help Viewer - Region of Interest",ImageROIHelp);
11370  break;
11371  }
11372  case ROIDismissCommand:
11373  {
11374  /*
11375  Prematurely exit.
11376  */
11377  state|=EscapeState;
11378  state|=ExitState;
11379  break;
11380  }
11381  default:
11382  break;
11383  }
11384  continue;
11385  }
11386  switch (event.type)
11387  {
11388  case ButtonPress:
11389  {
11390  if (event.xbutton.button != Button1)
11391  break;
11392  if (event.xbutton.window != windows->image.id)
11393  break;
11394  /*
11395  Note first corner of region of interest rectangle-- exit loop.
11396  */
11397  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11398  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11399  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11400  state|=ExitState;
11401  break;
11402  }
11403  case ButtonRelease:
11404  break;
11405  case Expose:
11406  break;
11407  case KeyPress:
11408  {
11409  KeySym
11410  key_symbol;
11411 
11412  if (event.xkey.window != windows->image.id)
11413  break;
11414  /*
11415  Respond to a user key press.
11416  */
11417  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11418  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11419  switch ((int) key_symbol)
11420  {
11421  case XK_Escape:
11422  case XK_F20:
11423  {
11424  /*
11425  Prematurely exit.
11426  */
11427  state|=EscapeState;
11428  state|=ExitState;
11429  break;
11430  }
11431  case XK_F1:
11432  case XK_Help:
11433  {
11434  XTextViewHelp(display,resource_info,windows,MagickFalse,
11435  "Help Viewer - Region of Interest",ImageROIHelp);
11436  break;
11437  }
11438  default:
11439  {
11440  (void) XBell(display,0);
11441  break;
11442  }
11443  }
11444  break;
11445  }
11446  case MotionNotify:
11447  {
11448  /*
11449  Map and unmap Info widget as text cursor crosses its boundaries.
11450  */
11451  x=event.xmotion.x;
11452  y=event.xmotion.y;
11453  if (windows->info.mapped != MagickFalse)
11454  {
11455  if ((x < (windows->info.x+(int) windows->info.width)) &&
11456  (y < (windows->info.y+(int) windows->info.height)))
11457  (void) XWithdrawWindow(display,windows->info.id,
11458  windows->info.screen);
11459  }
11460  else
11461  if ((x > (windows->info.x+(int) windows->info.width)) ||
11462  (y > (windows->info.y+(int) windows->info.height)))
11463  (void) XMapWindow(display,windows->info.id);
11464  roi_info.x=(ssize_t) windows->image.x+x;
11465  roi_info.y=(ssize_t) windows->image.y+y;
11466  break;
11467  }
11468  default:
11469  break;
11470  }
11471  } while ((state & ExitState) == 0);
11472  (void) XSelectInput(display,windows->image.id,
11473  windows->image.attributes.event_mask);
11474  if ((state & EscapeState) != 0)
11475  {
11476  /*
11477  User want to exit without region of interest.
11478  */
11479  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11480  (void) XFreeCursor(display,cursor);
11481  return(MagickTrue);
11482  }
11483  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11484  do
11485  {
11486  /*
11487  Size rectangle as pointer moves until the mouse button is released.
11488  */
11489  x=(int) roi_info.x;
11490  y=(int) roi_info.y;
11491  roi_info.width=0;
11492  roi_info.height=0;
11493  state=DefaultState;
11494  do
11495  {
11496  highlight_info=roi_info;
11497  highlight_info.x=roi_info.x-windows->image.x;
11498  highlight_info.y=roi_info.y-windows->image.y;
11499  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11500  {
11501  /*
11502  Display info and draw region of interest rectangle.
11503  */
11504  if (windows->info.mapped == MagickFalse)
11505  (void) XMapWindow(display,windows->info.id);
11506  (void) FormatLocaleString(text,MagickPathExtent,
11507  " %.17gx%.17g%+.20g%+.20g",(double) roi_info.width,(double)
11508  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11509  XInfoWidget(display,windows,text);
11510  XHighlightRectangle(display,windows->image.id,
11511  windows->image.highlight_context,&highlight_info);
11512  }
11513  else
11514  if (windows->info.mapped != MagickFalse)
11515  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11516  /*
11517  Wait for next event.
11518  */
11519  XScreenEvent(display,windows,&event,exception);
11520  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11521  XHighlightRectangle(display,windows->image.id,
11522  windows->image.highlight_context,&highlight_info);
11523  switch (event.type)
11524  {
11525  case ButtonPress:
11526  {
11527  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529  break;
11530  }
11531  case ButtonRelease:
11532  {
11533  /*
11534  User has committed to region of interest rectangle.
11535  */
11536  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538  XSetCursorState(display,windows,MagickFalse);
11539  state|=ExitState;
11540  if (LocaleCompare(windows->command.name,"Apply") == 0)
11541  break;
11542  (void) CloneString(&windows->command.name,"Apply");
11543  windows->command.data=ApplyMenus;
11544  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11545  break;
11546  }
11547  case Expose:
11548  break;
11549  case MotionNotify:
11550  {
11551  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11552  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11553  }
11554  default:
11555  break;
11556  }
11557  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11558  ((state & ExitState) != 0))
11559  {
11560  /*
11561  Check boundary conditions.
11562  */
11563  if (roi_info.x < 0)
11564  roi_info.x=0;
11565  else
11566  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11567  roi_info.x=(ssize_t) windows->image.ximage->width;
11568  if ((int) roi_info.x < x)
11569  roi_info.width=(unsigned int) (x-roi_info.x);
11570  else
11571  {
11572  roi_info.width=(unsigned int) (roi_info.x-x);
11573  roi_info.x=(ssize_t) x;
11574  }
11575  if (roi_info.y < 0)
11576  roi_info.y=0;
11577  else
11578  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11579  roi_info.y=(ssize_t) windows->image.ximage->height;
11580  if ((int) roi_info.y < y)
11581  roi_info.height=(unsigned int) (y-roi_info.y);
11582  else
11583  {
11584  roi_info.height=(unsigned int) (roi_info.y-y);
11585  roi_info.y=(ssize_t) y;
11586  }
11587  }
11588  } while ((state & ExitState) == 0);
11589  /*
11590  Wait for user to grab a corner of the rectangle or press return.
11591  */
11592  state=DefaultState;
11593  display_command=NullCommand;
11594  crop_info.x=0;
11595  crop_info.y=0;
11596  (void) XMapWindow(display,windows->info.id);
11597  do
11598  {
11599  if (windows->info.mapped != MagickFalse)
11600  {
11601  /*
11602  Display pointer position.
11603  */
11604  (void) FormatLocaleString(text,MagickPathExtent,
11605  " %.17gx%.17g%+.20g%+.20g",(double) roi_info.width,(double)
11606  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11607  XInfoWidget(display,windows,text);
11608  }
11609  highlight_info=roi_info;
11610  highlight_info.x=roi_info.x-windows->image.x;
11611  highlight_info.y=roi_info.y-windows->image.y;
11612  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11613  {
11614  state|=EscapeState;
11615  state|=ExitState;
11616  break;
11617  }
11618  if ((state & UpdateRegionState) != 0)
11619  {
11620  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11621  switch (display_command)
11622  {
11623  case UndoCommand:
11624  case RedoCommand:
11625  {
11626  (void) XMagickCommand(display,resource_info,windows,
11627  display_command,image,exception);
11628  break;
11629  }
11630  default:
11631  {
11632  /*
11633  Region of interest is relative to image configuration.
11634  */
11635  progress_monitor=SetImageProgressMonitor(*image,
11636  (MagickProgressMonitor) NULL,(*image)->client_data);
11637  crop_info=roi_info;
11638  width=(unsigned int) (*image)->columns;
11639  height=(unsigned int) (*image)->rows;
11640  x=0;
11641  y=0;
11642  if (windows->image.crop_geometry != (char *) NULL)
11643  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11644  &width,&height);
11645  scale_factor=(double) width/windows->image.ximage->width;
11646  crop_info.x+=x;
11647  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11648  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11649  scale_factor=(double)
11650  height/windows->image.ximage->height;
11651  crop_info.y+=y;
11652  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11653  crop_info.height=(unsigned int)
11654  (scale_factor*crop_info.height+0.5);
11655  roi_image=CropImage(*image,&crop_info,exception);
11656  (void) SetImageProgressMonitor(*image,progress_monitor,
11657  (*image)->client_data);
11658  if (roi_image == (Image *) NULL)
11659  continue;
11660  /*
11661  Apply image processing technique to the region of interest.
11662  */
11663  windows->image.orphan=MagickTrue;
11664  (void) XMagickCommand(display,resource_info,windows,
11665  display_command,&roi_image,exception);
11666  progress_monitor=SetImageProgressMonitor(*image,
11667  (MagickProgressMonitor) NULL,(*image)->client_data);
11668  (void) XMagickCommand(display,resource_info,windows,
11669  SaveToUndoBufferCommand,image,exception);
11670  windows->image.orphan=MagickFalse;
11671  (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11672  MagickTrue,crop_info.x,crop_info.y,exception);
11673  roi_image=DestroyImage(roi_image);
11674  (void) SetImageProgressMonitor(*image,progress_monitor,
11675  (*image)->client_data);
11676  break;
11677  }
11678  }
11679  if (display_command != InfoCommand)
11680  {
11681  XConfigureImageColormap(display,resource_info,windows,*image,
11682  exception);
11683  (void) XConfigureImage(display,resource_info,windows,*image,
11684  exception);
11685  }
11686  XCheckRefreshWindows(display,windows);
11687  XInfoWidget(display,windows,text);
11688  (void) XSetFunction(display,windows->image.highlight_context,
11689  GXinvert);
11690  state&=(unsigned int) (~UpdateRegionState);
11691  }
11692  XHighlightRectangle(display,windows->image.id,
11693  windows->image.highlight_context,&highlight_info);
11694  XScreenEvent(display,windows,&event,exception);
11695  if (event.xany.window == windows->command.id)
11696  {
11697  /*
11698  Select a command from the Command widget.
11699  */
11700  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11701  display_command=NullCommand;
11702  id=XCommandWidget(display,windows,ApplyMenu,&event);
11703  if (id >= 0)
11704  {
11705  (void) CopyMagickString(command,ApplyMenu[id],MagickPathExtent);
11706  display_command=ApplyCommands[id];
11707  if (id < ApplyMenus)
11708  {
11709  /*
11710  Select a command from a pop-up menu.
11711  */
11712  entry=XMenuWidget(display,windows,ApplyMenu[id],
11713  (const char **) Menus[id],command);
11714  if (entry >= 0)
11715  {
11716  (void) CopyMagickString(command,Menus[id][entry],
11717  MagickPathExtent);
11718  display_command=Commands[id][entry];
11719  }
11720  }
11721  }
11722  (void) XSetFunction(display,windows->image.highlight_context,
11723  GXinvert);
11724  XHighlightRectangle(display,windows->image.id,
11725  windows->image.highlight_context,&highlight_info);
11726  if (display_command == HelpCommand)
11727  {
11728  (void) XSetFunction(display,windows->image.highlight_context,
11729  GXcopy);
11730  XTextViewHelp(display,resource_info,windows,MagickFalse,
11731  "Help Viewer - Region of Interest",ImageROIHelp);
11732  (void) XSetFunction(display,windows->image.highlight_context,
11733  GXinvert);
11734  continue;
11735  }
11736  if (display_command == QuitCommand)
11737  {
11738  /*
11739  exit.
11740  */
11741  state|=EscapeState;
11742  state|=ExitState;
11743  continue;
11744  }
11745  if (display_command != NullCommand)
11746  state|=UpdateRegionState;
11747  continue;
11748  }
11749  XHighlightRectangle(display,windows->image.id,
11750  windows->image.highlight_context,&highlight_info);
11751  switch (event.type)
11752  {
11753  case ButtonPress:
11754  {
11755  x=windows->image.x;
11756  y=windows->image.y;
11757  if (event.xbutton.button != Button1)
11758  break;
11759  if (event.xbutton.window != windows->image.id)
11760  break;
11761  x=windows->image.x+event.xbutton.x;
11762  y=windows->image.y+event.xbutton.y;
11763  if ((x < (int) (roi_info.x+RoiDelta)) &&
11764  (x > (int) (roi_info.x-RoiDelta)) &&
11765  (y < (int) (roi_info.y+RoiDelta)) &&
11766  (y > (int) (roi_info.y-RoiDelta)))
11767  {
11768  roi_info.x=roi_info.x+(int) roi_info.width;
11769  roi_info.y=roi_info.y+(int) roi_info.height;
11770  state|=UpdateConfigurationState;
11771  break;
11772  }
11773  if ((x < (int) (roi_info.x+RoiDelta)) &&
11774  (x > (int) (roi_info.x-RoiDelta)) &&
11775  (y < (int) (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11776  (y > (int) (roi_info.y+(int) roi_info.height-RoiDelta)))
11777  {
11778  roi_info.x=roi_info.x+(int) roi_info.width;
11779  state|=UpdateConfigurationState;
11780  break;
11781  }
11782  if ((x < (int) (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11783  (x > (int) (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11784  (y < (int) (roi_info.y+RoiDelta)) &&
11785  (y > (int) (roi_info.y-RoiDelta)))
11786  {
11787  roi_info.y=roi_info.y+(int) roi_info.height;
11788  state|=UpdateConfigurationState;
11789  break;
11790  }
11791  if ((x < (int) (roi_info.x+(int) roi_info.width+RoiDelta)) &&
11792  (x > (int) (roi_info.x+(int) roi_info.width-RoiDelta)) &&
11793  (y < (int) (roi_info.y+(int) roi_info.height+RoiDelta)) &&
11794  (y > (int) (roi_info.y+(int) roi_info.height-RoiDelta)))
11795  {
11796  state|=UpdateConfigurationState;
11797  break;
11798  }
11799  magick_fallthrough;
11800  }
11801  case ButtonRelease:
11802  {
11803  if (event.xbutton.window == windows->pan.id)
11804  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11805  (highlight_info.y != crop_info.y-windows->image.y))
11806  XHighlightRectangle(display,windows->image.id,
11807  windows->image.highlight_context,&highlight_info);
11808  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11809  event.xbutton.time);
11810  break;
11811  }
11812  case Expose:
11813  {
11814  if (event.xexpose.window == windows->image.id)
11815  if (event.xexpose.count == 0)
11816  {
11817  event.xexpose.x=(int) highlight_info.x;
11818  event.xexpose.y=(int) highlight_info.y;
11819  event.xexpose.width=(int) highlight_info.width;
11820  event.xexpose.height=(int) highlight_info.height;
11821  XRefreshWindow(display,&windows->image,&event);
11822  }
11823  if (event.xexpose.window == windows->info.id)
11824  if (event.xexpose.count == 0)
11825  XInfoWidget(display,windows,text);
11826  break;
11827  }
11828  case KeyPress:
11829  {
11830  KeySym
11831  key_symbol;
11832 
11833  if (event.xkey.window != windows->image.id)
11834  break;
11835  /*
11836  Respond to a user key press.
11837  */
11838  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11839  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11840  switch ((int) key_symbol)
11841  {
11842  case XK_Shift_L:
11843  case XK_Shift_R:
11844  break;
11845  case XK_Escape:
11846  case XK_F20:
11847  {
11848  state|=EscapeState;
11849  magick_fallthrough;
11850  }
11851  case XK_Return:
11852  {
11853  state|=ExitState;
11854  break;
11855  }
11856  case XK_Home:
11857  case XK_KP_Home:
11858  {
11859  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11860  roi_info.y=(ssize_t) (windows->image.height/2L-
11861  roi_info.height/2L);
11862  break;
11863  }
11864  case XK_Left:
11865  case XK_KP_Left:
11866  {
11867  roi_info.x--;
11868  break;
11869  }
11870  case XK_Up:
11871  case XK_KP_Up:
11872  case XK_Next:
11873  {
11874  roi_info.y--;
11875  break;
11876  }
11877  case XK_Right:
11878  case XK_KP_Right:
11879  {
11880  roi_info.x++;
11881  break;
11882  }
11883  case XK_Prior:
11884  case XK_Down:
11885  case XK_KP_Down:
11886  {
11887  roi_info.y++;
11888  break;
11889  }
11890  case XK_F1:
11891  case XK_Help:
11892  {
11893  (void) XSetFunction(display,windows->image.highlight_context,
11894  GXcopy);
11895  XTextViewHelp(display,resource_info,windows,MagickFalse,
11896  "Help Viewer - Region of Interest",ImageROIHelp);
11897  (void) XSetFunction(display,windows->image.highlight_context,
11898  GXinvert);
11899  break;
11900  }
11901  default:
11902  {
11903  display_command=XImageWindowCommand(display,resource_info,windows,
11904  event.xkey.state,key_symbol,image,exception);
11905  if (display_command != NullCommand)
11906  state|=UpdateRegionState;
11907  break;
11908  }
11909  }
11910  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11911  event.xkey.time);
11912  break;
11913  }
11914  case KeyRelease:
11915  break;
11916  case MotionNotify:
11917  {
11918  if (event.xbutton.window != windows->image.id)
11919  break;
11920  /*
11921  Map and unmap Info widget as text cursor crosses its boundaries.
11922  */
11923  x=event.xmotion.x;
11924  y=event.xmotion.y;
11925  if (windows->info.mapped != MagickFalse)
11926  {
11927  if ((x < (windows->info.x+(int) windows->info.width)) &&
11928  (y < (windows->info.y+(int) windows->info.height)))
11929  (void) XWithdrawWindow(display,windows->info.id,
11930  windows->info.screen);
11931  }
11932  else
11933  if ((x > (windows->info.x+(int) windows->info.width)) ||
11934  (y > (windows->info.y+(int) windows->info.height)))
11935  (void) XMapWindow(display,windows->info.id);
11936  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11937  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11938  break;
11939  }
11940  case SelectionRequest:
11941  {
11942  XSelectionEvent
11943  notify;
11944 
11945  XSelectionRequestEvent
11946  *request;
11947 
11948  /*
11949  Set primary selection.
11950  */
11951  (void) FormatLocaleString(text,MagickPathExtent,
11952  "%.17gx%.17g%+.20g%+.20g",(double) roi_info.width,(double)
11953  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11954  request=(&(event.xselectionrequest));
11955  (void) XChangeProperty(request->display,request->requestor,
11956  request->property,request->target,8,PropModeReplace,
11957  (unsigned char *) text,(int) strlen(text));
11958  notify.type=SelectionNotify;
11959  notify.display=request->display;
11960  notify.requestor=request->requestor;
11961  notify.selection=request->selection;
11962  notify.target=request->target;
11963  notify.time=request->time;
11964  if (request->property == None)
11965  notify.property=request->target;
11966  else
11967  notify.property=request->property;
11968  (void) XSendEvent(request->display,request->requestor,False,0,
11969  (XEvent *) &notify);
11970  }
11971  default:
11972  break;
11973  }
11974  if ((state & UpdateConfigurationState) != 0)
11975  {
11976  (void) XPutBackEvent(display,&event);
11977  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11978  break;
11979  }
11980  } while ((state & ExitState) == 0);
11981  } while ((state & ExitState) == 0);
11982  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11983  XSetCursorState(display,windows,MagickFalse);
11984  if ((state & EscapeState) != 0)
11985  return(MagickTrue);
11986  return(MagickTrue);
11987 }
11988 ␌
11989 /*
11990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991 % %
11992 % %
11993 % %
11994 + X R o t a t e I m a g e %
11995 % %
11996 % %
11997 % %
11998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11999 %
12000 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
12001 % rotation angle is computed from the slope of a line drawn by the user.
12002 %
12003 % The format of the XRotateImage method is:
12004 %
12005 % MagickBooleanType XRotateImage(Display *display,
12006 % XResourceInfo *resource_info,XWindows *windows,double degrees,
12007 % Image **image,ExceptionInfo *exception)
12008 %
12009 % A description of each parameter follows:
12010 %
12011 % o display: Specifies a connection to an X server; returned from
12012 % XOpenDisplay.
12013 %
12014 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12015 %
12016 % o windows: Specifies a pointer to a XWindows structure.
12017 %
12018 % o degrees: Specifies the number of degrees to rotate the image.
12019 %
12020 % o image: the image.
12021 %
12022 % o exception: return any errors or warnings in this structure.
12023 %
12024 */
12025 static MagickBooleanType XRotateImage(Display *display,
12026  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12027  ExceptionInfo *exception)
12028 {
12029  const char
12030  *const RotateMenu[] =
12031  {
12032  "Pixel Color",
12033  "Direction",
12034  "Help",
12035  "Dismiss",
12036  (char *) NULL
12037  };
12038 
12039  static ModeType
12040  direction = HorizontalRotateCommand;
12041 
12042  static const ModeType
12043  DirectionCommands[] =
12044  {
12045  HorizontalRotateCommand,
12046  VerticalRotateCommand
12047  },
12048  RotateCommands[] =
12049  {
12050  RotateColorCommand,
12051  RotateDirectionCommand,
12052  RotateHelpCommand,
12053  RotateDismissCommand
12054  };
12055 
12056  static unsigned int
12057  pen_id = 0;
12058 
12059  char
12060  command[MagickPathExtent],
12061  text[MagickPathExtent];
12062 
12063  Image
12064  *rotate_image;
12065 
12066  int
12067  id,
12068  x,
12069  y;
12070 
12071  double
12072  normalized_degrees;
12073 
12074  int
12075  i;
12076 
12077  unsigned int
12078  height,
12079  rotations,
12080  width;
12081 
12082  if (degrees == 0.0)
12083  {
12084  unsigned int
12085  distance;
12086 
12087  size_t
12088  state;
12089 
12090  XEvent
12091  event;
12092 
12093  XSegment
12094  rotate_info;
12095 
12096  /*
12097  Map Command widget.
12098  */
12099  (void) CloneString(&windows->command.name,"Rotate");
12100  windows->command.data=2;
12101  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12102  (void) XMapRaised(display,windows->command.id);
12103  XClientMessage(display,windows->image.id,windows->im_protocols,
12104  windows->im_update_widget,CurrentTime);
12105  /*
12106  Wait for first button press.
12107  */
12108  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12109  XQueryPosition(display,windows->image.id,&x,&y);
12110  rotate_info.x1=x;
12111  rotate_info.y1=y;
12112  rotate_info.x2=x;
12113  rotate_info.y2=y;
12114  state=DefaultState;
12115  do
12116  {
12117  XHighlightLine(display,windows->image.id,
12118  windows->image.highlight_context,&rotate_info);
12119  /*
12120  Wait for next event.
12121  */
12122  XScreenEvent(display,windows,&event,exception);
12123  XHighlightLine(display,windows->image.id,
12124  windows->image.highlight_context,&rotate_info);
12125  if (event.xany.window == windows->command.id)
12126  {
12127  /*
12128  Select a command from the Command widget.
12129  */
12130  id=XCommandWidget(display,windows,RotateMenu,&event);
12131  if (id < 0)
12132  continue;
12133  (void) XSetFunction(display,windows->image.highlight_context,
12134  GXcopy);
12135  switch (RotateCommands[id])
12136  {
12137  case RotateColorCommand:
12138  {
12139  const char
12140  *ColorMenu[MaxNumberPens];
12141 
12142  int
12143  pen_number;
12144 
12145  XColor
12146  color;
12147 
12148  /*
12149  Initialize menu selections.
12150  */
12151  for (i=0; i < (int) (MaxNumberPens-2); i++)
12152  ColorMenu[i]=resource_info->pen_colors[i];
12153  ColorMenu[MaxNumberPens-2]="Browser...";
12154  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12155  /*
12156  Select a pen color from the pop-up menu.
12157  */
12158  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12159  (const char **) ColorMenu,command);
12160  if (pen_number < 0)
12161  break;
12162  if (pen_number == (MaxNumberPens-2))
12163  {
12164  static char
12165  color_name[MagickPathExtent] = "gray";
12166 
12167  /*
12168  Select a pen color from a dialog.
12169  */
12170  resource_info->pen_colors[pen_number]=color_name;
12171  XColorBrowserWidget(display,windows,"Select",color_name);
12172  if (*color_name == '\0')
12173  break;
12174  }
12175  /*
12176  Set pen color.
12177  */
12178  (void) XParseColor(display,windows->map_info->colormap,
12179  resource_info->pen_colors[pen_number],&color);
12180  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12181  (unsigned int) MaxColors,&color);
12182  windows->pixel_info->pen_colors[pen_number]=color;
12183  pen_id=(unsigned int) pen_number;
12184  break;
12185  }
12186  case RotateDirectionCommand:
12187  {
12188  const char
12189  *Directions[] =
12190  {
12191  "horizontal",
12192  "vertical",
12193  (char *) NULL,
12194  };
12195 
12196  /*
12197  Select a command from the pop-up menu.
12198  */
12199  id=XMenuWidget(display,windows,RotateMenu[id],
12200  Directions,command);
12201  if (id >= 0)
12202  direction=DirectionCommands[id];
12203  break;
12204  }
12205  case RotateHelpCommand:
12206  {
12207  XTextViewHelp(display,resource_info,windows,MagickFalse,
12208  "Help Viewer - Image Rotation",ImageRotateHelp);
12209  break;
12210  }
12211  case RotateDismissCommand:
12212  {
12213  /*
12214  Prematurely exit.
12215  */
12216  state|=EscapeState;
12217  state|=ExitState;
12218  break;
12219  }
12220  default:
12221  break;
12222  }
12223  (void) XSetFunction(display,windows->image.highlight_context,
12224  GXinvert);
12225  continue;
12226  }
12227  switch (event.type)
12228  {
12229  case ButtonPress:
12230  {
12231  if (event.xbutton.button != Button1)
12232  break;
12233  if (event.xbutton.window != windows->image.id)
12234  break;
12235  /*
12236  exit loop.
12237  */
12238  (void) XSetFunction(display,windows->image.highlight_context,
12239  GXcopy);
12240  rotate_info.x1=event.xbutton.x;
12241  rotate_info.y1=event.xbutton.y;
12242  state|=ExitState;
12243  break;
12244  }
12245  case ButtonRelease:
12246  break;
12247  case Expose:
12248  break;
12249  case KeyPress:
12250  {
12251  char
12252  command[MagickPathExtent];
12253 
12254  KeySym
12255  key_symbol;
12256 
12257  if (event.xkey.window != windows->image.id)
12258  break;
12259  /*
12260  Respond to a user key press.
12261  */
12262  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12263  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12264  switch ((int) key_symbol)
12265  {
12266  case XK_Escape:
12267  case XK_F20:
12268  {
12269  /*
12270  Prematurely exit.
12271  */
12272  state|=EscapeState;
12273  state|=ExitState;
12274  break;
12275  }
12276  case XK_F1:
12277  case XK_Help:
12278  {
12279  (void) XSetFunction(display,windows->image.highlight_context,
12280  GXcopy);
12281  XTextViewHelp(display,resource_info,windows,MagickFalse,
12282  "Help Viewer - Image Rotation",ImageRotateHelp);
12283  (void) XSetFunction(display,windows->image.highlight_context,
12284  GXinvert);
12285  break;
12286  }
12287  default:
12288  {
12289  (void) XBell(display,0);
12290  break;
12291  }
12292  }
12293  break;
12294  }
12295  case MotionNotify:
12296  {
12297  rotate_info.x1=event.xmotion.x;
12298  rotate_info.y1=event.xmotion.y;
12299  }
12300  }
12301  rotate_info.x2=rotate_info.x1;
12302  rotate_info.y2=rotate_info.y1;
12303  if (direction == HorizontalRotateCommand)
12304  rotate_info.x2+=32;
12305  else
12306  rotate_info.y2-=32;
12307  } while ((state & ExitState) == 0);
12308  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12309  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12310  if ((state & EscapeState) != 0)
12311  return(MagickTrue);
12312  /*
12313  Draw line as pointer moves until the mouse button is released.
12314  */
12315  distance=0;
12316  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12317  state=DefaultState;
12318  do
12319  {
12320  if (distance > 9)
12321  {
12322  /*
12323  Display info and draw rotation line.
12324  */
12325  if (windows->info.mapped == MagickFalse)
12326  (void) XMapWindow(display,windows->info.id);
12327  (void) FormatLocaleString(text,MagickPathExtent," %g",
12328  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12329  XInfoWidget(display,windows,text);
12330  XHighlightLine(display,windows->image.id,
12331  windows->image.highlight_context,&rotate_info);
12332  }
12333  else
12334  if (windows->info.mapped != MagickFalse)
12335  (void) XWithdrawWindow(display,windows->info.id,
12336  windows->info.screen);
12337  /*
12338  Wait for next event.
12339  */
12340  XScreenEvent(display,windows,&event,exception);
12341  if (distance > 9)
12342  XHighlightLine(display,windows->image.id,
12343  windows->image.highlight_context,&rotate_info);
12344  switch (event.type)
12345  {
12346  case ButtonPress:
12347  break;
12348  case ButtonRelease:
12349  {
12350  /*
12351  User has committed to rotation line.
12352  */
12353  rotate_info.x2=event.xbutton.x;
12354  rotate_info.y2=event.xbutton.y;
12355  state|=ExitState;
12356  break;
12357  }
12358  case Expose:
12359  break;
12360  case MotionNotify:
12361  {
12362  rotate_info.x2=event.xmotion.x;
12363  rotate_info.y2=event.xmotion.y;
12364  }
12365  default:
12366  break;
12367  }
12368  /*
12369  Check boundary conditions.
12370  */
12371  if (rotate_info.x2 < 0)
12372  rotate_info.x2=0;
12373  else
12374  if (rotate_info.x2 > (int) windows->image.width)
12375  rotate_info.x2=(short) windows->image.width;
12376  if (rotate_info.y2 < 0)
12377  rotate_info.y2=0;
12378  else
12379  if (rotate_info.y2 > (int) windows->image.height)
12380  rotate_info.y2=(short) windows->image.height;
12381  /*
12382  Compute rotation angle from the slope of the line.
12383  */
12384  degrees=0.0;
12385  distance=(unsigned int)
12386  (((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12387  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1)));
12388  if (distance > 9)
12389  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12390  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12391  } while ((state & ExitState) == 0);
12392  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12393  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12394  if (distance <= 9)
12395  return(MagickTrue);
12396  }
12397  if (direction == VerticalRotateCommand)
12398  degrees-=90.0;
12399  if (degrees == 0.0)
12400  return(MagickTrue);
12401  /*
12402  Rotate image.
12403  */
12404  normalized_degrees=degrees;
12405  while (normalized_degrees < -45.0)
12406  normalized_degrees+=360.0;
12407  for (rotations=0; normalized_degrees > 45.0; rotations++)
12408  normalized_degrees-=90.0;
12409  if (normalized_degrees != 0.0)
12410  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12411  exception);
12412  XSetCursorState(display,windows,MagickTrue);
12413  XCheckRefreshWindows(display,windows);
12414  (*image)->background_color.red=(double) ScaleShortToQuantum(
12415  windows->pixel_info->pen_colors[pen_id].red);
12416  (*image)->background_color.green=(double) ScaleShortToQuantum(
12417  windows->pixel_info->pen_colors[pen_id].green);
12418  (*image)->background_color.blue=(double) ScaleShortToQuantum(
12419  windows->pixel_info->pen_colors[pen_id].blue);
12420  rotate_image=RotateImage(*image,degrees,exception);
12421  XSetCursorState(display,windows,MagickFalse);
12422  if (rotate_image == (Image *) NULL)
12423  return(MagickFalse);
12424  *image=DestroyImage(*image);
12425  *image=rotate_image;
12426  if (windows->image.crop_geometry != (char *) NULL)
12427  {
12428  /*
12429  Rotate crop geometry.
12430  */
12431  width=(unsigned int) (*image)->columns;
12432  height=(unsigned int) (*image)->rows;
12433  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12434  switch (rotations % 4)
12435  {
12436  default:
12437  case 0:
12438  break;
12439  case 1:
12440  {
12441  /*
12442  Rotate 90 degrees.
12443  */
12444  (void) FormatLocaleString(windows->image.crop_geometry,
12445  MagickPathExtent,"%ux%u%+d%+d",height,width,(int) (*image)->columns-
12446  (int) height-y,x);
12447  break;
12448  }
12449  case 2:
12450  {
12451  /*
12452  Rotate 180 degrees.
12453  */
12454  (void) FormatLocaleString(windows->image.crop_geometry,
12455  MagickPathExtent,"%ux%u%+d%+d",width,height,(int) width-x,(int)
12456  height-y);
12457  break;
12458  }
12459  case 3:
12460  {
12461  /*
12462  Rotate 270 degrees.
12463  */
12464  (void) FormatLocaleString(windows->image.crop_geometry,
12465  MagickPathExtent,"%ux%u%+d%+d",height,width,y,(int) (*image)->rows-
12466  (int) width-x);
12467  break;
12468  }
12469  }
12470  }
12471  if (windows->image.orphan != MagickFalse)
12472  return(MagickTrue);
12473  if (normalized_degrees != 0.0)
12474  {
12475  /*
12476  Update image colormap.
12477  */
12478  windows->image.window_changes.width=(int) (*image)->columns;
12479  windows->image.window_changes.height=(int) (*image)->rows;
12480  if (windows->image.crop_geometry != (char *) NULL)
12481  {
12482  /*
12483  Obtain dimensions of image from crop geometry.
12484  */
12485  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12486  &width,&height);
12487  windows->image.window_changes.width=(int) width;
12488  windows->image.window_changes.height=(int) height;
12489  }
12490  XConfigureImageColormap(display,resource_info,windows,*image,exception);
12491  }
12492  else
12493  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12494  {
12495  windows->image.window_changes.width=windows->image.ximage->height;
12496  windows->image.window_changes.height=windows->image.ximage->width;
12497  }
12498  /*
12499  Update image configuration.
12500  */
12501  (void) XConfigureImage(display,resource_info,windows,*image,exception);
12502  return(MagickTrue);
12503 }
12504 ␌
12505 /*
12506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507 % %
12508 % %
12509 % %
12510 + X S a v e I m a g e %
12511 % %
12512 % %
12513 % %
12514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12515 %
12516 % XSaveImage() saves an image to a file.
12517 %
12518 % The format of the XSaveImage method is:
12519 %
12520 % MagickBooleanType XSaveImage(Display *display,
12521 % XResourceInfo *resource_info,XWindows *windows,Image *image,
12522 % ExceptionInfo *exception)
12523 %
12524 % A description of each parameter follows:
12525 %
12526 % o display: Specifies a connection to an X server; returned from
12527 % XOpenDisplay.
12528 %
12529 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12530 %
12531 % o windows: Specifies a pointer to a XWindows structure.
12532 %
12533 % o image: the image.
12534 %
12535 % o exception: return any errors or warnings in this structure.
12536 %
12537 */
12538 static MagickBooleanType XSaveImage(Display *display,
12539  XResourceInfo *resource_info,XWindows *windows,Image *image,
12540  ExceptionInfo *exception)
12541 {
12542  char
12543  filename[MagickPathExtent],
12544  geometry[MagickPathExtent];
12545 
12546  Image
12547  *save_image;
12548 
12549  ImageInfo
12550  *image_info;
12551 
12552  MagickStatusType
12553  status;
12554 
12555  /*
12556  Request file name from user.
12557  */
12558  if (resource_info->write_filename != (char *) NULL)
12559  (void) CopyMagickString(filename,resource_info->write_filename,
12560  MagickPathExtent);
12561  else
12562  {
12563  char
12564  path[MagickPathExtent];
12565 
12566  int
12567  status;
12568 
12569  GetPathComponent(image->filename,HeadPath,path);
12570  GetPathComponent(image->filename,TailPath,filename);
12571  if (*path != '\0')
12572  {
12573  status=chdir(path);
12574  if (status == -1)
12575  (void) ThrowMagickException(exception,GetMagickModule(),
12576  FileOpenError,"UnableToOpenFile","%s",path);
12577  }
12578  }
12579  XFileBrowserWidget(display,windows,"Save",filename);
12580  if (*filename == '\0')
12581  return(MagickTrue);
12582  if (IsPathAccessible(filename) != MagickFalse)
12583  {
12584  int
12585  status;
12586 
12587  /*
12588  File exists-- seek user's permission before overwriting.
12589  */
12590  status=XConfirmWidget(display,windows,"Overwrite",filename);
12591  if (status <= 0)
12592  return(MagickTrue);
12593  }
12594  image_info=CloneImageInfo(resource_info->image_info);
12595  (void) CopyMagickString(image_info->filename,filename,MagickPathExtent);
12596  (void) SetImageInfo(image_info,1,exception);
12597  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12598  (LocaleCompare(image_info->magick,"JPG") == 0))
12599  {
12600  char
12601  quality[MagickPathExtent];
12602 
12603  int
12604  status;
12605 
12606  /*
12607  Request JPEG quality from user.
12608  */
12609  (void) FormatLocaleString(quality,MagickPathExtent,"%.17g",(double)
12610  image->quality);
12611  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12612  quality);
12613  if (*quality == '\0')
12614  return(MagickTrue);
12615  image->quality=StringToUnsignedLong(quality);
12616  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12617  }
12618  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12619  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12620  (LocaleCompare(image_info->magick,"PS") == 0) ||
12621  (LocaleCompare(image_info->magick,"PS2") == 0))
12622  {
12623  char
12624  geometry[MagickPathExtent];
12625 
12626  const char
12627  *const PageSizes[] =
12628  {
12629  "Letter",
12630  "Tabloid",
12631  "Ledger",
12632  "Legal",
12633  "Statement",
12634  "Executive",
12635  "A3",
12636  "A4",
12637  "A5",
12638  "B4",
12639  "B5",
12640  "Folio",
12641  "Quarto",
12642  "10x14",
12643  (char *) NULL
12644  };
12645 
12646  /*
12647  Request page geometry from user.
12648  */
12649  (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12650  if (LocaleCompare(image_info->magick,"PDF") == 0)
12651  (void) CopyMagickString(geometry,PSPageGeometry,MagickPathExtent);
12652  if (image_info->page != (char *) NULL)
12653  (void) CopyMagickString(geometry,image_info->page,MagickPathExtent);
12654  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12655  "Select page geometry:",geometry);
12656  if (*geometry != '\0')
12657  image_info->page=GetPageGeometry(geometry);
12658  }
12659  /*
12660  Apply image transforms.
12661  */
12662  XSetCursorState(display,windows,MagickTrue);
12663  XCheckRefreshWindows(display,windows);
12664  save_image=CloneImage(image,0,0,MagickTrue,exception);
12665  if (save_image == (Image *) NULL)
12666  return(MagickFalse);
12667  (void) FormatLocaleString(geometry,MagickPathExtent,"%dx%d!",
12668  windows->image.ximage->width,windows->image.ximage->height);
12669  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12670  exception);
12671  /*
12672  Write image.
12673  */
12674  (void) CopyMagickString(save_image->filename,filename,MagickPathExtent);
12675  status=WriteImage(image_info,save_image,exception);
12676  if (status != MagickFalse)
12677  image->taint=MagickFalse;
12678  save_image=DestroyImage(save_image);
12679  image_info=DestroyImageInfo(image_info);
12680  XSetCursorState(display,windows,MagickFalse);
12681  return(status != 0 ? MagickTrue : MagickFalse);
12682 }
12683 ␌
12684 /*
12685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12686 % %
12687 % %
12688 % %
12689 + X S c r e e n E v e n t %
12690 % %
12691 % %
12692 % %
12693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12694 %
12695 % XScreenEvent() handles global events associated with the Pan and Magnify
12696 % windows.
12697 %
12698 % The format of the XScreenEvent function is:
12699 %
12700 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12701 % ExceptionInfo *exception)
12702 %
12703 % A description of each parameter follows:
12704 %
12705 % o display: Specifies a pointer to the Display structure; returned from
12706 % XOpenDisplay.
12707 %
12708 % o windows: Specifies a pointer to a XWindows structure.
12709 %
12710 % o event: Specifies a pointer to a X11 XEvent structure.
12711 %
12712 % o exception: return any errors or warnings in this structure.
12713 %
12714 */
12715 
12716 #if defined(__cplusplus) || defined(c_plusplus)
12717 extern "C" {
12718 #endif
12719 
12720 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12721 {
12722  XWindows
12723  *windows;
12724 
12725  windows=(XWindows *) data;
12726  if ((event->type == ClientMessage) &&
12727  (event->xclient.window == windows->image.id))
12728  return(MagickFalse);
12729  return(MagickTrue);
12730 }
12731 
12732 #if defined(__cplusplus) || defined(c_plusplus)
12733 }
12734 #endif
12735 
12736 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12737  ExceptionInfo *exception)
12738 {
12739  int
12740  x,
12741  y;
12742 
12743  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12744  if (event->xany.window == windows->command.id)
12745  return;
12746  switch (event->type)
12747  {
12748  case ButtonPress:
12749  case ButtonRelease:
12750  {
12751  if ((event->xbutton.button == Button3) &&
12752  (event->xbutton.state & Mod1Mask))
12753  {
12754  /*
12755  Convert Alt-Button3 to Button2.
12756  */
12757  event->xbutton.button=Button2;
12758  event->xbutton.state&=(unsigned int) (~Mod1Mask);
12759  }
12760  if (event->xbutton.window == windows->backdrop.id)
12761  {
12762  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12763  event->xbutton.time);
12764  break;
12765  }
12766  if (event->xbutton.window == windows->pan.id)
12767  {
12768  XPanImage(display,windows,event,exception);
12769  break;
12770  }
12771  if (event->xbutton.window == windows->image.id)
12772  if (event->xbutton.button == Button2)
12773  {
12774  /*
12775  Update magnified image.
12776  */
12777  x=event->xbutton.x;
12778  y=event->xbutton.y;
12779  if (x < 0)
12780  x=0;
12781  else
12782  if (x >= (int) windows->image.width)
12783  x=(int) (windows->image.width-1);
12784  windows->magnify.x=(int) windows->image.x+x;
12785  if (y < 0)
12786  y=0;
12787  else
12788  if (y >= (int) windows->image.height)
12789  y=(int) (windows->image.height-1);
12790  windows->magnify.y=windows->image.y+y;
12791  if (windows->magnify.mapped == MagickFalse)
12792  (void) XMapRaised(display,windows->magnify.id);
12793  XMakeMagnifyImage(display,windows,exception);
12794  if (event->type == ButtonRelease)
12795  (void) XWithdrawWindow(display,windows->info.id,
12796  windows->info.screen);
12797  break;
12798  }
12799  break;
12800  }
12801  case ClientMessage:
12802  {
12803  /*
12804  If client window delete message, exit.
12805  */
12806  if (event->xclient.message_type != windows->wm_protocols)
12807  break;
12808  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12809  break;
12810  if (event->xclient.window == windows->magnify.id)
12811  {
12812  (void) XWithdrawWindow(display,windows->magnify.id,
12813  windows->magnify.screen);
12814  break;
12815  }
12816  break;
12817  }
12818  case ConfigureNotify:
12819  {
12820  if (event->xconfigure.window == windows->magnify.id)
12821  {
12822  unsigned int
12823  magnify;
12824 
12825  /*
12826  Magnify window has a new configuration.
12827  */
12828  windows->magnify.width=(unsigned int) event->xconfigure.width;
12829  windows->magnify.height=(unsigned int) event->xconfigure.height;
12830  if (windows->magnify.mapped == MagickFalse)
12831  break;
12832  magnify=1;
12833  while ((int) magnify <= event->xconfigure.width)
12834  magnify<<=1;
12835  while ((int) magnify <= event->xconfigure.height)
12836  magnify<<=1;
12837  magnify>>=1;
12838  if (((int) magnify != event->xconfigure.width) ||
12839  ((int) magnify != event->xconfigure.height))
12840  {
12841  XWindowChanges
12842  window_changes;
12843 
12844  window_changes.width=(int) magnify;
12845  window_changes.height=(int) magnify;
12846  (void) XReconfigureWMWindow(display,windows->magnify.id,
12847  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12848  &window_changes);
12849  break;
12850  }
12851  XMakeMagnifyImage(display,windows,exception);
12852  break;
12853  }
12854  break;
12855  }
12856  case Expose:
12857  {
12858  if (event->xexpose.window == windows->image.id)
12859  {
12860  XRefreshWindow(display,&windows->image,event);
12861  break;
12862  }
12863  if (event->xexpose.window == windows->pan.id)
12864  if (event->xexpose.count == 0)
12865  {
12866  XDrawPanRectangle(display,windows);
12867  break;
12868  }
12869  if (event->xexpose.window == windows->magnify.id)
12870  if (event->xexpose.count == 0)
12871  {
12872  XMakeMagnifyImage(display,windows,exception);
12873  break;
12874  }
12875  break;
12876  }
12877  case KeyPress:
12878  {
12879  char
12880  command[MagickPathExtent];
12881 
12882  KeySym
12883  key_symbol;
12884 
12885  if (event->xkey.window != windows->magnify.id)
12886  break;
12887  /*
12888  Respond to a user key press.
12889  */
12890  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12891  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12892  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12893  exception);
12894  break;
12895  }
12896  case MapNotify:
12897  {
12898  if (event->xmap.window == windows->magnify.id)
12899  {
12900  windows->magnify.mapped=MagickTrue;
12901  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12902  break;
12903  }
12904  if (event->xmap.window == windows->info.id)
12905  {
12906  windows->info.mapped=MagickTrue;
12907  break;
12908  }
12909  break;
12910  }
12911  case MotionNotify:
12912  {
12913  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12914  if (event->xmotion.window == windows->image.id)
12915  if (windows->magnify.mapped != MagickFalse)
12916  {
12917  /*
12918  Update magnified image.
12919  */
12920  x=event->xmotion.x;
12921  y=event->xmotion.y;
12922  if (x < 0)
12923  x=0;
12924  else
12925  if (x >= (int) windows->image.width)
12926  x=(int) (windows->image.width-1);
12927  windows->magnify.x=(int) windows->image.x+x;
12928  if (y < 0)
12929  y=0;
12930  else
12931  if (y >= (int) windows->image.height)
12932  y=(int) (windows->image.height-1);
12933  windows->magnify.y=windows->image.y+y;
12934  XMakeMagnifyImage(display,windows,exception);
12935  }
12936  break;
12937  }
12938  case UnmapNotify:
12939  {
12940  if (event->xunmap.window == windows->magnify.id)
12941  {
12942  windows->magnify.mapped=MagickFalse;
12943  break;
12944  }
12945  if (event->xunmap.window == windows->info.id)
12946  {
12947  windows->info.mapped=MagickFalse;
12948  break;
12949  }
12950  break;
12951  }
12952  default:
12953  break;
12954  }
12955 }
12956 ␌
12957 /*
12958 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12959 % %
12960 % %
12961 % %
12962 + X S e t C r o p G e o m e t r y %
12963 % %
12964 % %
12965 % %
12966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12967 %
12968 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12969 % and translates it to a cropping geometry relative to the image.
12970 %
12971 % The format of the XSetCropGeometry method is:
12972 %
12973 % void XSetCropGeometry(Display *display,XWindows *windows,
12974 % RectangleInfo *crop_info,Image *image)
12975 %
12976 % A description of each parameter follows:
12977 %
12978 % o display: Specifies a connection to an X server; returned from
12979 % XOpenDisplay.
12980 %
12981 % o windows: Specifies a pointer to a XWindows structure.
12982 %
12983 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12984 % Image window to crop.
12985 %
12986 % o image: the image.
12987 %
12988 */
12989 static void XSetCropGeometry(Display *display,XWindows *windows,
12990  RectangleInfo *crop_info,Image *image)
12991 {
12992  char
12993  text[MagickPathExtent];
12994 
12995  int
12996  x,
12997  y;
12998 
12999  double
13000  scale_factor;
13001 
13002  unsigned int
13003  height,
13004  width;
13005 
13006  if (windows->info.mapped != MagickFalse)
13007  {
13008  /*
13009  Display info on cropping rectangle.
13010  */
13011  (void) FormatLocaleString(text,MagickPathExtent," %.17gx%.17g%+.20g%+.20g",
13012  (double) crop_info->width,(double) crop_info->height,(double)
13013  crop_info->x,(double) crop_info->y);
13014  XInfoWidget(display,windows,text);
13015  }
13016  /*
13017  Cropping geometry is relative to any previous crop geometry.
13018  */
13019  x=0;
13020  y=0;
13021  width=(unsigned int) image->columns;
13022  height=(unsigned int) image->rows;
13023  if (windows->image.crop_geometry != (char *) NULL)
13024  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13025  else
13026  windows->image.crop_geometry=AcquireString((char *) NULL);
13027  /*
13028  Define the crop geometry string from the cropping rectangle.
13029  */
13030  scale_factor=(double) width/windows->image.ximage->width;
13031  if (crop_info->x > 0)
13032  x+=(int) (scale_factor*crop_info->x+0.5);
13033  width=(unsigned int) (scale_factor*crop_info->width+0.5);
13034  if (width == 0)
13035  width=1;
13036  scale_factor=(double) height/windows->image.ximage->height;
13037  if (crop_info->y > 0)
13038  y+=(int) (scale_factor*crop_info->y+0.5);
13039  height=(unsigned int) (scale_factor*crop_info->height+0.5);
13040  if (height == 0)
13041  height=1;
13042  (void) FormatLocaleString(windows->image.crop_geometry,MagickPathExtent,
13043  "%ux%u%+d%+d",width,height,x,y);
13044 }
13045 ␌
13046 /*
13047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13048 % %
13049 % %
13050 % %
13051 + X T i l e I m a g e %
13052 % %
13053 % %
13054 % %
13055 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13056 %
13057 % XTileImage() loads or deletes a selected tile from a visual image directory.
13058 % The load or delete command is chosen from a menu.
13059 %
13060 % The format of the XTileImage method is:
13061 %
13062 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
13063 % XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13064 %
13065 % A description of each parameter follows:
13066 %
13067 % o tile_image: XTileImage reads or deletes the tile image
13068 % and returns it. A null image is returned if an error occurs.
13069 %
13070 % o display: Specifies a connection to an X server; returned from
13071 % XOpenDisplay.
13072 %
13073 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13074 %
13075 % o windows: Specifies a pointer to a XWindows structure.
13076 %
13077 % o image: the image; returned from ReadImage.
13078 %
13079 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
13080 % the entire image is refreshed.
13081 %
13082 % o exception: return any errors or warnings in this structure.
13083 %
13084 */
13085 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13086  XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13087 {
13088  const char
13089  *const VerbMenu[] =
13090  {
13091  "Load",
13092  "Next",
13093  "Former",
13094  "Delete",
13095  "Update",
13096  (char *) NULL,
13097  };
13098 
13099  static const ModeType
13100  TileCommands[] =
13101  {
13102  TileLoadCommand,
13103  TileNextCommand,
13104  TileFormerCommand,
13105  TileDeleteCommand,
13106  TileUpdateCommand
13107  };
13108 
13109  char
13110  command[MagickPathExtent],
13111  filename[MagickPathExtent];
13112 
13113  Image
13114  *tile_image;
13115 
13116  int
13117  id,
13118  status,
13119  tile,
13120  x,
13121  y;
13122 
13123  double
13124  scale_factor;
13125 
13126  char
13127  *p,
13128  *q;
13129 
13130  int
13131  i;
13132 
13133  unsigned int
13134  height,
13135  width;
13136 
13137  /*
13138  Tile image is relative to montage image configuration.
13139  */
13140  x=0;
13141  y=0;
13142  width=(unsigned int) image->columns;
13143  height=(unsigned int) image->rows;
13144  if (windows->image.crop_geometry != (char *) NULL)
13145  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13146  scale_factor=(double) width/windows->image.ximage->width;
13147  event->xbutton.x+=windows->image.x;
13148  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13149  scale_factor=(double) height/windows->image.ximage->height;
13150  event->xbutton.y+=windows->image.y;
13151  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13152  /*
13153  Determine size and location of each tile in the visual image directory.
13154  */
13155  width=(unsigned int) image->columns;
13156  height=(unsigned int) image->rows;
13157  x=0;
13158  y=0;
13159  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13160  tile=((event->xbutton.y-y)/(int) height)*(((int) image->columns-x)/(int)
13161  width)+(event->xbutton.x-x)/(int) width;
13162  if (tile < 0)
13163  {
13164  /*
13165  Button press is outside any tile.
13166  */
13167  (void) XBell(display,0);
13168  return((Image *) NULL);
13169  }
13170  /*
13171  Determine file name from the tile directory.
13172  */
13173  p=image->directory;
13174  for (i=tile; (i != 0) && (*p != '\0'); )
13175  {
13176  if (*p == '\xff')
13177  i--;
13178  p++;
13179  }
13180  if (*p == '\0')
13181  {
13182  /*
13183  Button press is outside any tile.
13184  */
13185  (void) XBell(display,0);
13186  return((Image *) NULL);
13187  }
13188  /*
13189  Select a command from the pop-up menu.
13190  */
13191  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13192  if (id < 0)
13193  return((Image *) NULL);
13194  q=p;
13195  while ((*q != '\xff') && (*q != '\0') &&
13196  ((size_t) (q-p+1) < sizeof(filename)))
13197  q++;
13198  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13199  /*
13200  Perform command for the selected tile.
13201  */
13202  XSetCursorState(display,windows,MagickTrue);
13203  XCheckRefreshWindows(display,windows);
13204  tile_image=NewImageList();
13205  switch (TileCommands[id])
13206  {
13207  case TileLoadCommand:
13208  {
13209  /*
13210  Load tile image.
13211  */
13212  XCheckRefreshWindows(display,windows);
13213  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13214  MagickPathExtent);
13215  (void) CopyMagickString(resource_info->image_info->filename,filename,
13216  MagickPathExtent);
13217  tile_image=ReadImage(resource_info->image_info,exception);
13218  CatchException(exception);
13219  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13220  break;
13221  }
13222  case TileNextCommand:
13223  {
13224  /*
13225  Display next image.
13226  */
13227  XClientMessage(display,windows->image.id,windows->im_protocols,
13228  windows->im_next_image,CurrentTime);
13229  break;
13230  }
13231  case TileFormerCommand:
13232  {
13233  /*
13234  Display former image.
13235  */
13236  XClientMessage(display,windows->image.id,windows->im_protocols,
13237  windows->im_former_image,CurrentTime);
13238  break;
13239  }
13240  case TileDeleteCommand:
13241  {
13242  /*
13243  Delete tile image.
13244  */
13245  if (IsPathAccessible(filename) == MagickFalse)
13246  {
13247  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13248  break;
13249  }
13250  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13251  if (status <= 0)
13252  break;
13253  status=ShredFile(filename) == MagickFalse ? 0 : 1;
13254  status|=remove_utf8(filename);
13255  if (status != MagickFalse)
13256  {
13257  XNoticeWidget(display,windows,"Unable to delete image file:",
13258  filename);
13259  break;
13260  }
13261  magick_fallthrough;
13262  }
13263  case TileUpdateCommand:
13264  {
13265  int
13266  x_offset,
13267  y_offset;
13268 
13269  PixelInfo
13270  pixel;
13271 
13272  int
13273  j;
13274 
13275  Quantum
13276  *s;
13277 
13278  /*
13279  Ensure all the images exist.
13280  */
13281  tile=0;
13282  GetPixelInfo(image,&pixel);
13283  for (p=image->directory; *p != '\0'; p++)
13284  {
13285  CacheView
13286  *image_view;
13287 
13288  q=p;
13289  while ((*q != '\xff') && (*q != '\0') &&
13290  ((size_t) (q-p+1) < sizeof(filename)))
13291  q++;
13292  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13293  p=q;
13294  if (IsPathAccessible(filename) != MagickFalse)
13295  {
13296  tile++;
13297  continue;
13298  }
13299  /*
13300  Overwrite tile with background color.
13301  */
13302  x_offset=((int) width*(tile % (((int) image->columns-x)/(int) width))+
13303  x);
13304  y_offset=((int) height*(tile/(((int) image->columns-x)/(int) width))+
13305  y);
13306  image_view=AcquireAuthenticCacheView(image,exception);
13307  (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13308  for (i=0; i < (int) height; i++)
13309  {
13310  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13311  y_offset+i,width,1,exception);
13312  if (s == (Quantum *) NULL)
13313  break;
13314  for (j=0; j < (int) width; j++)
13315  {
13316  SetPixelViaPixelInfo(image,&pixel,s);
13317  s+=(ptrdiff_t) GetPixelChannels(image);
13318  }
13319  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13320  break;
13321  }
13322  image_view=DestroyCacheView(image_view);
13323  tile++;
13324  }
13325  windows->image.window_changes.width=(int) image->columns;
13326  windows->image.window_changes.height=(int) image->rows;
13327  XConfigureImageColormap(display,resource_info,windows,image,exception);
13328  (void) XConfigureImage(display,resource_info,windows,image,exception);
13329  break;
13330  }
13331  default:
13332  break;
13333  }
13334  XSetCursorState(display,windows,MagickFalse);
13335  return(tile_image);
13336 }
13337 ␌
13338 /*
13339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13340 % %
13341 % %
13342 % %
13343 + X T r a n s l a t e I m a g e %
13344 % %
13345 % %
13346 % %
13347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13348 %
13349 % XTranslateImage() translates the image within an Image window by one pixel
13350 % as specified by the key symbol. If the image has a montage string the
13351 % translation is respect to the width and height contained within the string.
13352 %
13353 % The format of the XTranslateImage method is:
13354 %
13355 % void XTranslateImage(Display *display,XWindows *windows,
13356 % Image *image,const KeySym key_symbol)
13357 %
13358 % A description of each parameter follows:
13359 %
13360 % o display: Specifies a connection to an X server; returned from
13361 % XOpenDisplay.
13362 %
13363 % o windows: Specifies a pointer to a XWindows structure.
13364 %
13365 % o image: the image.
13366 %
13367 % o key_symbol: Specifies a KeySym which indicates which side of the image
13368 % to trim.
13369 %
13370 */
13371 static void XTranslateImage(Display *display,XWindows *windows,
13372  Image *image,const KeySym key_symbol)
13373 {
13374  char
13375  text[MagickPathExtent];
13376 
13377  int
13378  x,
13379  y;
13380 
13381  unsigned int
13382  x_offset,
13383  y_offset;
13384 
13385  /*
13386  User specified a pan position offset.
13387  */
13388  x_offset=windows->image.width;
13389  y_offset=windows->image.height;
13390  if (image->montage != (char *) NULL)
13391  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13392  switch ((int) key_symbol)
13393  {
13394  case XK_Home:
13395  case XK_KP_Home:
13396  {
13397  windows->image.x=(int) windows->image.width/2;
13398  windows->image.y=(int) windows->image.height/2;
13399  break;
13400  }
13401  case XK_Left:
13402  case XK_KP_Left:
13403  {
13404  windows->image.x-=(int) x_offset;
13405  break;
13406  }
13407  case XK_Next:
13408  case XK_Up:
13409  case XK_KP_Up:
13410  {
13411  windows->image.y-=(int) y_offset;
13412  break;
13413  }
13414  case XK_Right:
13415  case XK_KP_Right:
13416  {
13417  windows->image.x+=(int) x_offset;
13418  break;
13419  }
13420  case XK_Prior:
13421  case XK_Down:
13422  case XK_KP_Down:
13423  {
13424  windows->image.y+=(int) y_offset;
13425  break;
13426  }
13427  default:
13428  return;
13429  }
13430  /*
13431  Check boundary conditions.
13432  */
13433  if (windows->image.x < 0)
13434  windows->image.x=0;
13435  else
13436  if ((windows->image.x+(int) windows->image.width) > windows->image.ximage->width)
13437  windows->image.x=windows->image.ximage->width-(int) windows->image.width;
13438  if (windows->image.y < 0)
13439  windows->image.y=0;
13440  else
13441  if ((windows->image.y+(int) windows->image.height) > windows->image.ximage->height)
13442  windows->image.y=windows->image.ximage->height-(int)
13443  windows->image.height;
13444  /*
13445  Refresh Image window.
13446  */
13447  (void) FormatLocaleString(text,MagickPathExtent," %ux%u%+d%+d ",
13448  windows->image.width,windows->image.height,windows->image.x,
13449  windows->image.y);
13450  XInfoWidget(display,windows,text);
13451  XCheckRefreshWindows(display,windows);
13452  XDrawPanRectangle(display,windows);
13453  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13454  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13455 }
13456 ␌
13457 /*
13458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13459 % %
13460 % %
13461 % %
13462 + X T r i m I m a g e %
13463 % %
13464 % %
13465 % %
13466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13467 %
13468 % XTrimImage() trims the edges from the Image window.
13469 %
13470 % The format of the XTrimImage method is:
13471 %
13472 % MagickBooleanType XTrimImage(Display *display,
13473 % XResourceInfo *resource_info,XWindows *windows,Image *image,
13474 % ExceptionInfo *exception)
13475 %
13476 % A description of each parameter follows:
13477 %
13478 % o display: Specifies a connection to an X server; returned from
13479 % XOpenDisplay.
13480 %
13481 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13482 %
13483 % o windows: Specifies a pointer to a XWindows structure.
13484 %
13485 % o image: the image.
13486 %
13487 % o exception: return any errors or warnings in this structure.
13488 %
13489 */
13490 static MagickBooleanType XTrimImage(Display *display,
13491  XResourceInfo *resource_info,XWindows *windows,Image *image,
13492  ExceptionInfo *exception)
13493 {
13495  trim_info;
13496 
13497  int
13498  x,
13499  y;
13500 
13501  size_t
13502  background,
13503  pixel;
13504 
13505  /*
13506  Trim edges from image.
13507  */
13508  XSetCursorState(display,windows,MagickTrue);
13509  XCheckRefreshWindows(display,windows);
13510  /*
13511  Crop the left edge.
13512  */
13513  background=XGetPixel(windows->image.ximage,0,0);
13514  trim_info.width=(size_t) windows->image.ximage->width;
13515  for (x=0; x < windows->image.ximage->width; x++)
13516  {
13517  for (y=0; y < windows->image.ximage->height; y++)
13518  {
13519  pixel=XGetPixel(windows->image.ximage,x,y);
13520  if (pixel != background)
13521  break;
13522  }
13523  if (y < windows->image.ximage->height)
13524  break;
13525  }
13526  trim_info.x=(ssize_t) x;
13527  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13528  {
13529  XSetCursorState(display,windows,MagickFalse);
13530  return(MagickFalse);
13531  }
13532  /*
13533  Crop the right edge.
13534  */
13535  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13536  for (x=windows->image.ximage->width-1; x != 0; x--)
13537  {
13538  for (y=0; y < windows->image.ximage->height; y++)
13539  {
13540  pixel=XGetPixel(windows->image.ximage,x,y);
13541  if (pixel != background)
13542  break;
13543  }
13544  if (y < windows->image.ximage->height)
13545  break;
13546  }
13547  trim_info.width=(size_t) (x-trim_info.x+1);
13548  /*
13549  Crop the top edge.
13550  */
13551  background=XGetPixel(windows->image.ximage,0,0);
13552  trim_info.height=(size_t) windows->image.ximage->height;
13553  for (y=0; y < windows->image.ximage->height; y++)
13554  {
13555  for (x=0; x < windows->image.ximage->width; x++)
13556  {
13557  pixel=XGetPixel(windows->image.ximage,x,y);
13558  if (pixel != background)
13559  break;
13560  }
13561  if (x < windows->image.ximage->width)
13562  break;
13563  }
13564  trim_info.y=(ssize_t) y;
13565  /*
13566  Crop the bottom edge.
13567  */
13568  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13569  for (y=windows->image.ximage->height-1; y != 0; y--)
13570  {
13571  for (x=0; x < windows->image.ximage->width; x++)
13572  {
13573  pixel=XGetPixel(windows->image.ximage,x,y);
13574  if (pixel != background)
13575  break;
13576  }
13577  if (x < windows->image.ximage->width)
13578  break;
13579  }
13580  trim_info.height=(size_t) (y-trim_info.y+1);
13581  if (((unsigned int) trim_info.width != windows->image.width) ||
13582  ((unsigned int) trim_info.height != windows->image.height))
13583  {
13584  /*
13585  Reconfigure Image window as defined by the trimming rectangle.
13586  */
13587  XSetCropGeometry(display,windows,&trim_info,image);
13588  windows->image.window_changes.width=(int) trim_info.width;
13589  windows->image.window_changes.height=(int) trim_info.height;
13590  (void) XConfigureImage(display,resource_info,windows,image,exception);
13591  }
13592  XSetCursorState(display,windows,MagickFalse);
13593  return(MagickTrue);
13594 }
13595 ␌
13596 /*
13597 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13598 % %
13599 % %
13600 % %
13601 + X V i s u a l D i r e c t o r y I m a g e %
13602 % %
13603 % %
13604 % %
13605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13606 %
13607 % XVisualDirectoryImage() creates a Visual Image Directory.
13608 %
13609 % The format of the XVisualDirectoryImage method is:
13610 %
13611 % Image *XVisualDirectoryImage(Display *display,
13612 % XResourceInfo *resource_info,XWindows *windows,
13613 % ExceptionInfo *exception)
13614 %
13615 % A description of each parameter follows:
13616 %
13617 % o display: Specifies a connection to an X server; returned from
13618 % XOpenDisplay.
13619 %
13620 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13621 %
13622 % o windows: Specifies a pointer to a XWindows structure.
13623 %
13624 % o exception: return any errors or warnings in this structure.
13625 %
13626 */
13627 static Image *XVisualDirectoryImage(Display *display,
13628  XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13629 {
13630 #define TileImageTag "Scale/Image"
13631 #define XClientName "montage"
13632 
13633  char
13634  **filelist;
13635 
13636  Image
13637  *images,
13638  *montage_image,
13639  *next_image,
13640  *thumbnail_image;
13641 
13642  ImageInfo
13643  *read_info;
13644 
13645  int
13646  number_files;
13647 
13648  MagickBooleanType
13649  backdrop;
13650 
13651  MagickStatusType
13652  status;
13653 
13654  MontageInfo
13655  *montage_info;
13656 
13658  geometry;
13659 
13660  int
13661  i;
13662 
13663  static char
13664  filename[MagickPathExtent] = "\0",
13665  filenames[MagickPathExtent] = "*";
13666 
13667  XResourceInfo
13668  background_resources;
13669 
13670  /*
13671  Request file name from user.
13672  */
13673  XFileBrowserWidget(display,windows,"Directory",filenames);
13674  if (*filenames == '\0')
13675  return((Image *) NULL);
13676  /*
13677  Expand the filenames.
13678  */
13679  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13680  if (filelist == (char **) NULL)
13681  {
13682  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13683  filenames);
13684  return((Image *) NULL);
13685  }
13686  number_files=1;
13687  filelist[0]=filenames;
13688  status=ExpandFilenames(&number_files,&filelist);
13689  if ((status == MagickFalse) || (number_files == 0))
13690  {
13691  if (number_files == 0)
13692  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13693  else
13694  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13695  filenames);
13696  return((Image *) NULL);
13697  }
13698  /*
13699  Set image background resources.
13700  */
13701  background_resources=(*resource_info);
13702  background_resources.window_id=AcquireString("");
13703  (void) FormatLocaleString(background_resources.window_id,MagickPathExtent,
13704  "0x%lx",windows->image.id);
13705  background_resources.backdrop=MagickTrue;
13706  /*
13707  Read each image and convert them to a tile.
13708  */
13709  backdrop=((windows->visual_info->klass == TrueColor) ||
13710  (windows->visual_info->klass == DirectColor)) ? MagickTrue : MagickFalse;
13711  read_info=CloneImageInfo(resource_info->image_info);
13712  (void) SetImageOption(read_info,"jpeg:size","120x120");
13713  (void) CloneString(&read_info->size,DefaultTileGeometry);
13714  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13715  (void *) NULL);
13716  images=NewImageList();
13717  XSetCursorState(display,windows,MagickTrue);
13718  XCheckRefreshWindows(display,windows);
13719  for (i=0; i < (int) number_files; i++)
13720  {
13721  (void) CopyMagickString(read_info->filename,filelist[i],MagickPathExtent);
13722  filelist[i]=DestroyString(filelist[i]);
13723  *read_info->magick='\0';
13724  next_image=ReadImage(read_info,exception);
13725  CatchException(exception);
13726  if (next_image != (Image *) NULL)
13727  {
13728  (void) DeleteImageProperty(next_image,"label");
13729  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13730  read_info,next_image,DefaultTileLabel,exception),exception);
13731  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13732  exception);
13733  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13734  geometry.height,exception);
13735  if (thumbnail_image != (Image *) NULL)
13736  {
13737  next_image=DestroyImage(next_image);
13738  next_image=thumbnail_image;
13739  }
13740  if (backdrop)
13741  {
13742  (void) XDisplayBackgroundImage(display,&background_resources,
13743  next_image,exception);
13744  XSetCursorState(display,windows,MagickTrue);
13745  }
13746  AppendImageToList(&images,next_image);
13747  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13748  {
13749  MagickBooleanType
13750  proceed;
13751 
13752  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13753  (MagickSizeType) number_files);
13754  if (proceed == MagickFalse)
13755  break;
13756  }
13757  }
13758  }
13759  filelist=(char **) RelinquishMagickMemory(filelist);
13760  if (images == (Image *) NULL)
13761  {
13762  read_info=DestroyImageInfo(read_info);
13763  XSetCursorState(display,windows,MagickFalse);
13764  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13765  return((Image *) NULL);
13766  }
13767  /*
13768  Create the Visual Image Directory.
13769  */
13770  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13771  montage_info->pointsize=10;
13772  if (resource_info->font != (char *) NULL)
13773  (void) CloneString(&montage_info->font,resource_info->font);
13774  (void) CopyMagickString(montage_info->filename,filename,MagickPathExtent);
13775  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13776  images),exception);
13777  images=DestroyImageList(images);
13778  montage_info=DestroyMontageInfo(montage_info);
13779  read_info=DestroyImageInfo(read_info);
13780  XSetCursorState(display,windows,MagickFalse);
13781  if (montage_image == (Image *) NULL)
13782  return(montage_image);
13783  XClientMessage(display,windows->image.id,windows->im_protocols,
13784  windows->im_next_image,CurrentTime);
13785  return(montage_image);
13786 }
13787 ␌
13788 /*
13789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13790 % %
13791 % %
13792 % %
13793 % X D i s p l a y B a c k g r o u n d I m a g e %
13794 % %
13795 % %
13796 % %
13797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13798 %
13799 % XDisplayBackgroundImage() displays an image in the background of a window.
13800 %
13801 % The format of the XDisplayBackgroundImage method is:
13802 %
13803 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13804 % XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13805 %
13806 % A description of each parameter follows:
13807 %
13808 % o display: Specifies a connection to an X server; returned from
13809 % XOpenDisplay.
13810 %
13811 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13812 %
13813 % o image: the image.
13814 %
13815 % o exception: return any errors or warnings in this structure.
13816 %
13817 */
13818 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13819  XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13820 {
13821  char
13822  geometry[MagickPathExtent],
13823  visual_type[MagickPathExtent];
13824 
13825  int
13826  height,
13827  status,
13828  width;
13829 
13831  geometry_info;
13832 
13833  static XPixelInfo
13834  pixel;
13835 
13836  static XStandardColormap
13837  *map_info;
13838 
13839  static XVisualInfo
13840  *visual_info = (XVisualInfo *) NULL;
13841 
13842  static XWindowInfo
13843  window_info;
13844 
13845  size_t
13846  delay;
13847 
13848  Window
13849  root_window;
13850 
13851  XGCValues
13852  context_values;
13853 
13854  XResourceInfo
13855  resources;
13856 
13857  XWindowAttributes
13858  window_attributes;
13859 
13860  /*
13861  Determine target window.
13862  */
13863  assert(image != (Image *) NULL);
13864  assert(image->signature == MagickCoreSignature);
13865  if (IsEventLogging() != MagickFalse)
13866  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13867  resources=(*resource_info);
13868  window_info.id=(Window) NULL;
13869  root_window=XRootWindow(display,XDefaultScreen(display));
13870  if (LocaleCompare(resources.window_id,"root") == 0)
13871  window_info.id=root_window;
13872  else
13873  {
13874  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13875  window_info.id=XWindowByID(display,root_window,
13876  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13877  if (window_info.id == (Window) NULL)
13878  window_info.id=XWindowByName(display,root_window,resources.window_id);
13879  }
13880  if (window_info.id == (Window) NULL)
13881  {
13882  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13883  resources.window_id);
13884  return(MagickFalse);
13885  }
13886  /*
13887  Determine window visual id.
13888  */
13889  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13890  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13891  (void) CopyMagickString(visual_type,"default",MagickPathExtent);
13892  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13893  if (status != 0)
13894  (void) FormatLocaleString(visual_type,MagickPathExtent,"0x%lx",
13895  XVisualIDFromVisual(window_attributes.visual));
13896  if (visual_info == (XVisualInfo *) NULL)
13897  {
13898  /*
13899  Allocate standard colormap.
13900  */
13901  map_info=XAllocStandardColormap();
13902  if (map_info == (XStandardColormap *) NULL)
13903  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13904  image->filename);
13905  map_info->colormap=(Colormap) NULL;
13906  pixel.pixels=(unsigned long *) NULL;
13907  /*
13908  Initialize visual info.
13909  */
13910  resources.map_type=(char *) NULL;
13911  resources.visual_type=visual_type;
13912  visual_info=XBestVisualInfo(display,map_info,&resources);
13913  if (visual_info == (XVisualInfo *) NULL)
13914  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13915  resources.visual_type);
13916  /*
13917  Initialize window info.
13918  */
13919  window_info.ximage=(XImage *) NULL;
13920  window_info.matte_image=(XImage *) NULL;
13921  window_info.pixmap=(Pixmap) NULL;
13922  window_info.matte_pixmap=(Pixmap) NULL;
13923  }
13924  /*
13925  Free previous root colors.
13926  */
13927  if (window_info.id == root_window)
13928  (void) XDestroyWindowColors(display,root_window);
13929  /*
13930  Initialize Standard Colormap.
13931  */
13932  resources.colormap=SharedColormap;
13933  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13934  exception);
13935  /*
13936  Graphic context superclass.
13937  */
13938  context_values.background=pixel.foreground_color.pixel;
13939  context_values.foreground=pixel.background_color.pixel;
13940  pixel.annotate_context=XCreateGC(display,window_info.id,
13941  (size_t) (GCBackground | GCForeground),&context_values);
13942  if (pixel.annotate_context == (GC) NULL)
13943  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13944  image->filename);
13945  /*
13946  Initialize Image window attributes.
13947  */
13948  window_info.name=AcquireString("\0");
13949  window_info.icon_name=AcquireString("\0");
13950  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13951  &resources,&window_info);
13952  /*
13953  Create the X image.
13954  */
13955  window_info.width=(unsigned int) image->columns;
13956  window_info.height=(unsigned int) image->rows;
13957  if ((image->columns != window_info.width) ||
13958  (image->rows != window_info.height))
13959  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13960  image->filename);
13961  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>",
13962  window_attributes.width,window_attributes.height);
13963  geometry_info.width=window_info.width;
13964  geometry_info.height=window_info.height;
13965  geometry_info.x=(ssize_t) window_info.x;
13966  geometry_info.y=(ssize_t) window_info.y;
13967  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13968  &geometry_info.width,&geometry_info.height);
13969  window_info.width=(unsigned int) geometry_info.width;
13970  window_info.height=(unsigned int) geometry_info.height;
13971  window_info.x=(int) geometry_info.x;
13972  window_info.y=(int) geometry_info.y;
13973  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13974  window_info.height,exception) == MagickFalse ? 0 : 1;
13975  if (status == MagickFalse)
13976  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13977  image->filename);
13978  window_info.x=0;
13979  window_info.y=0;
13980  if (resource_info->debug != MagickFalse)
13981  {
13982  (void) LogMagickEvent(X11Event,GetMagickModule(),
13983  "Image: %s[%.17g] %.17gx%.17g ",image->filename,(double) image->scene,
13984  (double) image->columns,(double) image->rows);
13985  if (image->colors != 0)
13986  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.17gc ",(double)
13987  image->colors);
13988  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13989  }
13990  /*
13991  Adjust image dimensions as specified by backdrop or geometry options.
13992  */
13993  width=(int) window_info.width;
13994  height=(int) window_info.height;
13995  if (resources.backdrop != MagickFalse)
13996  {
13997  /*
13998  Center image on window.
13999  */
14000  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
14001  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
14002  width=window_attributes.width;
14003  height=window_attributes.height;
14004  }
14005  if ((resources.image_geometry != (char *) NULL) &&
14006  (*resources.image_geometry != '\0'))
14007  {
14008  char
14009  default_geometry[MagickPathExtent];
14010 
14011  int
14012  flags,
14013  gravity;
14014 
14015  XSizeHints
14016  *size_hints;
14017 
14018  /*
14019  User specified geometry.
14020  */
14021  size_hints=XAllocSizeHints();
14022  if (size_hints == (XSizeHints *) NULL)
14023  ThrowXWindowFatalException(ResourceLimitFatalError,
14024  "MemoryAllocationFailed",image->filename);
14025  size_hints->flags=0L;
14026  (void) FormatLocaleString(default_geometry,MagickPathExtent,"%dx%d",
14027  width,height);
14028  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14029  default_geometry,window_info.border_width,size_hints,&window_info.x,
14030  &window_info.y,&width,&height,&gravity);
14031  if (flags & (XValue | YValue))
14032  {
14033  width=window_attributes.width;
14034  height=window_attributes.height;
14035  }
14036  (void) XFree((void *) size_hints);
14037  }
14038  /*
14039  Create the X pixmap.
14040  */
14041  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14042  (unsigned int) height,window_info.depth);
14043  if (window_info.pixmap == (Pixmap) NULL)
14044  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14045  image->filename);
14046  /*
14047  Display pixmap on the window.
14048  */
14049  if (((unsigned int) width > window_info.width) ||
14050  ((unsigned int) height > window_info.height))
14051  (void) XFillRectangle(display,window_info.pixmap,
14052  window_info.annotate_context,0,0,(unsigned int) width,
14053  (unsigned int) height);
14054  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14055  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14056  window_info.width,(unsigned int) window_info.height);
14057  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14058  (void) XClearWindow(display,window_info.id);
14059  delay=1000*image->delay/(size_t) MagickMax(image->ticks_per_second,1L);
14060  XDelay(display,delay == 0UL ? 10UL : delay);
14061  (void) XSync(display,MagickFalse);
14062  return(window_info.id == root_window ? MagickTrue : MagickFalse);
14063 }
14064 ␌
14065 /*
14066 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14067 % %
14068 % %
14069 % %
14070 + X D i s p l a y I m a g e %
14071 % %
14072 % %
14073 % %
14074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14075 %
14076 % XDisplayImage() displays an image via X11. A new image is created and
14077 % returned if the user interactively transforms the displayed image.
14078 %
14079 % The format of the XDisplayImage method is:
14080 %
14081 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14082 % char **argv,int argc,Image **image,size_t *state,
14083 % ExceptionInfo *exception)
14084 %
14085 % A description of each parameter follows:
14086 %
14087 % o nexus: Method XDisplayImage returns an image when the
14088 % user chooses 'Open Image' from the command menu or picks a tile
14089 % from the image directory. Otherwise a null image is returned.
14090 %
14091 % o display: Specifies a connection to an X server; returned from
14092 % XOpenDisplay.
14093 %
14094 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14095 %
14096 % o argv: Specifies the application's argument list.
14097 %
14098 % o argc: Specifies the number of arguments.
14099 %
14100 % o image: Specifies an address to an address of an Image structure;
14101 %
14102 % o exception: return any errors or warnings in this structure.
14103 %
14104 */
14105 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14106  char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14107 {
14108 #define MagnifySize 256 /* must be a power of 2 */
14109 #define MagickMenus 10
14110 #define MagickTitle "Commands"
14111 
14112  const char
14113  *const CommandMenu[] =
14114  {
14115  "File",
14116  "Edit",
14117  "View",
14118  "Transform",
14119  "Enhance",
14120  "Effects",
14121  "F/X",
14122  "Image Edit",
14123  "Miscellany",
14124  "Help",
14125  (char *) NULL
14126  },
14127  *const FileMenu[] =
14128  {
14129  "Open...",
14130  "Next",
14131  "Former",
14132  "Select...",
14133  "Save...",
14134  "Print...",
14135  "Delete...",
14136  "New...",
14137  "Visual Directory...",
14138  "Quit",
14139  (char *) NULL
14140  },
14141  *const EditMenu[] =
14142  {
14143  "Undo",
14144  "Redo",
14145  "Cut",
14146  "Copy",
14147  "Paste",
14148  (char *) NULL
14149  },
14150  *const ViewMenu[] =
14151  {
14152  "Half Size",
14153  "Original Size",
14154  "Double Size",
14155  "Resize...",
14156  "Apply",
14157  "Refresh",
14158  "Restore",
14159  (char *) NULL
14160  },
14161  *const TransformMenu[] =
14162  {
14163  "Crop",
14164  "Chop",
14165  "Flop",
14166  "Flip",
14167  "Rotate Right",
14168  "Rotate Left",
14169  "Rotate...",
14170  "Shear...",
14171  "Roll...",
14172  "Trim Edges",
14173  (char *) NULL
14174  },
14175  *const EnhanceMenu[] =
14176  {
14177  "Hue...",
14178  "Saturation...",
14179  "Brightness...",
14180  "Gamma...",
14181  "Spiff",
14182  "Dull",
14183  "Contrast Stretch...",
14184  "Sigmoidal Contrast...",
14185  "Normalize",
14186  "Equalize",
14187  "Negate",
14188  "Grayscale",
14189  "Map...",
14190  "Quantize...",
14191  (char *) NULL
14192  },
14193  *const EffectsMenu[] =
14194  {
14195  "Despeckle",
14196  "Emboss",
14197  "Reduce Noise",
14198  "Add Noise...",
14199  "Sharpen...",
14200  "Blur...",
14201  "Threshold...",
14202  "Edge Detect...",
14203  "Spread...",
14204  "Shade...",
14205  "Raise...",
14206  "Segment...",
14207  (char *) NULL
14208  },
14209  *const FXMenu[] =
14210  {
14211  "Solarize...",
14212  "Sepia Tone...",
14213  "Swirl...",
14214  "Implode...",
14215  "Vignette...",
14216  "Wave...",
14217  "Oil Paint...",
14218  "Charcoal Draw...",
14219  (char *) NULL
14220  },
14221  *const ImageEditMenu[] =
14222  {
14223  "Annotate...",
14224  "Draw...",
14225  "Color...",
14226  "Matte...",
14227  "Composite...",
14228  "Add Border...",
14229  "Add Frame...",
14230  "Comment...",
14231  "Launch...",
14232  "Region of Interest...",
14233  (char *) NULL
14234  },
14235  *const MiscellanyMenu[] =
14236  {
14237  "Image Info",
14238  "Zoom Image",
14239  "Show Preview...",
14240  "Show Histogram",
14241  "Show Matte",
14242  "Background...",
14243  "Slide Show...",
14244  "Preferences...",
14245  (char *) NULL
14246  },
14247  *const HelpMenu[] =
14248  {
14249  "Overview",
14250  "Browse Documentation",
14251  "About Display",
14252  (char *) NULL
14253  },
14254  *const ShortCutsMenu[] =
14255  {
14256  "Next",
14257  "Former",
14258  "Open...",
14259  "Save...",
14260  "Print...",
14261  "Undo",
14262  "Restore",
14263  "Image Info",
14264  "Quit",
14265  (char *) NULL
14266  },
14267  *const VirtualMenu[] =
14268  {
14269  "Image Info",
14270  "Print",
14271  "Next",
14272  "Quit",
14273  (char *) NULL
14274  };
14275 
14276  const char
14277  *const *Menus[MagickMenus] =
14278  {
14279  FileMenu,
14280  EditMenu,
14281  ViewMenu,
14282  TransformMenu,
14283  EnhanceMenu,
14284  EffectsMenu,
14285  FXMenu,
14286  ImageEditMenu,
14287  MiscellanyMenu,
14288  HelpMenu
14289  };
14290 
14291  static DisplayCommand
14292  CommandMenus[] =
14293  {
14294  NullCommand,
14295  NullCommand,
14296  NullCommand,
14297  NullCommand,
14298  NullCommand,
14299  NullCommand,
14300  NullCommand,
14301  NullCommand,
14302  NullCommand,
14303  NullCommand,
14304  },
14305  FileCommands[] =
14306  {
14307  OpenCommand,
14308  NextCommand,
14309  FormerCommand,
14310  SelectCommand,
14311  SaveCommand,
14312  PrintCommand,
14313  DeleteCommand,
14314  NewCommand,
14315  VisualDirectoryCommand,
14316  QuitCommand
14317  },
14318  EditCommands[] =
14319  {
14320  UndoCommand,
14321  RedoCommand,
14322  CutCommand,
14323  CopyCommand,
14324  PasteCommand
14325  },
14326  ViewCommands[] =
14327  {
14328  HalfSizeCommand,
14329  OriginalSizeCommand,
14330  DoubleSizeCommand,
14331  ResizeCommand,
14332  ApplyCommand,
14333  RefreshCommand,
14334  RestoreCommand
14335  },
14336  TransformCommands[] =
14337  {
14338  CropCommand,
14339  ChopCommand,
14340  FlopCommand,
14341  FlipCommand,
14342  RotateRightCommand,
14343  RotateLeftCommand,
14344  RotateCommand,
14345  ShearCommand,
14346  RollCommand,
14347  TrimCommand
14348  },
14349  EnhanceCommands[] =
14350  {
14351  HueCommand,
14352  SaturationCommand,
14353  BrightnessCommand,
14354  GammaCommand,
14355  SpiffCommand,
14356  DullCommand,
14357  ContrastStretchCommand,
14358  SigmoidalContrastCommand,
14359  NormalizeCommand,
14360  EqualizeCommand,
14361  NegateCommand,
14362  GrayscaleCommand,
14363  MapCommand,
14364  QuantizeCommand
14365  },
14366  EffectsCommands[] =
14367  {
14368  DespeckleCommand,
14369  EmbossCommand,
14370  ReduceNoiseCommand,
14371  AddNoiseCommand,
14372  SharpenCommand,
14373  BlurCommand,
14374  ThresholdCommand,
14375  EdgeDetectCommand,
14376  SpreadCommand,
14377  ShadeCommand,
14378  RaiseCommand,
14379  SegmentCommand
14380  },
14381  FXCommands[] =
14382  {
14383  SolarizeCommand,
14384  SepiaToneCommand,
14385  SwirlCommand,
14386  ImplodeCommand,
14387  VignetteCommand,
14388  WaveCommand,
14389  OilPaintCommand,
14390  CharcoalDrawCommand
14391  },
14392  ImageEditCommands[] =
14393  {
14394  AnnotateCommand,
14395  DrawCommand,
14396  ColorCommand,
14397  MatteCommand,
14398  CompositeCommand,
14399  AddBorderCommand,
14400  AddFrameCommand,
14401  CommentCommand,
14402  LaunchCommand,
14403  RegionOfInterestCommand
14404  },
14405  MiscellanyCommands[] =
14406  {
14407  InfoCommand,
14408  ZoomCommand,
14409  ShowPreviewCommand,
14410  ShowHistogramCommand,
14411  ShowMatteCommand,
14412  BackgroundCommand,
14413  SlideShowCommand,
14414  PreferencesCommand
14415  },
14416  HelpCommands[] =
14417  {
14418  HelpCommand,
14419  BrowseDocumentationCommand,
14420  VersionCommand
14421  },
14422  ShortCutsCommands[] =
14423  {
14424  NextCommand,
14425  FormerCommand,
14426  OpenCommand,
14427  SaveCommand,
14428  PrintCommand,
14429  UndoCommand,
14430  RestoreCommand,
14431  InfoCommand,
14432  QuitCommand
14433  },
14434  VirtualCommands[] =
14435  {
14436  InfoCommand,
14437  PrintCommand,
14438  NextCommand,
14439  QuitCommand
14440  };
14441 
14442  static DisplayCommand
14443  *Commands[MagickMenus] =
14444  {
14445  FileCommands,
14446  EditCommands,
14447  ViewCommands,
14448  TransformCommands,
14449  EnhanceCommands,
14450  EffectsCommands,
14451  FXCommands,
14452  ImageEditCommands,
14453  MiscellanyCommands,
14454  HelpCommands
14455  };
14456 
14457  char
14458  command[MagickPathExtent],
14459  *directory,
14460  geometry[MagickPathExtent],
14461  resource_name[MagickPathExtent];
14462 
14463  DisplayCommand
14464  display_command;
14465 
14466  Image
14467  *display_image,
14468  *nexus;
14469 
14470  int
14471  entry,
14472  id;
14473 
14474  KeySym
14475  key_symbol;
14476 
14477  MagickStatusType
14478  context_mask,
14479  status;
14480 
14482  geometry_info;
14483 
14484  int
14485  i;
14486 
14487  static char
14488  working_directory[MagickPathExtent];
14489 
14490  static XPoint
14491  vid_info;
14492 
14493  static XWindowInfo
14494  *magick_windows[MaxXWindows];
14495 
14496  static unsigned int
14497  number_windows;
14498 
14499  struct stat
14500  attributes;
14501 
14502  time_t
14503  timer,
14504  timestamp,
14505  update_time;
14506 
14507  unsigned int
14508  height,
14509  width;
14510 
14511  size_t
14512  delay;
14513 
14514  WarningHandler
14515  warning_handler;
14516 
14517  Window
14518  root_window;
14519 
14520  XClassHint
14521  *class_hints;
14522 
14523  XEvent
14524  event;
14525 
14526  XFontStruct
14527  *font_info;
14528 
14529  XGCValues
14530  context_values;
14531 
14532  XPixelInfo
14533  *icon_pixel,
14534  *pixel;
14535 
14536  XResourceInfo
14537  *icon_resources;
14538 
14539  XStandardColormap
14540  *icon_map,
14541  *map_info;
14542 
14543  XVisualInfo
14544  *icon_visual,
14545  *visual_info;
14546 
14547  XWindowChanges
14548  window_changes;
14549 
14550  XWindows
14551  *windows;
14552 
14553  XWMHints
14554  *manager_hints;
14555 
14556  assert(image != (Image **) NULL);
14557  assert((*image)->signature == MagickCoreSignature);
14558  if (IsEventLogging() != MagickFalse)
14559  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14560  display_image=(*image);
14561  warning_handler=(WarningHandler) NULL;
14562  windows=XSetWindows((XWindows *) ~0);
14563  if (windows != (XWindows *) NULL)
14564  {
14565  int
14566  status;
14567 
14568  if (*working_directory == '\0')
14569  (void) CopyMagickString(working_directory,".",MagickPathExtent);
14570  status=chdir(working_directory);
14571  if (status == -1)
14572  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14573  "UnableToOpenFile","%s",working_directory);
14574  warning_handler=resource_info->display_warnings ?
14575  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14576  warning_handler=resource_info->display_warnings ?
14577  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14578  }
14579  else
14580  {
14581  /*
14582  Allocate windows structure.
14583  */
14584  resource_info->colors=display_image->colors;
14585  windows=XSetWindows(XInitializeWindows(display,resource_info));
14586  if (windows == (XWindows *) NULL)
14587  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14588  (*image)->filename);
14589  /*
14590  Initialize window id's.
14591  */
14592  number_windows=0;
14593  magick_windows[number_windows++]=(&windows->icon);
14594  magick_windows[number_windows++]=(&windows->backdrop);
14595  magick_windows[number_windows++]=(&windows->image);
14596  magick_windows[number_windows++]=(&windows->info);
14597  magick_windows[number_windows++]=(&windows->command);
14598  magick_windows[number_windows++]=(&windows->widget);
14599  magick_windows[number_windows++]=(&windows->popup);
14600  magick_windows[number_windows++]=(&windows->magnify);
14601  magick_windows[number_windows++]=(&windows->pan);
14602  for (i=0; i < (int) number_windows; i++)
14603  magick_windows[i]->id=(Window) NULL;
14604  vid_info.x=0;
14605  vid_info.y=0;
14606  }
14607  /*
14608  Initialize font info.
14609  */
14610  if (windows->font_info != (XFontStruct *) NULL)
14611  (void) XFreeFont(display,windows->font_info);
14612  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14613  if (windows->font_info == (XFontStruct *) NULL)
14614  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14615  resource_info->font);
14616  /*
14617  Initialize Standard Colormap.
14618  */
14619  map_info=windows->map_info;
14620  icon_map=windows->icon_map;
14621  visual_info=windows->visual_info;
14622  icon_visual=windows->icon_visual;
14623  pixel=windows->pixel_info;
14624  icon_pixel=windows->icon_pixel;
14625  font_info=windows->font_info;
14626  icon_resources=windows->icon_resources;
14627  class_hints=windows->class_hints;
14628  manager_hints=windows->manager_hints;
14629  root_window=XRootWindow(display,visual_info->screen);
14630  nexus=NewImageList();
14631  if (resource_info->debug != MagickFalse)
14632  {
14633  (void) LogMagickEvent(X11Event,GetMagickModule(),
14634  "Image: %s[%.17g] %.17gx%.17g ",display_image->filename,
14635  (double) display_image->scene,(double) display_image->columns,
14636  (double) display_image->rows);
14637  if (display_image->colors != 0)
14638  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.17gc ",(double)
14639  display_image->colors);
14640  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14641  display_image->magick);
14642  }
14643  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14644  map_info,pixel,exception);
14645  display_image->taint=MagickFalse;
14646  /*
14647  Initialize graphic context.
14648  */
14649  windows->context.id=(Window) NULL;
14650  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14651  resource_info,&windows->context);
14652  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14653  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14654  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14655  class_hints->res_class[0]);
14656  manager_hints->flags=InputHint | StateHint;
14657  manager_hints->input=MagickFalse;
14658  manager_hints->initial_state=WithdrawnState;
14659  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14660  &windows->context);
14661  if (resource_info->debug != MagickFalse)
14662  (void) LogMagickEvent(X11Event,GetMagickModule(),
14663  "Window id: 0x%lx (context)",windows->context.id);
14664  context_values.background=pixel->background_color.pixel;
14665  context_values.font=font_info->fid;
14666  context_values.foreground=pixel->foreground_color.pixel;
14667  context_values.graphics_exposures=MagickFalse;
14668  context_mask=(MagickStatusType)
14669  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14670  if (pixel->annotate_context != (GC) NULL)
14671  (void) XFreeGC(display,pixel->annotate_context);
14672  pixel->annotate_context=XCreateGC(display,windows->context.id,
14673  context_mask,&context_values);
14674  if (pixel->annotate_context == (GC) NULL)
14675  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14676  display_image->filename);
14677  context_values.background=pixel->depth_color.pixel;
14678  if (pixel->widget_context != (GC) NULL)
14679  (void) XFreeGC(display,pixel->widget_context);
14680  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14681  &context_values);
14682  if (pixel->widget_context == (GC) NULL)
14683  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14684  display_image->filename);
14685  context_values.background=pixel->foreground_color.pixel;
14686  context_values.foreground=pixel->background_color.pixel;
14687  context_values.plane_mask=context_values.background ^
14688  context_values.foreground;
14689  if (pixel->highlight_context != (GC) NULL)
14690  (void) XFreeGC(display,pixel->highlight_context);
14691  pixel->highlight_context=XCreateGC(display,windows->context.id,
14692  (size_t) (context_mask | GCPlaneMask),&context_values);
14693  if (pixel->highlight_context == (GC) NULL)
14694  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14695  display_image->filename);
14696  (void) XDestroyWindow(display,windows->context.id);
14697  /*
14698  Initialize icon window.
14699  */
14700  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14701  icon_resources,&windows->icon);
14702  windows->icon.geometry=resource_info->icon_geometry;
14703  XBestIconSize(display,&windows->icon,display_image);
14704  windows->icon.attributes.colormap=XDefaultColormap(display,
14705  icon_visual->screen);
14706  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14707  manager_hints->flags=InputHint | StateHint;
14708  manager_hints->input=MagickFalse;
14709  manager_hints->initial_state=IconicState;
14710  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14711  &windows->icon);
14712  if (resource_info->debug != MagickFalse)
14713  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14714  windows->icon.id);
14715  /*
14716  Initialize graphic context for icon window.
14717  */
14718  if (icon_pixel->annotate_context != (GC) NULL)
14719  (void) XFreeGC(display,icon_pixel->annotate_context);
14720  context_values.background=icon_pixel->background_color.pixel;
14721  context_values.foreground=icon_pixel->foreground_color.pixel;
14722  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14723  (size_t) (GCBackground | GCForeground),&context_values);
14724  if (icon_pixel->annotate_context == (GC) NULL)
14725  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14726  display_image->filename);
14727  windows->icon.annotate_context=icon_pixel->annotate_context;
14728  /*
14729  Initialize Image window.
14730  */
14731  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14732  &windows->image);
14733  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14734  if (resource_info->use_shared_memory == MagickFalse)
14735  windows->image.shared_memory=MagickFalse;
14736  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14737  {
14738  char
14739  *title;
14740 
14741  title=InterpretImageProperties(resource_info->image_info,display_image,
14742  resource_info->title,exception);
14743  (void) CloneString(&windows->image.name,title);
14744  (void) CloneString(&windows->image.icon_name,title);
14745  title=DestroyString(title);
14746  }
14747  else
14748  {
14749  char
14750  filename[MagickPathExtent],
14751  window_name[MagickPathExtent];
14752 
14753  /*
14754  Window name is the base of the filename.
14755  */
14756  GetPathComponent(display_image->magick_filename,TailPath,filename);
14757  if (display_image->scene == 0)
14758  (void) FormatLocaleString(window_name,MagickPathExtent,"%s: %s",
14759  MagickPackageName,filename);
14760  else
14761  (void) FormatLocaleString(window_name,MagickPathExtent,
14762  "%s: %s[scene: %.17g frames: %.17g]",MagickPackageName,filename,
14763  (double) display_image->scene,(double) GetImageListLength(
14764  display_image));
14765  (void) CloneString(&windows->image.name,window_name);
14766  (void) CloneString(&windows->image.icon_name,filename);
14767  }
14768  if (resource_info->immutable)
14769  windows->image.immutable=MagickTrue;
14770  windows->image.use_pixmap=resource_info->use_pixmap;
14771  windows->image.geometry=resource_info->image_geometry;
14772  (void) FormatLocaleString(geometry,MagickPathExtent,"%ux%u+0+0>!",
14773  XDisplayWidth(display,visual_info->screen),
14774  XDisplayHeight(display,visual_info->screen));
14775  geometry_info.width=display_image->columns;
14776  geometry_info.height=display_image->rows;
14777  geometry_info.x=0;
14778  geometry_info.y=0;
14779  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14780  &geometry_info.width,&geometry_info.height);
14781  windows->image.width=(unsigned int) geometry_info.width;
14782  windows->image.height=(unsigned int) geometry_info.height;
14783  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14784  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14785  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14786  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14787  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14788  resource_info,&windows->backdrop);
14789  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14790  {
14791  /*
14792  Initialize backdrop window.
14793  */
14794  windows->backdrop.x=0;
14795  windows->backdrop.y=0;
14796  (void) CloneString(&windows->backdrop.name,"Backdrop");
14797  windows->backdrop.flags=(size_t) (USSize | USPosition);
14798  windows->backdrop.width=(unsigned int)
14799  XDisplayWidth(display,visual_info->screen);
14800  windows->backdrop.height=(unsigned int)
14801  XDisplayHeight(display,visual_info->screen);
14802  windows->backdrop.border_width=0;
14803  windows->backdrop.immutable=MagickTrue;
14804  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14805  ButtonReleaseMask;
14806  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14807  StructureNotifyMask;
14808  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14809  manager_hints->icon_window=windows->icon.id;
14810  manager_hints->input=MagickTrue;
14811  manager_hints->initial_state=resource_info->iconic ? IconicState :
14812  NormalState;
14813  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14814  &windows->backdrop);
14815  if (resource_info->debug != MagickFalse)
14816  (void) LogMagickEvent(X11Event,GetMagickModule(),
14817  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14818  (void) XMapWindow(display,windows->backdrop.id);
14819  (void) XClearWindow(display,windows->backdrop.id);
14820  if (windows->image.id != (Window) NULL)
14821  {
14822  (void) XDestroyWindow(display,windows->image.id);
14823  windows->image.id=(Window) NULL;
14824  }
14825  /*
14826  Position image in the center the backdrop.
14827  */
14828  windows->image.flags|=USPosition;
14829  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14830  ((int) windows->image.width/2);
14831  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14832  ((int) windows->image.height/2);
14833  }
14834  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14835  manager_hints->icon_window=windows->icon.id;
14836  manager_hints->input=MagickTrue;
14837  manager_hints->initial_state=resource_info->iconic ? IconicState :
14838  NormalState;
14839  if (windows->group_leader.id != (Window) NULL)
14840  {
14841  /*
14842  Follow the leader.
14843  */
14844  manager_hints->flags|=WindowGroupHint;
14845  manager_hints->window_group=windows->group_leader.id;
14846  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14847  if (resource_info->debug != MagickFalse)
14848  (void) LogMagickEvent(X11Event,GetMagickModule(),
14849  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14850  }
14851  XMakeWindow(display,
14852  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14853  argv,argc,class_hints,manager_hints,&windows->image);
14854  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14855  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14856  if (windows->group_leader.id != (Window) NULL)
14857  (void) XSetTransientForHint(display,windows->image.id,
14858  windows->group_leader.id);
14859  if (resource_info->debug != MagickFalse)
14860  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14861  windows->image.id);
14862  /*
14863  Initialize Info widget.
14864  */
14865  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14866  &windows->info);
14867  (void) CloneString(&windows->info.name,"Info");
14868  (void) CloneString(&windows->info.icon_name,"Info");
14869  windows->info.border_width=1;
14870  windows->info.x=2;
14871  windows->info.y=2;
14872  windows->info.flags|=PPosition;
14873  windows->info.attributes.win_gravity=UnmapGravity;
14874  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14875  StructureNotifyMask;
14876  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14877  manager_hints->input=MagickFalse;
14878  manager_hints->initial_state=NormalState;
14879  manager_hints->window_group=windows->image.id;
14880  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14881  &windows->info);
14882  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14883  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14884  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14885  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14887  if (windows->image.mapped != MagickFalse)
14888  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14889  if (resource_info->debug != MagickFalse)
14890  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14891  windows->info.id);
14892  /*
14893  Initialize Command widget.
14894  */
14895  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14896  resource_info,&windows->command);
14897  windows->command.data=MagickMenus;
14898  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14899  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.command",
14900  resource_info->client_name);
14901  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14902  resource_name,"geometry",(char *) NULL);
14903  (void) CloneString(&windows->command.name,MagickTitle);
14904  windows->command.border_width=0;
14905  windows->command.flags|=PPosition;
14906  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14907  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14908  OwnerGrabButtonMask | StructureNotifyMask;
14909  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14910  manager_hints->input=MagickTrue;
14911  manager_hints->initial_state=NormalState;
14912  manager_hints->window_group=windows->image.id;
14913  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14914  &windows->command);
14915  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14916  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14917  HighlightHeight);
14918  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14919  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14920  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14921  if (windows->command.mapped != MagickFalse)
14922  (void) XMapRaised(display,windows->command.id);
14923  if (resource_info->debug != MagickFalse)
14924  (void) LogMagickEvent(X11Event,GetMagickModule(),
14925  "Window id: 0x%lx (command)",windows->command.id);
14926  /*
14927  Initialize Widget window.
14928  */
14929  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14930  resource_info,&windows->widget);
14931  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.widget",
14932  resource_info->client_name);
14933  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14934  resource_name,"geometry",(char *) NULL);
14935  windows->widget.border_width=0;
14936  windows->widget.flags|=PPosition;
14937  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14938  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14939  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14940  StructureNotifyMask;
14941  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14942  manager_hints->input=MagickTrue;
14943  manager_hints->initial_state=NormalState;
14944  manager_hints->window_group=windows->image.id;
14945  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14946  &windows->widget);
14947  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14948  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14949  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14950  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14951  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14952  if (resource_info->debug != MagickFalse)
14953  (void) LogMagickEvent(X11Event,GetMagickModule(),
14954  "Window id: 0x%lx (widget)",windows->widget.id);
14955  /*
14956  Initialize popup window.
14957  */
14958  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14959  resource_info,&windows->popup);
14960  windows->popup.border_width=0;
14961  windows->popup.flags|=PPosition;
14962  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14963  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14964  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14965  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14966  manager_hints->input=MagickTrue;
14967  manager_hints->initial_state=NormalState;
14968  manager_hints->window_group=windows->image.id;
14969  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14970  &windows->popup);
14971  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14972  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14973  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14974  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14975  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14976  if (resource_info->debug != MagickFalse)
14977  (void) LogMagickEvent(X11Event,GetMagickModule(),
14978  "Window id: 0x%lx (pop up)",windows->popup.id);
14979  /*
14980  Initialize Magnify window and cursor.
14981  */
14982  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14983  resource_info,&windows->magnify);
14984  if (resource_info->use_shared_memory == MagickFalse)
14985  windows->magnify.shared_memory=MagickFalse;
14986  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.magnify",
14987  resource_info->client_name);
14988  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14989  resource_name,"geometry",(char *) NULL);
14990  (void) FormatLocaleString(windows->magnify.name,MagickPathExtent,
14991  "Magnify %uX",resource_info->magnify);
14992  if (windows->magnify.cursor != (Cursor) NULL)
14993  (void) XFreeCursor(display,windows->magnify.cursor);
14994  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14995  map_info->colormap,resource_info->background_color,
14996  resource_info->foreground_color);
14997  if (windows->magnify.cursor == (Cursor) NULL)
14998  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14999  display_image->filename);
15000  windows->magnify.width=MagnifySize;
15001  windows->magnify.height=MagnifySize;
15002  windows->magnify.flags|=PPosition;
15003  windows->magnify.min_width=MagnifySize;
15004  windows->magnify.min_height=MagnifySize;
15005  windows->magnify.width_inc=MagnifySize;
15006  windows->magnify.height_inc=MagnifySize;
15007  windows->magnify.data=resource_info->magnify;
15008  windows->magnify.attributes.cursor=windows->magnify.cursor;
15009  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
15010  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
15011  StructureNotifyMask;
15012  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15013  manager_hints->input=MagickTrue;
15014  manager_hints->initial_state=NormalState;
15015  manager_hints->window_group=windows->image.id;
15016  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15017  &windows->magnify);
15018  if (resource_info->debug != MagickFalse)
15019  (void) LogMagickEvent(X11Event,GetMagickModule(),
15020  "Window id: 0x%lx (magnify)",windows->magnify.id);
15021  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15022  /*
15023  Initialize panning window.
15024  */
15025  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15026  resource_info,&windows->pan);
15027  (void) CloneString(&windows->pan.name,"Pan Icon");
15028  windows->pan.width=windows->icon.width;
15029  windows->pan.height=windows->icon.height;
15030  (void) FormatLocaleString(resource_name,MagickPathExtent,"%s.pan",
15031  resource_info->client_name);
15032  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15033  resource_name,"geometry",(char *) NULL);
15034  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15035  &windows->pan.width,&windows->pan.height);
15036  windows->pan.flags|=PPosition;
15037  windows->pan.immutable=MagickTrue;
15038  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15039  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15040  StructureNotifyMask;
15041  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15042  manager_hints->input=MagickFalse;
15043  manager_hints->initial_state=NormalState;
15044  manager_hints->window_group=windows->image.id;
15045  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15046  &windows->pan);
15047  if (resource_info->debug != MagickFalse)
15048  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15049  windows->pan.id);
15050  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15051  if (windows->info.mapped != MagickFalse)
15052  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15053  if ((windows->image.mapped == MagickFalse) ||
15054  (windows->backdrop.id != (Window) NULL))
15055  (void) XMapWindow(display,windows->image.id);
15056  /*
15057  Set our progress monitor and warning handlers.
15058  */
15059  if (warning_handler == (WarningHandler) NULL)
15060  {
15061  warning_handler=resource_info->display_warnings ?
15062  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15063  warning_handler=resource_info->display_warnings ?
15064  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15065  }
15066  /*
15067  Initialize Image and Magnify X images.
15068  */
15069  windows->image.x=0;
15070  windows->image.y=0;
15071  windows->magnify.shape=MagickFalse;
15072  width=(unsigned int) display_image->columns;
15073  height=(unsigned int) display_image->rows;
15074  if ((display_image->columns != width) || (display_image->rows != height))
15075  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15076  display_image->filename);
15077  status=XMakeImage(display,resource_info,&windows->image,display_image,
15078  width,height,exception);
15079  if (status == MagickFalse)
15080  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15081  display_image->filename);
15082  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15083  windows->magnify.width,windows->magnify.height,exception);
15084  if (status == MagickFalse)
15085  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15086  display_image->filename);
15087  if (windows->magnify.mapped != MagickFalse)
15088  (void) XMapRaised(display,windows->magnify.id);
15089  if (windows->pan.mapped != MagickFalse)
15090  (void) XMapRaised(display,windows->pan.id);
15091  windows->image.window_changes.width=(int) display_image->columns;
15092  windows->image.window_changes.height=(int) display_image->rows;
15093  (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15094  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15095  (void) XSync(display,MagickFalse);
15096  /*
15097  Respond to events.
15098  */
15099  delay=display_image->delay/(size_t)
15100  MagickMax(display_image->ticks_per_second,1L);
15101  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15102  update_time=0;
15103  if (resource_info->update != MagickFalse)
15104  {
15105  MagickBooleanType
15106  status;
15107 
15108  /*
15109  Determine when file data was last modified.
15110  */
15111  status=GetPathAttributes(display_image->filename,&attributes);
15112  if (status != MagickFalse)
15113  update_time=attributes.st_mtime;
15114  }
15115  *state&=(unsigned int) (~FormerImageState);
15116  *state&=(unsigned int) (~MontageImageState);
15117  *state&=(unsigned int) (~NextImageState);
15118  do
15119  {
15120  /*
15121  Handle a window event.
15122  */
15123  if (windows->image.mapped != MagickFalse)
15124  if ((display_image->delay != 0) || (resource_info->update != 0))
15125  {
15126  if (timer < GetMagickTime())
15127  {
15128  if (resource_info->update == MagickFalse)
15129  *state|=NextImageState | ExitState;
15130  else
15131  {
15132  MagickBooleanType
15133  status;
15134 
15135  /*
15136  Determine if image file was modified.
15137  */
15138  status=GetPathAttributes(display_image->filename,&attributes);
15139  if (status != MagickFalse)
15140  if (update_time != attributes.st_mtime)
15141  {
15142  /*
15143  Redisplay image.
15144  */
15145  (void) FormatLocaleString(
15146  resource_info->image_info->filename,MagickPathExtent,
15147  "%s:%s",display_image->magick,
15148  display_image->filename);
15149  nexus=ReadImage(resource_info->image_info,exception);
15150  if (nexus != (Image *) NULL)
15151  *state|=NextImageState | ExitState;
15152  }
15153  delay=display_image->delay/(size_t) MagickMax(
15154  display_image->ticks_per_second,1L);
15155  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15156  }
15157  }
15158  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15159  {
15160  /*
15161  Do not block if delay > 0.
15162  */
15163  XDelay(display,SuspendTime << 2);
15164  continue;
15165  }
15166  }
15167  timestamp=GetMagickTime();
15168  (void) XNextEvent(display,&event);
15169  if ((windows->image.stasis == MagickFalse) ||
15170  (windows->magnify.stasis == MagickFalse))
15171  {
15172  if ((GetMagickTime()-timestamp) > 0)
15173  {
15174  windows->image.stasis=MagickTrue;
15175  windows->magnify.stasis=MagickTrue;
15176  }
15177  }
15178  if (event.xany.window == windows->command.id)
15179  {
15180  /*
15181  Select a command from the Command widget.
15182  */
15183  id=XCommandWidget(display,windows,CommandMenu,&event);
15184  if (id < 0)
15185  continue;
15186  (void) CopyMagickString(command,CommandMenu[id],MagickPathExtent);
15187  display_command=CommandMenus[id];
15188  if (id < MagickMenus)
15189  {
15190  /*
15191  Select a command from a pop-up menu.
15192  */
15193  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15194  command);
15195  if (entry < 0)
15196  continue;
15197  (void) CopyMagickString(command,Menus[id][entry],MagickPathExtent);
15198  display_command=Commands[id][entry];
15199  }
15200  if (display_command != NullCommand)
15201  nexus=XMagickCommand(display,resource_info,windows,display_command,
15202  &display_image,exception);
15203  continue;
15204  }
15205  switch (event.type)
15206  {
15207  case ButtonPress:
15208  {
15209  if (resource_info->debug != MagickFalse)
15210  (void) LogMagickEvent(X11Event,GetMagickModule(),
15211  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15212  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15213  if ((event.xbutton.button == Button3) &&
15214  (event.xbutton.state & Mod1Mask))
15215  {
15216  /*
15217  Convert Alt-Button3 to Button2.
15218  */
15219  event.xbutton.button=Button2;
15220  event.xbutton.state&=(unsigned int) (~Mod1Mask);
15221  }
15222  if (event.xbutton.window == windows->backdrop.id)
15223  {
15224  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15225  event.xbutton.time);
15226  break;
15227  }
15228  if (event.xbutton.window == windows->image.id)
15229  {
15230  switch (event.xbutton.button)
15231  {
15232  case Button1:
15233  {
15234  if (resource_info->immutable)
15235  {
15236  /*
15237  Select a command from the Virtual menu.
15238  */
15239  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15240  command);
15241  if (entry >= 0)
15242  nexus=XMagickCommand(display,resource_info,windows,
15243  VirtualCommands[entry],&display_image,exception);
15244  break;
15245  }
15246  /*
15247  Map/unmap Command widget.
15248  */
15249  if (windows->command.mapped != MagickFalse)
15250  (void) XWithdrawWindow(display,windows->command.id,
15251  windows->command.screen);
15252  else
15253  {
15254  (void) XCommandWidget(display,windows,CommandMenu,
15255  (XEvent *) NULL);
15256  (void) XMapRaised(display,windows->command.id);
15257  }
15258  break;
15259  }
15260  case Button2:
15261  {
15262  /*
15263  User pressed the image magnify button.
15264  */
15265  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15266  &display_image,exception);
15267  XMagnifyImage(display,windows,&event,exception);
15268  break;
15269  }
15270  case Button3:
15271  {
15272  if (resource_info->immutable)
15273  {
15274  /*
15275  Select a command from the Virtual menu.
15276  */
15277  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15278  command);
15279  if (entry >= 0)
15280  nexus=XMagickCommand(display,resource_info,windows,
15281  VirtualCommands[entry],&display_image,exception);
15282  break;
15283  }
15284  if (display_image->montage != (char *) NULL)
15285  {
15286  /*
15287  Open or delete a tile from a visual image directory.
15288  */
15289  nexus=XTileImage(display,resource_info,windows,
15290  display_image,&event,exception);
15291  if (nexus != (Image *) NULL)
15292  *state|=MontageImageState | NextImageState | ExitState;
15293  vid_info.x=(short int) windows->image.x;
15294  vid_info.y=(short int) windows->image.y;
15295  break;
15296  }
15297  /*
15298  Select a command from the Short Cuts menu.
15299  */
15300  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15301  command);
15302  if (entry >= 0)
15303  nexus=XMagickCommand(display,resource_info,windows,
15304  ShortCutsCommands[entry],&display_image,exception);
15305  break;
15306  }
15307  case Button4:
15308  {
15309  /*
15310  Wheel up.
15311  */
15312  XTranslateImage(display,windows,*image,XK_Up);
15313  break;
15314  }
15315  case Button5:
15316  {
15317  /*
15318  Wheel down.
15319  */
15320  XTranslateImage(display,windows,*image,XK_Down);
15321  break;
15322  }
15323  default:
15324  break;
15325  }
15326  break;
15327  }
15328  if (event.xbutton.window == windows->magnify.id)
15329  {
15330  const char
15331  *const MagnifyMenu[] =
15332  {
15333  "2",
15334  "4",
15335  "5",
15336  "6",
15337  "7",
15338  "8",
15339  "9",
15340  "3",
15341  (char *) NULL,
15342  };
15343 
15344  int
15345  factor;
15346 
15347  static KeySym
15348  MagnifyCommands[] =
15349  {
15350  XK_2,
15351  XK_4,
15352  XK_5,
15353  XK_6,
15354  XK_7,
15355  XK_8,
15356  XK_9,
15357  XK_3
15358  };
15359 
15360  /*
15361  Select a magnify factor from the pop-up menu.
15362  */
15363  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15364  if (factor >= 0)
15365  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15366  exception);
15367  break;
15368  }
15369  if (event.xbutton.window == windows->pan.id)
15370  {
15371  switch (event.xbutton.button)
15372  {
15373  case Button4:
15374  {
15375  /*
15376  Wheel up.
15377  */
15378  XTranslateImage(display,windows,*image,XK_Up);
15379  break;
15380  }
15381  case Button5:
15382  {
15383  /*
15384  Wheel down.
15385  */
15386  XTranslateImage(display,windows,*image,XK_Down);
15387  break;
15388  }
15389  default:
15390  {
15391  XPanImage(display,windows,&event,exception);
15392  break;
15393  }
15394  }
15395  break;
15396  }
15397  delay=display_image->delay/(size_t)
15398  MagickMax(display_image->ticks_per_second,1L);
15399  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15400  break;
15401  }
15402  case ButtonRelease:
15403  {
15404  if (resource_info->debug != MagickFalse)
15405  (void) LogMagickEvent(X11Event,GetMagickModule(),
15406  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15407  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15408  break;
15409  }
15410  case ClientMessage:
15411  {
15412  if (resource_info->debug != MagickFalse)
15413  (void) LogMagickEvent(X11Event,GetMagickModule(),
15414  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15415  event.xclient.message_type,event.xclient.format,(unsigned long)
15416  event.xclient.data.l[0]);
15417  if (event.xclient.message_type == windows->im_protocols)
15418  {
15419  if (*event.xclient.data.l == (long) windows->im_update_widget)
15420  {
15421  (void) CloneString(&windows->command.name,MagickTitle);
15422  windows->command.data=MagickMenus;
15423  (void) XCommandWidget(display,windows,CommandMenu,
15424  (XEvent *) NULL);
15425  break;
15426  }
15427  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15428  {
15429  /*
15430  Update graphic context and window colormap.
15431  */
15432  for (i=0; i < (int) number_windows; i++)
15433  {
15434  if (magick_windows[i]->id == windows->icon.id)
15435  continue;
15436  context_values.background=pixel->background_color.pixel;
15437  context_values.foreground=pixel->foreground_color.pixel;
15438  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15439  context_mask,&context_values);
15440  (void) XChangeGC(display,magick_windows[i]->widget_context,
15441  context_mask,&context_values);
15442  context_values.background=pixel->foreground_color.pixel;
15443  context_values.foreground=pixel->background_color.pixel;
15444  context_values.plane_mask=context_values.background ^
15445  context_values.foreground;
15446  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15447  (size_t) (context_mask | GCPlaneMask),
15448  &context_values);
15449  magick_windows[i]->attributes.background_pixel=
15450  pixel->background_color.pixel;
15451  magick_windows[i]->attributes.border_pixel=
15452  pixel->border_color.pixel;
15453  magick_windows[i]->attributes.colormap=map_info->colormap;
15454  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15455  (unsigned long) magick_windows[i]->mask,
15456  &magick_windows[i]->attributes);
15457  }
15458  if (windows->pan.mapped != MagickFalse)
15459  {
15460  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15461  windows->pan.pixmap);
15462  (void) XClearWindow(display,windows->pan.id);
15463  XDrawPanRectangle(display,windows);
15464  }
15465  if (windows->backdrop.id != (Window) NULL)
15466  (void) XInstallColormap(display,map_info->colormap);
15467  break;
15468  }
15469  if (*event.xclient.data.l == (long) windows->im_former_image)
15470  {
15471  *state|=FormerImageState | ExitState;
15472  break;
15473  }
15474  if (*event.xclient.data.l == (long) windows->im_next_image)
15475  {
15476  *state|=NextImageState | ExitState;
15477  break;
15478  }
15479  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15480  {
15481  *state|=RetainColorsState;
15482  break;
15483  }
15484  if (*event.xclient.data.l == (long) windows->im_exit)
15485  {
15486  *state|=ExitState;
15487  break;
15488  }
15489  break;
15490  }
15491  if (event.xclient.message_type == windows->dnd_protocols)
15492  {
15493  Atom
15494  selection,
15495  type;
15496 
15497  int
15498  format,
15499  status;
15500 
15501  unsigned char
15502  *data;
15503 
15504  unsigned long
15505  after,
15506  length;
15507 
15508  /*
15509  Display image named by the Drag-and-Drop selection.
15510  */
15511  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15512  break;
15513  selection=XInternAtom(display,"DndSelection",MagickFalse);
15514  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15515  MagickPathExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15516  &length,&after,&data);
15517  if ((status != Success) || (length == 0))
15518  break;
15519  if (*event.xclient.data.l == 2)
15520  {
15521  /*
15522  Offix DND.
15523  */
15524  (void) CopyMagickString(resource_info->image_info->filename,
15525  (char *) data,MagickPathExtent);
15526  }
15527  else
15528  {
15529  /*
15530  XDND.
15531  */
15532  if (strncmp((char *) data, "file:", 5) != 0)
15533  {
15534  (void) XFree((void *) data);
15535  break;
15536  }
15537  (void) CopyMagickString(resource_info->image_info->filename,
15538  ((char *) data)+5,MagickPathExtent);
15539  }
15540  nexus=ReadImage(resource_info->image_info,exception);
15541  CatchException(exception);
15542  if (nexus != (Image *) NULL)
15543  *state|=NextImageState | ExitState;
15544  (void) XFree((void *) data);
15545  break;
15546  }
15547  /*
15548  If client window delete message, exit.
15549  */
15550  if (event.xclient.message_type != windows->wm_protocols)
15551  break;
15552  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15553  break;
15554  (void) XWithdrawWindow(display,event.xclient.window,
15555  visual_info->screen);
15556  if (event.xclient.window == windows->image.id)
15557  {
15558  *state|=ExitState;
15559  break;
15560  }
15561  if (event.xclient.window == windows->pan.id)
15562  {
15563  /*
15564  Restore original image size when pan window is deleted.
15565  */
15566  windows->image.window_changes.width=windows->image.ximage->width;
15567  windows->image.window_changes.height=windows->image.ximage->height;
15568  (void) XConfigureImage(display,resource_info,windows,
15569  display_image,exception);
15570  }
15571  break;
15572  }
15573  case ConfigureNotify:
15574  {
15575  if (resource_info->debug != MagickFalse)
15576  (void) LogMagickEvent(X11Event,GetMagickModule(),
15577  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15578  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15579  event.xconfigure.y,event.xconfigure.send_event);
15580  if (event.xconfigure.window == windows->image.id)
15581  {
15582  /*
15583  Image window has a new configuration.
15584  */
15585  if (event.xconfigure.send_event != 0)
15586  {
15587  XWindowChanges
15588  window_changes;
15589 
15590  /*
15591  Position the transient windows relative of the Image window.
15592  */
15593  if (windows->command.geometry == (char *) NULL)
15594  if (windows->command.mapped == MagickFalse)
15595  {
15596  windows->command.x=event.xconfigure.x-(int)
15597  windows->command.width-25;
15598  windows->command.y=event.xconfigure.y;
15599  XConstrainWindowPosition(display,&windows->command);
15600  window_changes.x=windows->command.x;
15601  window_changes.y=windows->command.y;
15602  (void) XReconfigureWMWindow(display,windows->command.id,
15603  windows->command.screen,(unsigned int) (CWX | CWY),
15604  &window_changes);
15605  }
15606  if (windows->widget.geometry == (char *) NULL)
15607  if (windows->widget.mapped == MagickFalse)
15608  {
15609  windows->widget.x=event.xconfigure.x+
15610  event.xconfigure.width/10;
15611  windows->widget.y=event.xconfigure.y+
15612  event.xconfigure.height/10;
15613  XConstrainWindowPosition(display,&windows->widget);
15614  window_changes.x=windows->widget.x;
15615  window_changes.y=windows->widget.y;
15616  (void) XReconfigureWMWindow(display,windows->widget.id,
15617  windows->widget.screen,(unsigned int) (CWX | CWY),
15618  &window_changes);
15619  }
15620  if (windows->magnify.geometry == (char *) NULL)
15621  if (windows->magnify.mapped == MagickFalse)
15622  {
15623  windows->magnify.x=event.xconfigure.x+
15624  event.xconfigure.width+25;
15625  windows->magnify.y=event.xconfigure.y;
15626  XConstrainWindowPosition(display,&windows->magnify);
15627  window_changes.x=windows->magnify.x;
15628  window_changes.y=windows->magnify.y;
15629  (void) XReconfigureWMWindow(display,windows->magnify.id,
15630  windows->magnify.screen,(unsigned int) (CWX | CWY),
15631  &window_changes);
15632  }
15633  if (windows->pan.geometry == (char *) NULL)
15634  if (windows->pan.mapped == MagickFalse)
15635  {
15636  windows->pan.x=event.xconfigure.x+(int)
15637  event.xconfigure.width+25;
15638  windows->pan.y=event.xconfigure.y+(int)
15639  windows->magnify.height+50;
15640  XConstrainWindowPosition(display,&windows->pan);
15641  window_changes.x=windows->pan.x;
15642  window_changes.y=windows->pan.y;
15643  (void) XReconfigureWMWindow(display,windows->pan.id,
15644  windows->pan.screen,(unsigned int) (CWX | CWY),
15645  &window_changes);
15646  }
15647  }
15648  if ((event.xconfigure.width == (int) windows->image.width) &&
15649  (event.xconfigure.height == (int) windows->image.height))
15650  break;
15651  windows->image.width=(unsigned int) event.xconfigure.width;
15652  windows->image.height=(unsigned int) event.xconfigure.height;
15653  windows->image.x=0;
15654  windows->image.y=0;
15655  if (display_image->montage != (char *) NULL)
15656  {
15657  windows->image.x=vid_info.x;
15658  windows->image.y=vid_info.y;
15659  }
15660  if (windows->image.mapped != MagickFalse &&
15661  windows->image.stasis != MagickFalse)
15662  {
15663  /*
15664  Update image window configuration.
15665  */
15666  windows->image.window_changes.width=event.xconfigure.width;
15667  windows->image.window_changes.height=event.xconfigure.height;
15668  (void) XConfigureImage(display,resource_info,windows,
15669  display_image,exception);
15670  }
15671  /*
15672  Update pan window configuration.
15673  */
15674  if ((event.xconfigure.width < windows->image.ximage->width) ||
15675  (event.xconfigure.height < windows->image.ximage->height))
15676  {
15677  (void) XMapRaised(display,windows->pan.id);
15678  XDrawPanRectangle(display,windows);
15679  }
15680  else
15681  if (windows->pan.mapped != MagickFalse)
15682  (void) XWithdrawWindow(display,windows->pan.id,
15683  windows->pan.screen);
15684  break;
15685  }
15686  if (event.xconfigure.window == windows->magnify.id)
15687  {
15688  unsigned int
15689  magnify;
15690 
15691  /*
15692  Magnify window has a new configuration.
15693  */
15694  windows->magnify.width=(unsigned int) event.xconfigure.width;
15695  windows->magnify.height=(unsigned int) event.xconfigure.height;
15696  if (windows->magnify.mapped == MagickFalse)
15697  break;
15698  magnify=1;
15699  while ((int) magnify <= event.xconfigure.width)
15700  magnify<<=1;
15701  while ((int) magnify <= event.xconfigure.height)
15702  magnify<<=1;
15703  magnify>>=1;
15704  if (((int) magnify != event.xconfigure.width) ||
15705  ((int) magnify != event.xconfigure.height))
15706  {
15707  window_changes.width=(int) magnify;
15708  window_changes.height=(int) magnify;
15709  (void) XReconfigureWMWindow(display,windows->magnify.id,
15710  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15711  &window_changes);
15712  break;
15713  }
15714  if (windows->magnify.mapped != MagickFalse &&
15715  windows->magnify.stasis != MagickFalse)
15716  {
15717  status=XMakeImage(display,resource_info,&windows->magnify,
15718  display_image,windows->magnify.width,windows->magnify.height,
15719  exception);
15720  XMakeMagnifyImage(display,windows,exception);
15721  }
15722  break;
15723  }
15724  if (windows->magnify.mapped != MagickFalse &&
15725  (event.xconfigure.window == windows->pan.id))
15726  {
15727  /*
15728  Pan icon window has a new configuration.
15729  */
15730  if (event.xconfigure.send_event != 0)
15731  {
15732  windows->pan.x=event.xconfigure.x;
15733  windows->pan.y=event.xconfigure.y;
15734  }
15735  windows->pan.width=(unsigned int) event.xconfigure.width;
15736  windows->pan.height=(unsigned int) event.xconfigure.height;
15737  break;
15738  }
15739  if (event.xconfigure.window == windows->icon.id)
15740  {
15741  /*
15742  Icon window has a new configuration.
15743  */
15744  windows->icon.width=(unsigned int) event.xconfigure.width;
15745  windows->icon.height=(unsigned int) event.xconfigure.height;
15746  break;
15747  }
15748  break;
15749  }
15750  case DestroyNotify:
15751  {
15752  /*
15753  Group leader has exited.
15754  */
15755  if (resource_info->debug != MagickFalse)
15756  (void) LogMagickEvent(X11Event,GetMagickModule(),
15757  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15758  if (event.xdestroywindow.window == windows->group_leader.id)
15759  {
15760  *state|=ExitState;
15761  break;
15762  }
15763  break;
15764  }
15765  case EnterNotify:
15766  {
15767  /*
15768  Selectively install colormap.
15769  */
15770  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15771  if (event.xcrossing.mode != NotifyUngrab)
15772  XInstallColormap(display,map_info->colormap);
15773  break;
15774  }
15775  case Expose:
15776  {
15777  if (resource_info->debug != MagickFalse)
15778  (void) LogMagickEvent(X11Event,GetMagickModule(),
15779  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15780  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15781  event.xexpose.y);
15782  /*
15783  Refresh windows that are now exposed.
15784  */
15785  if ((event.xexpose.window == windows->image.id) &&
15786  windows->image.mapped != MagickFalse)
15787  {
15788  XRefreshWindow(display,&windows->image,&event);
15789  delay=display_image->delay/(size_t) MagickMax(
15790  display_image->ticks_per_second,1L);
15791  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15792  break;
15793  }
15794  if ((event.xexpose.window == windows->magnify.id) &&
15795  windows->magnify.mapped != MagickFalse)
15796  {
15797  XMakeMagnifyImage(display,windows,exception);
15798  break;
15799  }
15800  if (event.xexpose.window == windows->pan.id)
15801  {
15802  XDrawPanRectangle(display,windows);
15803  break;
15804  }
15805  if (event.xexpose.window == windows->icon.id)
15806  {
15807  XRefreshWindow(display,&windows->icon,&event);
15808  break;
15809  }
15810  break;
15811  }
15812  case KeyPress:
15813  {
15814  int
15815  length;
15816 
15817  /*
15818  Respond to a user key press.
15819  */
15820  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15821  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15822  *(command+length)='\0';
15823  if (resource_info->debug != MagickFalse)
15824  (void) LogMagickEvent(X11Event,GetMagickModule(),
15825  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15826  key_symbol,command);
15827  if (event.xkey.window == windows->image.id)
15828  {
15829  display_command=XImageWindowCommand(display,resource_info,windows,
15830  event.xkey.state,key_symbol,&display_image,exception);
15831  if (display_command != NullCommand)
15832  nexus=XMagickCommand(display,resource_info,windows,
15833  display_command,&display_image,exception);
15834  }
15835  if (event.xkey.window == windows->magnify.id)
15836  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15837  exception);
15838  if (event.xkey.window == windows->pan.id)
15839  {
15840  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15841  (void) XWithdrawWindow(display,windows->pan.id,
15842  windows->pan.screen);
15843  else
15844  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15845  XTextViewHelp(display,resource_info,windows,MagickFalse,
15846  "Help Viewer - Image Pan",ImagePanHelp);
15847  else
15848  XTranslateImage(display,windows,*image,key_symbol);
15849  }
15850  delay=display_image->delay/(size_t) MagickMax(
15851  display_image->ticks_per_second,1L);
15852  timer=GetMagickTime()+(time_t) (delay == 0 ? 1 : delay)+1;
15853  break;
15854  }
15855  case KeyRelease:
15856  {
15857  /*
15858  Respond to a user key release.
15859  */
15860  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15861  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15862  if (resource_info->debug != MagickFalse)
15863  (void) LogMagickEvent(X11Event,GetMagickModule(),
15864  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15865  break;
15866  }
15867  case LeaveNotify:
15868  {
15869  /*
15870  Selectively uninstall colormap.
15871  */
15872  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15873  if (event.xcrossing.mode != NotifyUngrab)
15874  XUninstallColormap(display,map_info->colormap);
15875  break;
15876  }
15877  case MapNotify:
15878  {
15879  if (resource_info->debug != MagickFalse)
15880  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15881  event.xmap.window);
15882  if (event.xmap.window == windows->backdrop.id)
15883  {
15884  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15885  CurrentTime);
15886  windows->backdrop.mapped=MagickTrue;
15887  break;
15888  }
15889  if (event.xmap.window == windows->image.id)
15890  {
15891  if (windows->backdrop.id != (Window) NULL)
15892  (void) XInstallColormap(display,map_info->colormap);
15893  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15894  {
15895  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15896  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15897  }
15898  if (((int) windows->image.width < windows->image.ximage->width) ||
15899  ((int) windows->image.height < windows->image.ximage->height))
15900  (void) XMapRaised(display,windows->pan.id);
15901  windows->image.mapped=MagickTrue;
15902  break;
15903  }
15904  if (event.xmap.window == windows->magnify.id)
15905  {
15906  XMakeMagnifyImage(display,windows,exception);
15907  windows->magnify.mapped=MagickTrue;
15908  (void) XWithdrawWindow(display,windows->info.id,
15909  windows->info.screen);
15910  break;
15911  }
15912  if (event.xmap.window == windows->pan.id)
15913  {
15914  XMakePanImage(display,resource_info,windows,display_image,
15915  exception);
15916  windows->pan.mapped=MagickTrue;
15917  break;
15918  }
15919  if (event.xmap.window == windows->info.id)
15920  {
15921  windows->info.mapped=MagickTrue;
15922  break;
15923  }
15924  if (event.xmap.window == windows->icon.id)
15925  {
15926  MagickBooleanType
15927  taint;
15928 
15929  /*
15930  Create an icon image.
15931  */
15932  taint=display_image->taint;
15933  XMakeStandardColormap(display,icon_visual,icon_resources,
15934  display_image,icon_map,icon_pixel,exception);
15935  (void) XMakeImage(display,icon_resources,&windows->icon,
15936  display_image,windows->icon.width,windows->icon.height,
15937  exception);
15938  display_image->taint=taint;
15939  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15940  windows->icon.pixmap);
15941  (void) XClearWindow(display,windows->icon.id);
15942  (void) XWithdrawWindow(display,windows->info.id,
15943  windows->info.screen);
15944  windows->icon.mapped=MagickTrue;
15945  break;
15946  }
15947  if (event.xmap.window == windows->command.id)
15948  {
15949  windows->command.mapped=MagickTrue;
15950  break;
15951  }
15952  if (event.xmap.window == windows->popup.id)
15953  {
15954  windows->popup.mapped=MagickTrue;
15955  break;
15956  }
15957  if (event.xmap.window == windows->widget.id)
15958  {
15959  windows->widget.mapped=MagickTrue;
15960  break;
15961  }
15962  break;
15963  }
15964  case MappingNotify:
15965  {
15966  (void) XRefreshKeyboardMapping(&event.xmapping);
15967  break;
15968  }
15969  case NoExpose:
15970  break;
15971  case PropertyNotify:
15972  {
15973  Atom
15974  type;
15975 
15976  int
15977  format,
15978  status;
15979 
15980  unsigned char
15981  *data;
15982 
15983  unsigned long
15984  after,
15985  length;
15986 
15987  if (resource_info->debug != MagickFalse)
15988  (void) LogMagickEvent(X11Event,GetMagickModule(),
15989  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15990  event.xproperty.atom,event.xproperty.state);
15991  if (event.xproperty.atom != windows->im_remote_command)
15992  break;
15993  /*
15994  Display image named by the remote command protocol.
15995  */
15996  status=XGetWindowProperty(display,event.xproperty.window,
15997  event.xproperty.atom,0L,(long) MagickPathExtent,MagickFalse,(Atom)
15998  AnyPropertyType,&type,&format,&length,&after,&data);
15999  if ((status != Success) || (length == 0))
16000  break;
16001  if (LocaleCompare((char *) data,"-quit") == 0)
16002  {
16003  XClientMessage(display,windows->image.id,windows->im_protocols,
16004  windows->im_exit,CurrentTime);
16005  (void) XFree((void *) data);
16006  break;
16007  }
16008  (void) CopyMagickString(resource_info->image_info->filename,
16009  (char *) data,MagickPathExtent);
16010  (void) XFree((void *) data);
16011  nexus=ReadImage(resource_info->image_info,exception);
16012  CatchException(exception);
16013  if (nexus != (Image *) NULL)
16014  *state|=NextImageState | ExitState;
16015  break;
16016  }
16017  case ReparentNotify:
16018  {
16019  if (resource_info->debug != MagickFalse)
16020  (void) LogMagickEvent(X11Event,GetMagickModule(),
16021  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
16022  event.xreparent.window);
16023  break;
16024  }
16025  case UnmapNotify:
16026  {
16027  if (resource_info->debug != MagickFalse)
16028  (void) LogMagickEvent(X11Event,GetMagickModule(),
16029  "Unmap Notify: 0x%lx",event.xunmap.window);
16030  if (event.xunmap.window == windows->backdrop.id)
16031  {
16032  windows->backdrop.mapped=MagickFalse;
16033  break;
16034  }
16035  if (event.xunmap.window == windows->image.id)
16036  {
16037  windows->image.mapped=MagickFalse;
16038  break;
16039  }
16040  if (event.xunmap.window == windows->magnify.id)
16041  {
16042  windows->magnify.mapped=MagickFalse;
16043  break;
16044  }
16045  if (event.xunmap.window == windows->pan.id)
16046  {
16047  windows->pan.mapped=MagickFalse;
16048  break;
16049  }
16050  if (event.xunmap.window == windows->info.id)
16051  {
16052  windows->info.mapped=MagickFalse;
16053  break;
16054  }
16055  if (event.xunmap.window == windows->icon.id)
16056  {
16057  if (map_info->colormap == icon_map->colormap)
16058  XConfigureImageColormap(display,resource_info,windows,
16059  display_image,exception);
16060  (void) XFreeStandardColormap(display,icon_visual,icon_map,
16061  icon_pixel);
16062  windows->icon.mapped=MagickFalse;
16063  break;
16064  }
16065  if (event.xunmap.window == windows->command.id)
16066  {
16067  windows->command.mapped=MagickFalse;
16068  break;
16069  }
16070  if (event.xunmap.window == windows->popup.id)
16071  {
16072  if (windows->backdrop.id != (Window) NULL)
16073  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16074  CurrentTime);
16075  windows->popup.mapped=MagickFalse;
16076  break;
16077  }
16078  if (event.xunmap.window == windows->widget.id)
16079  {
16080  if (windows->backdrop.id != (Window) NULL)
16081  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16082  CurrentTime);
16083  windows->widget.mapped=MagickFalse;
16084  break;
16085  }
16086  break;
16087  }
16088  default:
16089  {
16090  if (resource_info->debug != MagickFalse)
16091  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16092  event.type);
16093  break;
16094  }
16095  }
16096  } while (!(*state & ExitState));
16097  if ((*state & ExitState) == 0)
16098  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16099  &display_image,exception);
16100  else
16101  if (resource_info->confirm_edit != MagickFalse)
16102  {
16103  /*
16104  Query user if image has changed.
16105  */
16106  if ((resource_info->immutable == MagickFalse) &&
16107  display_image->taint != MagickFalse)
16108  {
16109  int
16110  status;
16111 
16112  status=XConfirmWidget(display,windows,"Your image changed.",
16113  "Do you want to save it");
16114  if (status == 0)
16115  *state&=(unsigned int) (~ExitState);
16116  else
16117  if (status > 0)
16118  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16119  &display_image,exception);
16120  }
16121  }
16122  if ((windows->visual_info->klass == GrayScale) ||
16123  (windows->visual_info->klass == PseudoColor) ||
16124  (windows->visual_info->klass == DirectColor))
16125  {
16126  /*
16127  Withdraw pan and Magnify window.
16128  */
16129  if (windows->info.mapped != MagickFalse)
16130  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16131  if (windows->magnify.mapped != MagickFalse)
16132  (void) XWithdrawWindow(display,windows->magnify.id,
16133  windows->magnify.screen);
16134  if (windows->command.mapped != MagickFalse)
16135  (void) XWithdrawWindow(display,windows->command.id,
16136  windows->command.screen);
16137  }
16138  if (windows->pan.mapped != MagickFalse)
16139  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16140  if (resource_info->backdrop == MagickFalse)
16141  if (windows->backdrop.mapped)
16142  {
16143  (void) XWithdrawWindow(display,windows->backdrop.id,
16144  windows->backdrop.screen);
16145  (void) XDestroyWindow(display,windows->backdrop.id);
16146  windows->backdrop.id=(Window) NULL;
16147  (void) XWithdrawWindow(display,windows->image.id,
16148  windows->image.screen);
16149  (void) XDestroyWindow(display,windows->image.id);
16150  windows->image.id=(Window) NULL;
16151  }
16152  XSetCursorState(display,windows,MagickTrue);
16153  XCheckRefreshWindows(display,windows);
16154  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16155  *state&=(unsigned int) (~ExitState);
16156  if (*state & ExitState)
16157  {
16158  /*
16159  Free Standard Colormap.
16160  */
16161  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16162  if (resource_info->map_type == (char *) NULL)
16163  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16164  /*
16165  Free X resources.
16166  */
16167  if (resource_info->copy_image != (Image *) NULL)
16168  {
16169  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16170  resource_info->copy_image=NewImageList();
16171  }
16172  DestroyXResources();
16173  }
16174  (void) XSync(display,MagickFalse);
16175  /*
16176  Restore our progress monitor and warning handlers.
16177  */
16178  (void) SetErrorHandler(warning_handler);
16179  (void) SetWarningHandler(warning_handler);
16180  /*
16181  Change to home directory.
16182  */
16183  directory=getcwd(working_directory,MagickPathExtent);
16184  (void) directory;
16185  {
16186  int
16187  status;
16188 
16189  if (*resource_info->home_directory == '\0')
16190  (void) CopyMagickString(resource_info->home_directory,".",MagickPathExtent);
16191  status=chdir(resource_info->home_directory);
16192  if (status == -1)
16193  (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16194  "UnableToOpenFile","%s",resource_info->home_directory);
16195  }
16196  *image=display_image;
16197  return(nexus);
16198 }
16199 #else
16200 ␌
16201 /*
16202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16203 % %
16204 % %
16205 % %
16206 + D i s p l a y I m a g e s %
16207 % %
16208 % %
16209 % %
16210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16211 %
16212 % DisplayImages() displays an image sequence to any X window screen. It
16213 % returns a value other than 0 if successful. Check the exception member
16214 % of image to determine the reason for any failure.
16215 %
16216 % The format of the DisplayImages method is:
16217 %
16218 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16219 % Image *images,ExceptionInfo *exception)
16220 %
16221 % A description of each parameter follows:
16222 %
16223 % o image_info: the image info.
16224 %
16225 % o image: the image.
16226 %
16227 % o exception: return any errors or warnings in this structure.
16228 %
16229 */
16230 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16231  Image *image,ExceptionInfo *exception)
16232 {
16233  assert(image_info != (const ImageInfo *) NULL);
16234  assert(image_info->signature == MagickCoreSignature);
16235  assert(image != (Image *) NULL);
16236  assert(image->signature == MagickCoreSignature);
16237  (void) image_info;
16238  if (IsEventLogging() != MagickFalse)
16239  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16240  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16241  "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16242  return(MagickFalse);
16243 }
16244 ␌
16245 /*
16246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16247 % %
16248 % %
16249 % %
16250 + R e m o t e D i s p l a y C o m m a n d %
16251 % %
16252 % %
16253 % %
16254 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16255 %
16256 % RemoteDisplayCommand() encourages a remote display program to display the
16257 % specified image filename.
16258 %
16259 % The format of the RemoteDisplayCommand method is:
16260 %
16261 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16262 % const char *window,const char *filename,ExceptionInfo *exception)
16263 %
16264 % A description of each parameter follows:
16265 %
16266 % o image_info: the image info.
16267 %
16268 % o window: Specifies the name or id of an X window.
16269 %
16270 % o filename: the name of the image filename to display.
16271 %
16272 % o exception: return any errors or warnings in this structure.
16273 %
16274 */
16275 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16276  const char *window,const char *filename,ExceptionInfo *exception)
16277 {
16278  assert(image_info != (const ImageInfo *) NULL);
16279  assert(image_info->signature == MagickCoreSignature);
16280  assert(filename != (char *) NULL);
16281  if (IsEventLogging() != MagickFalse)
16282  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16283  (void) window;
16284  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16285  "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16286  return(MagickFalse);
16287 }
16288 #endif
Definition: image.h:132