MagickCore  6.9.12-67
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
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-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40  Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/attribute.h"
45 #include "magick/blob.h"
46 #include "magick/cache.h"
47 #include "magick/channel.h"
48 #include "magick/client.h"
49 #include "magick/color.h"
50 #include "magick/colorspace.h"
51 #include "magick/composite.h"
52 #include "magick/constitute.h"
53 #include "magick/decorate.h"
54 #include "magick/delegate.h"
55 #include "magick/display.h"
56 #include "magick/display-private.h"
57 #include "magick/distort.h"
58 #include "magick/draw.h"
59 #include "magick/effect.h"
60 #include "magick/enhance.h"
61 #include "magick/exception.h"
62 #include "magick/exception-private.h"
63 #include "magick/fx.h"
64 #include "magick/geometry.h"
65 #include "magick/image.h"
66 #include "magick/image-private.h"
67 #include "magick/list.h"
68 #include "magick/locale-private.h"
69 #include "magick/log.h"
70 #include "magick/magick.h"
71 #include "magick/memory_.h"
72 #include "magick/monitor.h"
73 #include "magick/monitor-private.h"
74 #include "magick/montage.h"
75 #include "magick/nt-base-private.h"
76 #include "magick/option.h"
77 #include "magick/paint.h"
78 #include "magick/pixel.h"
79 #include "magick/pixel-private.h"
80 #include "magick/property.h"
81 #include "magick/quantum.h"
82 #include "magick/resize.h"
83 #include "magick/resource_.h"
84 #include "magick/shear.h"
85 #include "magick/segment.h"
86 #include "magick/statistic.h"
87 #include "magick/string_.h"
88 #include "magick/string-private.h"
89 #include "magick/timer-private.h"
90 #include "magick/transform.h"
91 #include "magick/threshold.h"
92 #include "magick/utility.h"
93 #include "magick/utility-private.h"
94 #include "magick/version.h"
95 #include "magick/visual-effects.h"
96 #include "magick/widget.h"
97 #include "magick/xwindow-private.h"
98 
99 #if defined(MAGICKCORE_X11_DELEGATE)
100 /*
101  Define declarations.
102 */
103 #define MaxColors MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
104 
105 /*
106  Constant declarations.
107 */
108 static const unsigned char
109  HighlightBitmap[8] =
110  {
111  0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
112  },
113  OpaqueBitmap[8] =
114  {
115  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
116  },
117  ShadowBitmap[8] =
118  {
119  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
120  };
121 
122 /*
123  Help widget declarations.
124 */
125 static const char
126  ImageAnnotateHelp[] =
127  {
128  "In annotate mode, the Command widget has these options:\n"
129  "\n"
130  " Font Name\n"
131  " fixed\n"
132  " variable\n"
133  " 5x8\n"
134  " 6x10\n"
135  " 7x13bold\n"
136  " 8x13bold\n"
137  " 9x15bold\n"
138  " 10x20\n"
139  " 12x24\n"
140  " Browser...\n"
141  " Font Color\n"
142  " black\n"
143  " blue\n"
144  " cyan\n"
145  " green\n"
146  " gray\n"
147  " red\n"
148  " magenta\n"
149  " yellow\n"
150  " white\n"
151  " transparent\n"
152  " Browser...\n"
153  " Font Color\n"
154  " black\n"
155  " blue\n"
156  " cyan\n"
157  " green\n"
158  " gray\n"
159  " red\n"
160  " magenta\n"
161  " yellow\n"
162  " white\n"
163  " transparent\n"
164  " Browser...\n"
165  " Rotate Text\n"
166  " -90\n"
167  " -45\n"
168  " -30\n"
169  " 0\n"
170  " 30\n"
171  " 45\n"
172  " 90\n"
173  " 180\n"
174  " Dialog...\n"
175  " Help\n"
176  " Dismiss\n"
177  "\n"
178  "Choose a font name from the Font Name sub-menu. Additional\n"
179  "font names can be specified with the font browser. You can\n"
180  "change the menu names by setting the X resources font1\n"
181  "through font9.\n"
182  "\n"
183  "Choose a font color from the Font Color sub-menu.\n"
184  "Additional font colors can be specified with the color\n"
185  "browser. You can change the menu colors by setting the X\n"
186  "resources pen1 through pen9.\n"
187  "\n"
188  "If you select the color browser and press Grab, you can\n"
189  "choose the font color by moving the pointer to the desired\n"
190  "color on the screen and press any button.\n"
191  "\n"
192  "If you choose to rotate the text, choose Rotate Text from the\n"
193  "menu and select an angle. Typically you will only want to\n"
194  "rotate one line of text at a time. Depending on the angle you\n"
195  "choose, subsequent lines may end up overwriting each other.\n"
196  "\n"
197  "Choosing a font and its color is optional. The default font\n"
198  "is fixed and the default color is black. However, you must\n"
199  "choose a location to begin entering text and press button 1.\n"
200  "An underscore character will appear at the location of the\n"
201  "pointer. The cursor changes to a pencil to indicate you are\n"
202  "in text mode. To exit immediately, press Dismiss.\n"
203  "\n"
204  "In text mode, any key presses will display the character at\n"
205  "the location of the underscore and advance the underscore\n"
206  "cursor. Enter your text and once completed press Apply to\n"
207  "finish your image annotation. To correct errors press BACK\n"
208  "SPACE. To delete an entire line of text, press DELETE. Any\n"
209  "text that exceeds the boundaries of the image window is\n"
210  "automagically continued onto the next line.\n"
211  "\n"
212  "The actual color you request for the font is saved in the\n"
213  "image. However, the color that appears in your image window\n"
214  "may be different. For example, on a monochrome screen the\n"
215  "text will appear black or white even if you choose the color\n"
216  "red as the font color. However, the image saved to a file\n"
217  "with -write is written with red lettering. To assure the\n"
218  "correct color text in the final image, any PseudoClass image\n"
219  "is promoted to DirectClass (see miff(5)). To force a\n"
220  "PseudoClass image to remain PseudoClass, use -colors.\n"
221  },
222  ImageChopHelp[] =
223  {
224  "In chop mode, the Command widget has these options:\n"
225  "\n"
226  " Direction\n"
227  " horizontal\n"
228  " vertical\n"
229  " Help\n"
230  " Dismiss\n"
231  "\n"
232  "If the you choose the horizontal direction (this the\n"
233  "default), the area of the image between the two horizontal\n"
234  "endpoints of the chop line is removed. Otherwise, the area\n"
235  "of the image between the two vertical endpoints of the chop\n"
236  "line is removed.\n"
237  "\n"
238  "Select a location within the image window to begin your chop,\n"
239  "press and hold any button. Next, move the pointer to\n"
240  "another location in the image. As you move a line will\n"
241  "connect the initial location and the pointer. When you\n"
242  "release the button, the area within the image to chop is\n"
243  "determined by which direction you choose from the Command\n"
244  "widget.\n"
245  "\n"
246  "To cancel the image chopping, move the pointer back to the\n"
247  "starting point of the line and release the button.\n"
248  },
249  ImageColorEditHelp[] =
250  {
251  "In color edit mode, the Command widget has these options:\n"
252  "\n"
253  " Method\n"
254  " point\n"
255  " replace\n"
256  " floodfill\n"
257  " filltoborder\n"
258  " reset\n"
259  " Pixel Color\n"
260  " black\n"
261  " blue\n"
262  " cyan\n"
263  " green\n"
264  " gray\n"
265  " red\n"
266  " magenta\n"
267  " yellow\n"
268  " white\n"
269  " Browser...\n"
270  " Border Color\n"
271  " black\n"
272  " blue\n"
273  " cyan\n"
274  " green\n"
275  " gray\n"
276  " red\n"
277  " magenta\n"
278  " yellow\n"
279  " white\n"
280  " Browser...\n"
281  " Fuzz\n"
282  " 0%\n"
283  " 2%\n"
284  " 5%\n"
285  " 10%\n"
286  " 15%\n"
287  " Dialog...\n"
288  " Undo\n"
289  " Help\n"
290  " Dismiss\n"
291  "\n"
292  "Choose a color editing method from the Method sub-menu\n"
293  "of the Command widget. The point method recolors any pixel\n"
294  "selected with the pointer until the button is released. The\n"
295  "replace method recolors any pixel that matches the color of\n"
296  "the pixel you select with a button press. Floodfill recolors\n"
297  "any pixel that matches the color of the pixel you select with\n"
298  "a button press and is a neighbor. Whereas filltoborder recolors\n"
299  "any neighbor pixel that is not the border color. Finally reset\n"
300  "changes the entire image to the designated color.\n"
301  "\n"
302  "Next, choose a pixel color from the Pixel Color sub-menu.\n"
303  "Additional pixel colors can be specified with the color\n"
304  "browser. You can change the menu colors by setting the X\n"
305  "resources pen1 through pen9.\n"
306  "\n"
307  "Now press button 1 to select a pixel within the image window\n"
308  "to change its color. Additional pixels may be recolored as\n"
309  "prescribed by the method you choose.\n"
310  "\n"
311  "If the Magnify widget is mapped, it can be helpful in positioning\n"
312  "your pointer within the image (refer to button 2).\n"
313  "\n"
314  "The actual color you request for the pixels is saved in the\n"
315  "image. However, the color that appears in your image window\n"
316  "may be different. For example, on a monochrome screen the\n"
317  "pixel will appear black or white even if you choose the\n"
318  "color red as the pixel color. However, the image saved to a\n"
319  "file with -write is written with red pixels. To assure the\n"
320  "correct color text in the final image, any PseudoClass image\n"
321  "is promoted to DirectClass (see miff(5)). To force a\n"
322  "PseudoClass image to remain PseudoClass, use -colors.\n"
323  },
324  ImageCompositeHelp[] =
325  {
326  "First a widget window is displayed requesting you to enter an\n"
327  "image name. Press Composite, Grab or type a file name.\n"
328  "Press Cancel if you choose not to create a composite image.\n"
329  "When you choose Grab, move the pointer to the desired window\n"
330  "and press any button.\n"
331  "\n"
332  "If the Composite image does not have any matte information,\n"
333  "you are informed and the file browser is displayed again.\n"
334  "Enter the name of a mask image. The image is typically\n"
335  "grayscale and the same size as the composite image. If the\n"
336  "image is not grayscale, it is converted to grayscale and the\n"
337  "resulting intensities are used as matte information.\n"
338  "\n"
339  "A small window appears showing the location of the cursor in\n"
340  "the image window. You are now in composite mode. To exit\n"
341  "immediately, press Dismiss. In composite mode, the Command\n"
342  "widget has these options:\n"
343  "\n"
344  " Operators\n"
345  " Over\n"
346  " In\n"
347  " Out\n"
348  " Atop\n"
349  " Xor\n"
350  " Plus\n"
351  " Minus\n"
352  " Add\n"
353  " Subtract\n"
354  " Difference\n"
355  " Multiply\n"
356  " Bumpmap\n"
357  " Copy\n"
358  " CopyRed\n"
359  " CopyGreen\n"
360  " CopyBlue\n"
361  " CopyOpacity\n"
362  " Clear\n"
363  " Dissolve\n"
364  " Displace\n"
365  " Help\n"
366  " Dismiss\n"
367  "\n"
368  "Choose a composite operation from the Operators sub-menu of\n"
369  "the Command widget. How each operator behaves is described\n"
370  "below. Image window is the image currently displayed on\n"
371  "your X server and image is the image obtained with the File\n"
372  "Browser widget.\n"
373  "\n"
374  "Over The result is the union of the two image shapes,\n"
375  " with image obscuring image window in the region of\n"
376  " overlap.\n"
377  "\n"
378  "In The result is simply image cut by the shape of\n"
379  " image window. None of the image data of image\n"
380  " window is in the result.\n"
381  "\n"
382  "Out The resulting image is image with the shape of\n"
383  " image window cut out.\n"
384  "\n"
385  "Atop The result is the same shape as image image window,\n"
386  " with image obscuring image window where the image\n"
387  " shapes overlap. Note this differs from over\n"
388  " because the portion of image outside image window's\n"
389  " shape does not appear in the result.\n"
390  "\n"
391  "Xor The result is the image data from both image and\n"
392  " image window that is outside the overlap region.\n"
393  " The overlap region is blank.\n"
394  "\n"
395  "Plus The result is just the sum of the image data.\n"
396  " Output values are cropped to QuantumRange (no overflow).\n"
397  "\n"
398  "Minus The result of image - image window, with underflow\n"
399  " cropped to zero.\n"
400  "\n"
401  "Add The result of image + image window, with overflow\n"
402  " wrapping around (mod 256).\n"
403  "\n"
404  "Subtract The result of image - image window, with underflow\n"
405  " wrapping around (mod 256). The add and subtract\n"
406  " operators can be used to perform reversible\n"
407  " transformations.\n"
408  "\n"
409  "Difference\n"
410  " The result of abs(image - image window). This\n"
411  " useful for comparing two very similar images.\n"
412  "\n"
413  "Multiply\n"
414  " The result of image * image window. This\n"
415  " useful for the creation of drop-shadows.\n"
416  "\n"
417  "Bumpmap The result of surface normals from image * image\n"
418  " window.\n"
419  "\n"
420  "Copy The resulting image is image window replaced with\n"
421  " image. Here the matte information is ignored.\n"
422  "\n"
423  "CopyRed The red layer of the image window is replace with\n"
424  " the red layer of the image. The other layers are\n"
425  " untouched.\n"
426  "\n"
427  "CopyGreen\n"
428  " The green layer of the image window is replace with\n"
429  " the green layer of the image. The other layers are\n"
430  " untouched.\n"
431  "\n"
432  "CopyBlue The blue layer of the image window is replace with\n"
433  " the blue layer of the image. The other layers are\n"
434  " untouched.\n"
435  "\n"
436  "CopyOpacity\n"
437  " The matte layer of the image window is replace with\n"
438  " the matte layer of the image. The other layers are\n"
439  " untouched.\n"
440  "\n"
441  "The image compositor requires a matte, or alpha channel in\n"
442  "the image for some operations. This extra channel usually\n"
443  "defines a mask which represents a sort of a cookie-cutter\n"
444  "for the image. This the case when matte is opaque (full\n"
445  "coverage) for pixels inside the shape, zero outside, and\n"
446  "between 0 and QuantumRange on the boundary. If image does not\n"
447  "have a matte channel, it is initialized with 0 for any pixel\n"
448  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
449  "\n"
450  "If you choose Dissolve, the composite operator becomes Over. The\n"
451  "image matte channel percent transparency is initialized to factor.\n"
452  "The image window is initialized to (100-factor). Where factor is the\n"
453  "value you specify in the Dialog widget.\n"
454  "\n"
455  "Displace shifts the image pixels as defined by a displacement\n"
456  "map. With this option, image is used as a displacement map.\n"
457  "Black, within the displacement map, is a maximum positive\n"
458  "displacement. White is a maximum negative displacement and\n"
459  "middle gray is neutral. The displacement is scaled to determine\n"
460  "the pixel shift. By default, the displacement applies in both the\n"
461  "horizontal and vertical directions. However, if you specify a mask,\n"
462  "image is the horizontal X displacement and mask the vertical Y\n"
463  "displacement.\n"
464  "\n"
465  "Note that matte information for image window is not retained\n"
466  "for colormapped X server visuals (e.g. StaticColor,\n"
467  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
468  "behavior may require a TrueColor or DirectColor visual or a\n"
469  "Standard Colormap.\n"
470  "\n"
471  "Choosing a composite operator is optional. The default\n"
472  "operator is replace. However, you must choose a location to\n"
473  "composite your image and press button 1. Press and hold the\n"
474  "button before releasing and an outline of the image will\n"
475  "appear to help you identify your location.\n"
476  "\n"
477  "The actual colors of the composite image is saved. However,\n"
478  "the color that appears in image window may be different.\n"
479  "For example, on a monochrome screen image window will appear\n"
480  "black or white even though your composited image may have\n"
481  "many colors. If the image is saved to a file it is written\n"
482  "with the correct colors. To assure the correct colors are\n"
483  "saved in the final image, any PseudoClass image is promoted\n"
484  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
485  "to remain PseudoClass, use -colors.\n"
486  },
487  ImageCutHelp[] =
488  {
489  "In cut mode, the Command widget has these options:\n"
490  "\n"
491  " Help\n"
492  " Dismiss\n"
493  "\n"
494  "To define a cut region, press button 1 and drag. The\n"
495  "cut region is defined by a highlighted rectangle that\n"
496  "expands or contracts as it follows the pointer. Once you\n"
497  "are satisfied with the cut region, release the button.\n"
498  "You are now in rectify mode. In rectify mode, the Command\n"
499  "widget has these options:\n"
500  "\n"
501  " Cut\n"
502  " Help\n"
503  " Dismiss\n"
504  "\n"
505  "You can make adjustments by moving the pointer to one of the\n"
506  "cut rectangle corners, pressing a button, and dragging.\n"
507  "Finally, press Cut to commit your copy region. To\n"
508  "exit without cutting the image, press Dismiss.\n"
509  },
510  ImageCopyHelp[] =
511  {
512  "In copy mode, the Command widget has these options:\n"
513  "\n"
514  " Help\n"
515  " Dismiss\n"
516  "\n"
517  "To define a copy region, press button 1 and drag. The\n"
518  "copy region is defined by a highlighted rectangle that\n"
519  "expands or contracts as it follows the pointer. Once you\n"
520  "are satisfied with the copy region, release the button.\n"
521  "You are now in rectify mode. In rectify mode, the Command\n"
522  "widget has these options:\n"
523  "\n"
524  " Copy\n"
525  " Help\n"
526  " Dismiss\n"
527  "\n"
528  "You can make adjustments by moving the pointer to one of the\n"
529  "copy rectangle corners, pressing a button, and dragging.\n"
530  "Finally, press Copy to commit your copy region. To\n"
531  "exit without copying the image, press Dismiss.\n"
532  },
533  ImageCropHelp[] =
534  {
535  "In crop mode, the Command widget has these options:\n"
536  "\n"
537  " Help\n"
538  " Dismiss\n"
539  "\n"
540  "To define a cropping region, press button 1 and drag. The\n"
541  "cropping region is defined by a highlighted rectangle that\n"
542  "expands or contracts as it follows the pointer. Once you\n"
543  "are satisfied with the cropping region, release the button.\n"
544  "You are now in rectify mode. In rectify mode, the Command\n"
545  "widget has these options:\n"
546  "\n"
547  " Crop\n"
548  " Help\n"
549  " Dismiss\n"
550  "\n"
551  "You can make adjustments by moving the pointer to one of the\n"
552  "cropping rectangle corners, pressing a button, and dragging.\n"
553  "Finally, press Crop to commit your cropping region. To\n"
554  "exit without cropping the image, press Dismiss.\n"
555  },
556  ImageDrawHelp[] =
557  {
558  "The cursor changes to a crosshair to indicate you are in\n"
559  "draw mode. To exit immediately, press Dismiss. In draw mode,\n"
560  "the Command widget has these options:\n"
561  "\n"
562  " Element\n"
563  " point\n"
564  " line\n"
565  " rectangle\n"
566  " fill rectangle\n"
567  " circle\n"
568  " fill circle\n"
569  " ellipse\n"
570  " fill ellipse\n"
571  " polygon\n"
572  " fill polygon\n"
573  " Color\n"
574  " black\n"
575  " blue\n"
576  " cyan\n"
577  " green\n"
578  " gray\n"
579  " red\n"
580  " magenta\n"
581  " yellow\n"
582  " white\n"
583  " transparent\n"
584  " Browser...\n"
585  " Stipple\n"
586  " Brick\n"
587  " Diagonal\n"
588  " Scales\n"
589  " Vertical\n"
590  " Wavy\n"
591  " Translucent\n"
592  " Opaque\n"
593  " Open...\n"
594  " Width\n"
595  " 1\n"
596  " 2\n"
597  " 4\n"
598  " 8\n"
599  " 16\n"
600  " Dialog...\n"
601  " Undo\n"
602  " Help\n"
603  " Dismiss\n"
604  "\n"
605  "Choose a drawing primitive from the Element sub-menu.\n"
606  "\n"
607  "Choose a color from the Color sub-menu. Additional\n"
608  "colors can be specified with the color browser.\n"
609  "\n"
610  "If you choose the color browser and press Grab, you can\n"
611  "select the color by moving the pointer to the desired\n"
612  "color on the screen and press any button. The transparent\n"
613  "color updates the image matte channel and is useful for\n"
614  "image compositing.\n"
615  "\n"
616  "Choose a stipple, if appropriate, from the Stipple sub-menu.\n"
617  "Additional stipples can be specified with the file browser.\n"
618  "Stipples obtained from the file browser must be on disk in the\n"
619  "X11 bitmap format.\n"
620  "\n"
621  "Choose a width, if appropriate, from the Width sub-menu. To\n"
622  "choose a specific width select the Dialog widget.\n"
623  "\n"
624  "Choose a point in the Image window and press button 1 and\n"
625  "hold. Next, move the pointer to another location in the\n"
626  "image. As you move, a line connects the initial location and\n"
627  "the pointer. When you release the button, the image is\n"
628  "updated with the primitive you just drew. For polygons, the\n"
629  "image is updated when you press and release the button without\n"
630  "moving the pointer.\n"
631  "\n"
632  "To cancel image drawing, move the pointer back to the\n"
633  "starting point of the line and release the button.\n"
634  },
635  DisplayHelp[] =
636  {
637  "BUTTONS\n"
638  " The effects of each button press is described below. Three\n"
639  " buttons are required. If you have a two button mouse,\n"
640  " button 1 and 3 are returned. Press ALT and button 3 to\n"
641  " simulate button 2.\n"
642  "\n"
643  " 1 Press this button to map or unmap the Command widget.\n"
644  "\n"
645  " 2 Press and drag to define a region of the image to\n"
646  " magnify.\n"
647  "\n"
648  " 3 Press and drag to choose from a select set of commands.\n"
649  " This button behaves differently if the image being\n"
650  " displayed is a visual image directory. Here, choose a\n"
651  " particular tile of the directory and press this button and\n"
652  " drag to select a command from a pop-up menu. Choose from\n"
653  " these menu items:\n"
654  "\n"
655  " Open\n"
656  " Next\n"
657  " Former\n"
658  " Delete\n"
659  " Update\n"
660  "\n"
661  " If you choose Open, the image represented by the tile is\n"
662  " displayed. To return to the visual image directory, choose\n"
663  " Next from the Command widget. Next and Former moves to the\n"
664  " next or former image respectively. Choose Delete to delete\n"
665  " a particular image tile. Finally, choose Update to\n"
666  " synchronize all the image tiles with their respective\n"
667  " images.\n"
668  "\n"
669  "COMMAND WIDGET\n"
670  " The Command widget lists a number of sub-menus and commands.\n"
671  " They are\n"
672  "\n"
673  " File\n"
674  " Open...\n"
675  " Next\n"
676  " Former\n"
677  " Select...\n"
678  " Save...\n"
679  " Print...\n"
680  " Delete...\n"
681  " New...\n"
682  " Visual Directory...\n"
683  " Quit\n"
684  " Edit\n"
685  " Undo\n"
686  " Redo\n"
687  " Cut\n"
688  " Copy\n"
689  " Paste\n"
690  " View\n"
691  " Half Size\n"
692  " Original Size\n"
693  " Double Size\n"
694  " Resize...\n"
695  " Apply\n"
696  " Refresh\n"
697  " Restore\n"
698  " Transform\n"
699  " Crop\n"
700  " Chop\n"
701  " Flop\n"
702  " Flip\n"
703  " Rotate Right\n"
704  " Rotate Left\n"
705  " Rotate...\n"
706  " Shear...\n"
707  " Roll...\n"
708  " Trim Edges\n"
709  " Enhance\n"
710  " Brightness...\n"
711  " Saturation...\n"
712  " Hue...\n"
713  " Gamma...\n"
714  " Sharpen...\n"
715  " Dull\n"
716  " Contrast Stretch...\n"
717  " Sigmoidal Contrast...\n"
718  " Normalize\n"
719  " Equalize\n"
720  " Negate\n"
721  " Grayscale\n"
722  " Map...\n"
723  " Quantize...\n"
724  " Effects\n"
725  " Despeckle\n"
726  " Emboss\n"
727  " Reduce Noise\n"
728  " Add Noise\n"
729  " Sharpen...\n"
730  " Blur...\n"
731  " Threshold...\n"
732  " Edge Detect...\n"
733  " Spread...\n"
734  " Shade...\n"
735  " Painting...\n"
736  " Segment...\n"
737  " F/X\n"
738  " Solarize...\n"
739  " Sepia Tone...\n"
740  " Swirl...\n"
741  " Implode...\n"
742  " Vignette...\n"
743  " Wave...\n"
744  " Oil Painting...\n"
745  " Charcoal Drawing...\n"
746  " Image Edit\n"
747  " Annotate...\n"
748  " Draw...\n"
749  " Color...\n"
750  " Matte...\n"
751  " Composite...\n"
752  " Add Border...\n"
753  " Add Frame...\n"
754  " Comment...\n"
755  " Launch...\n"
756  " Region of Interest...\n"
757  " Miscellany\n"
758  " Image Info\n"
759  " Zoom Image\n"
760  " Show Preview...\n"
761  " Show Histogram\n"
762  " Show Matte\n"
763  " Background...\n"
764  " Slide Show\n"
765  " Preferences...\n"
766  " Help\n"
767  " Overview\n"
768  " Browse Documentation\n"
769  " About Display\n"
770  "\n"
771  " Menu items with a indented triangle have a sub-menu. They\n"
772  " are represented above as the indented items. To access a\n"
773  " sub-menu item, move the pointer to the appropriate menu and\n"
774  " press a button and drag. When you find the desired sub-menu\n"
775  " item, release the button and the command is executed. Move\n"
776  " the pointer away from the sub-menu if you decide not to\n"
777  " execute a particular command.\n"
778  "\n"
779  "KEYBOARD ACCELERATORS\n"
780  " Accelerators are one or two key presses that effect a\n"
781  " particular command. The keyboard accelerators that\n"
782  " display(1) understands is:\n"
783  "\n"
784  " Ctl+O Press to open an image from a file.\n"
785  "\n"
786  " space Press to display the next image.\n"
787  "\n"
788  " If the image is a multi-paged document such as a Postscript\n"
789  " document, you can skip ahead several pages by preceding\n"
790  " this command with a number. For example to display the\n"
791  " third page beyond the current page, press 3<space>.\n"
792  "\n"
793  " backspace Press to display the former image.\n"
794  "\n"
795  " If the image is a multi-paged document such as a Postscript\n"
796  " document, you can skip behind several pages by preceding\n"
797  " this command with a number. For example to display the\n"
798  " third page preceding the current page, press 3<backspace>.\n"
799  "\n"
800  " Ctl+S Press to write the image to a file.\n"
801  "\n"
802  " Ctl+P Press to print the image to a Postscript printer.\n"
803  "\n"
804  " Ctl+D Press to delete an image file.\n"
805  "\n"
806  " Ctl+N Press to create a blank canvas.\n"
807  "\n"
808  " Ctl+Q Press to discard all images and exit program.\n"
809  "\n"
810  " Ctl+Z Press to undo last image transformation.\n"
811  "\n"
812  " Ctl+R Press to redo last image transformation.\n"
813  "\n"
814  " Ctl+X Press to cut a region of the image.\n"
815  "\n"
816  " Ctl+C Press to copy a region of the image.\n"
817  "\n"
818  " Ctl+V Press to paste a region to the image.\n"
819  "\n"
820  " < Press to half the image size.\n"
821  "\n"
822  " - Press to return to the original image size.\n"
823  "\n"
824  " > Press to double the image size.\n"
825  "\n"
826  " % Press to resize the image to a width and height you\n"
827  " specify.\n"
828  "\n"
829  "Cmd-A Press to make any image transformations permanent."
830  "\n"
831  " By default, any image size transformations are applied\n"
832  " to the original image to create the image displayed on\n"
833  " the X server. However, the transformations are not\n"
834  " permanent (i.e. the original image does not change\n"
835  " size only the X image does). For example, if you\n"
836  " press > the X image will appear to double in size,\n"
837  " but the original image will in fact remain the same size.\n"
838  " To force the original image to double in size, press >\n"
839  " followed by Cmd-A.\n"
840  "\n"
841  " @ Press to refresh the image window.\n"
842  "\n"
843  " C Press to cut out a rectangular region of the image.\n"
844  "\n"
845  " [ Press to chop the image.\n"
846  "\n"
847  " H Press to flop image in the horizontal direction.\n"
848  "\n"
849  " V Press to flip image in the vertical direction.\n"
850  "\n"
851  " / Press to rotate the image 90 degrees clockwise.\n"
852  "\n"
853  " \\ Press to rotate the image 90 degrees counter-clockwise.\n"
854  "\n"
855  " * Press to rotate the image the number of degrees you\n"
856  " specify.\n"
857  "\n"
858  " S Press to shear the image the number of degrees you\n"
859  " specify.\n"
860  "\n"
861  " R Press to roll the image.\n"
862  "\n"
863  " T Press to trim the image edges.\n"
864  "\n"
865  " Shft-H Press to vary the image hue.\n"
866  "\n"
867  " Shft-S Press to vary the color saturation.\n"
868  "\n"
869  " Shft-L Press to vary the color brightness.\n"
870  "\n"
871  " Shft-G Press to gamma correct the image.\n"
872  "\n"
873  " Shft-C Press to sharpen the image contrast.\n"
874  "\n"
875  " Shft-Z Press to dull the image contrast.\n"
876  "\n"
877  " = Press to perform histogram equalization on the image.\n"
878  "\n"
879  " Shft-N Press to perform histogram normalization on the image.\n"
880  "\n"
881  " Shft-~ Press to negate the colors of the image.\n"
882  "\n"
883  " . Press to convert the image colors to gray.\n"
884  "\n"
885  " Shft-# Press to set the maximum number of unique colors in the\n"
886  " image.\n"
887  "\n"
888  " F2 Press to reduce the speckles in an image.\n"
889  "\n"
890  " F3 Press to eliminate peak noise from an image.\n"
891  "\n"
892  " F4 Press to add noise to an image.\n"
893  "\n"
894  " F5 Press to sharpen an image.\n"
895  "\n"
896  " F6 Press to delete an image file.\n"
897  "\n"
898  " F7 Press to threshold the image.\n"
899  "\n"
900  " F8 Press to detect edges within an image.\n"
901  "\n"
902  " F9 Press to emboss an image.\n"
903  "\n"
904  " F10 Press to displace pixels by a random amount.\n"
905  "\n"
906  " F11 Press to negate all pixels above the threshold level.\n"
907  "\n"
908  " F12 Press to shade the image using a distant light source.\n"
909  "\n"
910  " F13 Press to lighten or darken image edges to create a 3-D effect.\n"
911  "\n"
912  " F14 Press to segment the image by color.\n"
913  "\n"
914  " Meta-S Press to swirl image pixels about the center.\n"
915  "\n"
916  " Meta-I Press to implode image pixels about the center.\n"
917  "\n"
918  " Meta-W Press to alter an image along a sine wave.\n"
919  "\n"
920  " Meta-P Press to simulate an oil painting.\n"
921  "\n"
922  " Meta-C Press to simulate a charcoal drawing.\n"
923  "\n"
924  " Alt-A Press to annotate the image with text.\n"
925  "\n"
926  " Alt-D Press to draw on an image.\n"
927  "\n"
928  " Alt-P Press to edit an image pixel color.\n"
929  "\n"
930  " Alt-M Press to edit the image matte information.\n"
931  "\n"
932  " Alt-V Press to composite the image with another.\n"
933  "\n"
934  " Alt-B Press to add a border to the image.\n"
935  "\n"
936  " Alt-F Press to add an ornamental border to the image.\n"
937  "\n"
938  " Alt-Shft-!\n"
939  " Press to add an image comment.\n"
940  "\n"
941  " Ctl-A Press to apply image processing techniques to a region\n"
942  " of interest.\n"
943  "\n"
944  " Shft-? Press to display information about the image.\n"
945  "\n"
946  " Shft-+ Press to map the zoom image window.\n"
947  "\n"
948  " Shft-P Press to preview an image enhancement, effect, or f/x.\n"
949  "\n"
950  " F1 Press to display helpful information about display(1).\n"
951  "\n"
952  " Find Press to browse documentation about ImageMagick.\n"
953  "\n"
954  " 1-9 Press to change the level of magnification.\n"
955  "\n"
956  " Use the arrow keys to move the image one pixel up, down,\n"
957  " left, or right within the magnify window. Be sure to first\n"
958  " map the magnify window by pressing button 2.\n"
959  "\n"
960  " Press ALT and one of the arrow keys to trim off one pixel\n"
961  " from any side of the image.\n"
962  },
963  ImageMatteEditHelp[] =
964  {
965  "Matte information within an image is useful for some\n"
966  "operations such as image compositing (See IMAGE\n"
967  "COMPOSITING). This extra channel usually defines a mask\n"
968  "which represents a sort of a cookie-cutter for the image.\n"
969  "This the case when matte is opaque (full coverage) for\n"
970  "pixels inside the shape, zero outside, and between 0 and\n"
971  "QuantumRange on the boundary.\n"
972  "\n"
973  "A small window appears showing the location of the cursor in\n"
974  "the image window. You are now in matte edit mode. To exit\n"
975  "immediately, press Dismiss. In matte edit mode, the Command\n"
976  "widget has these options:\n"
977  "\n"
978  " Method\n"
979  " point\n"
980  " replace\n"
981  " floodfill\n"
982  " filltoborder\n"
983  " reset\n"
984  " Border Color\n"
985  " black\n"
986  " blue\n"
987  " cyan\n"
988  " green\n"
989  " gray\n"
990  " red\n"
991  " magenta\n"
992  " yellow\n"
993  " white\n"
994  " Browser...\n"
995  " Fuzz\n"
996  " 0%\n"
997  " 2%\n"
998  " 5%\n"
999  " 10%\n"
1000  " 15%\n"
1001  " Dialog...\n"
1002  " Matte\n"
1003  " Opaque\n"
1004  " Transparent\n"
1005  " Dialog...\n"
1006  " Undo\n"
1007  " Help\n"
1008  " Dismiss\n"
1009  "\n"
1010  "Choose a matte editing method from the Method sub-menu of\n"
1011  "the Command widget. The point method changes the matte value\n"
1012  "of any pixel selected with the pointer until the button is\n"
1013  "is released. The replace method changes the matte value of\n"
1014  "any pixel that matches the color of the pixel you select with\n"
1015  "a button press. Floodfill changes the matte value of any pixel\n"
1016  "that matches the color of the pixel you select with a button\n"
1017  "press and is a neighbor. Whereas filltoborder changes the matte\n"
1018  "value any neighbor pixel that is not the border color. Finally\n"
1019  "reset changes the entire image to the designated matte value.\n"
1020  "\n"
1021  "Choose Matte Value and pick Opaque or Transarent. For other values\n"
1022  "select the Dialog entry. Here a dialog appears requesting a matte\n"
1023  "value. The value you select is assigned as the opacity value of the\n"
1024  "selected pixel or pixels.\n"
1025  "\n"
1026  "Now, press any button to select a pixel within the image\n"
1027  "window to change its matte value.\n"
1028  "\n"
1029  "If the Magnify widget is mapped, it can be helpful in positioning\n"
1030  "your pointer within the image (refer to button 2).\n"
1031  "\n"
1032  "Matte information is only valid in a DirectClass image.\n"
1033  "Therefore, any PseudoClass image is promoted to DirectClass\n"
1034  "(see miff(5)). Note that matte information for PseudoClass\n"
1035  "is not retained for colormapped X server visuals (e.g.\n"
1036  "StaticColor, StaticColor, GrayScale, PseudoColor) unless you\n"
1037  "immediately save your image to a file (refer to Write).\n"
1038  "Correct matte editing behavior may require a TrueColor or\n"
1039  "DirectColor visual or a Standard Colormap.\n"
1040  },
1041  ImagePanHelp[] =
1042  {
1043  "When an image exceeds the width or height of the X server\n"
1044  "screen, display maps a small panning icon. The rectangle\n"
1045  "within the panning icon shows the area that is currently\n"
1046  "displayed in the image window. To pan about the image,\n"
1047  "press any button and drag the pointer within the panning\n"
1048  "icon. The pan rectangle moves with the pointer and the\n"
1049  "image window is updated to reflect the location of the\n"
1050  "rectangle within the panning icon. When you have selected\n"
1051  "the area of the image you wish to view, release the button.\n"
1052  "\n"
1053  "Use the arrow keys to pan the image one pixel up, down,\n"
1054  "left, or right within the image window.\n"
1055  "\n"
1056  "The panning icon is withdrawn if the image becomes smaller\n"
1057  "than the dimensions of the X server screen.\n"
1058  },
1059  ImagePasteHelp[] =
1060  {
1061  "A small window appears showing the location of the cursor in\n"
1062  "the image window. You are now in paste mode. To exit\n"
1063  "immediately, press Dismiss. In paste mode, the Command\n"
1064  "widget has these options:\n"
1065  "\n"
1066  " Operators\n"
1067  " over\n"
1068  " in\n"
1069  " out\n"
1070  " atop\n"
1071  " xor\n"
1072  " plus\n"
1073  " minus\n"
1074  " add\n"
1075  " subtract\n"
1076  " difference\n"
1077  " replace\n"
1078  " Help\n"
1079  " Dismiss\n"
1080  "\n"
1081  "Choose a composite operation from the Operators sub-menu of\n"
1082  "the Command widget. How each operator behaves is described\n"
1083  "below. Image window is the image currently displayed on\n"
1084  "your X server and image is the image obtained with the File\n"
1085  "Browser widget.\n"
1086  "\n"
1087  "Over The result is the union of the two image shapes,\n"
1088  " with image obscuring image window in the region of\n"
1089  " overlap.\n"
1090  "\n"
1091  "In The result is simply image cut by the shape of\n"
1092  " image window. None of the image data of image\n"
1093  " window is in the result.\n"
1094  "\n"
1095  "Out The resulting image is image with the shape of\n"
1096  " image window cut out.\n"
1097  "\n"
1098  "Atop The result is the same shape as image image window,\n"
1099  " with image obscuring image window where the image\n"
1100  " shapes overlap. Note this differs from over\n"
1101  " because the portion of image outside image window's\n"
1102  " shape does not appear in the result.\n"
1103  "\n"
1104  "Xor The result is the image data from both image and\n"
1105  " image window that is outside the overlap region.\n"
1106  " The overlap region is blank.\n"
1107  "\n"
1108  "Plus The result is just the sum of the image data.\n"
1109  " Output values are cropped to QuantumRange (no overflow).\n"
1110  " This operation is independent of the matte\n"
1111  " channels.\n"
1112  "\n"
1113  "Minus The result of image - image window, with underflow\n"
1114  " cropped to zero.\n"
1115  "\n"
1116  "Add The result of image + image window, with overflow\n"
1117  " wrapping around (mod 256).\n"
1118  "\n"
1119  "Subtract The result of image - image window, with underflow\n"
1120  " wrapping around (mod 256). The add and subtract\n"
1121  " operators can be used to perform reversible\n"
1122  " transformations.\n"
1123  "\n"
1124  "Difference\n"
1125  " The result of abs(image - image window). This\n"
1126  " useful for comparing two very similar images.\n"
1127  "\n"
1128  "Copy The resulting image is image window replaced with\n"
1129  " image. Here the matte information is ignored.\n"
1130  "\n"
1131  "CopyRed The red layer of the image window is replace with\n"
1132  " the red layer of the image. The other layers are\n"
1133  " untouched.\n"
1134  "\n"
1135  "CopyGreen\n"
1136  " The green layer of the image window is replace with\n"
1137  " the green layer of the image. The other layers are\n"
1138  " untouched.\n"
1139  "\n"
1140  "CopyBlue The blue layer of the image window is replace with\n"
1141  " the blue layer of the image. The other layers are\n"
1142  " untouched.\n"
1143  "\n"
1144  "CopyOpacity\n"
1145  " The matte layer of the image window is replace with\n"
1146  " the matte layer of the image. The other layers are\n"
1147  " untouched.\n"
1148  "\n"
1149  "The image compositor requires a matte, or alpha channel in\n"
1150  "the image for some operations. This extra channel usually\n"
1151  "defines a mask which represents a sort of a cookie-cutter\n"
1152  "for the image. This the case when matte is opaque (full\n"
1153  "coverage) for pixels inside the shape, zero outside, and\n"
1154  "between 0 and QuantumRange on the boundary. If image does not\n"
1155  "have a matte channel, it is initialized with 0 for any pixel\n"
1156  "matching in color to pixel location (0,0), otherwise QuantumRange.\n"
1157  "\n"
1158  "Note that matte information for image window is not retained\n"
1159  "for colormapped X server visuals (e.g. StaticColor,\n"
1160  "StaticColor, GrayScale, PseudoColor). Correct compositing\n"
1161  "behavior may require a TrueColor or DirectColor visual or a\n"
1162  "Standard Colormap.\n"
1163  "\n"
1164  "Choosing a composite operator is optional. The default\n"
1165  "operator is replace. However, you must choose a location to\n"
1166  "paste your image and press button 1. Press and hold the\n"
1167  "button before releasing and an outline of the image will\n"
1168  "appear to help you identify your location.\n"
1169  "\n"
1170  "The actual colors of the pasted image is saved. However,\n"
1171  "the color that appears in image window may be different.\n"
1172  "For example, on a monochrome screen image window will appear\n"
1173  "black or white even though your pasted image may have\n"
1174  "many colors. If the image is saved to a file it is written\n"
1175  "with the correct colors. To assure the correct colors are\n"
1176  "saved in the final image, any PseudoClass image is promoted\n"
1177  "to DirectClass (see miff(5)). To force a PseudoClass image\n"
1178  "to remain PseudoClass, use -colors.\n"
1179  },
1180  ImageROIHelp[] =
1181  {
1182  "In region of interest mode, the Command widget has these\n"
1183  "options:\n"
1184  "\n"
1185  " Help\n"
1186  " Dismiss\n"
1187  "\n"
1188  "To define a region of interest, press button 1 and drag.\n"
1189  "The region of interest is defined by a highlighted rectangle\n"
1190  "that expands or contracts as it follows the pointer. Once\n"
1191  "you are satisfied with the region of interest, release the\n"
1192  "button. You are now in apply mode. In apply mode the\n"
1193  "Command widget has these options:\n"
1194  "\n"
1195  " File\n"
1196  " Save...\n"
1197  " Print...\n"
1198  " Edit\n"
1199  " Undo\n"
1200  " Redo\n"
1201  " Transform\n"
1202  " Flop\n"
1203  " Flip\n"
1204  " Rotate Right\n"
1205  " Rotate Left\n"
1206  " Enhance\n"
1207  " Hue...\n"
1208  " Saturation...\n"
1209  " Brightness...\n"
1210  " Gamma...\n"
1211  " Spiff\n"
1212  " Dull\n"
1213  " Contrast Stretch\n"
1214  " Sigmoidal Contrast...\n"
1215  " Normalize\n"
1216  " Equalize\n"
1217  " Negate\n"
1218  " Grayscale\n"
1219  " Map...\n"
1220  " Quantize...\n"
1221  " Effects\n"
1222  " Despeckle\n"
1223  " Emboss\n"
1224  " Reduce Noise\n"
1225  " Sharpen...\n"
1226  " Blur...\n"
1227  " Threshold...\n"
1228  " Edge Detect...\n"
1229  " Spread...\n"
1230  " Shade...\n"
1231  " Raise...\n"
1232  " Segment...\n"
1233  " F/X\n"
1234  " Solarize...\n"
1235  " Sepia Tone...\n"
1236  " Swirl...\n"
1237  " Implode...\n"
1238  " Vignette...\n"
1239  " Wave...\n"
1240  " Oil Painting...\n"
1241  " Charcoal Drawing...\n"
1242  " Miscellany\n"
1243  " Image Info\n"
1244  " Zoom Image\n"
1245  " Show Preview...\n"
1246  " Show Histogram\n"
1247  " Show Matte\n"
1248  " Help\n"
1249  " Dismiss\n"
1250  "\n"
1251  "You can make adjustments to the region of interest by moving\n"
1252  "the pointer to one of the rectangle corners, pressing a\n"
1253  "button, and dragging. Finally, choose an image processing\n"
1254  "technique from the Command widget. You can choose more than\n"
1255  "one image processing technique to apply to an area.\n"
1256  "Alternatively, you can move the region of interest before\n"
1257  "applying another image processing technique. To exit, press\n"
1258  "Dismiss.\n"
1259  },
1260  ImageRotateHelp[] =
1261  {
1262  "In rotate mode, the Command widget has these options:\n"
1263  "\n"
1264  " Pixel Color\n"
1265  " black\n"
1266  " blue\n"
1267  " cyan\n"
1268  " green\n"
1269  " gray\n"
1270  " red\n"
1271  " magenta\n"
1272  " yellow\n"
1273  " white\n"
1274  " Browser...\n"
1275  " Direction\n"
1276  " horizontal\n"
1277  " vertical\n"
1278  " Help\n"
1279  " Dismiss\n"
1280  "\n"
1281  "Choose a background color from the Pixel Color sub-menu.\n"
1282  "Additional background colors can be specified with the color\n"
1283  "browser. You can change the menu colors by setting the X\n"
1284  "resources pen1 through pen9.\n"
1285  "\n"
1286  "If you choose the color browser and press Grab, you can\n"
1287  "select the background color by moving the pointer to the\n"
1288  "desired color on the screen and press any button.\n"
1289  "\n"
1290  "Choose a point in the image window and press this button and\n"
1291  "hold. Next, move the pointer to another location in the\n"
1292  "image. As you move a line connects the initial location and\n"
1293  "the pointer. When you release the button, the degree of\n"
1294  "image rotation is determined by the slope of the line you\n"
1295  "just drew. The slope is relative to the direction you\n"
1296  "choose from the Direction sub-menu of the Command widget.\n"
1297  "\n"
1298  "To cancel the image rotation, move the pointer back to the\n"
1299  "starting point of the line and release the button.\n"
1300  };
1301 
1302 /*
1303  Enumeration declarations.
1304 */
1305 typedef enum
1306 {
1307  CopyMode,
1308  CropMode,
1309  CutMode
1310 } ClipboardMode;
1311 
1312 typedef enum
1313 {
1314  OpenCommand,
1315  NextCommand,
1316  FormerCommand,
1317  SelectCommand,
1318  SaveCommand,
1319  PrintCommand,
1320  DeleteCommand,
1321  NewCommand,
1322  VisualDirectoryCommand,
1323  QuitCommand,
1324  UndoCommand,
1325  RedoCommand,
1326  CutCommand,
1327  CopyCommand,
1328  PasteCommand,
1329  HalfSizeCommand,
1330  OriginalSizeCommand,
1331  DoubleSizeCommand,
1332  ResizeCommand,
1333  ApplyCommand,
1334  RefreshCommand,
1335  RestoreCommand,
1336  CropCommand,
1337  ChopCommand,
1338  FlopCommand,
1339  FlipCommand,
1340  RotateRightCommand,
1341  RotateLeftCommand,
1342  RotateCommand,
1343  ShearCommand,
1344  RollCommand,
1345  TrimCommand,
1346  HueCommand,
1347  SaturationCommand,
1348  BrightnessCommand,
1349  GammaCommand,
1350  SpiffCommand,
1351  DullCommand,
1352  ContrastStretchCommand,
1353  SigmoidalContrastCommand,
1354  NormalizeCommand,
1355  EqualizeCommand,
1356  NegateCommand,
1357  GrayscaleCommand,
1358  MapCommand,
1359  QuantizeCommand,
1360  DespeckleCommand,
1361  EmbossCommand,
1362  ReduceNoiseCommand,
1363  AddNoiseCommand,
1364  SharpenCommand,
1365  BlurCommand,
1366  ThresholdCommand,
1367  EdgeDetectCommand,
1368  SpreadCommand,
1369  ShadeCommand,
1370  RaiseCommand,
1371  SegmentCommand,
1372  SolarizeCommand,
1373  SepiaToneCommand,
1374  SwirlCommand,
1375  ImplodeCommand,
1376  VignetteCommand,
1377  WaveCommand,
1378  OilPaintCommand,
1379  CharcoalDrawCommand,
1380  AnnotateCommand,
1381  DrawCommand,
1382  ColorCommand,
1383  MatteCommand,
1384  CompositeCommand,
1385  AddBorderCommand,
1386  AddFrameCommand,
1387  CommentCommand,
1388  LaunchCommand,
1389  RegionofInterestCommand,
1390  ROIHelpCommand,
1391  ROIDismissCommand,
1392  InfoCommand,
1393  ZoomCommand,
1394  ShowPreviewCommand,
1395  ShowHistogramCommand,
1396  ShowMatteCommand,
1397  BackgroundCommand,
1398  SlideShowCommand,
1399  PreferencesCommand,
1400  HelpCommand,
1401  BrowseDocumentationCommand,
1402  VersionCommand,
1403  SaveToUndoBufferCommand,
1404  FreeBuffersCommand,
1405  NullCommand
1406 } CommandType;
1407 
1408 typedef enum
1409 {
1410  AnnotateNameCommand,
1411  AnnotateFontColorCommand,
1412  AnnotateBackgroundColorCommand,
1413  AnnotateRotateCommand,
1414  AnnotateHelpCommand,
1415  AnnotateDismissCommand,
1416  TextHelpCommand,
1417  TextApplyCommand,
1418  ChopDirectionCommand,
1419  ChopHelpCommand,
1420  ChopDismissCommand,
1421  HorizontalChopCommand,
1422  VerticalChopCommand,
1423  ColorEditMethodCommand,
1424  ColorEditColorCommand,
1425  ColorEditBorderCommand,
1426  ColorEditFuzzCommand,
1427  ColorEditUndoCommand,
1428  ColorEditHelpCommand,
1429  ColorEditDismissCommand,
1430  CompositeOperatorsCommand,
1431  CompositeDissolveCommand,
1432  CompositeDisplaceCommand,
1433  CompositeHelpCommand,
1434  CompositeDismissCommand,
1435  CropHelpCommand,
1436  CropDismissCommand,
1437  RectifyCopyCommand,
1438  RectifyHelpCommand,
1439  RectifyDismissCommand,
1440  DrawElementCommand,
1441  DrawColorCommand,
1442  DrawStippleCommand,
1443  DrawWidthCommand,
1444  DrawUndoCommand,
1445  DrawHelpCommand,
1446  DrawDismissCommand,
1447  MatteEditMethod,
1448  MatteEditBorderCommand,
1449  MatteEditFuzzCommand,
1450  MatteEditValueCommand,
1451  MatteEditUndoCommand,
1452  MatteEditHelpCommand,
1453  MatteEditDismissCommand,
1454  PasteOperatorsCommand,
1455  PasteHelpCommand,
1456  PasteDismissCommand,
1457  RotateColorCommand,
1458  RotateDirectionCommand,
1459  RotateCropCommand,
1460  RotateSharpenCommand,
1461  RotateHelpCommand,
1462  RotateDismissCommand,
1463  HorizontalRotateCommand,
1464  VerticalRotateCommand,
1465  TileLoadCommand,
1466  TileNextCommand,
1467  TileFormerCommand,
1468  TileDeleteCommand,
1469  TileUpdateCommand
1470 } ModeType;
1471 
1472 /*
1473  Stipples.
1474 */
1475 #define BricksWidth 20
1476 #define BricksHeight 20
1477 #define DiagonalWidth 16
1478 #define DiagonalHeight 16
1479 #define HighlightWidth 8
1480 #define HighlightHeight 8
1481 #define OpaqueWidth 8
1482 #define OpaqueHeight 8
1483 #define ScalesWidth 16
1484 #define ScalesHeight 16
1485 #define ShadowWidth 8
1486 #define ShadowHeight 8
1487 #define VerticalWidth 16
1488 #define VerticalHeight 16
1489 #define WavyWidth 16
1490 #define WavyHeight 16
1491 
1492 /*
1493  Constant declaration.
1494 */
1495 static const int
1496  RoiDelta = 8;
1497 
1498 static const unsigned char
1499  BricksBitmap[] =
1500  {
1501  0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1502  0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1503  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1504  0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1505  0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1506  },
1507  DiagonalBitmap[] =
1508  {
1509  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1510  0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1511  0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1512  },
1513  ScalesBitmap[] =
1514  {
1515  0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1516  0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1517  0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1518  },
1519  VerticalBitmap[] =
1520  {
1521  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1522  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1523  0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1524  },
1525  WavyBitmap[] =
1526  {
1527  0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1528  0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1529  0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1530  };
1531 
1532 /*
1533  Function prototypes.
1534 */
1535 static CommandType
1536  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537  const MagickStatusType,KeySym,Image **);
1538 
1539 static Image
1540  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1541  Image **),
1542  *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1543  *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1544  *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1545 
1546 static MagickBooleanType
1547  XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1548  XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1549  XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1550  XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1551  XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1552  XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1553  XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1554  XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1555  XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1556  XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1557  XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1558  XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1559  XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1560  XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1561  XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1562 
1563 static void
1564  XDrawPanRectangle(Display *,XWindows *),
1565  XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1566  XMagnifyImage(Display *,XWindows *,XEvent *),
1567  XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1568  XPanImage(Display *,XWindows *,XEvent *),
1569  XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1570  const KeySym),
1571  XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1572  XScreenEvent(Display *,XWindows *,XEvent *),
1573  XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1574 
1575 /*
1576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1577 % %
1578 % %
1579 % %
1580 % D i s p l a y I m a g e s %
1581 % %
1582 % %
1583 % %
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 %
1586 % DisplayImages() displays an image sequence to any X window screen. It
1587 % returns a value other than 0 if successful. Check the exception member
1588 % of image to determine the reason for any failure.
1589 %
1590 % The format of the DisplayImages method is:
1591 %
1592 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
1593 % Image *images)
1594 %
1595 % A description of each parameter follows:
1596 %
1597 % o image_info: the image info.
1598 %
1599 % o image: the image.
1600 %
1601 */
1602 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1603  Image *images)
1604 {
1605  char
1606  *argv[1];
1607 
1608  Display
1609  *display;
1610 
1611  Image
1612  *image;
1613 
1614  ssize_t
1615  i;
1616 
1617  size_t
1618  state;
1619 
1620  XrmDatabase
1621  resource_database;
1622 
1623  XResourceInfo
1624  resource_info;
1625 
1626  assert(image_info != (const ImageInfo *) NULL);
1627  assert(image_info->signature == MagickCoreSignature);
1628  assert(images != (Image *) NULL);
1629  assert(images->signature == MagickCoreSignature);
1630  if (IsEventLogging() != MagickFalse)
1631  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1632  display=XOpenDisplay(image_info->server_name);
1633  if (display == (Display *) NULL)
1634  {
1635  (void) ThrowMagickException(&images->exception,GetMagickModule(),
1636  XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1637  image_info->server_name));
1638  return(MagickFalse);
1639  }
1640  if (images->exception.severity != UndefinedException)
1641  CatchException(&images->exception);
1642  (void) XSetErrorHandler(XError);
1643  resource_database=XGetResourceDatabase(display,GetClientName());
1644  (void) memset(&resource_info,0,sizeof(resource_info));
1645  XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1646  if (image_info->page != (char *) NULL)
1647  resource_info.image_geometry=AcquireString(image_info->page);
1648  resource_info.immutable=MagickTrue;
1649  argv[0]=AcquireString(GetClientName());
1650  state=DefaultState;
1651  for (i=0; (state & ExitState) == 0; i++)
1652  {
1653  if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1654  break;
1655  image=GetImageFromList(images,i % GetImageListLength(images));
1656  (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1657  }
1658  (void) SetErrorHandler((ErrorHandler) NULL);
1659  (void) SetWarningHandler((WarningHandler) NULL);
1660  argv[0]=DestroyString(argv[0]);
1661  XDestroyResourceInfo(&resource_info);
1662  if (images->exception.severity != UndefinedException)
1663  return(MagickFalse);
1664  return(MagickTrue);
1665 }
1666 
1667 /*
1668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1669 % %
1670 % %
1671 % %
1672 % R e m o t e D i s p l a y C o m m a n d %
1673 % %
1674 % %
1675 % %
1676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1677 %
1678 % RemoteDisplayCommand() encourages a remote display program to display the
1679 % specified image filename.
1680 %
1681 % The format of the RemoteDisplayCommand method is:
1682 %
1683 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1684 % const char *window,const char *filename,ExceptionInfo *exception)
1685 %
1686 % A description of each parameter follows:
1687 %
1688 % o image_info: the image info.
1689 %
1690 % o window: Specifies the name or id of an X window.
1691 %
1692 % o filename: the name of the image filename to display.
1693 %
1694 % o exception: return any errors or warnings in this structure.
1695 %
1696 */
1697 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1698  const char *window,const char *filename,ExceptionInfo *exception)
1699 {
1700  Display
1701  *display;
1702 
1703  MagickStatusType
1704  status;
1705 
1706  assert(image_info != (const ImageInfo *) NULL);
1707  assert(image_info->signature == MagickCoreSignature);
1708  assert(filename != (char *) NULL);
1709  if (IsEventLogging() != MagickFalse)
1710  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1711  display=XOpenDisplay(image_info->server_name);
1712  if (display == (Display *) NULL)
1713  {
1714  (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1715  "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1716  return(MagickFalse);
1717  }
1718  (void) XSetErrorHandler(XError);
1719  status=XRemoteCommand(display,window,filename);
1720  (void) XCloseDisplay(display);
1721  return(status != 0 ? MagickTrue : MagickFalse);
1722 }
1723 
1724 /*
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 % %
1727 % %
1728 % %
1729 + X A n n o t a t e E d i t I m a g e %
1730 % %
1731 % %
1732 % %
1733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734 %
1735 % XAnnotateEditImage() annotates the image with text.
1736 %
1737 % The format of the XAnnotateEditImage method is:
1738 %
1739 % MagickBooleanType XAnnotateEditImage(Display *display,
1740 % XResourceInfo *resource_info,XWindows *windows,Image *image)
1741 %
1742 % A description of each parameter follows:
1743 %
1744 % o display: Specifies a connection to an X server; returned from
1745 % XOpenDisplay.
1746 %
1747 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1748 %
1749 % o windows: Specifies a pointer to a XWindows structure.
1750 %
1751 % o image: the image; returned from ReadImage.
1752 %
1753 */
1754 
1755 static MagickBooleanType XAnnotateEditImage(Display *display,
1756  XResourceInfo *resource_info,XWindows *windows,Image *image)
1757 {
1758  const char
1759  *const AnnotateMenu[] =
1760  {
1761  "Font Name",
1762  "Font Color",
1763  "Box Color",
1764  "Rotate Text",
1765  "Help",
1766  "Dismiss",
1767  (char *) NULL
1768  },
1769  *const TextMenu[] =
1770  {
1771  "Help",
1772  "Apply",
1773  (char *) NULL
1774  };
1775 
1776  static const ModeType
1777  AnnotateCommands[] =
1778  {
1779  AnnotateNameCommand,
1780  AnnotateFontColorCommand,
1781  AnnotateBackgroundColorCommand,
1782  AnnotateRotateCommand,
1783  AnnotateHelpCommand,
1784  AnnotateDismissCommand
1785  },
1786  TextCommands[] =
1787  {
1788  TextHelpCommand,
1789  TextApplyCommand
1790  };
1791 
1792  static MagickBooleanType
1793  transparent_box = MagickTrue,
1794  transparent_pen = MagickFalse;
1795 
1796  static MagickRealType
1797  degrees = 0.0;
1798 
1799  static unsigned int
1800  box_id = MaxNumberPens-2,
1801  font_id = 0,
1802  pen_id = 0;
1803 
1804  char
1805  command[MaxTextExtent],
1806  text[MaxTextExtent];
1807 
1808  const char
1809  *ColorMenu[MaxNumberPens+1];
1810 
1811  Cursor
1812  cursor;
1813 
1814  GC
1815  annotate_context;
1816 
1817  int
1818  id,
1819  pen_number,
1820  status,
1821  x,
1822  y;
1823 
1824  KeySym
1825  key_symbol;
1826 
1827  char
1828  *p;
1829 
1830  ssize_t
1831  i;
1832 
1833  unsigned int
1834  height,
1835  width;
1836 
1837  size_t
1838  state;
1839 
1840  XAnnotateInfo
1841  *annotate_info,
1842  *previous_info;
1843 
1844  XColor
1845  color;
1846 
1847  XFontStruct
1848  *font_info;
1849 
1850  XEvent
1851  event,
1852  text_event;
1853 
1854  /*
1855  Map Command widget.
1856  */
1857  (void) CloneString(&windows->command.name,"Annotate");
1858  windows->command.data=4;
1859  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1860  (void) XMapRaised(display,windows->command.id);
1861  XClientMessage(display,windows->image.id,windows->im_protocols,
1862  windows->im_update_widget,CurrentTime);
1863  /*
1864  Track pointer until button 1 is pressed.
1865  */
1866  XQueryPosition(display,windows->image.id,&x,&y);
1867  (void) XSelectInput(display,windows->image.id,
1868  windows->image.attributes.event_mask | PointerMotionMask);
1869  cursor=XCreateFontCursor(display,XC_left_side);
1870  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1871  state=DefaultState;
1872  do
1873  {
1874  if (windows->info.mapped != MagickFalse)
1875  {
1876  /*
1877  Display pointer position.
1878  */
1879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1880  x+windows->image.x,y+windows->image.y);
1881  XInfoWidget(display,windows,text);
1882  }
1883  /*
1884  Wait for next event.
1885  */
1886  XScreenEvent(display,windows,&event);
1887  if (event.xany.window == windows->command.id)
1888  {
1889  /*
1890  Select a command from the Command widget.
1891  */
1892  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1893  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1894  if (id < 0)
1895  continue;
1896  switch (AnnotateCommands[id])
1897  {
1898  case AnnotateNameCommand:
1899  {
1900  const char
1901  *FontMenu[MaxNumberFonts];
1902 
1903  int
1904  font_number;
1905 
1906  /*
1907  Initialize menu selections.
1908  */
1909  for (i=0; i < MaxNumberFonts; i++)
1910  FontMenu[i]=resource_info->font_name[i];
1911  FontMenu[MaxNumberFonts-2]="Browser...";
1912  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1913  /*
1914  Select a font name from the pop-up menu.
1915  */
1916  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1917  (const char **) FontMenu,command);
1918  if (font_number < 0)
1919  break;
1920  if (font_number == (MaxNumberFonts-2))
1921  {
1922  static char
1923  font_name[MaxTextExtent] = "fixed";
1924 
1925  /*
1926  Select a font name from a browser.
1927  */
1928  resource_info->font_name[font_number]=font_name;
1929  XFontBrowserWidget(display,windows,"Select",font_name);
1930  if (*font_name == '\0')
1931  break;
1932  }
1933  /*
1934  Initialize font info.
1935  */
1936  font_info=XLoadQueryFont(display,resource_info->font_name[
1937  font_number]);
1938  if (font_info == (XFontStruct *) NULL)
1939  {
1940  XNoticeWidget(display,windows,"Unable to load font:",
1941  resource_info->font_name[font_number]);
1942  break;
1943  }
1944  font_id=(unsigned int) font_number;
1945  (void) XFreeFont(display,font_info);
1946  break;
1947  }
1948  case AnnotateFontColorCommand:
1949  {
1950  /*
1951  Initialize menu selections.
1952  */
1953  for (i=0; i < (int) (MaxNumberPens-2); i++)
1954  ColorMenu[i]=resource_info->pen_colors[i];
1955  ColorMenu[MaxNumberPens-2]="transparent";
1956  ColorMenu[MaxNumberPens-1]="Browser...";
1957  ColorMenu[MaxNumberPens]=(const char *) NULL;
1958  /*
1959  Select a pen color from the pop-up menu.
1960  */
1961  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1962  (const char **) ColorMenu,command);
1963  if (pen_number < 0)
1964  break;
1965  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1966  MagickFalse;
1967  if (transparent_pen != MagickFalse)
1968  break;
1969  if (pen_number == (MaxNumberPens-1))
1970  {
1971  static char
1972  color_name[MaxTextExtent] = "gray";
1973 
1974  /*
1975  Select a pen color from a dialog.
1976  */
1977  resource_info->pen_colors[pen_number]=color_name;
1978  XColorBrowserWidget(display,windows,"Select",color_name);
1979  if (*color_name == '\0')
1980  break;
1981  }
1982  /*
1983  Set pen color.
1984  */
1985  (void) XParseColor(display,windows->map_info->colormap,
1986  resource_info->pen_colors[pen_number],&color);
1987  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1988  (unsigned int) MaxColors,&color);
1989  windows->pixel_info->pen_colors[pen_number]=color;
1990  pen_id=(unsigned int) pen_number;
1991  break;
1992  }
1993  case AnnotateBackgroundColorCommand:
1994  {
1995  /*
1996  Initialize menu selections.
1997  */
1998  for (i=0; i < (int) (MaxNumberPens-2); i++)
1999  ColorMenu[i]=resource_info->pen_colors[i];
2000  ColorMenu[MaxNumberPens-2]="transparent";
2001  ColorMenu[MaxNumberPens-1]="Browser...";
2002  ColorMenu[MaxNumberPens]=(const char *) NULL;
2003  /*
2004  Select a pen color from the pop-up menu.
2005  */
2006  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2007  (const char **) ColorMenu,command);
2008  if (pen_number < 0)
2009  break;
2010  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2011  MagickFalse;
2012  if (transparent_box != MagickFalse)
2013  break;
2014  if (pen_number == (MaxNumberPens-1))
2015  {
2016  static char
2017  color_name[MaxTextExtent] = "gray";
2018 
2019  /*
2020  Select a pen color from a dialog.
2021  */
2022  resource_info->pen_colors[pen_number]=color_name;
2023  XColorBrowserWidget(display,windows,"Select",color_name);
2024  if (*color_name == '\0')
2025  break;
2026  }
2027  /*
2028  Set pen color.
2029  */
2030  (void) XParseColor(display,windows->map_info->colormap,
2031  resource_info->pen_colors[pen_number],&color);
2032  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2033  (unsigned int) MaxColors,&color);
2034  windows->pixel_info->pen_colors[pen_number]=color;
2035  box_id=(unsigned int) pen_number;
2036  break;
2037  }
2038  case AnnotateRotateCommand:
2039  {
2040  int
2041  entry;
2042 
2043  const char
2044  *const RotateMenu[] =
2045  {
2046  "-90",
2047  "-45",
2048  "-30",
2049  "0",
2050  "30",
2051  "45",
2052  "90",
2053  "180",
2054  "Dialog...",
2055  (char *) NULL,
2056  };
2057 
2058  static char
2059  angle[MaxTextExtent] = "30.0";
2060 
2061  /*
2062  Select a command from the pop-up menu.
2063  */
2064  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2065  command);
2066  if (entry < 0)
2067  break;
2068  if (entry != 8)
2069  {
2070  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2071  break;
2072  }
2073  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2074  angle);
2075  if (*angle == '\0')
2076  break;
2077  degrees=StringToDouble(angle,(char **) NULL);
2078  break;
2079  }
2080  case AnnotateHelpCommand:
2081  {
2082  XTextViewHelp(display,resource_info,windows,MagickFalse,
2083  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2084  break;
2085  }
2086  case AnnotateDismissCommand:
2087  {
2088  /*
2089  Prematurely exit.
2090  */
2091  state|=EscapeState;
2092  state|=ExitState;
2093  break;
2094  }
2095  default:
2096  break;
2097  }
2098  continue;
2099  }
2100  switch (event.type)
2101  {
2102  case ButtonPress:
2103  {
2104  if (event.xbutton.button != Button1)
2105  break;
2106  if (event.xbutton.window != windows->image.id)
2107  break;
2108  /*
2109  Change to text entering mode.
2110  */
2111  x=event.xbutton.x;
2112  y=event.xbutton.y;
2113  state|=ExitState;
2114  break;
2115  }
2116  case ButtonRelease:
2117  break;
2118  case Expose:
2119  break;
2120  case KeyPress:
2121  {
2122  if (event.xkey.window != windows->image.id)
2123  break;
2124  /*
2125  Respond to a user key press.
2126  */
2127  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2128  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2129  switch ((int) key_symbol)
2130  {
2131  case XK_Escape:
2132  case XK_F20:
2133  {
2134  /*
2135  Prematurely exit.
2136  */
2137  state|=EscapeState;
2138  state|=ExitState;
2139  break;
2140  }
2141  case XK_F1:
2142  case XK_Help:
2143  {
2144  XTextViewHelp(display,resource_info,windows,MagickFalse,
2145  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146  break;
2147  }
2148  default:
2149  {
2150  (void) XBell(display,0);
2151  break;
2152  }
2153  }
2154  break;
2155  }
2156  case MotionNotify:
2157  {
2158  /*
2159  Map and unmap Info widget as cursor crosses its boundaries.
2160  */
2161  x=event.xmotion.x;
2162  y=event.xmotion.y;
2163  if (windows->info.mapped != MagickFalse)
2164  {
2165  if ((x < (int) (windows->info.x+windows->info.width)) &&
2166  (y < (int) (windows->info.y+windows->info.height)))
2167  (void) XWithdrawWindow(display,windows->info.id,
2168  windows->info.screen);
2169  }
2170  else
2171  if ((x > (int) (windows->info.x+windows->info.width)) ||
2172  (y > (int) (windows->info.y+windows->info.height)))
2173  (void) XMapWindow(display,windows->info.id);
2174  break;
2175  }
2176  default:
2177  break;
2178  }
2179  } while ((state & ExitState) == 0);
2180  (void) XSelectInput(display,windows->image.id,
2181  windows->image.attributes.event_mask);
2182  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2183  if ((state & EscapeState) != 0)
2184  return(MagickTrue);
2185  /*
2186  Set font info and check boundary conditions.
2187  */
2188  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2189  if (font_info == (XFontStruct *) NULL)
2190  {
2191  XNoticeWidget(display,windows,"Unable to load font:",
2192  resource_info->font_name[font_id]);
2193  font_info=windows->font_info;
2194  }
2195  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2196  x=(int) windows->image.width-font_info->max_bounds.width;
2197  if (y < (int) (font_info->ascent+font_info->descent))
2198  y=(int) font_info->ascent+font_info->descent;
2199  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2200  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2201  return(MagickFalse);
2202  /*
2203  Initialize annotate structure.
2204  */
2205  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2206  if (annotate_info == (XAnnotateInfo *) NULL)
2207  return(MagickFalse);
2208  XGetAnnotateInfo(annotate_info);
2209  annotate_info->x=x;
2210  annotate_info->y=y;
2211  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2212  annotate_info->stencil=OpaqueStencil;
2213  else
2214  if (transparent_box == MagickFalse)
2215  annotate_info->stencil=BackgroundStencil;
2216  else
2217  annotate_info->stencil=ForegroundStencil;
2218  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2219  annotate_info->degrees=degrees;
2220  annotate_info->font_info=font_info;
2221  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2222  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2223  sizeof(*annotate_info->text));
2224  if (annotate_info->text == (char *) NULL)
2225  return(MagickFalse);
2226  /*
2227  Create cursor and set graphic context.
2228  */
2229  cursor=XCreateFontCursor(display,XC_pencil);
2230  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2231  annotate_context=windows->image.annotate_context;
2232  (void) XSetFont(display,annotate_context,font_info->fid);
2233  (void) XSetBackground(display,annotate_context,
2234  windows->pixel_info->pen_colors[box_id].pixel);
2235  (void) XSetForeground(display,annotate_context,
2236  windows->pixel_info->pen_colors[pen_id].pixel);
2237  /*
2238  Begin annotating the image with text.
2239  */
2240  (void) CloneString(&windows->command.name,"Text");
2241  windows->command.data=0;
2242  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2243  state=DefaultState;
2244  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2245  text_event.xexpose.width=(int) font_info->max_bounds.width;
2246  text_event.xexpose.height=font_info->max_bounds.ascent+
2247  font_info->max_bounds.descent;
2248  p=annotate_info->text;
2249  do
2250  {
2251  /*
2252  Display text cursor.
2253  */
2254  *p='\0';
2255  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2256  /*
2257  Wait for next event.
2258  */
2259  XScreenEvent(display,windows,&event);
2260  if (event.xany.window == windows->command.id)
2261  {
2262  /*
2263  Select a command from the Command widget.
2264  */
2265  (void) XSetBackground(display,annotate_context,
2266  windows->pixel_info->background_color.pixel);
2267  (void) XSetForeground(display,annotate_context,
2268  windows->pixel_info->foreground_color.pixel);
2269  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2270  (void) XSetBackground(display,annotate_context,
2271  windows->pixel_info->pen_colors[box_id].pixel);
2272  (void) XSetForeground(display,annotate_context,
2273  windows->pixel_info->pen_colors[pen_id].pixel);
2274  if (id < 0)
2275  continue;
2276  switch (TextCommands[id])
2277  {
2278  case TextHelpCommand:
2279  {
2280  XTextViewHelp(display,resource_info,windows,MagickFalse,
2281  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2282  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2283  break;
2284  }
2285  case TextApplyCommand:
2286  {
2287  /*
2288  Finished annotating.
2289  */
2290  annotate_info->width=(unsigned int) XTextWidth(font_info,
2291  annotate_info->text,(int) strlen(annotate_info->text));
2292  XRefreshWindow(display,&windows->image,&text_event);
2293  state|=ExitState;
2294  break;
2295  }
2296  default:
2297  break;
2298  }
2299  continue;
2300  }
2301  /*
2302  Erase text cursor.
2303  */
2304  text_event.xexpose.x=x;
2305  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2306  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2307  (unsigned int) text_event.xexpose.width,(unsigned int)
2308  text_event.xexpose.height,MagickFalse);
2309  XRefreshWindow(display,&windows->image,&text_event);
2310  switch (event.type)
2311  {
2312  case ButtonPress:
2313  {
2314  if (event.xbutton.window != windows->image.id)
2315  break;
2316  if (event.xbutton.button == Button2)
2317  {
2318  /*
2319  Request primary selection.
2320  */
2321  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2322  windows->image.id,CurrentTime);
2323  break;
2324  }
2325  break;
2326  }
2327  case Expose:
2328  {
2329  if (event.xexpose.count == 0)
2330  {
2331  XAnnotateInfo
2332  *text_info;
2333 
2334  /*
2335  Refresh Image window.
2336  */
2337  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2338  text_info=annotate_info;
2339  while (text_info != (XAnnotateInfo *) NULL)
2340  {
2341  if (annotate_info->stencil == ForegroundStencil)
2342  (void) XDrawString(display,windows->image.id,annotate_context,
2343  text_info->x,text_info->y,text_info->text,
2344  (int) strlen(text_info->text));
2345  else
2346  (void) XDrawImageString(display,windows->image.id,
2347  annotate_context,text_info->x,text_info->y,text_info->text,
2348  (int) strlen(text_info->text));
2349  text_info=text_info->previous;
2350  }
2351  (void) XDrawString(display,windows->image.id,annotate_context,
2352  x,y,"_",1);
2353  }
2354  break;
2355  }
2356  case KeyPress:
2357  {
2358  int
2359  length;
2360 
2361  if (event.xkey.window != windows->image.id)
2362  break;
2363  /*
2364  Respond to a user key press.
2365  */
2366  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2367  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2368  *(command+length)='\0';
2369  if (((event.xkey.state & ControlMask) != 0) ||
2370  ((event.xkey.state & Mod1Mask) != 0))
2371  state|=ModifierState;
2372  if ((state & ModifierState) != 0)
2373  switch ((int) key_symbol)
2374  {
2375  case XK_u:
2376  case XK_U:
2377  {
2378  key_symbol=DeleteCommand;
2379  break;
2380  }
2381  default:
2382  break;
2383  }
2384  switch ((int) key_symbol)
2385  {
2386  case XK_BackSpace:
2387  {
2388  /*
2389  Erase one character.
2390  */
2391  if (p == annotate_info->text)
2392  {
2393  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2394  break;
2395  else
2396  {
2397  /*
2398  Go to end of the previous line of text.
2399  */
2400  annotate_info=annotate_info->previous;
2401  p=annotate_info->text;
2402  x=annotate_info->x+annotate_info->width;
2403  y=annotate_info->y;
2404  if (annotate_info->width != 0)
2405  p+=strlen(annotate_info->text);
2406  break;
2407  }
2408  }
2409  p--;
2410  x-=XTextWidth(font_info,p,1);
2411  text_event.xexpose.x=x;
2412  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2413  XRefreshWindow(display,&windows->image,&text_event);
2414  break;
2415  }
2416  case XK_bracketleft:
2417  {
2418  key_symbol=XK_Escape;
2419  break;
2420  }
2421  case DeleteCommand:
2422  {
2423  /*
2424  Erase the entire line of text.
2425  */
2426  while (p != annotate_info->text)
2427  {
2428  p--;
2429  x-=XTextWidth(font_info,p,1);
2430  text_event.xexpose.x=x;
2431  XRefreshWindow(display,&windows->image,&text_event);
2432  }
2433  break;
2434  }
2435  case XK_Escape:
2436  case XK_F20:
2437  {
2438  /*
2439  Finished annotating.
2440  */
2441  annotate_info->width=(unsigned int) XTextWidth(font_info,
2442  annotate_info->text,(int) strlen(annotate_info->text));
2443  XRefreshWindow(display,&windows->image,&text_event);
2444  state|=ExitState;
2445  break;
2446  }
2447  default:
2448  {
2449  /*
2450  Draw a single character on the Image window.
2451  */
2452  if ((state & ModifierState) != 0)
2453  break;
2454  if (*command == '\0')
2455  break;
2456  *p=(*command);
2457  if (annotate_info->stencil == ForegroundStencil)
2458  (void) XDrawString(display,windows->image.id,annotate_context,
2459  x,y,p,1);
2460  else
2461  (void) XDrawImageString(display,windows->image.id,
2462  annotate_context,x,y,p,1);
2463  x+=XTextWidth(font_info,p,1);
2464  p++;
2465  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2466  break;
2467  }
2468  case XK_Return:
2469  case XK_KP_Enter:
2470  {
2471  /*
2472  Advance to the next line of text.
2473  */
2474  *p='\0';
2475  annotate_info->width=(unsigned int) XTextWidth(font_info,
2476  annotate_info->text,(int) strlen(annotate_info->text));
2477  if (annotate_info->next != (XAnnotateInfo *) NULL)
2478  {
2479  /*
2480  Line of text already exists.
2481  */
2482  annotate_info=annotate_info->next;
2483  x=annotate_info->x;
2484  y=annotate_info->y;
2485  p=annotate_info->text;
2486  break;
2487  }
2488  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2489  sizeof(*annotate_info->next));
2490  if (annotate_info->next == (XAnnotateInfo *) NULL)
2491  return(MagickFalse);
2492  *annotate_info->next=(*annotate_info);
2493  annotate_info->next->previous=annotate_info;
2494  annotate_info=annotate_info->next;
2495  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2496  windows->image.width/MagickMax((ssize_t)
2497  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2498  if (annotate_info->text == (char *) NULL)
2499  return(MagickFalse);
2500  annotate_info->y+=annotate_info->height;
2501  if (annotate_info->y > (int) windows->image.height)
2502  annotate_info->y=(int) annotate_info->height;
2503  annotate_info->next=(XAnnotateInfo *) NULL;
2504  x=annotate_info->x;
2505  y=annotate_info->y;
2506  p=annotate_info->text;
2507  break;
2508  }
2509  }
2510  break;
2511  }
2512  case KeyRelease:
2513  {
2514  /*
2515  Respond to a user key release.
2516  */
2517  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2518  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2519  state&=(~ModifierState);
2520  break;
2521  }
2522  case SelectionNotify:
2523  {
2524  Atom
2525  type;
2526 
2527  int
2528  format;
2529 
2530  unsigned char
2531  *data;
2532 
2533  unsigned long
2534  after,
2535  length;
2536 
2537  /*
2538  Obtain response from primary selection.
2539  */
2540  if (event.xselection.property == (Atom) None)
2541  break;
2542  status=XGetWindowProperty(display,event.xselection.requestor,
2543  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2544  &type,&format,&length,&after,&data);
2545  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2546  (length == 0))
2547  break;
2548  /*
2549  Annotate Image window with primary selection.
2550  */
2551  for (i=0; i < (ssize_t) length; i++)
2552  {
2553  if ((char) data[i] != '\n')
2554  {
2555  /*
2556  Draw a single character on the Image window.
2557  */
2558  *p=(char) data[i];
2559  (void) XDrawString(display,windows->image.id,annotate_context,
2560  x,y,p,1);
2561  x+=XTextWidth(font_info,p,1);
2562  p++;
2563  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2564  continue;
2565  }
2566  /*
2567  Advance to the next line of text.
2568  */
2569  *p='\0';
2570  annotate_info->width=(unsigned int) XTextWidth(font_info,
2571  annotate_info->text,(int) strlen(annotate_info->text));
2572  if (annotate_info->next != (XAnnotateInfo *) NULL)
2573  {
2574  /*
2575  Line of text already exists.
2576  */
2577  annotate_info=annotate_info->next;
2578  x=annotate_info->x;
2579  y=annotate_info->y;
2580  p=annotate_info->text;
2581  continue;
2582  }
2583  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2584  sizeof(*annotate_info->next));
2585  if (annotate_info->next == (XAnnotateInfo *) NULL)
2586  return(MagickFalse);
2587  *annotate_info->next=(*annotate_info);
2588  annotate_info->next->previous=annotate_info;
2589  annotate_info=annotate_info->next;
2590  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2591  windows->image.width/MagickMax((ssize_t)
2592  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2593  if (annotate_info->text == (char *) NULL)
2594  return(MagickFalse);
2595  annotate_info->y+=annotate_info->height;
2596  if (annotate_info->y > (int) windows->image.height)
2597  annotate_info->y=(int) annotate_info->height;
2598  annotate_info->next=(XAnnotateInfo *) NULL;
2599  x=annotate_info->x;
2600  y=annotate_info->y;
2601  p=annotate_info->text;
2602  }
2603  (void) XFree((void *) data);
2604  break;
2605  }
2606  default:
2607  break;
2608  }
2609  } while ((state & ExitState) == 0);
2610  (void) XFreeCursor(display,cursor);
2611  /*
2612  Annotation is relative to image configuration.
2613  */
2614  width=(unsigned int) image->columns;
2615  height=(unsigned int) image->rows;
2616  x=0;
2617  y=0;
2618  if (windows->image.crop_geometry != (char *) NULL)
2619  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2620  /*
2621  Initialize annotated image.
2622  */
2623  XSetCursorState(display,windows,MagickTrue);
2624  XCheckRefreshWindows(display,windows);
2625  while (annotate_info != (XAnnotateInfo *) NULL)
2626  {
2627  if (annotate_info->width == 0)
2628  {
2629  /*
2630  No text on this line-- go to the next line of text.
2631  */
2632  previous_info=annotate_info->previous;
2633  annotate_info->text=(char *)
2634  RelinquishMagickMemory(annotate_info->text);
2635  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2636  annotate_info=previous_info;
2637  continue;
2638  }
2639  /*
2640  Determine pixel index for box and pen color.
2641  */
2642  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2643  if (windows->pixel_info->colors != 0)
2644  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2645  if (windows->pixel_info->pixels[i] ==
2646  windows->pixel_info->pen_colors[box_id].pixel)
2647  {
2648  windows->pixel_info->box_index=(unsigned short) i;
2649  break;
2650  }
2651  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2652  if (windows->pixel_info->colors != 0)
2653  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2654  if (windows->pixel_info->pixels[i] ==
2655  windows->pixel_info->pen_colors[pen_id].pixel)
2656  {
2657  windows->pixel_info->pen_index=(unsigned short) i;
2658  break;
2659  }
2660  /*
2661  Define the annotate geometry string.
2662  */
2663  annotate_info->x=(int)
2664  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2665  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2666  windows->image.y)/windows->image.ximage->height;
2667  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2668  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2669  height*annotate_info->height/windows->image.ximage->height,
2670  annotate_info->x+x,annotate_info->y+y);
2671  /*
2672  Annotate image with text.
2673  */
2674  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2675  if (status == 0)
2676  return(MagickFalse);
2677  /*
2678  Free up memory.
2679  */
2680  previous_info=annotate_info->previous;
2681  annotate_info->text=DestroyString(annotate_info->text);
2682  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2683  annotate_info=previous_info;
2684  }
2685  (void) XSetForeground(display,annotate_context,
2686  windows->pixel_info->foreground_color.pixel);
2687  (void) XSetBackground(display,annotate_context,
2688  windows->pixel_info->background_color.pixel);
2689  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2690  XSetCursorState(display,windows,MagickFalse);
2691  (void) XFreeFont(display,font_info);
2692  /*
2693  Update image configuration.
2694  */
2695  XConfigureImageColormap(display,resource_info,windows,image);
2696  (void) XConfigureImage(display,resource_info,windows,image);
2697  return(MagickTrue);
2698 }
2699 
2700 /*
2701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2702 % %
2703 % %
2704 % %
2705 + X B a c k g r o u n d I m a g e %
2706 % %
2707 % %
2708 % %
2709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2710 %
2711 % XBackgroundImage() displays the image in the background of a window.
2712 %
2713 % The format of the XBackgroundImage method is:
2714 %
2715 % MagickBooleanType XBackgroundImage(Display *display,
2716 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2717 %
2718 % A description of each parameter follows:
2719 %
2720 % o display: Specifies a connection to an X server; returned from
2721 % XOpenDisplay.
2722 %
2723 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2724 %
2725 % o windows: Specifies a pointer to a XWindows structure.
2726 %
2727 % o image: the image.
2728 %
2729 */
2730 static MagickBooleanType XBackgroundImage(Display *display,
2731  XResourceInfo *resource_info,XWindows *windows,Image **image)
2732 {
2733 #define BackgroundImageTag "Background/Image"
2734 
2735  int
2736  status;
2737 
2738  static char
2739  window_id[MaxTextExtent] = "root";
2740 
2741  XResourceInfo
2742  background_resources;
2743 
2744  /*
2745  Put image in background.
2746  */
2747  status=XDialogWidget(display,windows,"Background",
2748  "Enter window id (id 0x00 selects window with pointer):",window_id);
2749  if (*window_id == '\0')
2750  return(MagickFalse);
2751  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2752  XInfoWidget(display,windows,BackgroundImageTag);
2753  XSetCursorState(display,windows,MagickTrue);
2754  XCheckRefreshWindows(display,windows);
2755  background_resources=(*resource_info);
2756  background_resources.window_id=window_id;
2757  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2758  status=XDisplayBackgroundImage(display,&background_resources,*image);
2759  if (status != MagickFalse)
2760  XClientMessage(display,windows->image.id,windows->im_protocols,
2761  windows->im_retain_colors,CurrentTime);
2762  XSetCursorState(display,windows,MagickFalse);
2763  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2764  return(MagickTrue);
2765 }
2766 
2767 /*
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 % %
2770 % %
2771 % %
2772 + X C h o p I m a g e %
2773 % %
2774 % %
2775 % %
2776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2777 %
2778 % XChopImage() chops the X image.
2779 %
2780 % The format of the XChopImage method is:
2781 %
2782 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2783 % XWindows *windows,Image **image)
2784 %
2785 % A description of each parameter follows:
2786 %
2787 % o display: Specifies a connection to an X server; returned from
2788 % XOpenDisplay.
2789 %
2790 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2791 %
2792 % o windows: Specifies a pointer to a XWindows structure.
2793 %
2794 % o image: the image.
2795 %
2796 */
2797 static MagickBooleanType XChopImage(Display *display,
2798  XResourceInfo *resource_info,XWindows *windows,Image **image)
2799 {
2800  const char
2801  *const ChopMenu[] =
2802  {
2803  "Direction",
2804  "Help",
2805  "Dismiss",
2806  (char *) NULL
2807  };
2808 
2809  static ModeType
2810  direction = HorizontalChopCommand;
2811 
2812  static const ModeType
2813  ChopCommands[] =
2814  {
2815  ChopDirectionCommand,
2816  ChopHelpCommand,
2817  ChopDismissCommand
2818  },
2819  DirectionCommands[] =
2820  {
2821  HorizontalChopCommand,
2822  VerticalChopCommand
2823  };
2824 
2825  char
2826  text[MaxTextExtent];
2827 
2828  Image
2829  *chop_image;
2830 
2831  int
2832  id,
2833  x,
2834  y;
2835 
2836  MagickRealType
2837  scale_factor;
2838 
2840  chop_info;
2841 
2842  unsigned int
2843  distance,
2844  height,
2845  width;
2846 
2847  size_t
2848  state;
2849 
2850  XEvent
2851  event;
2852 
2853  XSegment
2854  segment_info;
2855 
2856  /*
2857  Map Command widget.
2858  */
2859  (void) CloneString(&windows->command.name,"Chop");
2860  windows->command.data=1;
2861  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2862  (void) XMapRaised(display,windows->command.id);
2863  XClientMessage(display,windows->image.id,windows->im_protocols,
2864  windows->im_update_widget,CurrentTime);
2865  /*
2866  Track pointer until button 1 is pressed.
2867  */
2868  XQueryPosition(display,windows->image.id,&x,&y);
2869  (void) XSelectInput(display,windows->image.id,
2870  windows->image.attributes.event_mask | PointerMotionMask);
2871  state=DefaultState;
2872  (void) memset(&segment_info,0,sizeof(segment_info));
2873  do
2874  {
2875  if (windows->info.mapped != MagickFalse)
2876  {
2877  /*
2878  Display pointer position.
2879  */
2880  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2881  x+windows->image.x,y+windows->image.y);
2882  XInfoWidget(display,windows,text);
2883  }
2884  /*
2885  Wait for next event.
2886  */
2887  XScreenEvent(display,windows,&event);
2888  if (event.xany.window == windows->command.id)
2889  {
2890  /*
2891  Select a command from the Command widget.
2892  */
2893  id=XCommandWidget(display,windows,ChopMenu,&event);
2894  if (id < 0)
2895  continue;
2896  switch (ChopCommands[id])
2897  {
2898  case ChopDirectionCommand:
2899  {
2900  char
2901  command[MaxTextExtent];
2902 
2903  const char
2904  *const Directions[] =
2905  {
2906  "horizontal",
2907  "vertical",
2908  (char *) NULL,
2909  };
2910 
2911  /*
2912  Select a command from the pop-up menu.
2913  */
2914  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2915  if (id >= 0)
2916  direction=DirectionCommands[id];
2917  break;
2918  }
2919  case ChopHelpCommand:
2920  {
2921  XTextViewHelp(display,resource_info,windows,MagickFalse,
2922  "Help Viewer - Image Chop",ImageChopHelp);
2923  break;
2924  }
2925  case ChopDismissCommand:
2926  {
2927  /*
2928  Prematurely exit.
2929  */
2930  state|=EscapeState;
2931  state|=ExitState;
2932  break;
2933  }
2934  default:
2935  break;
2936  }
2937  continue;
2938  }
2939  switch (event.type)
2940  {
2941  case ButtonPress:
2942  {
2943  if (event.xbutton.button != Button1)
2944  break;
2945  if (event.xbutton.window != windows->image.id)
2946  break;
2947  /*
2948  User has committed to start point of chopping line.
2949  */
2950  segment_info.x1=(short int) event.xbutton.x;
2951  segment_info.x2=(short int) event.xbutton.x;
2952  segment_info.y1=(short int) event.xbutton.y;
2953  segment_info.y2=(short int) event.xbutton.y;
2954  state|=ExitState;
2955  break;
2956  }
2957  case ButtonRelease:
2958  break;
2959  case Expose:
2960  break;
2961  case KeyPress:
2962  {
2963  char
2964  command[MaxTextExtent];
2965 
2966  KeySym
2967  key_symbol;
2968 
2969  if (event.xkey.window != windows->image.id)
2970  break;
2971  /*
2972  Respond to a user key press.
2973  */
2974  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2975  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2976  switch ((int) key_symbol)
2977  {
2978  case XK_Escape:
2979  case XK_F20:
2980  {
2981  /*
2982  Prematurely exit.
2983  */
2984  state|=EscapeState;
2985  state|=ExitState;
2986  break;
2987  }
2988  case XK_F1:
2989  case XK_Help:
2990  {
2991  (void) XSetFunction(display,windows->image.highlight_context,
2992  GXcopy);
2993  XTextViewHelp(display,resource_info,windows,MagickFalse,
2994  "Help Viewer - Image Chop",ImageChopHelp);
2995  (void) XSetFunction(display,windows->image.highlight_context,
2996  GXinvert);
2997  break;
2998  }
2999  default:
3000  {
3001  (void) XBell(display,0);
3002  break;
3003  }
3004  }
3005  break;
3006  }
3007  case MotionNotify:
3008  {
3009  /*
3010  Map and unmap Info widget as text cursor crosses its boundaries.
3011  */
3012  x=event.xmotion.x;
3013  y=event.xmotion.y;
3014  if (windows->info.mapped != MagickFalse)
3015  {
3016  if ((x < (int) (windows->info.x+windows->info.width)) &&
3017  (y < (int) (windows->info.y+windows->info.height)))
3018  (void) XWithdrawWindow(display,windows->info.id,
3019  windows->info.screen);
3020  }
3021  else
3022  if ((x > (int) (windows->info.x+windows->info.width)) ||
3023  (y > (int) (windows->info.y+windows->info.height)))
3024  (void) XMapWindow(display,windows->info.id);
3025  }
3026  }
3027  } while ((state & ExitState) == 0);
3028  (void) XSelectInput(display,windows->image.id,
3029  windows->image.attributes.event_mask);
3030  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3031  if ((state & EscapeState) != 0)
3032  return(MagickTrue);
3033  /*
3034  Draw line as pointer moves until the mouse button is released.
3035  */
3036  chop_info.width=0;
3037  chop_info.height=0;
3038  chop_info.x=0;
3039  chop_info.y=0;
3040  distance=0;
3041  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3042  state=DefaultState;
3043  do
3044  {
3045  if (distance > 9)
3046  {
3047  /*
3048  Display info and draw chopping line.
3049  */
3050  if (windows->info.mapped == MagickFalse)
3051  (void) XMapWindow(display,windows->info.id);
3052  (void) FormatLocaleString(text,MaxTextExtent,
3053  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3054  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3055  XInfoWidget(display,windows,text);
3056  XHighlightLine(display,windows->image.id,
3057  windows->image.highlight_context,&segment_info);
3058  }
3059  else
3060  if (windows->info.mapped != MagickFalse)
3061  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3062  /*
3063  Wait for next event.
3064  */
3065  XScreenEvent(display,windows,&event);
3066  if (distance > 9)
3067  XHighlightLine(display,windows->image.id,
3068  windows->image.highlight_context,&segment_info);
3069  switch (event.type)
3070  {
3071  case ButtonPress:
3072  {
3073  segment_info.x2=(short int) event.xmotion.x;
3074  segment_info.y2=(short int) event.xmotion.y;
3075  break;
3076  }
3077  case ButtonRelease:
3078  {
3079  /*
3080  User has committed to chopping line.
3081  */
3082  segment_info.x2=(short int) event.xbutton.x;
3083  segment_info.y2=(short int) event.xbutton.y;
3084  state|=ExitState;
3085  break;
3086  }
3087  case Expose:
3088  break;
3089  case MotionNotify:
3090  {
3091  segment_info.x2=(short int) event.xmotion.x;
3092  segment_info.y2=(short int) event.xmotion.y;
3093  }
3094  default:
3095  break;
3096  }
3097  /*
3098  Check boundary conditions.
3099  */
3100  if (segment_info.x2 < 0)
3101  segment_info.x2=0;
3102  else
3103  if (segment_info.x2 > windows->image.ximage->width)
3104  segment_info.x2=windows->image.ximage->width;
3105  if (segment_info.y2 < 0)
3106  segment_info.y2=0;
3107  else
3108  if (segment_info.y2 > windows->image.ximage->height)
3109  segment_info.y2=windows->image.ximage->height;
3110  distance=(unsigned int)
3111  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3112  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3113  /*
3114  Compute chopping geometry.
3115  */
3116  if (direction == HorizontalChopCommand)
3117  {
3118  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3119  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3120  chop_info.height=0;
3121  chop_info.y=0;
3122  if (segment_info.x1 > (int) segment_info.x2)
3123  {
3124  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3125  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3126  }
3127  }
3128  else
3129  {
3130  chop_info.width=0;
3131  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3132  chop_info.x=0;
3133  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3134  if (segment_info.y1 > segment_info.y2)
3135  {
3136  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3137  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3138  }
3139  }
3140  } while ((state & ExitState) == 0);
3141  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3142  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3143  if (distance <= 9)
3144  return(MagickTrue);
3145  /*
3146  Image chopping is relative to image configuration.
3147  */
3148  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3149  XSetCursorState(display,windows,MagickTrue);
3150  XCheckRefreshWindows(display,windows);
3151  windows->image.window_changes.width=windows->image.ximage->width-
3152  (unsigned int) chop_info.width;
3153  windows->image.window_changes.height=windows->image.ximage->height-
3154  (unsigned int) chop_info.height;
3155  width=(unsigned int) (*image)->columns;
3156  height=(unsigned int) (*image)->rows;
3157  x=0;
3158  y=0;
3159  if (windows->image.crop_geometry != (char *) NULL)
3160  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3161  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3162  chop_info.x+=x;
3163  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3164  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3165  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3166  chop_info.y+=y;
3167  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3168  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3169  /*
3170  Chop image.
3171  */
3172  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3173  XSetCursorState(display,windows,MagickFalse);
3174  if (chop_image == (Image *) NULL)
3175  return(MagickFalse);
3176  *image=DestroyImage(*image);
3177  *image=chop_image;
3178  /*
3179  Update image configuration.
3180  */
3181  XConfigureImageColormap(display,resource_info,windows,*image);
3182  (void) XConfigureImage(display,resource_info,windows,*image);
3183  return(MagickTrue);
3184 }
3185 
3186 /*
3187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3188 % %
3189 % %
3190 % %
3191 + X C o l o r E d i t I m a g e %
3192 % %
3193 % %
3194 % %
3195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3196 %
3197 % XColorEditImage() allows the user to interactively change the color of one
3198 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3199 %
3200 % The format of the XColorEditImage method is:
3201 %
3202 % MagickBooleanType XColorEditImage(Display *display,
3203 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3204 %
3205 % A description of each parameter follows:
3206 %
3207 % o display: Specifies a connection to an X server; returned from
3208 % XOpenDisplay.
3209 %
3210 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3211 %
3212 % o windows: Specifies a pointer to a XWindows structure.
3213 %
3214 % o image: the image; returned from ReadImage.
3215 %
3216 */
3217 
3218 
3219 static MagickBooleanType XColorEditImage(Display *display,
3220  XResourceInfo *resource_info,XWindows *windows,Image **image)
3221 {
3222  const char
3223  *const ColorEditMenu[] =
3224  {
3225  "Method",
3226  "Pixel Color",
3227  "Border Color",
3228  "Fuzz",
3229  "Undo",
3230  "Help",
3231  "Dismiss",
3232  (char *) NULL
3233  };
3234 
3235  static const ModeType
3236  ColorEditCommands[] =
3237  {
3238  ColorEditMethodCommand,
3239  ColorEditColorCommand,
3240  ColorEditBorderCommand,
3241  ColorEditFuzzCommand,
3242  ColorEditUndoCommand,
3243  ColorEditHelpCommand,
3244  ColorEditDismissCommand
3245  };
3246 
3247  static PaintMethod
3248  method = PointMethod;
3249 
3250  static unsigned int
3251  pen_id = 0;
3252 
3253  static XColor
3254  border_color = { 0, 0, 0, 0, 0, 0 };
3255 
3256  char
3257  command[MaxTextExtent] = "",
3258  text[MaxTextExtent] = "";
3259 
3260  Cursor
3261  cursor;
3262 
3264  *exception;
3265 
3266  int
3267  entry,
3268  id,
3269  x,
3270  x_offset,
3271  y,
3272  y_offset;
3273 
3274  PixelPacket
3275  *q;
3276 
3277  ssize_t
3278  i;
3279 
3280  unsigned int
3281  height,
3282  width;
3283 
3284  size_t
3285  state;
3286 
3287  XColor
3288  color;
3289 
3290  XEvent
3291  event;
3292 
3293  /*
3294  Map Command widget.
3295  */
3296  (void) CloneString(&windows->command.name,"Color Edit");
3297  windows->command.data=4;
3298  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3299  (void) XMapRaised(display,windows->command.id);
3300  XClientMessage(display,windows->image.id,windows->im_protocols,
3301  windows->im_update_widget,CurrentTime);
3302  /*
3303  Make cursor.
3304  */
3305  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3306  resource_info->background_color,resource_info->foreground_color);
3307  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3308  /*
3309  Track pointer until button 1 is pressed.
3310  */
3311  XQueryPosition(display,windows->image.id,&x,&y);
3312  (void) XSelectInput(display,windows->image.id,
3313  windows->image.attributes.event_mask | PointerMotionMask);
3314  state=DefaultState;
3315  do
3316  {
3317  if (windows->info.mapped != MagickFalse)
3318  {
3319  /*
3320  Display pointer position.
3321  */
3322  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3323  x+windows->image.x,y+windows->image.y);
3324  XInfoWidget(display,windows,text);
3325  }
3326  /*
3327  Wait for next event.
3328  */
3329  XScreenEvent(display,windows,&event);
3330  if (event.xany.window == windows->command.id)
3331  {
3332  /*
3333  Select a command from the Command widget.
3334  */
3335  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3336  if (id < 0)
3337  {
3338  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3339  continue;
3340  }
3341  switch (ColorEditCommands[id])
3342  {
3343  case ColorEditMethodCommand:
3344  {
3345  char
3346  **methods;
3347 
3348  /*
3349  Select a method from the pop-up menu.
3350  */
3351  methods=(char **) GetCommandOptions(MagickMethodOptions);
3352  if (methods == (char **) NULL)
3353  break;
3354  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3355  (const char **) methods,command);
3356  if (entry >= 0)
3357  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3358  MagickFalse,methods[entry]);
3359  methods=DestroyStringList(methods);
3360  break;
3361  }
3362  case ColorEditColorCommand:
3363  {
3364  const char
3365  *ColorMenu[MaxNumberPens];
3366 
3367  int
3368  pen_number;
3369 
3370  /*
3371  Initialize menu selections.
3372  */
3373  for (i=0; i < (int) (MaxNumberPens-2); i++)
3374  ColorMenu[i]=resource_info->pen_colors[i];
3375  ColorMenu[MaxNumberPens-2]="Browser...";
3376  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3377  /*
3378  Select a pen color from the pop-up menu.
3379  */
3380  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3381  (const char **) ColorMenu,command);
3382  if (pen_number < 0)
3383  break;
3384  if (pen_number == (MaxNumberPens-2))
3385  {
3386  static char
3387  color_name[MaxTextExtent] = "gray";
3388 
3389  /*
3390  Select a pen color from a dialog.
3391  */
3392  resource_info->pen_colors[pen_number]=color_name;
3393  XColorBrowserWidget(display,windows,"Select",color_name);
3394  if (*color_name == '\0')
3395  break;
3396  }
3397  /*
3398  Set pen color.
3399  */
3400  (void) XParseColor(display,windows->map_info->colormap,
3401  resource_info->pen_colors[pen_number],&color);
3402  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3403  (unsigned int) MaxColors,&color);
3404  windows->pixel_info->pen_colors[pen_number]=color;
3405  pen_id=(unsigned int) pen_number;
3406  break;
3407  }
3408  case ColorEditBorderCommand:
3409  {
3410  const char
3411  *ColorMenu[MaxNumberPens];
3412 
3413  int
3414  pen_number;
3415 
3416  /*
3417  Initialize menu selections.
3418  */
3419  for (i=0; i < (int) (MaxNumberPens-2); i++)
3420  ColorMenu[i]=resource_info->pen_colors[i];
3421  ColorMenu[MaxNumberPens-2]="Browser...";
3422  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3423  /*
3424  Select a pen color from the pop-up menu.
3425  */
3426  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3427  (const char **) ColorMenu,command);
3428  if (pen_number < 0)
3429  break;
3430  if (pen_number == (MaxNumberPens-2))
3431  {
3432  static char
3433  color_name[MaxTextExtent] = "gray";
3434 
3435  /*
3436  Select a pen color from a dialog.
3437  */
3438  resource_info->pen_colors[pen_number]=color_name;
3439  XColorBrowserWidget(display,windows,"Select",color_name);
3440  if (*color_name == '\0')
3441  break;
3442  }
3443  /*
3444  Set border color.
3445  */
3446  (void) XParseColor(display,windows->map_info->colormap,
3447  resource_info->pen_colors[pen_number],&border_color);
3448  break;
3449  }
3450  case ColorEditFuzzCommand:
3451  {
3452  static char
3453  fuzz[MaxTextExtent];
3454 
3455  static const char
3456  *FuzzMenu[] =
3457  {
3458  "0%",
3459  "2%",
3460  "5%",
3461  "10%",
3462  "15%",
3463  "Dialog...",
3464  (char *) NULL,
3465  };
3466 
3467  /*
3468  Select a command from the pop-up menu.
3469  */
3470  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3471  command);
3472  if (entry < 0)
3473  break;
3474  if (entry != 5)
3475  {
3476  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3477  QuantumRange+1.0);
3478  break;
3479  }
3480  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3481  (void) XDialogWidget(display,windows,"Ok",
3482  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3483  if (*fuzz == '\0')
3484  break;
3485  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3486  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3487  1.0);
3488  break;
3489  }
3490  case ColorEditUndoCommand:
3491  {
3492  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3493  image);
3494  break;
3495  }
3496  case ColorEditHelpCommand:
3497  default:
3498  {
3499  XTextViewHelp(display,resource_info,windows,MagickFalse,
3500  "Help Viewer - Image Annotation",ImageColorEditHelp);
3501  break;
3502  }
3503  case ColorEditDismissCommand:
3504  {
3505  /*
3506  Prematurely exit.
3507  */
3508  state|=EscapeState;
3509  state|=ExitState;
3510  break;
3511  }
3512  }
3513  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3514  continue;
3515  }
3516  switch (event.type)
3517  {
3518  case ButtonPress:
3519  {
3520  if (event.xbutton.button != Button1)
3521  break;
3522  if ((event.xbutton.window != windows->image.id) &&
3523  (event.xbutton.window != windows->magnify.id))
3524  break;
3525  /*
3526  exit loop.
3527  */
3528  x=event.xbutton.x;
3529  y=event.xbutton.y;
3530  (void) XMagickCommand(display,resource_info,windows,
3531  SaveToUndoBufferCommand,image);
3532  state|=UpdateConfigurationState;
3533  break;
3534  }
3535  case ButtonRelease:
3536  {
3537  if (event.xbutton.button != Button1)
3538  break;
3539  if ((event.xbutton.window != windows->image.id) &&
3540  (event.xbutton.window != windows->magnify.id))
3541  break;
3542  /*
3543  Update colormap information.
3544  */
3545  x=event.xbutton.x;
3546  y=event.xbutton.y;
3547  XConfigureImageColormap(display,resource_info,windows,*image);
3548  (void) XConfigureImage(display,resource_info,windows,*image);
3549  XInfoWidget(display,windows,text);
3550  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3551  state&=(~UpdateConfigurationState);
3552  break;
3553  }
3554  case Expose:
3555  break;
3556  case KeyPress:
3557  {
3558  KeySym
3559  key_symbol;
3560 
3561  if (event.xkey.window == windows->magnify.id)
3562  {
3563  Window
3564  window;
3565 
3566  window=windows->magnify.id;
3567  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3568  }
3569  if (event.xkey.window != windows->image.id)
3570  break;
3571  /*
3572  Respond to a user key press.
3573  */
3574  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3575  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3576  switch ((int) key_symbol)
3577  {
3578  case XK_Escape:
3579  case XK_F20:
3580  {
3581  /*
3582  Prematurely exit.
3583  */
3584  state|=ExitState;
3585  break;
3586  }
3587  case XK_F1:
3588  case XK_Help:
3589  {
3590  XTextViewHelp(display,resource_info,windows,MagickFalse,
3591  "Help Viewer - Image Annotation",ImageColorEditHelp);
3592  break;
3593  }
3594  default:
3595  {
3596  (void) XBell(display,0);
3597  break;
3598  }
3599  }
3600  break;
3601  }
3602  case MotionNotify:
3603  {
3604  /*
3605  Map and unmap Info widget as cursor crosses its boundaries.
3606  */
3607  x=event.xmotion.x;
3608  y=event.xmotion.y;
3609  if (windows->info.mapped != MagickFalse)
3610  {
3611  if ((x < (int) (windows->info.x+windows->info.width)) &&
3612  (y < (int) (windows->info.y+windows->info.height)))
3613  (void) XWithdrawWindow(display,windows->info.id,
3614  windows->info.screen);
3615  }
3616  else
3617  if ((x > (int) (windows->info.x+windows->info.width)) ||
3618  (y > (int) (windows->info.y+windows->info.height)))
3619  (void) XMapWindow(display,windows->info.id);
3620  break;
3621  }
3622  default:
3623  break;
3624  }
3625  if (event.xany.window == windows->magnify.id)
3626  {
3627  x=windows->magnify.x-windows->image.x;
3628  y=windows->magnify.y-windows->image.y;
3629  }
3630  x_offset=x;
3631  y_offset=y;
3632  if ((state & UpdateConfigurationState) != 0)
3633  {
3634  CacheView
3635  *image_view;
3636 
3637  int
3638  x,
3639  y;
3640 
3641  /*
3642  Pixel edit is relative to image configuration.
3643  */
3644  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3645  MagickTrue);
3646  color=windows->pixel_info->pen_colors[pen_id];
3647  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3648  width=(unsigned int) (*image)->columns;
3649  height=(unsigned int) (*image)->rows;
3650  x=0;
3651  y=0;
3652  if (windows->image.crop_geometry != (char *) NULL)
3653  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3654  &width,&height);
3655  x_offset=(int)
3656  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3657  y_offset=(int)
3658  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3659  if ((x_offset < 0) || (y_offset < 0))
3660  continue;
3661  if ((x_offset >= (int) (*image)->columns) ||
3662  (y_offset >= (int) (*image)->rows))
3663  continue;
3664  exception=(&(*image)->exception);
3665  image_view=AcquireAuthenticCacheView(*image,exception);
3666  switch (method)
3667  {
3668  case PointMethod:
3669  default:
3670  {
3671  /*
3672  Update color information using point algorithm.
3673  */
3674  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3675  return(MagickFalse);
3676  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3677  (ssize_t)y_offset,1,1,exception);
3678  if (q == (PixelPacket *) NULL)
3679  break;
3680  q->red=ScaleShortToQuantum(color.red);
3681  q->green=ScaleShortToQuantum(color.green);
3682  q->blue=ScaleShortToQuantum(color.blue);
3683  (void) SyncCacheViewAuthenticPixels(image_view,
3684  &(*image)->exception);
3685  break;
3686  }
3687  case ReplaceMethod:
3688  {
3689  PixelPacket
3690  target;
3691 
3692  /*
3693  Update color information using replace algorithm.
3694  */
3695  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3696  (ssize_t) y_offset,&target,&(*image)->exception);
3697  if ((*image)->storage_class == DirectClass)
3698  {
3699  for (y=0; y < (int) (*image)->rows; y++)
3700  {
3701  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3702  (*image)->columns,1,exception);
3703  if (q == (PixelPacket *) NULL)
3704  break;
3705  for (x=0; x < (int) (*image)->columns; x++)
3706  {
3707  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3708  {
3709  q->red=ScaleShortToQuantum(color.red);
3710  q->green=ScaleShortToQuantum(color.green);
3711  q->blue=ScaleShortToQuantum(color.blue);
3712  }
3713  q++;
3714  }
3715  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3716  break;
3717  }
3718  }
3719  else
3720  {
3721  for (i=0; i < (ssize_t) (*image)->colors; i++)
3722  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3723  {
3724  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3725  (*image)->colormap[i].green=ScaleShortToQuantum(
3726  color.green);
3727  (*image)->colormap[i].blue=ScaleShortToQuantum(
3728  color.blue);
3729  }
3730  (void) SyncImage(*image);
3731  }
3732  break;
3733  }
3734  case FloodfillMethod:
3735  case FillToBorderMethod:
3736  {
3737  DrawInfo
3738  *draw_info;
3739 
3741  target;
3742 
3743  /*
3744  Update color information using floodfill algorithm.
3745  */
3746  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3747  (ssize_t) y_offset,&target,exception);
3748  if (method == FillToBorderMethod)
3749  {
3750  target.red=(MagickRealType)
3751  ScaleShortToQuantum(border_color.red);
3752  target.green=(MagickRealType)
3753  ScaleShortToQuantum(border_color.green);
3754  target.blue=(MagickRealType)
3755  ScaleShortToQuantum(border_color.blue);
3756  }
3757  draw_info=CloneDrawInfo(resource_info->image_info,
3758  (DrawInfo *) NULL);
3759  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3760  &draw_info->fill,exception);
3761  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3762  (ssize_t) x_offset,(ssize_t) y_offset,
3763  method == FloodfillMethod ? MagickFalse : MagickTrue);
3764  draw_info=DestroyDrawInfo(draw_info);
3765  break;
3766  }
3767  case ResetMethod:
3768  {
3769  /*
3770  Update color information using reset algorithm.
3771  */
3772  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3773  return(MagickFalse);
3774  for (y=0; y < (int) (*image)->rows; y++)
3775  {
3776  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3777  (*image)->columns,1,exception);
3778  if (q == (PixelPacket *) NULL)
3779  break;
3780  for (x=0; x < (int) (*image)->columns; x++)
3781  {
3782  q->red=ScaleShortToQuantum(color.red);
3783  q->green=ScaleShortToQuantum(color.green);
3784  q->blue=ScaleShortToQuantum(color.blue);
3785  q++;
3786  }
3787  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3788  break;
3789  }
3790  break;
3791  }
3792  }
3793  image_view=DestroyCacheView(image_view);
3794  state&=(~UpdateConfigurationState);
3795  }
3796  } while ((state & ExitState) == 0);
3797  (void) XSelectInput(display,windows->image.id,
3798  windows->image.attributes.event_mask);
3799  XSetCursorState(display,windows,MagickFalse);
3800  (void) XFreeCursor(display,cursor);
3801  return(MagickTrue);
3802 }
3803 
3804 /*
3805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3806 % %
3807 % %
3808 % %
3809 + X C o m p o s i t e I m a g e %
3810 % %
3811 % %
3812 % %
3813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3814 %
3815 % XCompositeImage() requests an image name from the user, reads the image and
3816 % composites it with the X window image at a location the user chooses with
3817 % the pointer.
3818 %
3819 % The format of the XCompositeImage method is:
3820 %
3821 % MagickBooleanType XCompositeImage(Display *display,
3822 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3823 %
3824 % A description of each parameter follows:
3825 %
3826 % o display: Specifies a connection to an X server; returned from
3827 % XOpenDisplay.
3828 %
3829 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3830 %
3831 % o windows: Specifies a pointer to a XWindows structure.
3832 %
3833 % o image: the image; returned from ReadImage.
3834 %
3835 */
3836 static MagickBooleanType XCompositeImage(Display *display,
3837  XResourceInfo *resource_info,XWindows *windows,Image *image)
3838 {
3839  const char
3840  *const CompositeMenu[] =
3841  {
3842  "Operators",
3843  "Dissolve",
3844  "Displace",
3845  "Help",
3846  "Dismiss",
3847  (char *) NULL
3848  };
3849 
3850  static char
3851  displacement_geometry[MaxTextExtent] = "30x30",
3852  filename[MaxTextExtent] = "\0";
3853 
3854  static CompositeOperator
3855  compose = CopyCompositeOp;
3856 
3857  static const ModeType
3858  CompositeCommands[] =
3859  {
3860  CompositeOperatorsCommand,
3861  CompositeDissolveCommand,
3862  CompositeDisplaceCommand,
3863  CompositeHelpCommand,
3864  CompositeDismissCommand
3865  };
3866 
3867  char
3868  text[MaxTextExtent];
3869 
3870  Cursor
3871  cursor;
3872 
3873  Image
3874  *composite_image;
3875 
3876  int
3877  entry,
3878  id,
3879  x,
3880  y;
3881 
3882  MagickRealType
3883  blend,
3884  scale_factor;
3885 
3887  highlight_info,
3888  composite_info;
3889 
3890  unsigned int
3891  height,
3892  width;
3893 
3894  size_t
3895  state;
3896 
3897  XEvent
3898  event;
3899 
3900  /*
3901  Request image file name from user.
3902  */
3903  XFileBrowserWidget(display,windows,"Composite",filename);
3904  if (*filename == '\0')
3905  return(MagickTrue);
3906  /*
3907  Read image.
3908  */
3909  XSetCursorState(display,windows,MagickTrue);
3910  XCheckRefreshWindows(display,windows);
3911  (void) CopyMagickString(resource_info->image_info->filename,filename,
3912  MaxTextExtent);
3913  composite_image=ReadImage(resource_info->image_info,&image->exception);
3914  CatchException(&image->exception);
3915  XSetCursorState(display,windows,MagickFalse);
3916  if (composite_image == (Image *) NULL)
3917  return(MagickFalse);
3918  /*
3919  Map Command widget.
3920  */
3921  (void) CloneString(&windows->command.name,"Composite");
3922  windows->command.data=1;
3923  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3924  (void) XMapRaised(display,windows->command.id);
3925  XClientMessage(display,windows->image.id,windows->im_protocols,
3926  windows->im_update_widget,CurrentTime);
3927  /*
3928  Track pointer until button 1 is pressed.
3929  */
3930  XQueryPosition(display,windows->image.id,&x,&y);
3931  (void) XSelectInput(display,windows->image.id,
3932  windows->image.attributes.event_mask | PointerMotionMask);
3933  composite_info.x=(ssize_t) windows->image.x+x;
3934  composite_info.y=(ssize_t) windows->image.y+y;
3935  composite_info.width=0;
3936  composite_info.height=0;
3937  cursor=XCreateFontCursor(display,XC_ul_angle);
3938  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3939  blend=0.0;
3940  state=DefaultState;
3941  do
3942  {
3943  if (windows->info.mapped != MagickFalse)
3944  {
3945  /*
3946  Display pointer position.
3947  */
3948  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3949  (long) composite_info.x,(long) composite_info.y);
3950  XInfoWidget(display,windows,text);
3951  }
3952  highlight_info=composite_info;
3953  highlight_info.x=composite_info.x-windows->image.x;
3954  highlight_info.y=composite_info.y-windows->image.y;
3955  XHighlightRectangle(display,windows->image.id,
3956  windows->image.highlight_context,&highlight_info);
3957  /*
3958  Wait for next event.
3959  */
3960  XScreenEvent(display,windows,&event);
3961  XHighlightRectangle(display,windows->image.id,
3962  windows->image.highlight_context,&highlight_info);
3963  if (event.xany.window == windows->command.id)
3964  {
3965  /*
3966  Select a command from the Command widget.
3967  */
3968  id=XCommandWidget(display,windows,CompositeMenu,&event);
3969  if (id < 0)
3970  continue;
3971  switch (CompositeCommands[id])
3972  {
3973  case CompositeOperatorsCommand:
3974  {
3975  char
3976  command[MaxTextExtent],
3977  **operators;
3978 
3979  /*
3980  Select a command from the pop-up menu.
3981  */
3982  operators=GetCommandOptions(MagickComposeOptions);
3983  if (operators == (char **) NULL)
3984  break;
3985  entry=XMenuWidget(display,windows,CompositeMenu[id],
3986  (const char **) operators,command);
3987  if (entry >= 0)
3988  compose=(CompositeOperator) ParseCommandOption(
3989  MagickComposeOptions,MagickFalse,operators[entry]);
3990  operators=DestroyStringList(operators);
3991  break;
3992  }
3993  case CompositeDissolveCommand:
3994  {
3995  static char
3996  factor[MaxTextExtent] = "20.0";
3997 
3998  /*
3999  Dissolve the two images a given percent.
4000  */
4001  (void) XSetFunction(display,windows->image.highlight_context,
4002  GXcopy);
4003  (void) XDialogWidget(display,windows,"Dissolve",
4004  "Enter the blend factor (0.0 - 99.9%):",factor);
4005  (void) XSetFunction(display,windows->image.highlight_context,
4006  GXinvert);
4007  if (*factor == '\0')
4008  break;
4009  blend=StringToDouble(factor,(char **) NULL);
4010  compose=DissolveCompositeOp;
4011  break;
4012  }
4013  case CompositeDisplaceCommand:
4014  {
4015  /*
4016  Get horizontal and vertical scale displacement geometry.
4017  */
4018  (void) XSetFunction(display,windows->image.highlight_context,
4019  GXcopy);
4020  (void) XDialogWidget(display,windows,"Displace",
4021  "Enter the horizontal and vertical scale:",displacement_geometry);
4022  (void) XSetFunction(display,windows->image.highlight_context,
4023  GXinvert);
4024  if (*displacement_geometry == '\0')
4025  break;
4026  compose=DisplaceCompositeOp;
4027  break;
4028  }
4029  case CompositeHelpCommand:
4030  {
4031  (void) XSetFunction(display,windows->image.highlight_context,
4032  GXcopy);
4033  XTextViewHelp(display,resource_info,windows,MagickFalse,
4034  "Help Viewer - Image Composite",ImageCompositeHelp);
4035  (void) XSetFunction(display,windows->image.highlight_context,
4036  GXinvert);
4037  break;
4038  }
4039  case CompositeDismissCommand:
4040  {
4041  /*
4042  Prematurely exit.
4043  */
4044  state|=EscapeState;
4045  state|=ExitState;
4046  break;
4047  }
4048  default:
4049  break;
4050  }
4051  continue;
4052  }
4053  switch (event.type)
4054  {
4055  case ButtonPress:
4056  {
4057  if (resource_info->debug != MagickFalse)
4058  (void) LogMagickEvent(X11Event,GetMagickModule(),
4059  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4060  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4061  if (event.xbutton.button != Button1)
4062  break;
4063  if (event.xbutton.window != windows->image.id)
4064  break;
4065  /*
4066  Change cursor.
4067  */
4068  composite_info.width=composite_image->columns;
4069  composite_info.height=composite_image->rows;
4070  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4071  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4072  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4073  break;
4074  }
4075  case ButtonRelease:
4076  {
4077  if (resource_info->debug != MagickFalse)
4078  (void) LogMagickEvent(X11Event,GetMagickModule(),
4079  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4080  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4081  if (event.xbutton.button != Button1)
4082  break;
4083  if (event.xbutton.window != windows->image.id)
4084  break;
4085  if ((composite_info.width != 0) && (composite_info.height != 0))
4086  {
4087  /*
4088  User has selected the location of the composite image.
4089  */
4090  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4091  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4092  state|=ExitState;
4093  }
4094  break;
4095  }
4096  case Expose:
4097  break;
4098  case KeyPress:
4099  {
4100  char
4101  command[MaxTextExtent];
4102 
4103  KeySym
4104  key_symbol;
4105 
4106  int
4107  length;
4108 
4109  if (event.xkey.window != windows->image.id)
4110  break;
4111  /*
4112  Respond to a user key press.
4113  */
4114  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4115  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4116  *(command+length)='\0';
4117  if (resource_info->debug != MagickFalse)
4118  (void) LogMagickEvent(X11Event,GetMagickModule(),
4119  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4120  switch ((int) key_symbol)
4121  {
4122  case XK_Escape:
4123  case XK_F20:
4124  {
4125  /*
4126  Prematurely exit.
4127  */
4128  composite_image=DestroyImage(composite_image);
4129  state|=EscapeState;
4130  state|=ExitState;
4131  break;
4132  }
4133  case XK_F1:
4134  case XK_Help:
4135  {
4136  (void) XSetFunction(display,windows->image.highlight_context,
4137  GXcopy);
4138  XTextViewHelp(display,resource_info,windows,MagickFalse,
4139  "Help Viewer - Image Composite",ImageCompositeHelp);
4140  (void) XSetFunction(display,windows->image.highlight_context,
4141  GXinvert);
4142  break;
4143  }
4144  default:
4145  {
4146  (void) XBell(display,0);
4147  break;
4148  }
4149  }
4150  break;
4151  }
4152  case MotionNotify:
4153  {
4154  /*
4155  Map and unmap Info widget as text cursor crosses its boundaries.
4156  */
4157  x=event.xmotion.x;
4158  y=event.xmotion.y;
4159  if (windows->info.mapped != MagickFalse)
4160  {
4161  if ((x < (int) (windows->info.x+windows->info.width)) &&
4162  (y < (int) (windows->info.y+windows->info.height)))
4163  (void) XWithdrawWindow(display,windows->info.id,
4164  windows->info.screen);
4165  }
4166  else
4167  if ((x > (int) (windows->info.x+windows->info.width)) ||
4168  (y > (int) (windows->info.y+windows->info.height)))
4169  (void) XMapWindow(display,windows->info.id);
4170  composite_info.x=(ssize_t) windows->image.x+x;
4171  composite_info.y=(ssize_t) windows->image.y+y;
4172  break;
4173  }
4174  default:
4175  {
4176  if (resource_info->debug != MagickFalse)
4177  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4178  event.type);
4179  break;
4180  }
4181  }
4182  } while ((state & ExitState) == 0);
4183  (void) XSelectInput(display,windows->image.id,
4184  windows->image.attributes.event_mask);
4185  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4186  XSetCursorState(display,windows,MagickFalse);
4187  (void) XFreeCursor(display,cursor);
4188  if ((state & EscapeState) != 0)
4189  return(MagickTrue);
4190  /*
4191  Image compositing is relative to image configuration.
4192  */
4193  XSetCursorState(display,windows,MagickTrue);
4194  XCheckRefreshWindows(display,windows);
4195  width=(unsigned int) image->columns;
4196  height=(unsigned int) image->rows;
4197  x=0;
4198  y=0;
4199  if (windows->image.crop_geometry != (char *) NULL)
4200  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4201  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4202  composite_info.x+=x;
4203  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4204  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4205  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4206  composite_info.y+=y;
4207  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4208  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4209  if ((composite_info.width != composite_image->columns) ||
4210  (composite_info.height != composite_image->rows))
4211  {
4212  Image
4213  *resize_image;
4214 
4215  /*
4216  Scale composite image.
4217  */
4218  resize_image=ResizeImage(composite_image,composite_info.width,
4219  composite_info.height,composite_image->filter,composite_image->blur,
4220  &image->exception);
4221  composite_image=DestroyImage(composite_image);
4222  if (resize_image == (Image *) NULL)
4223  {
4224  XSetCursorState(display,windows,MagickFalse);
4225  return(MagickFalse);
4226  }
4227  composite_image=resize_image;
4228  }
4229  if (compose == DisplaceCompositeOp)
4230  (void) SetImageArtifact(composite_image,"compose:args",
4231  displacement_geometry);
4232  if (blend != 0.0)
4233  {
4234  CacheView
4235  *image_view;
4236 
4238  *exception;
4239 
4240  int
4241  y;
4242 
4243  Quantum
4244  opacity;
4245 
4246  int
4247  x;
4248 
4249  PixelPacket
4250  *q;
4251 
4252  /*
4253  Create mattes for blending.
4254  */
4255  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4256  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4257  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4258  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4259  return(MagickFalse);
4260  image->matte=MagickTrue;
4261  exception=(&image->exception);
4262  image_view=AcquireAuthenticCacheView(image,exception);
4263  for (y=0; y < (int) image->rows; y++)
4264  {
4265  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4266  exception);
4267  if (q == (PixelPacket *) NULL)
4268  break;
4269  for (x=0; x < (int) image->columns; x++)
4270  {
4271  q->opacity=opacity;
4272  q++;
4273  }
4274  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4275  break;
4276  }
4277  image_view=DestroyCacheView(image_view);
4278  }
4279  /*
4280  Composite image with X Image window.
4281  */
4282  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4283  composite_info.y);
4284  composite_image=DestroyImage(composite_image);
4285  XSetCursorState(display,windows,MagickFalse);
4286  /*
4287  Update image configuration.
4288  */
4289  XConfigureImageColormap(display,resource_info,windows,image);
4290  (void) XConfigureImage(display,resource_info,windows,image);
4291  return(MagickTrue);
4292 }
4293 
4294 /*
4295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4296 % %
4297 % %
4298 % %
4299 + X C o n f i g u r e I m a g e %
4300 % %
4301 % %
4302 % %
4303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4304 %
4305 % XConfigureImage() creates a new X image. It also notifies the window
4306 % manager of the new image size and configures the transient widows.
4307 %
4308 % The format of the XConfigureImage method is:
4309 %
4310 % MagickBooleanType XConfigureImage(Display *display,
4311 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4312 %
4313 % A description of each parameter follows:
4314 %
4315 % o display: Specifies a connection to an X server; returned from
4316 % XOpenDisplay.
4317 %
4318 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4319 %
4320 % o windows: Specifies a pointer to a XWindows structure.
4321 %
4322 % o image: the image.
4323 %
4324 %
4325 */
4326 static MagickBooleanType XConfigureImage(Display *display,
4327  XResourceInfo *resource_info,XWindows *windows,Image *image)
4328 {
4329  char
4330  geometry[MaxTextExtent];
4331 
4332  MagickStatusType
4333  status;
4334 
4335  size_t
4336  mask,
4337  height,
4338  width;
4339 
4340  ssize_t
4341  x,
4342  y;
4343 
4344  XSizeHints
4345  *size_hints;
4346 
4347  XWindowChanges
4348  window_changes;
4349 
4350  /*
4351  Dismiss if window dimensions are zero.
4352  */
4353  width=(unsigned int) windows->image.window_changes.width;
4354  height=(unsigned int) windows->image.window_changes.height;
4355  if (resource_info->debug != MagickFalse)
4356  (void) LogMagickEvent(X11Event,GetMagickModule(),
4357  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4358  windows->image.ximage->height,(double) width,(double) height);
4359  if ((width*height) == 0)
4360  return(MagickTrue);
4361  x=0;
4362  y=0;
4363  /*
4364  Resize image to fit Image window dimensions.
4365  */
4366  XSetCursorState(display,windows,MagickTrue);
4367  (void) XFlush(display);
4368  if (((int) width != windows->image.ximage->width) ||
4369  ((int) height != windows->image.ximage->height))
4370  image->taint=MagickTrue;
4371  windows->magnify.x=(int)
4372  width*windows->magnify.x/windows->image.ximage->width;
4373  windows->magnify.y=(int)
4374  height*windows->magnify.y/windows->image.ximage->height;
4375  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4376  windows->image.y=(int)
4377  (height*windows->image.y/windows->image.ximage->height);
4378  status=XMakeImage(display,resource_info,&windows->image,image,
4379  (unsigned int) width,(unsigned int) height);
4380  if (status == MagickFalse)
4381  XNoticeWidget(display,windows,"Unable to configure X image:",
4382  windows->image.name);
4383  /*
4384  Notify window manager of the new configuration.
4385  */
4386  if (resource_info->image_geometry != (char *) NULL)
4387  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4388  resource_info->image_geometry);
4389  else
4390  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4391  XDisplayWidth(display,windows->image.screen),
4392  XDisplayHeight(display,windows->image.screen));
4393  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4394  window_changes.width=(int) width;
4395  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4396  window_changes.width=XDisplayWidth(display,windows->image.screen);
4397  window_changes.height=(int) height;
4398  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4399  window_changes.height=XDisplayHeight(display,windows->image.screen);
4400  mask=(size_t) (CWWidth | CWHeight);
4401  if (resource_info->backdrop)
4402  {
4403  mask|=CWX | CWY;
4404  window_changes.x=(int)
4405  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4406  window_changes.y=(int)
4407  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4408  }
4409  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4410  (unsigned int) mask,&window_changes);
4411  (void) XClearWindow(display,windows->image.id);
4412  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4413  /*
4414  Update Magnify window configuration.
4415  */
4416  if (windows->magnify.mapped != MagickFalse)
4417  XMakeMagnifyImage(display,windows);
4418  windows->pan.crop_geometry=windows->image.crop_geometry;
4419  XBestIconSize(display,&windows->pan,image);
4420  while (((windows->pan.width << 1) < MaxIconSize) &&
4421  ((windows->pan.height << 1) < MaxIconSize))
4422  {
4423  windows->pan.width<<=1;
4424  windows->pan.height<<=1;
4425  }
4426  if (windows->pan.geometry != (char *) NULL)
4427  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4428  &windows->pan.width,&windows->pan.height);
4429  window_changes.width=(int) windows->pan.width;
4430  window_changes.height=(int) windows->pan.height;
4431  size_hints=XAllocSizeHints();
4432  if (size_hints != (XSizeHints *) NULL)
4433  {
4434  /*
4435  Set new size hints.
4436  */
4437  size_hints->flags=PSize | PMinSize | PMaxSize;
4438  size_hints->width=window_changes.width;
4439  size_hints->height=window_changes.height;
4440  size_hints->min_width=size_hints->width;
4441  size_hints->min_height=size_hints->height;
4442  size_hints->max_width=size_hints->width;
4443  size_hints->max_height=size_hints->height;
4444  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4445  (void) XFree((void *) size_hints);
4446  }
4447  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4448  (unsigned int) (CWWidth | CWHeight),&window_changes);
4449  /*
4450  Update icon window configuration.
4451  */
4452  windows->icon.crop_geometry=windows->image.crop_geometry;
4453  XBestIconSize(display,&windows->icon,image);
4454  window_changes.width=(int) windows->icon.width;
4455  window_changes.height=(int) windows->icon.height;
4456  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4457  (unsigned int) (CWWidth | CWHeight),&window_changes);
4458  XSetCursorState(display,windows,MagickFalse);
4459  return(status != 0 ? MagickTrue : MagickFalse);
4460 }
4461 
4462 /*
4463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4464 % %
4465 % %
4466 % %
4467 + X C r o p I m a g e %
4468 % %
4469 % %
4470 % %
4471 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4472 %
4473 % XCropImage() allows the user to select a region of the image and crop, copy,
4474 % or cut it. For copy or cut, the image can subsequently be composited onto
4475 % the image with XPasteImage.
4476 %
4477 % The format of the XCropImage method is:
4478 %
4479 % MagickBooleanType XCropImage(Display *display,
4480 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4481 % const ClipboardMode mode)
4482 %
4483 % A description of each parameter follows:
4484 %
4485 % o display: Specifies a connection to an X server; returned from
4486 % XOpenDisplay.
4487 %
4488 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4489 %
4490 % o windows: Specifies a pointer to a XWindows structure.
4491 %
4492 % o image: the image; returned from ReadImage.
4493 %
4494 % o mode: This unsigned value specified whether the image should be
4495 % cropped, copied, or cut.
4496 %
4497 */
4498 static MagickBooleanType XCropImage(Display *display,
4499  XResourceInfo *resource_info,XWindows *windows,Image *image,
4500  const ClipboardMode mode)
4501 {
4502  static const char
4503  *CropModeMenu[] =
4504  {
4505  "Help",
4506  "Dismiss",
4507  (char *) NULL
4508  },
4509  *RectifyModeMenu[] =
4510  {
4511  "Crop",
4512  "Help",
4513  "Dismiss",
4514  (char *) NULL
4515  };
4516 
4517  static const ModeType
4518  CropCommands[] =
4519  {
4520  CropHelpCommand,
4521  CropDismissCommand
4522  },
4523  RectifyCommands[] =
4524  {
4525  RectifyCopyCommand,
4526  RectifyHelpCommand,
4527  RectifyDismissCommand
4528  };
4529 
4530  CacheView
4531  *image_view;
4532 
4533  char
4534  command[MaxTextExtent],
4535  text[MaxTextExtent];
4536 
4537  Cursor
4538  cursor;
4539 
4541  *exception;
4542 
4543  int
4544  id,
4545  x,
4546  y;
4547 
4548  KeySym
4549  key_symbol;
4550 
4551  Image
4552  *crop_image;
4553 
4554  MagickRealType
4555  scale_factor;
4556 
4558  crop_info,
4559  highlight_info;
4560 
4561  PixelPacket
4562  *q;
4563 
4564  unsigned int
4565  height,
4566  width;
4567 
4568  size_t
4569  state;
4570 
4571  XEvent
4572  event;
4573 
4574  /*
4575  Map Command widget.
4576  */
4577  switch (mode)
4578  {
4579  case CopyMode:
4580  {
4581  (void) CloneString(&windows->command.name,"Copy");
4582  break;
4583  }
4584  case CropMode:
4585  {
4586  (void) CloneString(&windows->command.name,"Crop");
4587  break;
4588  }
4589  case CutMode:
4590  {
4591  (void) CloneString(&windows->command.name,"Cut");
4592  break;
4593  }
4594  }
4595  RectifyModeMenu[0]=windows->command.name;
4596  windows->command.data=0;
4597  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4598  (void) XMapRaised(display,windows->command.id);
4599  XClientMessage(display,windows->image.id,windows->im_protocols,
4600  windows->im_update_widget,CurrentTime);
4601  /*
4602  Track pointer until button 1 is pressed.
4603  */
4604  XQueryPosition(display,windows->image.id,&x,&y);
4605  (void) XSelectInput(display,windows->image.id,
4606  windows->image.attributes.event_mask | PointerMotionMask);
4607  crop_info.x=(ssize_t) windows->image.x+x;
4608  crop_info.y=(ssize_t) windows->image.y+y;
4609  crop_info.width=0;
4610  crop_info.height=0;
4611  cursor=XCreateFontCursor(display,XC_fleur);
4612  state=DefaultState;
4613  do
4614  {
4615  if (windows->info.mapped != MagickFalse)
4616  {
4617  /*
4618  Display pointer position.
4619  */
4620  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4621  (long) crop_info.x,(long) crop_info.y);
4622  XInfoWidget(display,windows,text);
4623  }
4624  /*
4625  Wait for next event.
4626  */
4627  XScreenEvent(display,windows,&event);
4628  if (event.xany.window == windows->command.id)
4629  {
4630  /*
4631  Select a command from the Command widget.
4632  */
4633  id=XCommandWidget(display,windows,CropModeMenu,&event);
4634  if (id < 0)
4635  continue;
4636  switch (CropCommands[id])
4637  {
4638  case CropHelpCommand:
4639  {
4640  switch (mode)
4641  {
4642  case CopyMode:
4643  {
4644  XTextViewHelp(display,resource_info,windows,MagickFalse,
4645  "Help Viewer - Image Copy",ImageCopyHelp);
4646  break;
4647  }
4648  case CropMode:
4649  {
4650  XTextViewHelp(display,resource_info,windows,MagickFalse,
4651  "Help Viewer - Image Crop",ImageCropHelp);
4652  break;
4653  }
4654  case CutMode:
4655  {
4656  XTextViewHelp(display,resource_info,windows,MagickFalse,
4657  "Help Viewer - Image Cut",ImageCutHelp);
4658  break;
4659  }
4660  }
4661  break;
4662  }
4663  case CropDismissCommand:
4664  {
4665  /*
4666  Prematurely exit.
4667  */
4668  state|=EscapeState;
4669  state|=ExitState;
4670  break;
4671  }
4672  default:
4673  break;
4674  }
4675  continue;
4676  }
4677  switch (event.type)
4678  {
4679  case ButtonPress:
4680  {
4681  if (event.xbutton.button != Button1)
4682  break;
4683  if (event.xbutton.window != windows->image.id)
4684  break;
4685  /*
4686  Note first corner of cropping rectangle-- exit loop.
4687  */
4688  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4689  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4690  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4691  state|=ExitState;
4692  break;
4693  }
4694  case ButtonRelease:
4695  break;
4696  case Expose:
4697  break;
4698  case KeyPress:
4699  {
4700  if (event.xkey.window != windows->image.id)
4701  break;
4702  /*
4703  Respond to a user key press.
4704  */
4705  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4706  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4707  switch ((int) key_symbol)
4708  {
4709  case XK_Escape:
4710  case XK_F20:
4711  {
4712  /*
4713  Prematurely exit.
4714  */
4715  state|=EscapeState;
4716  state|=ExitState;
4717  break;
4718  }
4719  case XK_F1:
4720  case XK_Help:
4721  {
4722  switch (mode)
4723  {
4724  case CopyMode:
4725  {
4726  XTextViewHelp(display,resource_info,windows,MagickFalse,
4727  "Help Viewer - Image Copy",ImageCopyHelp);
4728  break;
4729  }
4730  case CropMode:
4731  {
4732  XTextViewHelp(display,resource_info,windows,MagickFalse,
4733  "Help Viewer - Image Crop",ImageCropHelp);
4734  break;
4735  }
4736  case CutMode:
4737  {
4738  XTextViewHelp(display,resource_info,windows,MagickFalse,
4739  "Help Viewer - Image Cut",ImageCutHelp);
4740  break;
4741  }
4742  }
4743  break;
4744  }
4745  default:
4746  {
4747  (void) XBell(display,0);
4748  break;
4749  }
4750  }
4751  break;
4752  }
4753  case MotionNotify:
4754  {
4755  if (event.xmotion.window != windows->image.id)
4756  break;
4757  /*
4758  Map and unmap Info widget as text cursor crosses its boundaries.
4759  */
4760  x=event.xmotion.x;
4761  y=event.xmotion.y;
4762  if (windows->info.mapped != MagickFalse)
4763  {
4764  if ((x < (int) (windows->info.x+windows->info.width)) &&
4765  (y < (int) (windows->info.y+windows->info.height)))
4766  (void) XWithdrawWindow(display,windows->info.id,
4767  windows->info.screen);
4768  }
4769  else
4770  if ((x > (int) (windows->info.x+windows->info.width)) ||
4771  (y > (int) (windows->info.y+windows->info.height)))
4772  (void) XMapWindow(display,windows->info.id);
4773  crop_info.x=(ssize_t) windows->image.x+x;
4774  crop_info.y=(ssize_t) windows->image.y+y;
4775  break;
4776  }
4777  default:
4778  break;
4779  }
4780  } while ((state & ExitState) == 0);
4781  (void) XSelectInput(display,windows->image.id,
4782  windows->image.attributes.event_mask);
4783  if ((state & EscapeState) != 0)
4784  {
4785  /*
4786  User want to exit without cropping.
4787  */
4788  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4789  (void) XFreeCursor(display,cursor);
4790  return(MagickTrue);
4791  }
4792  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4793  do
4794  {
4795  /*
4796  Size rectangle as pointer moves until the mouse button is released.
4797  */
4798  x=(int) crop_info.x;
4799  y=(int) crop_info.y;
4800  crop_info.width=0;
4801  crop_info.height=0;
4802  state=DefaultState;
4803  do
4804  {
4805  highlight_info=crop_info;
4806  highlight_info.x=crop_info.x-windows->image.x;
4807  highlight_info.y=crop_info.y-windows->image.y;
4808  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4809  {
4810  /*
4811  Display info and draw cropping rectangle.
4812  */
4813  if (windows->info.mapped == MagickFalse)
4814  (void) XMapWindow(display,windows->info.id);
4815  (void) FormatLocaleString(text,MaxTextExtent,
4816  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4817  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4818  XInfoWidget(display,windows,text);
4819  XHighlightRectangle(display,windows->image.id,
4820  windows->image.highlight_context,&highlight_info);
4821  }
4822  else
4823  if (windows->info.mapped != MagickFalse)
4824  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4825  /*
4826  Wait for next event.
4827  */
4828  XScreenEvent(display,windows,&event);
4829  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4830  XHighlightRectangle(display,windows->image.id,
4831  windows->image.highlight_context,&highlight_info);
4832  switch (event.type)
4833  {
4834  case ButtonPress:
4835  {
4836  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4837  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4838  break;
4839  }
4840  case ButtonRelease:
4841  {
4842  /*
4843  User has committed to cropping rectangle.
4844  */
4845  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4846  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4847  XSetCursorState(display,windows,MagickFalse);
4848  state|=ExitState;
4849  windows->command.data=0;
4850  (void) XCommandWidget(display,windows,RectifyModeMenu,
4851  (XEvent *) NULL);
4852  break;
4853  }
4854  case Expose:
4855  break;
4856  case MotionNotify:
4857  {
4858  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4859  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4860  }
4861  default:
4862  break;
4863  }
4864  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4865  ((state & ExitState) != 0))
4866  {
4867  /*
4868  Check boundary conditions.
4869  */
4870  if (crop_info.x < 0)
4871  crop_info.x=0;
4872  else
4873  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4874  crop_info.x=(ssize_t) windows->image.ximage->width;
4875  if ((int) crop_info.x < x)
4876  crop_info.width=(unsigned int) (x-crop_info.x);
4877  else
4878  {
4879  crop_info.width=(unsigned int) (crop_info.x-x);
4880  crop_info.x=(ssize_t) x;
4881  }
4882  if (crop_info.y < 0)
4883  crop_info.y=0;
4884  else
4885  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4886  crop_info.y=(ssize_t) windows->image.ximage->height;
4887  if ((int) crop_info.y < y)
4888  crop_info.height=(unsigned int) (y-crop_info.y);
4889  else
4890  {
4891  crop_info.height=(unsigned int) (crop_info.y-y);
4892  crop_info.y=(ssize_t) y;
4893  }
4894  }
4895  } while ((state & ExitState) == 0);
4896  /*
4897  Wait for user to grab a corner of the rectangle or press return.
4898  */
4899  state=DefaultState;
4900  (void) XMapWindow(display,windows->info.id);
4901  do
4902  {
4903  if (windows->info.mapped != MagickFalse)
4904  {
4905  /*
4906  Display pointer position.
4907  */
4908  (void) FormatLocaleString(text,MaxTextExtent,
4909  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4910  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4911  XInfoWidget(display,windows,text);
4912  }
4913  highlight_info=crop_info;
4914  highlight_info.x=crop_info.x-windows->image.x;
4915  highlight_info.y=crop_info.y-windows->image.y;
4916  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4917  {
4918  state|=EscapeState;
4919  state|=ExitState;
4920  break;
4921  }
4922  XHighlightRectangle(display,windows->image.id,
4923  windows->image.highlight_context,&highlight_info);
4924  XScreenEvent(display,windows,&event);
4925  if (event.xany.window == windows->command.id)
4926  {
4927  /*
4928  Select a command from the Command widget.
4929  */
4930  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4931  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4932  (void) XSetFunction(display,windows->image.highlight_context,
4933  GXinvert);
4934  XHighlightRectangle(display,windows->image.id,
4935  windows->image.highlight_context,&highlight_info);
4936  if (id >= 0)
4937  switch (RectifyCommands[id])
4938  {
4939  case RectifyCopyCommand:
4940  {
4941  state|=ExitState;
4942  break;
4943  }
4944  case RectifyHelpCommand:
4945  {
4946  (void) XSetFunction(display,windows->image.highlight_context,
4947  GXcopy);
4948  switch (mode)
4949  {
4950  case CopyMode:
4951  {
4952  XTextViewHelp(display,resource_info,windows,MagickFalse,
4953  "Help Viewer - Image Copy",ImageCopyHelp);
4954  break;
4955  }
4956  case CropMode:
4957  {
4958  XTextViewHelp(display,resource_info,windows,MagickFalse,
4959  "Help Viewer - Image Crop",ImageCropHelp);
4960  break;
4961  }
4962  case CutMode:
4963  {
4964  XTextViewHelp(display,resource_info,windows,MagickFalse,
4965  "Help Viewer - Image Cut",ImageCutHelp);
4966  break;
4967  }
4968  }
4969  (void) XSetFunction(display,windows->image.highlight_context,
4970  GXinvert);
4971  break;
4972  }
4973  case RectifyDismissCommand:
4974  {
4975  /*
4976  Prematurely exit.
4977  */
4978  state|=EscapeState;
4979  state|=ExitState;
4980  break;
4981  }
4982  default:
4983  break;
4984  }
4985  continue;
4986  }
4987  XHighlightRectangle(display,windows->image.id,
4988  windows->image.highlight_context,&highlight_info);
4989  switch (event.type)
4990  {
4991  case ButtonPress:
4992  {
4993  if (event.xbutton.button != Button1)
4994  break;
4995  if (event.xbutton.window != windows->image.id)
4996  break;
4997  x=windows->image.x+event.xbutton.x;
4998  y=windows->image.y+event.xbutton.y;
4999  if ((x < (int) (crop_info.x+RoiDelta)) &&
5000  (x > (int) (crop_info.x-RoiDelta)) &&
5001  (y < (int) (crop_info.y+RoiDelta)) &&
5002  (y > (int) (crop_info.y-RoiDelta)))
5003  {
5004  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5005  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5006  state|=UpdateConfigurationState;
5007  break;
5008  }
5009  if ((x < (int) (crop_info.x+RoiDelta)) &&
5010  (x > (int) (crop_info.x-RoiDelta)) &&
5011  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5012  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5013  {
5014  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5015  state|=UpdateConfigurationState;
5016  break;
5017  }
5018  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5019  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5020  (y < (int) (crop_info.y+RoiDelta)) &&
5021  (y > (int) (crop_info.y-RoiDelta)))
5022  {
5023  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5024  state|=UpdateConfigurationState;
5025  break;
5026  }
5027  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5028  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5029  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5030  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5031  {
5032  state|=UpdateConfigurationState;
5033  break;
5034  }
5035  }
5036  case ButtonRelease:
5037  {
5038  if (event.xbutton.window == windows->pan.id)
5039  if ((highlight_info.x != crop_info.x-windows->image.x) ||
5040  (highlight_info.y != crop_info.y-windows->image.y))
5041  XHighlightRectangle(display,windows->image.id,
5042  windows->image.highlight_context,&highlight_info);
5043  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5044  event.xbutton.time);
5045  break;
5046  }
5047  case Expose:
5048  {
5049  if (event.xexpose.window == windows->image.id)
5050  if (event.xexpose.count == 0)
5051  {
5052  event.xexpose.x=(int) highlight_info.x;
5053  event.xexpose.y=(int) highlight_info.y;
5054  event.xexpose.width=(int) highlight_info.width;
5055  event.xexpose.height=(int) highlight_info.height;
5056  XRefreshWindow(display,&windows->image,&event);
5057  }
5058  if (event.xexpose.window == windows->info.id)
5059  if (event.xexpose.count == 0)
5060  XInfoWidget(display,windows,text);
5061  break;
5062  }
5063  case KeyPress:
5064  {
5065  if (event.xkey.window != windows->image.id)
5066  break;
5067  /*
5068  Respond to a user key press.
5069  */
5070  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5071  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5072  switch ((int) key_symbol)
5073  {
5074  case XK_Escape:
5075  case XK_F20:
5076  state|=EscapeState;
5077  case XK_Return:
5078  {
5079  state|=ExitState;
5080  break;
5081  }
5082  case XK_Home:
5083  case XK_KP_Home:
5084  {
5085  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5086  2L);
5087  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5088  2L);
5089  break;
5090  }
5091  case XK_Left:
5092  case XK_KP_Left:
5093  {
5094  crop_info.x--;
5095  break;
5096  }
5097  case XK_Up:
5098  case XK_KP_Up:
5099  case XK_Next:
5100  {
5101  crop_info.y--;
5102  break;
5103  }
5104  case XK_Right:
5105  case XK_KP_Right:
5106  {
5107  crop_info.x++;
5108  break;
5109  }
5110  case XK_Prior:
5111  case XK_Down:
5112  case XK_KP_Down:
5113  {
5114  crop_info.y++;
5115  break;
5116  }
5117  case XK_F1:
5118  case XK_Help:
5119  {
5120  (void) XSetFunction(display,windows->image.highlight_context,
5121  GXcopy);
5122  switch (mode)
5123  {
5124  case CopyMode:
5125  {
5126  XTextViewHelp(display,resource_info,windows,MagickFalse,
5127  "Help Viewer - Image Copy",ImageCopyHelp);
5128  break;
5129  }
5130  case CropMode:
5131  {
5132  XTextViewHelp(display,resource_info,windows,MagickFalse,
5133  "Help Viewer - Image Cropg",ImageCropHelp);
5134  break;
5135  }
5136  case CutMode:
5137  {
5138  XTextViewHelp(display,resource_info,windows,MagickFalse,
5139  "Help Viewer - Image Cutg",ImageCutHelp);
5140  break;
5141  }
5142  }
5143  (void) XSetFunction(display,windows->image.highlight_context,
5144  GXinvert);
5145  break;
5146  }
5147  default:
5148  {
5149  (void) XBell(display,0);
5150  break;
5151  }
5152  }
5153  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5154  event.xkey.time);
5155  break;
5156  }
5157  case KeyRelease:
5158  break;
5159  case MotionNotify:
5160  {
5161  if (event.xmotion.window != windows->image.id)
5162  break;
5163  /*
5164  Map and unmap Info widget as text cursor crosses its boundaries.
5165  */
5166  x=event.xmotion.x;
5167  y=event.xmotion.y;
5168  if (windows->info.mapped != MagickFalse)
5169  {
5170  if ((x < (int) (windows->info.x+windows->info.width)) &&
5171  (y < (int) (windows->info.y+windows->info.height)))
5172  (void) XWithdrawWindow(display,windows->info.id,
5173  windows->info.screen);
5174  }
5175  else
5176  if ((x > (int) (windows->info.x+windows->info.width)) ||
5177  (y > (int) (windows->info.y+windows->info.height)))
5178  (void) XMapWindow(display,windows->info.id);
5179  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5180  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5181  break;
5182  }
5183  case SelectionRequest:
5184  {
5185  XSelectionEvent
5186  notify;
5187 
5188  XSelectionRequestEvent
5189  *request;
5190 
5191  /*
5192  Set primary selection.
5193  */
5194  (void) FormatLocaleString(text,MaxTextExtent,
5195  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5196  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5197  request=(&(event.xselectionrequest));
5198  (void) XChangeProperty(request->display,request->requestor,
5199  request->property,request->target,8,PropModeReplace,
5200  (unsigned char *) text,(int) strlen(text));
5201  notify.type=SelectionNotify;
5202  notify.display=request->display;
5203  notify.requestor=request->requestor;
5204  notify.selection=request->selection;
5205  notify.target=request->target;
5206  notify.time=request->time;
5207  if (request->property == None)
5208  notify.property=request->target;
5209  else
5210  notify.property=request->property;
5211  (void) XSendEvent(request->display,request->requestor,False,0,
5212  (XEvent *) &notify);
5213  }
5214  default:
5215  break;
5216  }
5217  if ((state & UpdateConfigurationState) != 0)
5218  {
5219  (void) XPutBackEvent(display,&event);
5220  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5221  break;
5222  }
5223  } while ((state & ExitState) == 0);
5224  } while ((state & ExitState) == 0);
5225  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5226  XSetCursorState(display,windows,MagickFalse);
5227  if ((state & EscapeState) != 0)
5228  return(MagickTrue);
5229  if (mode == CropMode)
5230  if (((int) crop_info.width != windows->image.ximage->width) ||
5231  ((int) crop_info.height != windows->image.ximage->height))
5232  {
5233  /*
5234  Reconfigure Image window as defined by cropping rectangle.
5235  */
5236  XSetCropGeometry(display,windows,&crop_info,image);
5237  windows->image.window_changes.width=(int) crop_info.width;
5238  windows->image.window_changes.height=(int) crop_info.height;
5239  (void) XConfigureImage(display,resource_info,windows,image);
5240  return(MagickTrue);
5241  }
5242  /*
5243  Copy image before applying image transforms.
5244  */
5245  XSetCursorState(display,windows,MagickTrue);
5246  XCheckRefreshWindows(display,windows);
5247  width=(unsigned int) image->columns;
5248  height=(unsigned int) image->rows;
5249  x=0;
5250  y=0;
5251  if (windows->image.crop_geometry != (char *) NULL)
5252  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5253  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5254  crop_info.x+=x;
5255  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5256  crop_info.x+=image->page.x;
5257  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5258  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5259  crop_info.y+=y;
5260  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5261  crop_info.y+=image->page.y;
5262  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5263  crop_image=CropImage(image,&crop_info,&image->exception);
5264  XSetCursorState(display,windows,MagickFalse);
5265  if (crop_image == (Image *) NULL)
5266  return(MagickFalse);
5267  if (resource_info->copy_image != (Image *) NULL)
5268  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5269  resource_info->copy_image=crop_image;
5270  if (mode == CopyMode)
5271  {
5272  (void) XConfigureImage(display,resource_info,windows,image);
5273  return(MagickTrue);
5274  }
5275  /*
5276  Cut image.
5277  */
5278  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5279  return(MagickFalse);
5280  image->matte=MagickTrue;
5281  exception=(&image->exception);
5282  image_view=AcquireAuthenticCacheView(image,exception);
5283  for (y=0; y < (int) crop_info.height; y++)
5284  {
5285  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5286  crop_info.width,1,exception);
5287  if (q == (PixelPacket *) NULL)
5288  break;
5289  for (x=0; x < (int) crop_info.width; x++)
5290  {
5291  q->opacity=(Quantum) TransparentOpacity;
5292  q++;
5293  }
5294  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5295  break;
5296  }
5297  image_view=DestroyCacheView(image_view);
5298  /*
5299  Update image configuration.
5300  */
5301  XConfigureImageColormap(display,resource_info,windows,image);
5302  (void) XConfigureImage(display,resource_info,windows,image);
5303  return(MagickTrue);
5304 }
5305 
5306 /*
5307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5308 % %
5309 % %
5310 % %
5311 + X D r a w I m a g e %
5312 % %
5313 % %
5314 % %
5315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5316 %
5317 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5318 % the image.
5319 %
5320 % The format of the XDrawEditImage method is:
5321 %
5322 % MagickBooleanType XDrawEditImage(Display *display,
5323 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5324 %
5325 % A description of each parameter follows:
5326 %
5327 % o display: Specifies a connection to an X server; returned from
5328 % XOpenDisplay.
5329 %
5330 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5331 %
5332 % o windows: Specifies a pointer to a XWindows structure.
5333 %
5334 % o image: the image.
5335 %
5336 */
5337 static MagickBooleanType XDrawEditImage(Display *display,
5338  XResourceInfo *resource_info,XWindows *windows,Image **image)
5339 {
5340  const char
5341  *const DrawMenu[] =
5342  {
5343  "Element",
5344  "Color",
5345  "Stipple",
5346  "Width",
5347  "Undo",
5348  "Help",
5349  "Dismiss",
5350  (char *) NULL
5351  };
5352 
5353  static ElementType
5354  element = PointElement;
5355 
5356  static const ModeType
5357  DrawCommands[] =
5358  {
5359  DrawElementCommand,
5360  DrawColorCommand,
5361  DrawStippleCommand,
5362  DrawWidthCommand,
5363  DrawUndoCommand,
5364  DrawHelpCommand,
5365  DrawDismissCommand
5366  };
5367 
5368  static Pixmap
5369  stipple = (Pixmap) NULL;
5370 
5371  static unsigned int
5372  pen_id = 0,
5373  line_width = 1;
5374 
5375  char
5376  command[MaxTextExtent],
5377  text[MaxTextExtent];
5378 
5379  Cursor
5380  cursor;
5381 
5382  int
5383  entry,
5384  id,
5385  number_coordinates,
5386  x,
5387  y;
5388 
5389  MagickRealType
5390  degrees;
5391 
5392  MagickStatusType
5393  status;
5394 
5396  rectangle_info;
5397 
5398  int
5399  i;
5400 
5401  unsigned int
5402  distance,
5403  height,
5404  max_coordinates,
5405  width;
5406 
5407  size_t
5408  state;
5409 
5410  Window
5411  root_window;
5412 
5413  XDrawInfo
5414  draw_info;
5415 
5416  XEvent
5417  event;
5418 
5419  XPoint
5420  *coordinate_info;
5421 
5422  XSegment
5423  line_info;
5424 
5425  /*
5426  Allocate polygon info.
5427  */
5428  max_coordinates=2048;
5429  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5430  sizeof(*coordinate_info));
5431  if (coordinate_info == (XPoint *) NULL)
5432  {
5433  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5434  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5435  return(MagickFalse);
5436  }
5437  /*
5438  Map Command widget.
5439  */
5440  (void) CloneString(&windows->command.name,"Draw");
5441  windows->command.data=4;
5442  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5443  (void) XMapRaised(display,windows->command.id);
5444  XClientMessage(display,windows->image.id,windows->im_protocols,
5445  windows->im_update_widget,CurrentTime);
5446  /*
5447  Wait for first button press.
5448  */
5449  root_window=XRootWindow(display,XDefaultScreen(display));
5450  draw_info.stencil=OpaqueStencil;
5451  status=MagickTrue;
5452  cursor=XCreateFontCursor(display,XC_tcross);
5453  for ( ; ; )
5454  {
5455  XQueryPosition(display,windows->image.id,&x,&y);
5456  (void) XSelectInput(display,windows->image.id,
5457  windows->image.attributes.event_mask | PointerMotionMask);
5458  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5459  state=DefaultState;
5460  do
5461  {
5462  if (windows->info.mapped != MagickFalse)
5463  {
5464  /*
5465  Display pointer position.
5466  */
5467  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5468  x+windows->image.x,y+windows->image.y);
5469  XInfoWidget(display,windows,text);
5470  }
5471  /*
5472  Wait for next event.
5473  */
5474  XScreenEvent(display,windows,&event);
5475  if (event.xany.window == windows->command.id)
5476  {
5477  /*
5478  Select a command from the Command widget.
5479  */
5480  id=XCommandWidget(display,windows,DrawMenu,&event);
5481  if (id < 0)
5482  continue;
5483  switch (DrawCommands[id])
5484  {
5485  case DrawElementCommand:
5486  {
5487  const char
5488  *const Elements[] =
5489  {
5490  "point",
5491  "line",
5492  "rectangle",
5493  "fill rectangle",
5494  "circle",
5495  "fill circle",
5496  "ellipse",
5497  "fill ellipse",
5498  "polygon",
5499  "fill polygon",
5500  (char *) NULL,
5501  };
5502 
5503  /*
5504  Select a command from the pop-up menu.
5505  */
5506  element=(ElementType) (XMenuWidget(display,windows,
5507  DrawMenu[id],Elements,command)+1);
5508  break;
5509  }
5510  case DrawColorCommand:
5511  {
5512  const char
5513  *ColorMenu[MaxNumberPens+1];
5514 
5515  int
5516  pen_number;
5517 
5518  MagickBooleanType
5519  transparent;
5520 
5521  XColor
5522  color;
5523 
5524  /*
5525  Initialize menu selections.
5526  */
5527  for (i=0; i < (int) (MaxNumberPens-2); i++)
5528  ColorMenu[i]=resource_info->pen_colors[i];
5529  ColorMenu[MaxNumberPens-2]="transparent";
5530  ColorMenu[MaxNumberPens-1]="Browser...";
5531  ColorMenu[MaxNumberPens]=(char *) NULL;
5532  /*
5533  Select a pen color from the pop-up menu.
5534  */
5535  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5536  (const char **) ColorMenu,command);
5537  if (pen_number < 0)
5538  break;
5539  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5540  MagickFalse;
5541  if (transparent != MagickFalse)
5542  {
5543  draw_info.stencil=TransparentStencil;
5544  break;
5545  }
5546  if (pen_number == (MaxNumberPens-1))
5547  {
5548  static char
5549  color_name[MaxTextExtent] = "gray";
5550 
5551  /*
5552  Select a pen color from a dialog.
5553  */
5554  resource_info->pen_colors[pen_number]=color_name;
5555  XColorBrowserWidget(display,windows,"Select",color_name);
5556  if (*color_name == '\0')
5557  break;
5558  }
5559  /*
5560  Set pen color.
5561  */
5562  (void) XParseColor(display,windows->map_info->colormap,
5563  resource_info->pen_colors[pen_number],&color);
5564  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5565  (unsigned int) MaxColors,&color);
5566  windows->pixel_info->pen_colors[pen_number]=color;
5567  pen_id=(unsigned int) pen_number;
5568  draw_info.stencil=OpaqueStencil;
5569  break;
5570  }
5571  case DrawStippleCommand:
5572  {
5573  const char
5574  *StipplesMenu[] =
5575  {
5576  "Brick",
5577  "Diagonal",
5578  "Scales",
5579  "Vertical",
5580  "Wavy",
5581  "Translucent",
5582  "Opaque",
5583  (char *) NULL,
5584  (char *) NULL,
5585  };
5586 
5587  Image
5588  *stipple_image;
5589 
5590  ImageInfo
5591  *image_info;
5592 
5593  int
5594  status;
5595 
5596  static char
5597  filename[MaxTextExtent] = "\0";
5598 
5599  /*
5600  Select a command from the pop-up menu.
5601  */
5602  StipplesMenu[7]="Open...";
5603  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5604  command);
5605  if (entry < 0)
5606  break;
5607  if (stipple != (Pixmap) NULL)
5608  (void) XFreePixmap(display,stipple);
5609  stipple=(Pixmap) NULL;
5610  if (entry != 7)
5611  {
5612  switch (entry)
5613  {
5614  case 0:
5615  {
5616  stipple=XCreateBitmapFromData(display,root_window,
5617  (char *) BricksBitmap,BricksWidth,BricksHeight);
5618  break;
5619  }
5620  case 1:
5621  {
5622  stipple=XCreateBitmapFromData(display,root_window,
5623  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5624  break;
5625  }
5626  case 2:
5627  {
5628  stipple=XCreateBitmapFromData(display,root_window,
5629  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5630  break;
5631  }
5632  case 3:
5633  {
5634  stipple=XCreateBitmapFromData(display,root_window,
5635  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5636  break;
5637  }
5638  case 4:
5639  {
5640  stipple=XCreateBitmapFromData(display,root_window,
5641  (char *) WavyBitmap,WavyWidth,WavyHeight);
5642  break;
5643  }
5644  case 5:
5645  {
5646  stipple=XCreateBitmapFromData(display,root_window,
5647  (char *) HighlightBitmap,HighlightWidth,
5648  HighlightHeight);
5649  break;
5650  }
5651  case 6:
5652  default:
5653  {
5654  stipple=XCreateBitmapFromData(display,root_window,
5655  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5656  break;
5657  }
5658  }
5659  break;
5660  }
5661  XFileBrowserWidget(display,windows,"Stipple",filename);
5662  if (*filename == '\0')
5663  break;
5664  /*
5665  Read image.
5666  */
5667  XSetCursorState(display,windows,MagickTrue);
5668  XCheckRefreshWindows(display,windows);
5669  image_info=AcquireImageInfo();
5670  (void) CopyMagickString(image_info->filename,filename,
5671  MaxTextExtent);
5672  stipple_image=ReadImage(image_info,&(*image)->exception);
5673  CatchException(&(*image)->exception);
5674  XSetCursorState(display,windows,MagickFalse);
5675  if (stipple_image == (Image *) NULL)
5676  break;
5677  (void) AcquireUniqueFileResource(filename);
5678  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5679  "xbm:%s",filename);
5680  (void) WriteImage(image_info,stipple_image);
5681  stipple_image=DestroyImage(stipple_image);
5682  image_info=DestroyImageInfo(image_info);
5683  status=XReadBitmapFile(display,root_window,filename,&width,
5684  &height,&stipple,&x,&y);
5685  (void) RelinquishUniqueFileResource(filename);
5686  if ((status != BitmapSuccess) != 0)
5687  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5688  filename);
5689  break;
5690  }
5691  case DrawWidthCommand:
5692  {
5693  const char
5694  *const WidthsMenu[] =
5695  {
5696  "1",
5697  "2",
5698  "4",
5699  "8",
5700  "16",
5701  "Dialog...",
5702  (char *) NULL,
5703  };
5704 
5705  static char
5706  width[MaxTextExtent] = "0";
5707 
5708  /*
5709  Select a command from the pop-up menu.
5710  */
5711  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5712  command);
5713  if (entry < 0)
5714  break;
5715  if (entry != 5)
5716  {
5717  line_width=(unsigned int) StringToUnsignedLong(
5718  WidthsMenu[entry]);
5719  break;
5720  }
5721  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5722  width);
5723  if (*width == '\0')
5724  break;
5725  line_width=(unsigned int) StringToUnsignedLong(width);
5726  break;
5727  }
5728  case DrawUndoCommand:
5729  {
5730  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5731  image);
5732  break;
5733  }
5734  case DrawHelpCommand:
5735  {
5736  XTextViewHelp(display,resource_info,windows,MagickFalse,
5737  "Help Viewer - Image Rotation",ImageDrawHelp);
5738  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5739  break;
5740  }
5741  case DrawDismissCommand:
5742  {
5743  /*
5744  Prematurely exit.
5745  */
5746  state|=EscapeState;
5747  state|=ExitState;
5748  break;
5749  }
5750  default:
5751  break;
5752  }
5753  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5754  continue;
5755  }
5756  switch (event.type)
5757  {
5758  case ButtonPress:
5759  {
5760  if (event.xbutton.button != Button1)
5761  break;
5762  if (event.xbutton.window != windows->image.id)
5763  break;
5764  /*
5765  exit loop.
5766  */
5767  x=event.xbutton.x;
5768  y=event.xbutton.y;
5769  state|=ExitState;
5770  break;
5771  }
5772  case ButtonRelease:
5773  break;
5774  case Expose:
5775  break;
5776  case KeyPress:
5777  {
5778  KeySym
5779  key_symbol;
5780 
5781  if (event.xkey.window != windows->image.id)
5782  break;
5783  /*
5784  Respond to a user key press.
5785  */
5786  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5787  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5788  switch ((int) key_symbol)
5789  {
5790  case XK_Escape:
5791  case XK_F20:
5792  {
5793  /*
5794  Prematurely exit.
5795  */
5796  state|=EscapeState;
5797  state|=ExitState;
5798  break;
5799  }
5800  case XK_F1:
5801  case XK_Help:
5802  {
5803  XTextViewHelp(display,resource_info,windows,MagickFalse,
5804  "Help Viewer - Image Rotation",ImageDrawHelp);
5805  break;
5806  }
5807  default:
5808  {
5809  (void) XBell(display,0);
5810  break;
5811  }
5812  }
5813  break;
5814  }
5815  case MotionNotify:
5816  {
5817  /*
5818  Map and unmap Info widget as text cursor crosses its boundaries.
5819  */
5820  x=event.xmotion.x;
5821  y=event.xmotion.y;
5822  if (windows->info.mapped != MagickFalse)
5823  {
5824  if ((x < (int) (windows->info.x+windows->info.width)) &&
5825  (y < (int) (windows->info.y+windows->info.height)))
5826  (void) XWithdrawWindow(display,windows->info.id,
5827  windows->info.screen);
5828  }
5829  else
5830  if ((x > (int) (windows->info.x+windows->info.width)) ||
5831  (y > (int) (windows->info.y+windows->info.height)))
5832  (void) XMapWindow(display,windows->info.id);
5833  break;
5834  }
5835  }
5836  } while ((state & ExitState) == 0);
5837  (void) XSelectInput(display,windows->image.id,
5838  windows->image.attributes.event_mask);
5839  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5840  if ((state & EscapeState) != 0)
5841  break;
5842  /*
5843  Draw element as pointer moves until the button is released.
5844  */
5845  distance=0;
5846  degrees=0.0;
5847  line_info.x1=x;
5848  line_info.y1=y;
5849  line_info.x2=x;
5850  line_info.y2=y;
5851  rectangle_info.x=(ssize_t) x;
5852  rectangle_info.y=(ssize_t) y;
5853  rectangle_info.width=0;
5854  rectangle_info.height=0;
5855  number_coordinates=1;
5856  coordinate_info->x=x;
5857  coordinate_info->y=y;
5858  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5859  state=DefaultState;
5860  do
5861  {
5862  switch (element)
5863  {
5864  case PointElement:
5865  default:
5866  {
5867  if (number_coordinates > 1)
5868  {
5869  (void) XDrawLines(display,windows->image.id,
5870  windows->image.highlight_context,coordinate_info,
5871  number_coordinates,CoordModeOrigin);
5872  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5873  coordinate_info[number_coordinates-1].x,
5874  coordinate_info[number_coordinates-1].y);
5875  XInfoWidget(display,windows,text);
5876  }
5877  break;
5878  }
5879  case LineElement:
5880  {
5881  if (distance > 9)
5882  {
5883  /*
5884  Display angle of the line.
5885  */
5886  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5887  line_info.y1),(double) (line_info.x2-line_info.x1)));
5888  (void) FormatLocaleString(text,MaxTextExtent," %g",
5889  (double) degrees);
5890  XInfoWidget(display,windows,text);
5891  XHighlightLine(display,windows->image.id,
5892  windows->image.highlight_context,&line_info);
5893  }
5894  else
5895  if (windows->info.mapped != MagickFalse)
5896  (void) XWithdrawWindow(display,windows->info.id,
5897  windows->info.screen);
5898  break;
5899  }
5900  case RectangleElement:
5901  case FillRectangleElement:
5902  {
5903  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5904  {
5905  /*
5906  Display info and draw drawing rectangle.
5907  */
5908  (void) FormatLocaleString(text,MaxTextExtent,
5909  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5910  (double) rectangle_info.height,(double) rectangle_info.x,
5911  (double) rectangle_info.y);
5912  XInfoWidget(display,windows,text);
5913  XHighlightRectangle(display,windows->image.id,
5914  windows->image.highlight_context,&rectangle_info);
5915  }
5916  else
5917  if (windows->info.mapped != MagickFalse)
5918  (void) XWithdrawWindow(display,windows->info.id,
5919  windows->info.screen);
5920  break;
5921  }
5922  case CircleElement:
5923  case FillCircleElement:
5924  case EllipseElement:
5925  case FillEllipseElement:
5926  {
5927  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5928  {
5929  /*
5930  Display info and draw drawing rectangle.
5931  */
5932  (void) FormatLocaleString(text,MaxTextExtent,
5933  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5934  (double) rectangle_info.height,(double) rectangle_info.x,
5935  (double) rectangle_info.y);
5936  XInfoWidget(display,windows,text);
5937  XHighlightEllipse(display,windows->image.id,
5938  windows->image.highlight_context,&rectangle_info);
5939  }
5940  else
5941  if (windows->info.mapped != MagickFalse)
5942  (void) XWithdrawWindow(display,windows->info.id,
5943  windows->info.screen);
5944  break;
5945  }
5946  case PolygonElement:
5947  case FillPolygonElement:
5948  {
5949  if (number_coordinates > 1)
5950  (void) XDrawLines(display,windows->image.id,
5951  windows->image.highlight_context,coordinate_info,
5952  number_coordinates,CoordModeOrigin);
5953  if (distance > 9)
5954  {
5955  /*
5956  Display angle of the line.
5957  */
5958  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5959  line_info.y1),(double) (line_info.x2-line_info.x1)));
5960  (void) FormatLocaleString(text,MaxTextExtent," %g",
5961  (double) degrees);
5962  XInfoWidget(display,windows,text);
5963  XHighlightLine(display,windows->image.id,
5964  windows->image.highlight_context,&line_info);
5965  }
5966  else
5967  if (windows->info.mapped != MagickFalse)
5968  (void) XWithdrawWindow(display,windows->info.id,
5969  windows->info.screen);
5970  break;
5971  }
5972  }
5973  /*
5974  Wait for next event.
5975  */
5976  XScreenEvent(display,windows,&event);
5977  switch (element)
5978  {
5979  case PointElement:
5980  default:
5981  {
5982  if (number_coordinates > 1)
5983  (void) XDrawLines(display,windows->image.id,
5984  windows->image.highlight_context,coordinate_info,
5985  number_coordinates,CoordModeOrigin);
5986  break;
5987  }
5988  case LineElement:
5989  {
5990  if (distance > 9)
5991  XHighlightLine(display,windows->image.id,
5992  windows->image.highlight_context,&line_info);
5993  break;
5994  }
5995  case RectangleElement:
5996  case FillRectangleElement:
5997  {
5998  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999  XHighlightRectangle(display,windows->image.id,
6000  windows->image.highlight_context,&rectangle_info);
6001  break;
6002  }
6003  case CircleElement:
6004  case FillCircleElement:
6005  case EllipseElement:
6006  case FillEllipseElement:
6007  {
6008  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6009  XHighlightEllipse(display,windows->image.id,
6010  windows->image.highlight_context,&rectangle_info);
6011  break;
6012  }
6013  case PolygonElement:
6014  case FillPolygonElement:
6015  {
6016  if (number_coordinates > 1)
6017  (void) XDrawLines(display,windows->image.id,
6018  windows->image.highlight_context,coordinate_info,
6019  number_coordinates,CoordModeOrigin);
6020  if (distance > 9)
6021  XHighlightLine(display,windows->image.id,
6022  windows->image.highlight_context,&line_info);
6023  break;
6024  }
6025  }
6026  switch (event.type)
6027  {
6028  case ButtonPress:
6029  break;
6030  case ButtonRelease:
6031  {
6032  /*
6033  User has committed to element.
6034  */
6035  line_info.x2=event.xbutton.x;
6036  line_info.y2=event.xbutton.y;
6037  rectangle_info.x=(ssize_t) event.xbutton.x;
6038  rectangle_info.y=(ssize_t) event.xbutton.y;
6039  coordinate_info[number_coordinates].x=event.xbutton.x;
6040  coordinate_info[number_coordinates].y=event.xbutton.y;
6041  if (((element != PolygonElement) &&
6042  (element != FillPolygonElement)) || (distance <= 9))
6043  {
6044  state|=ExitState;
6045  break;
6046  }
6047  number_coordinates++;
6048  if (number_coordinates < (int) max_coordinates)
6049  {
6050  line_info.x1=event.xbutton.x;
6051  line_info.y1=event.xbutton.y;
6052  break;
6053  }
6054  max_coordinates<<=1;
6055  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6056  max_coordinates,sizeof(*coordinate_info));
6057  if (coordinate_info == (XPoint *) NULL)
6058  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6059  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6060  break;
6061  }
6062  case Expose:
6063  break;
6064  case MotionNotify:
6065  {
6066  if (event.xmotion.window != windows->image.id)
6067  break;
6068  if (element != PointElement)
6069  {
6070  line_info.x2=event.xmotion.x;
6071  line_info.y2=event.xmotion.y;
6072  rectangle_info.x=(ssize_t) event.xmotion.x;
6073  rectangle_info.y=(ssize_t) event.xmotion.y;
6074  break;
6075  }
6076  coordinate_info[number_coordinates].x=event.xbutton.x;
6077  coordinate_info[number_coordinates].y=event.xbutton.y;
6078  number_coordinates++;
6079  if (number_coordinates < (int) max_coordinates)
6080  break;
6081  max_coordinates<<=1;
6082  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6083  max_coordinates,sizeof(*coordinate_info));
6084  if (coordinate_info == (XPoint *) NULL)
6085  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6086  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6087  break;
6088  }
6089  default:
6090  break;
6091  }
6092  /*
6093  Check boundary conditions.
6094  */
6095  if (line_info.x2 < 0)
6096  line_info.x2=0;
6097  else
6098  if (line_info.x2 > (int) windows->image.width)
6099  line_info.x2=(short) windows->image.width;
6100  if (line_info.y2 < 0)
6101  line_info.y2=0;
6102  else
6103  if (line_info.y2 > (int) windows->image.height)
6104  line_info.y2=(short) windows->image.height;
6105  distance=(unsigned int)
6106  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6107  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6108  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6109  ((state & ExitState) != 0))
6110  {
6111  if (rectangle_info.x < 0)
6112  rectangle_info.x=0;
6113  else
6114  if (rectangle_info.x > (ssize_t) windows->image.width)
6115  rectangle_info.x=(ssize_t) windows->image.width;
6116  if ((int) rectangle_info.x < x)
6117  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6118  else
6119  {
6120  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6121  rectangle_info.x=(ssize_t) x;
6122  }
6123  if (rectangle_info.y < 0)
6124  rectangle_info.y=0;
6125  else
6126  if (rectangle_info.y > (ssize_t) windows->image.height)
6127  rectangle_info.y=(ssize_t) windows->image.height;
6128  if ((int) rectangle_info.y < y)
6129  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6130  else
6131  {
6132  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6133  rectangle_info.y=(ssize_t) y;
6134  }
6135  }
6136  } while ((state & ExitState) == 0);
6137  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6138  if ((element == PointElement) || (element == PolygonElement) ||
6139  (element == FillPolygonElement))
6140  {
6141  /*
6142  Determine polygon bounding box.
6143  */
6144  rectangle_info.x=(ssize_t) coordinate_info->x;
6145  rectangle_info.y=(ssize_t) coordinate_info->y;
6146  x=coordinate_info->x;
6147  y=coordinate_info->y;
6148  for (i=1; i < number_coordinates; i++)
6149  {
6150  if (coordinate_info[i].x > x)
6151  x=coordinate_info[i].x;
6152  if (coordinate_info[i].y > y)
6153  y=coordinate_info[i].y;
6154  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6155  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6156  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6157  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6158  }
6159  rectangle_info.width=(size_t) (x-rectangle_info.x);
6160  rectangle_info.height=(size_t) (y-rectangle_info.y);
6161  for (i=0; i < number_coordinates; i++)
6162  {
6163  coordinate_info[i].x-=rectangle_info.x;
6164  coordinate_info[i].y-=rectangle_info.y;
6165  }
6166  }
6167  else
6168  if (distance <= 9)
6169  continue;
6170  else
6171  if ((element == RectangleElement) ||
6172  (element == CircleElement) || (element == EllipseElement))
6173  {
6174  rectangle_info.width--;
6175  rectangle_info.height--;
6176  }
6177  /*
6178  Drawing is relative to image configuration.
6179  */
6180  draw_info.x=(int) rectangle_info.x;
6181  draw_info.y=(int) rectangle_info.y;
6182  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6183  image);
6184  width=(unsigned int) (*image)->columns;
6185  height=(unsigned int) (*image)->rows;
6186  x=0;
6187  y=0;
6188  if (windows->image.crop_geometry != (char *) NULL)
6189  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6190  draw_info.x+=windows->image.x-(line_width/2);
6191  if (draw_info.x < 0)
6192  draw_info.x=0;
6193  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6194  draw_info.y+=windows->image.y-(line_width/2);
6195  if (draw_info.y < 0)
6196  draw_info.y=0;
6197  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6198  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6199  if (draw_info.width > (unsigned int) (*image)->columns)
6200  draw_info.width=(unsigned int) (*image)->columns;
6201  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6202  if (draw_info.height > (unsigned int) (*image)->rows)
6203  draw_info.height=(unsigned int) (*image)->rows;
6204  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6205  width*draw_info.width/windows->image.ximage->width,
6206  height*draw_info.height/windows->image.ximage->height,
6207  draw_info.x+x,draw_info.y+y);
6208  /*
6209  Initialize drawing attributes.
6210  */
6211  draw_info.degrees=0.0;
6212  draw_info.element=element;
6213  draw_info.stipple=stipple;
6214  draw_info.line_width=line_width;
6215  draw_info.line_info=line_info;
6216  if (line_info.x1 > (int) (line_width/2))
6217  draw_info.line_info.x1=(short) line_width/2;
6218  if (line_info.y1 > (int) (line_width/2))
6219  draw_info.line_info.y1=(short) line_width/2;
6220  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6221  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6222  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6223  {
6224  draw_info.line_info.x2=(-draw_info.line_info.x2);
6225  draw_info.line_info.y2=(-draw_info.line_info.y2);
6226  }
6227  if (draw_info.line_info.x2 < 0)
6228  {
6229  draw_info.line_info.x2=(-draw_info.line_info.x2);
6230  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6231  }
6232  if (draw_info.line_info.y2 < 0)
6233  {
6234  draw_info.line_info.y2=(-draw_info.line_info.y2);
6235  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6236  }
6237  draw_info.rectangle_info=rectangle_info;
6238  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6239  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6240  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6241  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6242  draw_info.number_coordinates=(unsigned int) number_coordinates;
6243  draw_info.coordinate_info=coordinate_info;
6244  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6245  /*
6246  Draw element on image.
6247  */
6248  XSetCursorState(display,windows,MagickTrue);
6249  XCheckRefreshWindows(display,windows);
6250  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6251  XSetCursorState(display,windows,MagickFalse);
6252  /*
6253  Update image colormap and return to image drawing.
6254  */
6255  XConfigureImageColormap(display,resource_info,windows,*image);
6256  (void) XConfigureImage(display,resource_info,windows,*image);
6257  }
6258  XSetCursorState(display,windows,MagickFalse);
6259  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6260  return(status != 0 ? MagickTrue : MagickFalse);
6261 }
6262 
6263 /*
6264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6265 % %
6266 % %
6267 % %
6268 + X D r a w P a n R e c t a n g l e %
6269 % %
6270 % %
6271 % %
6272 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6273 %
6274 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6275 % displays a zoom image and the rectangle shows which portion of the image is
6276 % displayed in the Image window.
6277 %
6278 % The format of the XDrawPanRectangle method is:
6279 %
6280 % XDrawPanRectangle(Display *display,XWindows *windows)
6281 %
6282 % A description of each parameter follows:
6283 %
6284 % o display: Specifies a connection to an X server; returned from
6285 % XOpenDisplay.
6286 %
6287 % o windows: Specifies a pointer to a XWindows structure.
6288 %
6289 */
6290 static void XDrawPanRectangle(Display *display,XWindows *windows)
6291 {
6292  MagickRealType
6293  scale_factor;
6294 
6296  highlight_info;
6297 
6298  /*
6299  Determine dimensions of the panning rectangle.
6300  */
6301  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6302  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6303  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6304  scale_factor=(MagickRealType)
6305  windows->pan.height/windows->image.ximage->height;
6306  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6307  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6308  /*
6309  Display the panning rectangle.
6310  */
6311  (void) XClearWindow(display,windows->pan.id);
6312  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6313  &highlight_info);
6314 }
6315 
6316 /*
6317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6318 % %
6319 % %
6320 % %
6321 + X I m a g e C a c h e %
6322 % %
6323 % %
6324 % %
6325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6326 %
6327 % XImageCache() handles the creation, manipulation, and destruction of the
6328 % image cache (undo and redo buffers).
6329 %
6330 % The format of the XImageCache method is:
6331 %
6332 % void XImageCache(Display *display,XResourceInfo *resource_info,
6333 % XWindows *windows,const CommandType command,Image **image)
6334 %
6335 % A description of each parameter follows:
6336 %
6337 % o display: Specifies a connection to an X server; returned from
6338 % XOpenDisplay.
6339 %
6340 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6341 %
6342 % o windows: Specifies a pointer to a XWindows structure.
6343 %
6344 % o command: Specifies a command to perform.
6345 %
6346 % o image: the image; XImageCache may transform the image and return a new
6347 % image pointer.
6348 %
6349 */
6350 static void XImageCache(Display *display,XResourceInfo *resource_info,
6351  XWindows *windows,const CommandType command,Image **image)
6352 {
6353  Image
6354  *cache_image;
6355 
6356  static Image
6357  *redo_image = (Image *) NULL,
6358  *undo_image = (Image *) NULL;
6359 
6360  switch (command)
6361  {
6362  case FreeBuffersCommand:
6363  {
6364  /*
6365  Free memory from the undo and redo cache.
6366  */
6367  while (undo_image != (Image *) NULL)
6368  {
6369  cache_image=undo_image;
6370  undo_image=GetPreviousImageInList(undo_image);
6371  cache_image->list=DestroyImage(cache_image->list);
6372  cache_image=DestroyImage(cache_image);
6373  }
6374  undo_image=NewImageList();
6375  if (redo_image != (Image *) NULL)
6376  redo_image=DestroyImage(redo_image);
6377  redo_image=NewImageList();
6378  return;
6379  }
6380  case UndoCommand:
6381  {
6382  char
6383  image_geometry[MaxTextExtent];
6384 
6385  /*
6386  Undo the last image transformation.
6387  */
6388  if (undo_image == (Image *) NULL)
6389  {
6390  (void) XBell(display,0);
6391  return;
6392  }
6393  cache_image=undo_image;
6394  undo_image=GetPreviousImageInList(undo_image);
6395  windows->image.window_changes.width=(int) cache_image->columns;
6396  windows->image.window_changes.height=(int) cache_image->rows;
6397  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6398  windows->image.ximage->width,windows->image.ximage->height);
6399  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6400  if (windows->image.crop_geometry != (char *) NULL)
6401  windows->image.crop_geometry=(char *)
6402  RelinquishMagickMemory(windows->image.crop_geometry);
6403  windows->image.crop_geometry=cache_image->geometry;
6404  if (redo_image != (Image *) NULL)
6405  redo_image=DestroyImage(redo_image);
6406  redo_image=(*image);
6407  *image=cache_image->list;
6408  cache_image=DestroyImage(cache_image);
6409  if (windows->image.orphan != MagickFalse)
6410  return;
6411  XConfigureImageColormap(display,resource_info,windows,*image);
6412  (void) XConfigureImage(display,resource_info,windows,*image);
6413  return;
6414  }
6415  case CutCommand:
6416  case PasteCommand:
6417  case ApplyCommand:
6418  case HalfSizeCommand:
6419  case OriginalSizeCommand:
6420  case DoubleSizeCommand:
6421  case ResizeCommand:
6422  case TrimCommand:
6423  case CropCommand:
6424  case ChopCommand:
6425  case FlipCommand:
6426  case FlopCommand:
6427  case RotateRightCommand:
6428  case RotateLeftCommand:
6429  case RotateCommand:
6430  case ShearCommand:
6431  case RollCommand:
6432  case NegateCommand:
6433  case ContrastStretchCommand:
6434  case SigmoidalContrastCommand:
6435  case NormalizeCommand:
6436  case EqualizeCommand:
6437  case HueCommand:
6438  case SaturationCommand:
6439  case BrightnessCommand:
6440  case GammaCommand:
6441  case SpiffCommand:
6442  case DullCommand:
6443  case GrayscaleCommand:
6444  case MapCommand:
6445  case QuantizeCommand:
6446  case DespeckleCommand:
6447  case EmbossCommand:
6448  case ReduceNoiseCommand:
6449  case AddNoiseCommand:
6450  case SharpenCommand:
6451  case BlurCommand:
6452  case ThresholdCommand:
6453  case EdgeDetectCommand:
6454  case SpreadCommand:
6455  case ShadeCommand:
6456  case RaiseCommand:
6457  case SegmentCommand:
6458  case SolarizeCommand:
6459  case SepiaToneCommand:
6460  case SwirlCommand:
6461  case ImplodeCommand:
6462  case VignetteCommand:
6463  case WaveCommand:
6464  case OilPaintCommand:
6465  case CharcoalDrawCommand:
6466  case AnnotateCommand:
6467  case AddBorderCommand:
6468  case AddFrameCommand:
6469  case CompositeCommand:
6470  case CommentCommand:
6471  case LaunchCommand:
6472  case RegionofInterestCommand:
6473  case SaveToUndoBufferCommand:
6474  case RedoCommand:
6475  {
6476  Image
6477  *previous_image;
6478 
6479  ssize_t
6480  bytes;
6481 
6482  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6483  if (undo_image != (Image *) NULL)
6484  {
6485  /*
6486  Ensure the undo cache has enough memory available.
6487  */
6488  previous_image=undo_image;
6489  while (previous_image != (Image *) NULL)
6490  {
6491  bytes+=previous_image->list->columns*previous_image->list->rows*
6492  sizeof(PixelPacket);
6493  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6494  {
6495  previous_image=GetPreviousImageInList(previous_image);
6496  continue;
6497  }
6498  bytes-=previous_image->list->columns*previous_image->list->rows*
6499  sizeof(PixelPacket);
6500  if (previous_image == undo_image)
6501  undo_image=NewImageList();
6502  else
6503  previous_image->next->previous=NewImageList();
6504  break;
6505  }
6506  while (previous_image != (Image *) NULL)
6507  {
6508  /*
6509  Delete any excess memory from undo cache.
6510  */
6511  cache_image=previous_image;
6512  previous_image=GetPreviousImageInList(previous_image);
6513  cache_image->list=DestroyImage(cache_image->list);
6514  cache_image=DestroyImage(cache_image);
6515  }
6516  }
6517  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6518  break;
6519  /*
6520  Save image before transformations are applied.
6521  */
6522  cache_image=AcquireImage((ImageInfo *) NULL);
6523  if (cache_image == (Image *) NULL)
6524  break;
6525  XSetCursorState(display,windows,MagickTrue);
6526  XCheckRefreshWindows(display,windows);
6527  cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6528  XSetCursorState(display,windows,MagickFalse);
6529  if (cache_image->list == (Image *) NULL)
6530  {
6531  cache_image=DestroyImage(cache_image);
6532  break;
6533  }
6534  cache_image->columns=(size_t) windows->image.ximage->width;
6535  cache_image->rows=(size_t) windows->image.ximage->height;
6536  cache_image->geometry=windows->image.crop_geometry;
6537  if (windows->image.crop_geometry != (char *) NULL)
6538  {
6539  cache_image->geometry=AcquireString((char *) NULL);
6540  (void) CopyMagickString(cache_image->geometry,
6541  windows->image.crop_geometry,MaxTextExtent);
6542  }
6543  if (undo_image == (Image *) NULL)
6544  {
6545  undo_image=cache_image;
6546  break;
6547  }
6548  undo_image->next=cache_image;
6549  undo_image->next->previous=undo_image;
6550  undo_image=undo_image->next;
6551  break;
6552  }
6553  default:
6554  break;
6555  }
6556  if (command == RedoCommand)
6557  {
6558  /*
6559  Redo the last image transformation.
6560  */
6561  if (redo_image == (Image *) NULL)
6562  {
6563  (void) XBell(display,0);
6564  return;
6565  }
6566  windows->image.window_changes.width=(int) redo_image->columns;
6567  windows->image.window_changes.height=(int) redo_image->rows;
6568  if (windows->image.crop_geometry != (char *) NULL)
6569  windows->image.crop_geometry=(char *)
6570  RelinquishMagickMemory(windows->image.crop_geometry);
6571  windows->image.crop_geometry=redo_image->geometry;
6572  *image=DestroyImage(*image);
6573  *image=redo_image;
6574  redo_image=NewImageList();
6575  if (windows->image.orphan != MagickFalse)
6576  return;
6577  XConfigureImageColormap(display,resource_info,windows,*image);
6578  (void) XConfigureImage(display,resource_info,windows,*image);
6579  return;
6580  }
6581  if (command != InfoCommand)
6582  return;
6583  /*
6584  Display image info.
6585  */
6586  XSetCursorState(display,windows,MagickTrue);
6587  XCheckRefreshWindows(display,windows);
6588  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6589  XSetCursorState(display,windows,MagickFalse);
6590 }
6591 
6592 /*
6593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6594 % %
6595 % %
6596 % %
6597 + X I m a g e W i n d o w C o m m a n d %
6598 % %
6599 % %
6600 % %
6601 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6602 %
6603 % XImageWindowCommand() makes a transform to the image or Image window as
6604 % specified by a user menu button or keyboard command.
6605 %
6606 % The format of the XMagickCommand method is:
6607 %
6608 % CommandType XImageWindowCommand(Display *display,
6609 % XResourceInfo *resource_info,XWindows *windows,
6610 % const MagickStatusType state,KeySym key_symbol,Image **image)
6611 %
6612 % A description of each parameter follows:
6613 %
6614 % o nexus: Method XImageWindowCommand returns an image when the
6615 % user chooses 'Open Image' from the command menu. Otherwise a null
6616 % image is returned.
6617 %
6618 % o display: Specifies a connection to an X server; returned from
6619 % XOpenDisplay.
6620 %
6621 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6622 %
6623 % o windows: Specifies a pointer to a XWindows structure.
6624 %
6625 % o state: key mask.
6626 %
6627 % o key_symbol: Specifies a command to perform.
6628 %
6629 % o image: the image; XImageWIndowCommand
6630 % may transform the image and return a new image pointer.
6631 %
6632 */
6633 static CommandType XImageWindowCommand(Display *display,
6634  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6635  KeySym key_symbol,Image **image)
6636 {
6637  static char
6638  delta[MaxTextExtent] = "";
6639 
6640  static const char
6641  Digits[] = "01234567890";
6642 
6643  static KeySym
6644  last_symbol = XK_0;
6645 
6646  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6647  {
6648  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6649  {
6650  *delta='\0';
6651  resource_info->quantum=1;
6652  }
6653  last_symbol=key_symbol;
6654  delta[strlen(delta)+1]='\0';
6655  delta[strlen(delta)]=Digits[key_symbol-XK_0];
6656  resource_info->quantum=StringToLong(delta);
6657  return(NullCommand);
6658  }
6659  last_symbol=key_symbol;
6660  if (resource_info->immutable)
6661  {
6662  /*
6663  Virtual image window has a restricted command set.
6664  */
6665  switch (key_symbol)
6666  {
6667  case XK_question:
6668  return(InfoCommand);
6669  case XK_p:
6670  case XK_Print:
6671  return(PrintCommand);
6672  case XK_space:
6673  return(NextCommand);
6674  case XK_q:
6675  case XK_Escape:
6676  return(QuitCommand);
6677  default:
6678  break;
6679  }
6680  return(NullCommand);
6681  }
6682  switch ((int) key_symbol)
6683  {
6684  case XK_o:
6685  {
6686  if ((state & ControlMask) == 0)
6687  break;
6688  return(OpenCommand);
6689  }
6690  case XK_space:
6691  return(NextCommand);
6692  case XK_BackSpace:
6693  return(FormerCommand);
6694  case XK_s:
6695  {
6696  if ((state & Mod1Mask) != 0)
6697  return(SwirlCommand);
6698  if ((state & ControlMask) == 0)
6699  return(ShearCommand);
6700  return(SaveCommand);
6701  }
6702  case XK_p:
6703  case XK_Print:
6704  {
6705  if ((state & Mod1Mask) != 0)
6706  return(OilPaintCommand);
6707  if ((state & Mod4Mask) != 0)
6708  return(ColorCommand);
6709  if ((state & ControlMask) == 0)
6710  return(NullCommand);
6711  return(PrintCommand);
6712  }
6713  case XK_d:
6714  {
6715  if ((state & Mod4Mask) != 0)
6716  return(DrawCommand);
6717  if ((state & ControlMask) == 0)
6718  return(NullCommand);
6719  return(DeleteCommand);
6720  }
6721  case XK_Select:
6722  {
6723  if ((state & ControlMask) == 0)
6724  return(NullCommand);
6725  return(SelectCommand);
6726  }
6727  case XK_n:
6728  {
6729  if ((state & ControlMask) == 0)
6730  return(NullCommand);
6731  return(NewCommand);
6732  }
6733  case XK_q:
6734  case XK_Escape:
6735  return(QuitCommand);
6736  case XK_z:
6737  case XK_Undo:
6738  {
6739  if ((state & ControlMask) == 0)
6740  return(NullCommand);
6741  return(UndoCommand);
6742  }
6743  case XK_r:
6744  case XK_Redo:
6745  {
6746  if ((state & ControlMask) == 0)
6747  return(RollCommand);
6748  return(RedoCommand);
6749  }
6750  case XK_x:
6751  {
6752  if ((state & ControlMask) == 0)
6753  return(NullCommand);
6754  return(CutCommand);
6755  }
6756  case XK_c:
6757  {
6758  if ((state & Mod1Mask) != 0)
6759  return(CharcoalDrawCommand);
6760  if ((state & ControlMask) == 0)
6761  return(CropCommand);
6762  return(CopyCommand);
6763  }
6764  case XK_v:
6765  case XK_Insert:
6766  {
6767  if ((state & Mod4Mask) != 0)
6768  return(CompositeCommand);
6769  if ((state & ControlMask) == 0)
6770  return(FlipCommand);
6771  return(PasteCommand);
6772  }
6773  case XK_less:
6774  return(HalfSizeCommand);
6775  case XK_minus:
6776  return(OriginalSizeCommand);
6777  case XK_greater:
6778  return(DoubleSizeCommand);
6779  case XK_percent:
6780  return(ResizeCommand);
6781  case XK_at:
6782  return(RefreshCommand);
6783  case XK_bracketleft:
6784  return(ChopCommand);
6785  case XK_h:
6786  return(FlopCommand);
6787  case XK_slash:
6788  return(RotateRightCommand);
6789  case XK_backslash:
6790  return(RotateLeftCommand);
6791  case XK_asterisk:
6792  return(RotateCommand);
6793  case XK_t:
6794  return(TrimCommand);
6795  case XK_H:
6796  return(HueCommand);
6797  case XK_S:
6798  return(SaturationCommand);
6799  case XK_L:
6800  return(BrightnessCommand);
6801  case XK_G:
6802  return(GammaCommand);
6803  case XK_C:
6804  return(SpiffCommand);
6805  case XK_Z:
6806  return(DullCommand);
6807  case XK_N:
6808  return(NormalizeCommand);
6809  case XK_equal:
6810  return(EqualizeCommand);
6811  case XK_asciitilde:
6812  return(NegateCommand);
6813  case XK_period:
6814  return(GrayscaleCommand);
6815  case XK_numbersign:
6816  return(QuantizeCommand);
6817  case XK_F2:
6818  return(DespeckleCommand);
6819  case XK_F3:
6820  return(EmbossCommand);
6821  case XK_F4:
6822  return(ReduceNoiseCommand);
6823  case XK_F5:
6824  return(AddNoiseCommand);
6825  case XK_F6:
6826  return(SharpenCommand);
6827  case XK_F7:
6828  return(BlurCommand);
6829  case XK_F8:
6830  return(ThresholdCommand);
6831  case XK_F9:
6832  return(EdgeDetectCommand);
6833  case XK_F10:
6834  return(SpreadCommand);
6835  case XK_F11:
6836  return(ShadeCommand);
6837  case XK_F12:
6838  return(RaiseCommand);
6839  case XK_F13:
6840  return(SegmentCommand);
6841  case XK_i:
6842  {
6843  if ((state & Mod1Mask) == 0)
6844  return(NullCommand);
6845  return(ImplodeCommand);
6846  }
6847  case XK_w:
6848  {
6849  if ((state & Mod1Mask) == 0)
6850  return(NullCommand);
6851  return(WaveCommand);
6852  }
6853  case XK_m:
6854  {
6855  if ((state & Mod4Mask) == 0)
6856  return(NullCommand);
6857  return(MatteCommand);
6858  }
6859  case XK_b:
6860  {
6861  if ((state & Mod4Mask) == 0)
6862  return(NullCommand);
6863  return(AddBorderCommand);
6864  }
6865  case XK_f:
6866  {
6867  if ((state & Mod4Mask) == 0)
6868  return(NullCommand);
6869  return(AddFrameCommand);
6870  }
6871  case XK_exclam:
6872  {
6873  if ((state & Mod4Mask) == 0)
6874  return(NullCommand);
6875  return(CommentCommand);
6876  }
6877  case XK_a:
6878  {
6879  if ((state & Mod1Mask) != 0)
6880  return(ApplyCommand);
6881  if ((state & Mod4Mask) != 0)
6882  return(AnnotateCommand);
6883  if ((state & ControlMask) == 0)
6884  return(NullCommand);
6885  return(RegionofInterestCommand);
6886  }
6887  case XK_question:
6888  return(InfoCommand);
6889  case XK_plus:
6890  return(ZoomCommand);
6891  case XK_P:
6892  {
6893  if ((state & ShiftMask) == 0)
6894  return(NullCommand);
6895  return(ShowPreviewCommand);
6896  }
6897  case XK_Execute:
6898  return(LaunchCommand);
6899  case XK_F1:
6900  return(HelpCommand);
6901  case XK_Find:
6902  return(BrowseDocumentationCommand);
6903  case XK_Menu:
6904  {
6905  (void) XMapRaised(display,windows->command.id);
6906  return(NullCommand);
6907  }
6908  case XK_Next:
6909  case XK_Prior:
6910  case XK_Home:
6911  case XK_KP_Home:
6912  {
6913  XTranslateImage(display,windows,*image,key_symbol);
6914  return(NullCommand);
6915  }
6916  case XK_Up:
6917  case XK_KP_Up:
6918  case XK_Down:
6919  case XK_KP_Down:
6920  case XK_Left:
6921  case XK_KP_Left:
6922  case XK_Right:
6923  case XK_KP_Right:
6924  {
6925  if ((state & Mod1Mask) != 0)
6926  {
6928  crop_info;
6929 
6930  /*
6931  Trim one pixel from edge of image.
6932  */
6933  crop_info.x=0;
6934  crop_info.y=0;
6935  crop_info.width=(size_t) windows->image.ximage->width;
6936  crop_info.height=(size_t) windows->image.ximage->height;
6937  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6938  {
6939  if (resource_info->quantum >= (int) crop_info.height)
6940  resource_info->quantum=(int) crop_info.height-1;
6941  crop_info.height-=resource_info->quantum;
6942  }
6943  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6944  {
6945  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6946  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6947  crop_info.y+=resource_info->quantum;
6948  crop_info.height-=resource_info->quantum;
6949  }
6950  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6951  {
6952  if (resource_info->quantum >= (int) crop_info.width)
6953  resource_info->quantum=(int) crop_info.width-1;
6954  crop_info.width-=resource_info->quantum;
6955  }
6956  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6957  {
6958  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6959  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6960  crop_info.x+=resource_info->quantum;
6961  crop_info.width-=resource_info->quantum;
6962  }
6963  if ((int) (windows->image.x+windows->image.width) >
6964  (int) crop_info.width)
6965  windows->image.x=(int) (crop_info.width-windows->image.width);
6966  if ((int) (windows->image.y+windows->image.height) >
6967  (int) crop_info.height)
6968  windows->image.y=(int) (crop_info.height-windows->image.height);
6969  XSetCropGeometry(display,windows,&crop_info,*image);
6970  windows->image.window_changes.width=(int) crop_info.width;
6971  windows->image.window_changes.height=(int) crop_info.height;
6972  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6973  (void) XConfigureImage(display,resource_info,windows,*image);
6974  return(NullCommand);
6975  }
6976  XTranslateImage(display,windows,*image,key_symbol);
6977  return(NullCommand);
6978  }
6979  default:
6980  return(NullCommand);
6981  }
6982  return(NullCommand);
6983 }
6984 
6985 /*
6986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6987 % %
6988 % %
6989 % %
6990 + X M a g i c k C o m m a n d %
6991 % %
6992 % %
6993 % %
6994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6995 %
6996 % XMagickCommand() makes a transform to the image or Image window as
6997 % specified by a user menu button or keyboard command.
6998 %
6999 % The format of the XMagickCommand method is:
7000 %
7001 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7002 % XWindows *windows,const CommandType command,Image **image)
7003 %
7004 % A description of each parameter follows:
7005 %
7006 % o nexus: Method XMagickCommand returns an image when the
7007 % user chooses 'Load Image' from the command menu. Otherwise a null
7008 % image is returned.
7009 %
7010 % o display: Specifies a connection to an X server; returned from
7011 % XOpenDisplay.
7012 %
7013 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7014 %
7015 % o windows: Specifies a pointer to a XWindows structure.
7016 %
7017 % o command: Specifies a command to perform.
7018 %
7019 % o image: the image; XMagickCommand
7020 % may transform the image and return a new image pointer.
7021 %
7022 */
7023 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7024  XWindows *windows,const CommandType command,Image **image)
7025 {
7026  char
7027  filename[MaxTextExtent],
7028  geometry[MaxTextExtent],
7029  modulate_factors[MaxTextExtent];
7030 
7031  GeometryInfo
7032  geometry_info;
7033 
7034  Image
7035  *nexus;
7036 
7037  ImageInfo
7038  *image_info;
7039 
7040  int
7041  x,
7042  y;
7043 
7044  MagickStatusType
7045  flags,
7046  status;
7047 
7048  QuantizeInfo
7049  quantize_info;
7050 
7052  page_geometry;
7053 
7054  int
7055  i;
7056 
7057  static char
7058  color[MaxTextExtent] = "gray";
7059 
7060  unsigned int
7061  height,
7062  width;
7063 
7064  /*
7065  Process user command.
7066  */
7067  XCheckRefreshWindows(display,windows);
7068  XImageCache(display,resource_info,windows,command,image);
7069  nexus=NewImageList();
7070  windows->image.window_changes.width=windows->image.ximage->width;
7071  windows->image.window_changes.height=windows->image.ximage->height;
7072  image_info=CloneImageInfo(resource_info->image_info);
7073  SetGeometryInfo(&geometry_info);
7074  GetQuantizeInfo(&quantize_info);
7075  switch (command)
7076  {
7077  case OpenCommand:
7078  {
7079  /*
7080  Load image.
7081  */
7082  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7083  break;
7084  }
7085  case NextCommand:
7086  {
7087  /*
7088  Display next image.
7089  */
7090  for (i=0; i < resource_info->quantum; i++)
7091  XClientMessage(display,windows->image.id,windows->im_protocols,
7092  windows->im_next_image,CurrentTime);
7093  break;
7094  }
7095  case FormerCommand:
7096  {
7097  /*
7098  Display former image.
7099  */
7100  for (i=0; i < resource_info->quantum; i++)
7101  XClientMessage(display,windows->image.id,windows->im_protocols,
7102  windows->im_former_image,CurrentTime);
7103  break;
7104  }
7105  case SelectCommand:
7106  {
7107  int
7108  status;
7109 
7110  /*
7111  Select image.
7112  */
7113  if (*resource_info->home_directory == '\0')
7114  (void) CopyMagickString(resource_info->home_directory,".",
7115  MaxTextExtent);
7116  status=chdir(resource_info->home_directory);
7117  if (status == -1)
7118  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7119  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7120  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7121  break;
7122  }
7123  case SaveCommand:
7124  {
7125  /*
7126  Save image.
7127  */
7128  status=XSaveImage(display,resource_info,windows,*image);
7129  if (status == MagickFalse)
7130  {
7131  char
7132  message[MaxTextExtent];
7133 
7134  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7135  (*image)->exception.reason != (char *) NULL ?
7136  (*image)->exception.reason : "",
7137  (*image)->exception.description != (char *) NULL ?
7138  (*image)->exception.description : "");
7139  XNoticeWidget(display,windows,"Unable to save file:",message);
7140  break;
7141  }
7142  break;
7143  }
7144  case PrintCommand:
7145  {
7146  /*
7147  Print image.
7148  */
7149  status=XPrintImage(display,resource_info,windows,*image);
7150  if (status == MagickFalse)
7151  {
7152  char
7153  message[MaxTextExtent];
7154 
7155  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7156  (*image)->exception.reason != (char *) NULL ?
7157  (*image)->exception.reason : "",
7158  (*image)->exception.description != (char *) NULL ?
7159  (*image)->exception.description : "");
7160  XNoticeWidget(display,windows,"Unable to print file:",message);
7161  break;
7162  }
7163  break;
7164  }
7165  case DeleteCommand:
7166  {
7167  static char
7168  filename[MaxTextExtent] = "\0";
7169 
7170  /*
7171  Delete image file.
7172  */
7173  XFileBrowserWidget(display,windows,"Delete",filename);
7174  if (*filename == '\0')
7175  break;
7176  status=ShredFile(filename);
7177  status|=remove_utf8(filename);
7178  if (status != MagickFalse)
7179  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7180  break;
7181  }
7182  case NewCommand:
7183  {
7184  int
7185  status;
7186 
7187  static char
7188  color[MaxTextExtent] = "gray",
7189  geometry[MaxTextExtent] = "640x480";
7190 
7191  static const char
7192  *format = "gradient";
7193 
7194  /*
7195  Query user for canvas geometry.
7196  */
7197  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7198  geometry);
7199  if (*geometry == '\0')
7200  break;
7201  if (status == 0)
7202  format="xc";
7203  XColorBrowserWidget(display,windows,"Select",color);
7204  if (*color == '\0')
7205  break;
7206  /*
7207  Create canvas.
7208  */
7209  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7210  "%s:%s",format,color);
7211  (void) CloneString(&image_info->size,geometry);
7212  nexus=ReadImage(image_info,&(*image)->exception);
7213  CatchException(&(*image)->exception);
7214  XClientMessage(display,windows->image.id,windows->im_protocols,
7215  windows->im_next_image,CurrentTime);
7216  break;
7217  }
7218  case VisualDirectoryCommand:
7219  {
7220  /*
7221  Visual Image directory.
7222  */
7223  nexus=XVisualDirectoryImage(display,resource_info,windows);
7224  break;
7225  }
7226  case QuitCommand:
7227  {
7228  /*
7229  exit program.
7230  */
7231  if (resource_info->confirm_exit == MagickFalse)
7232  XClientMessage(display,windows->image.id,windows->im_protocols,
7233  windows->im_exit,CurrentTime);
7234  else
7235  {
7236  int
7237  status;
7238 
7239  /*
7240  Confirm program exit.
7241  */
7242  status=XConfirmWidget(display,windows,"Do you really want to exit",
7243  resource_info->client_name);
7244  if (status > 0)
7245  XClientMessage(display,windows->image.id,windows->im_protocols,
7246  windows->im_exit,CurrentTime);
7247  }
7248  break;
7249  }
7250  case CutCommand:
7251  {
7252  /*
7253  Cut image.
7254  */
7255  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7256  break;
7257  }
7258  case CopyCommand:
7259  {
7260  /*
7261  Copy image.
7262  */
7263  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7264  break;
7265  }
7266  case PasteCommand:
7267  {
7268  /*
7269  Paste image.
7270  */
7271  status=XPasteImage(display,resource_info,windows,*image);
7272  if (status == MagickFalse)
7273  {
7274  XNoticeWidget(display,windows,"Unable to paste X image",
7275  (*image)->filename);
7276  break;
7277  }
7278  break;
7279  }
7280  case HalfSizeCommand:
7281  {
7282  /*
7283  Half image size.
7284  */
7285  windows->image.window_changes.width=windows->image.ximage->width/2;
7286  windows->image.window_changes.height=windows->image.ximage->height/2;
7287  (void) XConfigureImage(display,resource_info,windows,*image);
7288  break;
7289  }
7290  case OriginalSizeCommand:
7291  {
7292  /*
7293  Original image size.
7294  */
7295  windows->image.window_changes.width=(int) (*image)->columns;
7296  windows->image.window_changes.height=(int) (*image)->rows;
7297  (void) XConfigureImage(display,resource_info,windows,*image);
7298  break;
7299  }
7300  case DoubleSizeCommand:
7301  {
7302  /*
7303  Double the image size.
7304  */
7305  windows->image.window_changes.width=windows->image.ximage->width << 1;
7306  windows->image.window_changes.height=windows->image.ximage->height << 1;
7307  (void) XConfigureImage(display,resource_info,windows,*image);
7308  break;
7309  }
7310  case ResizeCommand:
7311  {
7312  int
7313  status;
7314 
7315  size_t
7316  height,
7317  width;
7318 
7319  ssize_t
7320  x,
7321  y;
7322 
7323  /*
7324  Resize image.
7325  */
7326  width=(size_t) windows->image.ximage->width;
7327  height=(size_t) windows->image.ximage->height;
7328  x=0;
7329  y=0;
7330  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7331  (double) width,(double) height);
7332  status=XDialogWidget(display,windows,"Resize",
7333  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7334  if (*geometry == '\0')
7335  break;
7336  if (status == 0)
7337  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7338  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7339  windows->image.window_changes.width=(int) width;
7340  windows->image.window_changes.height=(int) height;
7341  (void) XConfigureImage(display,resource_info,windows,*image);
7342  break;
7343  }
7344  case ApplyCommand:
7345  {
7346  char
7347  image_geometry[MaxTextExtent];
7348 
7349  if ((windows->image.crop_geometry == (char *) NULL) &&
7350  ((int) (*image)->columns == windows->image.ximage->width) &&
7351  ((int) (*image)->rows == windows->image.ximage->height))
7352  break;
7353  /*
7354  Apply size transforms to image.
7355  */
7356  XSetCursorState(display,windows,MagickTrue);
7357  XCheckRefreshWindows(display,windows);
7358  /*
7359  Crop and/or scale displayed image.
7360  */
7361  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7362  windows->image.ximage->width,windows->image.ximage->height);
7363  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7364  if (windows->image.crop_geometry != (char *) NULL)
7365  windows->image.crop_geometry=(char *)
7366  RelinquishMagickMemory(windows->image.crop_geometry);
7367  windows->image.x=0;
7368  windows->image.y=0;
7369  XConfigureImageColormap(display,resource_info,windows,*image);
7370  (void) XConfigureImage(display,resource_info,windows,*image);
7371  break;
7372  }
7373  case RefreshCommand:
7374  {
7375  (void) XConfigureImage(display,resource_info,windows,*image);
7376  break;
7377  }
7378  case RestoreCommand:
7379  {
7380  /*
7381  Restore Image window to its original size.
7382  */
7383  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7384  (windows->image.height == (unsigned int) (*image)->rows) &&
7385  (windows->image.crop_geometry == (char *) NULL))
7386  {
7387  (void) XBell(display,0);
7388  break;
7389  }
7390  windows->image.window_changes.width=(int) (*image)->columns;
7391  windows->image.window_changes.height=(int) (*image)->rows;
7392  if (windows->image.crop_geometry != (char *) NULL)
7393  {
7394  windows->image.crop_geometry=(char *)
7395  RelinquishMagickMemory(windows->image.crop_geometry);
7396  windows->image.crop_geometry=(char *) NULL;
7397  windows->image.x=0;
7398  windows->image.y=0;
7399  }
7400  XConfigureImageColormap(display,resource_info,windows,*image);
7401  (void) XConfigureImage(display,resource_info,windows,*image);
7402  break;
7403  }
7404  case CropCommand:
7405  {
7406  /*
7407  Crop image.
7408  */
7409  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7410  break;
7411  }
7412  case ChopCommand:
7413  {
7414  /*
7415  Chop image.
7416  */
7417  status=XChopImage(display,resource_info,windows,image);
7418  if (status == MagickFalse)
7419  {
7420  XNoticeWidget(display,windows,"Unable to cut X image",
7421  (*image)->filename);
7422  break;
7423  }
7424  break;
7425  }
7426  case FlopCommand:
7427  {
7428  Image
7429  *flop_image;
7430 
7431  /*
7432  Flop image scanlines.
7433  */
7434  XSetCursorState(display,windows,MagickTrue);
7435  XCheckRefreshWindows(display,windows);
7436  flop_image=FlopImage(*image,&(*image)->exception);
7437  if (flop_image != (Image *) NULL)
7438  {
7439  *image=DestroyImage(*image);
7440  *image=flop_image;
7441  }
7442  CatchException(&(*image)->exception);
7443  XSetCursorState(display,windows,MagickFalse);
7444  if (windows->image.crop_geometry != (char *) NULL)
7445  {
7446  /*
7447  Flop crop geometry.
7448  */
7449  width=(unsigned int) (*image)->columns;
7450  height=(unsigned int) (*image)->rows;
7451  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7452  &width,&height);
7453  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7454  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7455  }
7456  if (windows->image.orphan != MagickFalse)
7457  break;
7458  (void) XConfigureImage(display,resource_info,windows,*image);
7459  break;
7460  }
7461  case FlipCommand:
7462  {
7463  Image
7464  *flip_image;
7465 
7466  /*
7467  Flip image scanlines.
7468  */
7469  XSetCursorState(display,windows,MagickTrue);
7470  XCheckRefreshWindows(display,windows);
7471  flip_image=FlipImage(*image,&(*image)->exception);
7472  if (flip_image != (Image *) NULL)
7473  {
7474  *image=DestroyImage(*image);
7475  *image=flip_image;
7476  }
7477  CatchException(&(*image)->exception);
7478  XSetCursorState(display,windows,MagickFalse);
7479  if (windows->image.crop_geometry != (char *) NULL)
7480  {
7481  /*
7482  Flip crop geometry.
7483  */
7484  width=(unsigned int) (*image)->columns;
7485  height=(unsigned int) (*image)->rows;
7486  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7487  &width,&height);
7488  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7489  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7490  }
7491  if (windows->image.orphan != MagickFalse)
7492  break;
7493  (void) XConfigureImage(display,resource_info,windows,*image);
7494  break;
7495  }
7496  case RotateRightCommand:
7497  {
7498  /*
7499  Rotate image 90 degrees clockwise.
7500  */
7501  status=XRotateImage(display,resource_info,windows,90.0,image);
7502  if (status == MagickFalse)
7503  {
7504  XNoticeWidget(display,windows,"Unable to rotate X image",
7505  (*image)->filename);
7506  break;
7507  }
7508  break;
7509  }
7510  case RotateLeftCommand:
7511  {
7512  /*
7513  Rotate image 90 degrees counter-clockwise.
7514  */
7515  status=XRotateImage(display,resource_info,windows,-90.0,image);
7516  if (status == MagickFalse)
7517  {
7518  XNoticeWidget(display,windows,"Unable to rotate X image",
7519  (*image)->filename);
7520  break;
7521  }
7522  break;
7523  }
7524  case RotateCommand:
7525  {
7526  /*
7527  Rotate image.
7528  */
7529  status=XRotateImage(display,resource_info,windows,0.0,image);
7530  if (status == MagickFalse)
7531  {
7532  XNoticeWidget(display,windows,"Unable to rotate X image",
7533  (*image)->filename);
7534  break;
7535  }
7536  break;
7537  }
7538  case ShearCommand:
7539  {
7540  Image
7541  *shear_image;
7542 
7543  static char
7544  geometry[MaxTextExtent] = "45.0x45.0";
7545 
7546  /*
7547  Query user for shear color and geometry.
7548  */
7549  XColorBrowserWidget(display,windows,"Select",color);
7550  if (*color == '\0')
7551  break;
7552  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7553  geometry);
7554  if (*geometry == '\0')
7555  break;
7556  /*
7557  Shear image.
7558  */
7559  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7560  XSetCursorState(display,windows,MagickTrue);
7561  XCheckRefreshWindows(display,windows);
7562  (void) QueryColorDatabase(color,&(*image)->background_color,
7563  &(*image)->exception);
7564  flags=ParseGeometry(geometry,&geometry_info);
7565  if ((flags & SigmaValue) == 0)
7566  geometry_info.sigma=geometry_info.rho;
7567  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7568  &(*image)->exception);
7569  if (shear_image != (Image *) NULL)
7570  {
7571  *image=DestroyImage(*image);
7572  *image=shear_image;
7573  }
7574  CatchException(&(*image)->exception);
7575  XSetCursorState(display,windows,MagickFalse);
7576  if (windows->image.orphan != MagickFalse)
7577  break;
7578  windows->image.window_changes.width=(int) (*image)->columns;
7579  windows->image.window_changes.height=(int) (*image)->rows;
7580  XConfigureImageColormap(display,resource_info,windows,*image);
7581  (void) XConfigureImage(display,resource_info,windows,*image);
7582  break;
7583  }
7584  case RollCommand:
7585  {
7586  Image
7587  *roll_image;
7588 
7589  static char
7590  geometry[MaxTextExtent] = "+2+2";
7591 
7592  /*
7593  Query user for the roll geometry.
7594  */
7595  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7596  geometry);
7597  if (*geometry == '\0')
7598  break;
7599  /*
7600  Roll image.
7601  */
7602  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7603  XSetCursorState(display,windows,MagickTrue);
7604  XCheckRefreshWindows(display,windows);
7605  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7606  &(*image)->exception);
7607  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7608  &(*image)->exception);
7609  if (roll_image != (Image *) NULL)
7610  {
7611  *image=DestroyImage(*image);
7612  *image=roll_image;
7613  }
7614  CatchException(&(*image)->exception);
7615  XSetCursorState(display,windows,MagickFalse);
7616  if (windows->image.orphan != MagickFalse)
7617  break;
7618  windows->image.window_changes.width=(int) (*image)->columns;
7619  windows->image.window_changes.height=(int) (*image)->rows;
7620  XConfigureImageColormap(display,resource_info,windows,*image);
7621  (void) XConfigureImage(display,resource_info,windows,*image);
7622  break;
7623  }
7624  case TrimCommand:
7625  {
7626  static char
7627  fuzz[MaxTextExtent];
7628 
7629  /*
7630  Query user for the fuzz factor.
7631  */
7632  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7633  (*image)->fuzz/(QuantumRange+1.0));
7634  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7635  if (*fuzz == '\0')
7636  break;
7637  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7638  /*
7639  Trim image.
7640  */
7641  status=XTrimImage(display,resource_info,windows,*image);
7642  if (status == MagickFalse)
7643  {
7644  XNoticeWidget(display,windows,"Unable to trim X image",
7645  (*image)->filename);
7646  break;
7647  }
7648  break;
7649  }
7650  case HueCommand:
7651  {
7652  static char
7653  hue_percent[MaxTextExtent] = "110";
7654 
7655  /*
7656  Query user for percent hue change.
7657  */
7658  (void) XDialogWidget(display,windows,"Apply",
7659  "Enter percent change in image hue (0-200):",hue_percent);
7660  if (*hue_percent == '\0')
7661  break;
7662  /*
7663  Vary the image hue.
7664  */
7665  XSetCursorState(display,windows,MagickTrue);
7666  XCheckRefreshWindows(display,windows);
7667  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7668  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7669  MaxTextExtent);
7670  (void) ModulateImage(*image,modulate_factors);
7671  XSetCursorState(display,windows,MagickFalse);
7672  if (windows->image.orphan != MagickFalse)
7673  break;
7674  XConfigureImageColormap(display,resource_info,windows,*image);
7675  (void) XConfigureImage(display,resource_info,windows,*image);
7676  break;
7677  }
7678  case SaturationCommand:
7679  {
7680  static char
7681  saturation_percent[MaxTextExtent] = "110";
7682 
7683  /*
7684  Query user for percent saturation change.
7685  */
7686  (void) XDialogWidget(display,windows,"Apply",
7687  "Enter percent change in color saturation (0-200):",saturation_percent);
7688  if (*saturation_percent == '\0')
7689  break;
7690  /*
7691  Vary color saturation.
7692  */
7693  XSetCursorState(display,windows,MagickTrue);
7694  XCheckRefreshWindows(display,windows);
7695  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7696  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7697  MaxTextExtent);
7698  (void) ModulateImage(*image,modulate_factors);
7699  XSetCursorState(display,windows,MagickFalse);
7700  if (windows->image.orphan != MagickFalse)
7701  break;
7702  XConfigureImageColormap(display,resource_info,windows,*image);
7703  (void) XConfigureImage(display,resource_info,windows,*image);
7704  break;
7705  }
7706  case BrightnessCommand:
7707  {
7708  static char
7709  brightness_percent[MaxTextExtent] = "110";
7710 
7711  /*
7712  Query user for percent brightness change.
7713  */
7714  (void) XDialogWidget(display,windows,"Apply",
7715  "Enter percent change in color brightness (0-200):",brightness_percent);
7716  if (*brightness_percent == '\0')
7717  break;
7718  /*
7719  Vary the color brightness.
7720  */
7721  XSetCursorState(display,windows,MagickTrue);
7722  XCheckRefreshWindows(display,windows);
7723  (void) CopyMagickString(modulate_factors,brightness_percent,
7724  MaxTextExtent);
7725  (void) ModulateImage(*image,modulate_factors);
7726  XSetCursorState(display,windows,MagickFalse);
7727  if (windows->image.orphan != MagickFalse)
7728  break;
7729  XConfigureImageColormap(display,resource_info,windows,*image);
7730  (void) XConfigureImage(display,resource_info,windows,*image);
7731  break;
7732  }
7733  case GammaCommand:
7734  {
7735  static char
7736  factor[MaxTextExtent] = "1.6";
7737 
7738  /*
7739  Query user for gamma value.
7740  */
7741  (void) XDialogWidget(display,windows,"Gamma",
7742  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7743  if (*factor == '\0')
7744  break;
7745  /*
7746  Gamma correct image.
7747  */
7748  XSetCursorState(display,windows,MagickTrue);
7749  XCheckRefreshWindows(display,windows);
7750  (void) GammaImage(*image,factor);
7751  XSetCursorState(display,windows,MagickFalse);
7752  if (windows->image.orphan != MagickFalse)
7753  break;
7754  XConfigureImageColormap(display,resource_info,windows,*image);
7755  (void) XConfigureImage(display,resource_info,windows,*image);
7756  break;
7757  }
7758  case SpiffCommand:
7759  {
7760  /*
7761  Sharpen the image contrast.
7762  */
7763  XSetCursorState(display,windows,MagickTrue);
7764  XCheckRefreshWindows(display,windows);
7765  (void) ContrastImage(*image,MagickTrue);
7766  XSetCursorState(display,windows,MagickFalse);
7767  if (windows->image.orphan != MagickFalse)
7768  break;
7769  XConfigureImageColormap(display,resource_info,windows,*image);
7770  (void) XConfigureImage(display,resource_info,windows,*image);
7771  break;
7772  }
7773  case DullCommand:
7774  {
7775  /*
7776  Dull the image contrast.
7777  */
7778  XSetCursorState(display,windows,MagickTrue);
7779  XCheckRefreshWindows(display,windows);
7780  (void) ContrastImage(*image,MagickFalse);
7781  XSetCursorState(display,windows,MagickFalse);
7782  if (windows->image.orphan != MagickFalse)
7783  break;
7784  XConfigureImageColormap(display,resource_info,windows,*image);
7785  (void) XConfigureImage(display,resource_info,windows,*image);
7786  break;
7787  }
7788  case ContrastStretchCommand:
7789  {
7790  double
7791  black_point,
7792  white_point;
7793 
7794  static char
7795  levels[MaxTextExtent] = "1%";
7796 
7797  /*
7798  Query user for gamma value.
7799  */
7800  (void) XDialogWidget(display,windows,"Contrast Stretch",
7801  "Enter black and white points:",levels);
7802  if (*levels == '\0')
7803  break;
7804  /*
7805  Contrast stretch image.
7806  */
7807  XSetCursorState(display,windows,MagickTrue);
7808  XCheckRefreshWindows(display,windows);
7809  flags=ParseGeometry(levels,&geometry_info);
7810  black_point=geometry_info.rho;
7811  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7812  if ((flags & PercentValue) != 0)
7813  {
7814  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7815  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7816  }
7817  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7818  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7819  white_point);
7820  XSetCursorState(display,windows,MagickFalse);
7821  if (windows->image.orphan != MagickFalse)
7822  break;
7823  XConfigureImageColormap(display,resource_info,windows,*image);
7824  (void) XConfigureImage(display,resource_info,windows,*image);
7825  break;
7826  }
7827  case SigmoidalContrastCommand:
7828  {
7829  static char
7830  levels[MaxTextExtent] = "3x50%";
7831 
7832  /*
7833  Query user for gamma value.
7834  */
7835  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7836  "Enter contrast and midpoint:",levels);
7837  if (*levels == '\0')
7838  break;
7839  /*
7840  Contrast stretch image.
7841  */
7842  XSetCursorState(display,windows,MagickTrue);
7843  XCheckRefreshWindows(display,windows);
7844  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7845  XSetCursorState(display,windows,MagickFalse);
7846  if (windows->image.orphan != MagickFalse)
7847  break;
7848  XConfigureImageColormap(display,resource_info,windows,*image);
7849  (void) XConfigureImage(display,resource_info,windows,*image);
7850  break;
7851  }
7852  case NormalizeCommand:
7853  {
7854  /*
7855  Perform histogram normalization on the image.
7856  */
7857  XSetCursorState(display,windows,MagickTrue);
7858  XCheckRefreshWindows(display,windows);
7859  (void) NormalizeImage(*image);
7860  XSetCursorState(display,windows,MagickFalse);
7861  if (windows->image.orphan != MagickFalse)
7862  break;
7863  XConfigureImageColormap(display,resource_info,windows,*image);
7864  (void) XConfigureImage(display,resource_info,windows,*image);
7865  break;
7866  }
7867  case EqualizeCommand:
7868  {
7869  /*
7870  Perform histogram equalization on the image.
7871  */
7872  XSetCursorState(display,windows,MagickTrue);
7873  XCheckRefreshWindows(display,windows);
7874  (void) EqualizeImage(*image);
7875  XSetCursorState(display,windows,MagickFalse);
7876  if (windows->image.orphan != MagickFalse)
7877  break;
7878  XConfigureImageColormap(display,resource_info,windows,*image);
7879  (void) XConfigureImage(display,resource_info,windows,*image);
7880  break;
7881  }
7882  case NegateCommand:
7883  {
7884  /*
7885  Negate colors in image.
7886  */
7887  XSetCursorState(display,windows,MagickTrue);
7888  XCheckRefreshWindows(display,windows);
7889  (void) NegateImage(*image,MagickFalse);
7890  XSetCursorState(display,windows,MagickFalse);
7891  if (windows->image.orphan != MagickFalse)
7892  break;
7893  XConfigureImageColormap(display,resource_info,windows,*image);
7894  (void) XConfigureImage(display,resource_info,windows,*image);
7895  break;
7896  }
7897  case GrayscaleCommand:
7898  {
7899  /*
7900  Convert image to grayscale.
7901  */
7902  XSetCursorState(display,windows,MagickTrue);
7903  XCheckRefreshWindows(display,windows);
7904  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7905  GrayscaleType : GrayscaleMatteType);
7906  XSetCursorState(display,windows,MagickFalse);
7907  if (windows->image.orphan != MagickFalse)
7908  break;
7909  XConfigureImageColormap(display,resource_info,windows,*image);
7910  (void) XConfigureImage(display,resource_info,windows,*image);
7911  break;
7912  }
7913  case MapCommand:
7914  {
7915  Image
7916  *affinity_image;
7917 
7918  static char
7919  filename[MaxTextExtent] = "\0";
7920 
7921  /*
7922  Request image file name from user.
7923  */
7924  XFileBrowserWidget(display,windows,"Map",filename);
7925  if (*filename == '\0')
7926  break;
7927  /*
7928  Map image.
7929  */
7930  XSetCursorState(display,windows,MagickTrue);
7931  XCheckRefreshWindows(display,windows);
7932  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7933  affinity_image=ReadImage(image_info,&(*image)->exception);
7934  if (affinity_image != (Image *) NULL)
7935  {
7936  (void) RemapImage(&quantize_info,*image,affinity_image);
7937  affinity_image=DestroyImage(affinity_image);
7938  }
7939  CatchException(&(*image)->exception);
7940  XSetCursorState(display,windows,MagickFalse);
7941  if (windows->image.orphan != MagickFalse)
7942  break;
7943  XConfigureImageColormap(display,resource_info,windows,*image);
7944  (void) XConfigureImage(display,resource_info,windows,*image);
7945  break;
7946  }
7947  case QuantizeCommand:
7948  {
7949  int
7950  status;
7951 
7952  static char
7953  colors[MaxTextExtent] = "256";
7954 
7955  /*
7956  Query user for maximum number of colors.
7957  */
7958  status=XDialogWidget(display,windows,"Quantize",
7959  "Maximum number of colors:",colors);
7960  if (*colors == '\0')
7961  break;
7962  /*
7963  Color reduce the image.
7964  */
7965  XSetCursorState(display,windows,MagickTrue);
7966  XCheckRefreshWindows(display,windows);
7967  quantize_info.number_colors=StringToUnsignedLong(colors);
7968  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7969  (void) QuantizeImage(&quantize_info,*image);
7970  XSetCursorState(display,windows,MagickFalse);
7971  if (windows->image.orphan != MagickFalse)
7972  break;
7973  XConfigureImageColormap(display,resource_info,windows,*image);
7974  (void) XConfigureImage(display,resource_info,windows,*image);
7975  break;
7976  }
7977  case DespeckleCommand:
7978  {
7979  Image
7980  *despeckle_image;
7981 
7982  /*
7983  Despeckle image.
7984  */
7985  XSetCursorState(display,windows,MagickTrue);
7986  XCheckRefreshWindows(display,windows);
7987  despeckle_image=DespeckleImage(*image,&(*image)->exception);
7988  if (despeckle_image != (Image *) NULL)
7989  {
7990  *image=DestroyImage(*image);
7991  *image=despeckle_image;
7992  }
7993  CatchException(&(*image)->exception);
7994  XSetCursorState(display,windows,MagickFalse);
7995  if (windows->image.orphan != MagickFalse)
7996  break;
7997  XConfigureImageColormap(display,resource_info,windows,*image);
7998  (void) XConfigureImage(display,resource_info,windows,*image);
7999  break;
8000  }
8001  case EmbossCommand:
8002  {
8003  Image
8004  *emboss_image;
8005 
8006  static char
8007  radius[MaxTextExtent] = "0.0x1.0";
8008 
8009  /*
8010  Query user for emboss radius.
8011  */
8012  (void) XDialogWidget(display,windows,"Emboss",
8013  "Enter the emboss radius and standard deviation:",radius);
8014  if (*radius == '\0')
8015  break;
8016  /*
8017  Reduce noise in the image.
8018  */
8019  XSetCursorState(display,windows,MagickTrue);
8020  XCheckRefreshWindows(display,windows);
8021  flags=ParseGeometry(radius,&geometry_info);
8022  if ((flags & SigmaValue) == 0)
8023  geometry_info.sigma=1.0;
8024  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8025  &(*image)->exception);
8026  if (emboss_image != (Image *) NULL)
8027  {
8028  *image=DestroyImage(*image);
8029  *image=emboss_image;
8030  }
8031  CatchException(&(*image)->exception);
8032  XSetCursorState(display,windows,MagickFalse);
8033  if (windows->image.orphan != MagickFalse)
8034  break;
8035  XConfigureImageColormap(display,resource_info,windows,*image);
8036  (void) XConfigureImage(display,resource_info,windows,*image);
8037  break;
8038  }
8039  case ReduceNoiseCommand:
8040  {
8041  Image
8042  *noise_image;
8043 
8044  static char
8045  radius[MaxTextExtent] = "0";
8046 
8047  /*
8048  Query user for noise radius.
8049  */
8050  (void) XDialogWidget(display,windows,"Reduce Noise",
8051  "Enter the noise radius:",radius);
8052  if (*radius == '\0')
8053  break;
8054  /*
8055  Reduce noise in the image.
8056  */
8057  XSetCursorState(display,windows,MagickTrue);
8058  XCheckRefreshWindows(display,windows);
8059  flags=ParseGeometry(radius,&geometry_info);
8060  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8061  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8062  if (noise_image != (Image *) NULL)
8063  {
8064  *image=DestroyImage(*image);
8065  *image=noise_image;
8066  }
8067  CatchException(&(*image)->exception);
8068  XSetCursorState(display,windows,MagickFalse);
8069  if (windows->image.orphan != MagickFalse)
8070  break;
8071  XConfigureImageColormap(display,resource_info,windows,*image);
8072  (void) XConfigureImage(display,resource_info,windows,*image);
8073  break;
8074  }
8075  case AddNoiseCommand:
8076  {
8077  char
8078  **noises;
8079 
8080  Image
8081  *noise_image;
8082 
8083  static char
8084  noise_type[MaxTextExtent] = "Gaussian";
8085 
8086  /*
8087  Add noise to the image.
8088  */
8089  noises=GetCommandOptions(MagickNoiseOptions);
8090  if (noises == (char **) NULL)
8091  break;
8092  XListBrowserWidget(display,windows,&windows->widget,
8093  (const char **) noises,"Add Noise",
8094  "Select a type of noise to add to your image:",noise_type);
8095  noises=DestroyStringList(noises);
8096  if (*noise_type == '\0')
8097  break;
8098  XSetCursorState(display,windows,MagickTrue);
8099  XCheckRefreshWindows(display,windows);
8100  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8101  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8102  if (noise_image != (Image *) NULL)
8103  {
8104  *image=DestroyImage(*image);
8105  *image=noise_image;
8106  }
8107  CatchException(&(*image)->exception);
8108  XSetCursorState(display,windows,MagickFalse);
8109  if (windows->image.orphan != MagickFalse)
8110  break;
8111  XConfigureImageColormap(display,resource_info,windows,*image);
8112  (void) XConfigureImage(display,resource_info,windows,*image);
8113  break;
8114  }
8115  case SharpenCommand:
8116  {
8117  Image
8118  *sharp_image;
8119 
8120  static char
8121  radius[MaxTextExtent] = "0.0x1.0";
8122 
8123  /*
8124  Query user for sharpen radius.
8125  */
8126  (void) XDialogWidget(display,windows,"Sharpen",
8127  "Enter the sharpen radius and standard deviation:",radius);
8128  if (*radius == '\0')
8129  break;
8130  /*
8131  Sharpen image scanlines.
8132  */
8133  XSetCursorState(display,windows,MagickTrue);
8134  XCheckRefreshWindows(display,windows);
8135  flags=ParseGeometry(radius,&geometry_info);
8136  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8137  &(*image)->exception);
8138  if (sharp_image != (Image *) NULL)
8139  {
8140  *image=DestroyImage(*image);
8141  *image=sharp_image;
8142  }
8143  CatchException(&(*image)->exception);
8144  XSetCursorState(display,windows,MagickFalse);
8145  if (windows->image.orphan != MagickFalse)
8146  break;
8147  XConfigureImageColormap(display,resource_info,windows,*image);
8148  (void) XConfigureImage(display,resource_info,windows,*image);
8149  break;
8150  }
8151  case BlurCommand:
8152  {
8153  Image
8154  *blur_image;
8155 
8156  static char
8157  radius[MaxTextExtent] = "0.0x1.0";
8158 
8159  /*
8160  Query user for blur radius.
8161  */
8162  (void) XDialogWidget(display,windows,"Blur",
8163  "Enter the blur radius and standard deviation:",radius);
8164  if (*radius == '\0')
8165  break;
8166  /*
8167  Blur an image.
8168  */
8169  XSetCursorState(display,windows,MagickTrue);
8170  XCheckRefreshWindows(display,windows);
8171  flags=ParseGeometry(radius,&geometry_info);
8172  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8173  &(*image)->exception);
8174  if (blur_image != (Image *) NULL)
8175  {
8176  *image=DestroyImage(*image);
8177  *image=blur_image;
8178  }
8179  CatchException(&(*image)->exception);
8180  XSetCursorState(display,windows,MagickFalse);
8181  if (windows->image.orphan != MagickFalse)
8182  break;
8183  XConfigureImageColormap(display,resource_info,windows,*image);
8184  (void) XConfigureImage(display,resource_info,windows,*image);
8185  break;
8186  }
8187  case ThresholdCommand:
8188  {
8189  double
8190  threshold;
8191 
8192  static char
8193  factor[MaxTextExtent] = "128";
8194 
8195  /*
8196  Query user for threshold value.
8197  */
8198  (void) XDialogWidget(display,windows,"Threshold",
8199  "Enter threshold value:",factor);
8200  if (*factor == '\0')
8201  break;
8202  /*
8203  Gamma correct image.
8204  */
8205  XSetCursorState(display,windows,MagickTrue);
8206  XCheckRefreshWindows(display,windows);
8207  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8208  (void) BilevelImage(*image,threshold);
8209  XSetCursorState(display,windows,MagickFalse);
8210  if (windows->image.orphan != MagickFalse)
8211  break;
8212  XConfigureImageColormap(display,resource_info,windows,*image);
8213  (void) XConfigureImage(display,resource_info,windows,*image);
8214  break;
8215  }
8216  case EdgeDetectCommand:
8217  {
8218  Image
8219  *edge_image;
8220 
8221  static char
8222  radius[MaxTextExtent] = "0";
8223 
8224  /*
8225  Query user for edge factor.
8226  */
8227  (void) XDialogWidget(display,windows,"Detect Edges",
8228  "Enter the edge detect radius:",radius);
8229  if (*radius == '\0')
8230  break;
8231  /*
8232  Detect edge in image.
8233  */
8234  XSetCursorState(display,windows,MagickTrue);
8235  XCheckRefreshWindows(display,windows);
8236  flags=ParseGeometry(radius,&geometry_info);
8237  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8238  if (edge_image != (Image *) NULL)
8239  {
8240  *image=DestroyImage(*image);
8241  *image=edge_image;
8242  }
8243  CatchException(&(*image)->exception);
8244  XSetCursorState(display,windows,MagickFalse);
8245  if (windows->image.orphan != MagickFalse)
8246  break;
8247  XConfigureImageColormap(display,resource_info,windows,*image);
8248  (void) XConfigureImage(display,resource_info,windows,*image);
8249  break;
8250  }
8251  case SpreadCommand:
8252  {
8253  Image
8254  *spread_image;
8255 
8256  static char
8257  amount[MaxTextExtent] = "2";
8258 
8259  /*
8260  Query user for spread amount.
8261  */
8262  (void) XDialogWidget(display,windows,"Spread",
8263  "Enter the displacement amount:",amount);
8264  if (*amount == '\0')
8265  break;
8266  /*
8267  Displace image pixels by a random amount.
8268  */
8269  XSetCursorState(display,windows,MagickTrue);
8270  XCheckRefreshWindows(display,windows);
8271  flags=ParseGeometry(amount,&geometry_info);
8272  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8273  if (spread_image != (Image *) NULL)
8274  {
8275  *image=DestroyImage(*image);
8276  *image=spread_image;
8277  }
8278  CatchException(&(*image)->exception);
8279  XSetCursorState(display,windows,MagickFalse);
8280  if (windows->image.orphan != MagickFalse)
8281  break;
8282  XConfigureImageColormap(display,resource_info,windows,*image);
8283  (void) XConfigureImage(display,resource_info,windows,*image);
8284  break;
8285  }
8286  case ShadeCommand:
8287  {
8288  Image
8289  *shade_image;
8290 
8291  int
8292  status;
8293 
8294  static char
8295  geometry[MaxTextExtent] = "30x30";
8296 
8297  /*
8298  Query user for the shade geometry.
8299  */
8300  status=XDialogWidget(display,windows,"Shade",
8301  "Enter the azimuth and elevation of the light source:",geometry);
8302  if (*geometry == '\0')
8303  break;
8304  /*
8305  Shade image pixels.
8306  */
8307  XSetCursorState(display,windows,MagickTrue);
8308  XCheckRefreshWindows(display,windows);
8309  flags=ParseGeometry(geometry,&geometry_info);
8310  if ((flags & SigmaValue) == 0)
8311  geometry_info.sigma=1.0;
8312  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8313  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8314  if (shade_image != (Image *) NULL)
8315  {
8316  *image=DestroyImage(*image);
8317  *image=shade_image;
8318  }
8319  CatchException(&(*image)->exception);
8320  XSetCursorState(display,windows,MagickFalse);
8321  if (windows->image.orphan != MagickFalse)
8322  break;
8323  XConfigureImageColormap(display,resource_info,windows,*image);
8324  (void) XConfigureImage(display,resource_info,windows,*image);
8325  break;
8326  }
8327  case RaiseCommand:
8328  {
8329  static char
8330  bevel_width[MaxTextExtent] = "10";
8331 
8332  /*
8333  Query user for bevel width.
8334  */
8335  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8336  if (*bevel_width == '\0')
8337  break;
8338  /*
8339  Raise an image.
8340  */
8341  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8342  XSetCursorState(display,windows,MagickTrue);
8343  XCheckRefreshWindows(display,windows);
8344  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8345  &(*image)->exception);
8346  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8347  XSetCursorState(display,windows,MagickFalse);
8348  if (windows->image.orphan != MagickFalse)
8349  break;
8350  XConfigureImageColormap(display,resource_info,windows,*image);
8351  (void) XConfigureImage(display,resource_info,windows,*image);
8352  break;
8353  }
8354  case SegmentCommand:
8355  {
8356  static char
8357  threshold[MaxTextExtent] = "1.0x1.5";
8358 
8359  /*
8360  Query user for smoothing threshold.
8361  */
8362  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8363  threshold);
8364  if (*threshold == '\0')
8365  break;
8366  /*
8367  Segment an image.
8368  */
8369  XSetCursorState(display,windows,MagickTrue);
8370  XCheckRefreshWindows(display,windows);
8371  flags=ParseGeometry(threshold,&geometry_info);
8372  if ((flags & SigmaValue) == 0)
8373  geometry_info.sigma=1.0;
8374  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8375  geometry_info.sigma);
8376  XSetCursorState(display,windows,MagickFalse);
8377  if (windows->image.orphan != MagickFalse)
8378  break;
8379  XConfigureImageColormap(display,resource_info,windows,*image);
8380  (void) XConfigureImage(display,resource_info,windows,*image);
8381  break;
8382  }
8383  case SepiaToneCommand:
8384  {
8385  double
8386  threshold;
8387 
8388  Image
8389  *sepia_image;
8390 
8391  static char
8392  factor[MaxTextExtent] = "80%";
8393 
8394  /*
8395  Query user for sepia-tone factor.
8396  */
8397  (void) XDialogWidget(display,windows,"Sepia Tone",
8398  "Enter the sepia tone factor (0 - 99.9%):",factor);
8399  if (*factor == '\0')
8400  break;
8401  /*
8402  Sepia tone image pixels.
8403  */
8404  XSetCursorState(display,windows,MagickTrue);
8405  XCheckRefreshWindows(display,windows);
8406  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8407  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8408  if (sepia_image != (Image *) NULL)
8409  {
8410  *image=DestroyImage(*image);
8411  *image=sepia_image;
8412  }
8413  CatchException(&(*image)->exception);
8414  XSetCursorState(display,windows,MagickFalse);
8415  if (windows->image.orphan != MagickFalse)
8416  break;
8417  XConfigureImageColormap(display,resource_info,windows,*image);
8418  (void) XConfigureImage(display,resource_info,windows,*image);
8419  break;
8420  }
8421  case SolarizeCommand:
8422  {
8423  double
8424  threshold;
8425 
8426  static char
8427  factor[MaxTextExtent] = "60%";
8428 
8429  /*
8430  Query user for solarize factor.
8431  */
8432  (void) XDialogWidget(display,windows,"Solarize",
8433  "Enter the solarize factor (0 - 99.9%):",factor);
8434  if (*factor == '\0')
8435  break;
8436  /*
8437  Solarize image pixels.
8438  */
8439  XSetCursorState(display,windows,MagickTrue);
8440  XCheckRefreshWindows(display,windows);
8441  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8442  (void) SolarizeImage(*image,threshold);
8443  XSetCursorState(display,windows,MagickFalse);
8444  if (windows->image.orphan != MagickFalse)
8445  break;
8446  XConfigureImageColormap(display,resource_info,windows,*image);
8447  (void) XConfigureImage(display,resource_info,windows,*image);
8448  break;
8449  }
8450  case SwirlCommand:
8451  {
8452  Image
8453  *swirl_image;
8454 
8455  static char
8456  degrees[MaxTextExtent] = "60";
8457 
8458  /*
8459  Query user for swirl angle.
8460  */
8461  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8462  degrees);
8463  if (*degrees == '\0')
8464  break;
8465  /*
8466  Swirl image pixels about the center.
8467  */
8468  XSetCursorState(display,windows,MagickTrue);
8469  XCheckRefreshWindows(display,windows);
8470  flags=ParseGeometry(degrees,&geometry_info);
8471  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8472  if (swirl_image != (Image *) NULL)
8473  {
8474  *image=DestroyImage(*image);
8475  *image=swirl_image;
8476  }
8477  CatchException(&(*image)->exception);
8478  XSetCursorState(display,windows,MagickFalse);
8479  if (windows->image.orphan != MagickFalse)
8480  break;
8481  XConfigureImageColormap(display,resource_info,windows,*image);
8482  (void) XConfigureImage(display,resource_info,windows,*image);
8483  break;
8484  }
8485  case ImplodeCommand:
8486  {
8487  Image
8488  *implode_image;
8489 
8490  static char
8491  factor[MaxTextExtent] = "0.3";
8492 
8493  /*
8494  Query user for implode factor.
8495  */
8496  (void) XDialogWidget(display,windows,"Implode",
8497  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8498  if (*factor == '\0')
8499  break;
8500  /*
8501  Implode image pixels about the center.
8502  */
8503  XSetCursorState(display,windows,MagickTrue);
8504  XCheckRefreshWindows(display,windows);
8505  flags=ParseGeometry(factor,&geometry_info);
8506  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8507  if (implode_image != (Image *) NULL)
8508  {
8509  *image=DestroyImage(*image);
8510  *image=implode_image;
8511  }
8512  CatchException(&(*image)->exception);
8513  XSetCursorState(display,windows,MagickFalse);
8514  if (windows->image.orphan != MagickFalse)
8515  break;
8516  XConfigureImageColormap(display,resource_info,windows,*image);
8517  (void) XConfigureImage(display,resource_info,windows,*image);
8518  break;
8519  }
8520  case VignetteCommand:
8521  {
8522  Image
8523  *vignette_image;
8524 
8525  static char
8526  geometry[MaxTextExtent] = "0x20";
8527 
8528  /*
8529  Query user for the vignette geometry.
8530  */
8531  (void) XDialogWidget(display,windows,"Vignette",
8532  "Enter the radius, sigma, and x and y offsets:",geometry);
8533  if (*geometry == '\0')
8534  break;
8535  /*
8536  Soften the edges of the image in vignette style
8537  */
8538  XSetCursorState(display,windows,MagickTrue);
8539  XCheckRefreshWindows(display,windows);
8540  flags=ParseGeometry(geometry,&geometry_info);
8541  if ((flags & SigmaValue) == 0)
8542  geometry_info.sigma=1.0;
8543  if ((flags & XiValue) == 0)
8544  geometry_info.xi=0.1*(*image)->columns;
8545  if ((flags & PsiValue) == 0)
8546  geometry_info.psi=0.1*(*image)->rows;
8547  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8548  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8549  0.5),&(*image)->exception);
8550  if (vignette_image != (Image *) NULL)
8551  {
8552  *image=DestroyImage(*image);
8553  *image=vignette_image;
8554  }
8555  CatchException(&(*image)->exception);
8556  XSetCursorState(display,windows,MagickFalse);
8557  if (windows->image.orphan != MagickFalse)
8558  break;
8559  XConfigureImageColormap(display,resource_info,windows,*image);
8560  (void) XConfigureImage(display,resource_info,windows,*image);
8561  break;
8562  }
8563  case WaveCommand:
8564  {
8565  Image
8566  *wave_image;
8567 
8568  static char
8569  geometry[MaxTextExtent] = "25x150";
8570 
8571  /*
8572  Query user for the wave geometry.
8573  */
8574  (void) XDialogWidget(display,windows,"Wave",
8575  "Enter the amplitude and length of the wave:",geometry);
8576  if (*geometry == '\0')
8577  break;
8578  /*
8579  Alter an image along a sine wave.
8580  */
8581  XSetCursorState(display,windows,MagickTrue);
8582  XCheckRefreshWindows(display,windows);
8583  flags=ParseGeometry(geometry,&geometry_info);
8584  if ((flags & SigmaValue) == 0)
8585  geometry_info.sigma=1.0;
8586  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8587  &(*image)->exception);
8588  if (wave_image != (Image *) NULL)
8589  {
8590  *image=DestroyImage(*image);
8591  *image=wave_image;
8592  }
8593  CatchException(&(*image)->exception);
8594  XSetCursorState(display,windows,MagickFalse);
8595  if (windows->image.orphan != MagickFalse)
8596  break;
8597  XConfigureImageColormap(display,resource_info,windows,*image);
8598  (void) XConfigureImage(display,resource_info,windows,*image);
8599  break;
8600  }
8601  case OilPaintCommand:
8602  {
8603  Image
8604  *paint_image;
8605 
8606  static char
8607  radius[MaxTextExtent] = "0";
8608 
8609  /*
8610  Query user for circular neighborhood radius.
8611  */
8612  (void) XDialogWidget(display,windows,"Oil Paint",
8613  "Enter the mask radius:",radius);
8614  if (*radius == '\0')
8615  break;
8616  /*
8617  OilPaint image scanlines.
8618  */
8619  XSetCursorState(display,windows,MagickTrue);
8620  XCheckRefreshWindows(display,windows);
8621  flags=ParseGeometry(radius,&geometry_info);
8622  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8623  if (paint_image != (Image *) NULL)
8624  {
8625  *image=DestroyImage(*image);
8626  *image=paint_image;
8627  }
8628  CatchException(&(*image)->exception);
8629  XSetCursorState(display,windows,MagickFalse);
8630  if (windows->image.orphan != MagickFalse)
8631  break;
8632  XConfigureImageColormap(display,resource_info,windows,*image);
8633  (void) XConfigureImage(display,resource_info,windows,*image);
8634  break;
8635  }
8636  case CharcoalDrawCommand:
8637  {
8638  Image
8639  *charcoal_image;
8640 
8641  static char
8642  radius[MaxTextExtent] = "0x1";
8643 
8644  /*
8645  Query user for charcoal radius.
8646  */
8647  (void) XDialogWidget(display,windows,"Charcoal Draw",
8648  "Enter the charcoal radius and sigma:",radius);
8649  if (*radius == '\0')
8650  break;
8651  /*
8652  Charcoal the image.
8653  */
8654  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8655  XSetCursorState(display,windows,MagickTrue);
8656  XCheckRefreshWindows(display,windows);
8657  flags=ParseGeometry(radius,&geometry_info);
8658  if ((flags & SigmaValue) == 0)
8659  geometry_info.sigma=geometry_info.rho;
8660  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8661  &(*image)->exception);
8662  if (charcoal_image != (Image *) NULL)
8663  {
8664  *image=DestroyImage(*image);
8665  *image=charcoal_image;
8666  }
8667  CatchException(&(*image)->exception);
8668  XSetCursorState(display,windows,MagickFalse);
8669  if (windows->image.orphan != MagickFalse)
8670  break;
8671  XConfigureImageColormap(display,resource_info,windows,*image);
8672  (void) XConfigureImage(display,resource_info,windows,*image);
8673  break;
8674  }
8675  case AnnotateCommand:
8676  {
8677  /*
8678  Annotate the image with text.
8679  */
8680  status=XAnnotateEditImage(display,resource_info,windows,*image);
8681  if (status == MagickFalse)
8682  {
8683  XNoticeWidget(display,windows,"Unable to annotate X image",
8684  (*image)->filename);
8685  break;
8686  }
8687  break;
8688  }
8689  case DrawCommand:
8690  {
8691  /*
8692  Draw image.
8693  */
8694  status=XDrawEditImage(display,resource_info,windows,image);
8695  if (status == MagickFalse)
8696  {
8697  XNoticeWidget(display,windows,"Unable to draw on the X image",
8698  (*image)->filename);
8699  break;
8700  }
8701  break;
8702  }
8703  case ColorCommand:
8704  {
8705  /*
8706  Color edit.
8707  */
8708  status=XColorEditImage(display,resource_info,windows,image);
8709  if (status == MagickFalse)
8710  {
8711  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8712  (*image)->filename);
8713  break;
8714  }
8715  break;
8716  }
8717  case MatteCommand:
8718  {
8719  /*
8720  Matte edit.
8721  */
8722  status=XMatteEditImage(display,resource_info,windows,image);
8723  if (status == MagickFalse)
8724  {
8725  XNoticeWidget(display,windows,"Unable to matte edit X image",
8726  (*image)->filename);
8727  break;
8728  }
8729  break;
8730  }
8731  case CompositeCommand:
8732  {
8733  /*
8734  Composite image.
8735  */
8736  status=XCompositeImage(display,resource_info,windows,*image);
8737  if (status == MagickFalse)
8738  {
8739  XNoticeWidget(display,windows,"Unable to composite X image",
8740  (*image)->filename);
8741  break;
8742  }
8743  break;
8744  }
8745  case AddBorderCommand:
8746  {
8747  Image
8748  *border_image;
8749 
8750  static char
8751  geometry[MaxTextExtent] = "6x6";
8752 
8753  /*
8754  Query user for border color and geometry.
8755  */
8756  XColorBrowserWidget(display,windows,"Select",color);
8757  if (*color == '\0')
8758  break;
8759  (void) XDialogWidget(display,windows,"Add Border",
8760  "Enter border geometry:",geometry);
8761  if (*geometry == '\0')
8762  break;
8763  /*
8764  Add a border to the image.
8765  */
8766  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8767  XSetCursorState(display,windows,MagickTrue);
8768  XCheckRefreshWindows(display,windows);
8769  (void) QueryColorDatabase(color,&(*image)->border_color,
8770  &(*image)->exception);
8771  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8772  &(*image)->exception);
8773  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8774  if (border_image != (Image *) NULL)
8775  {
8776  *image=DestroyImage(*image);
8777  *image=border_image;
8778  }
8779  CatchException(&(*image)->exception);
8780  XSetCursorState(display,windows,MagickFalse);
8781  if (windows->image.orphan != MagickFalse)
8782  break;
8783  windows->image.window_changes.width=(int) (*image)->columns;
8784  windows->image.window_changes.height=(int) (*image)->rows;
8785  XConfigureImageColormap(display,resource_info,windows,*image);
8786  (void) XConfigureImage(display,resource_info,windows,*image);
8787  break;
8788  }
8789  case AddFrameCommand:
8790  {
8791  FrameInfo
8792  frame_info;
8793 
8794  Image
8795  *frame_image;
8796 
8797  static char
8798  geometry[MaxTextExtent] = "6x6";
8799 
8800  /*
8801  Query user for frame color and geometry.
8802  */
8803  XColorBrowserWidget(display,windows,"Select",color);
8804  if (*color == '\0')
8805  break;
8806  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8807  geometry);
8808  if (*geometry == '\0')
8809  break;
8810  /*
8811  Surround image with an ornamental border.
8812  */
8813  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8814  XSetCursorState(display,windows,MagickTrue);
8815  XCheckRefreshWindows(display,windows);
8816  (void) QueryColorDatabase(color,&(*image)->matte_color,
8817  &(*image)->exception);
8818  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8819  &(*image)->exception);
8820  frame_info.width=page_geometry.width;
8821  frame_info.height=page_geometry.height;
8822  frame_info.outer_bevel=page_geometry.x;
8823  frame_info.inner_bevel=page_geometry.y;
8824  frame_info.x=(ssize_t) frame_info.width;
8825  frame_info.y=(ssize_t) frame_info.height;
8826  frame_info.width=(*image)->columns+2*frame_info.width;
8827  frame_info.height=(*image)->rows+2*frame_info.height;
8828  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8829  if (frame_image != (Image *) NULL)
8830  {
8831  *image=DestroyImage(*image);
8832  *image=frame_image;
8833  }
8834  CatchException(&(*image)->exception);
8835  XSetCursorState(display,windows,MagickFalse);
8836  if (windows->image.orphan != MagickFalse)
8837  break;
8838  windows->image.window_changes.width=(int) (*image)->columns;
8839  windows->image.window_changes.height=(int) (*image)->rows;
8840  XConfigureImageColormap(display,resource_info,windows,*image);
8841  (void) XConfigureImage(display,resource_info,windows,*image);
8842  break;
8843  }
8844  case CommentCommand:
8845  {
8846  const char
8847  *value;
8848 
8849  FILE
8850  *file;
8851 
8852  int
8853  unique_file;
8854 
8855  /*
8856  Edit image comment.
8857  */
8858  unique_file=AcquireUniqueFileResource(image_info->filename);
8859  if (unique_file == -1)
8860  {
8861  XNoticeWidget(display,windows,"Unable to edit image comment",
8862  image_info->filename);
8863  break;
8864  }
8865  value=GetImageProperty(*image,"comment");
8866  if (value == (char *) NULL)
8867  unique_file=close(unique_file)-1;
8868  else
8869  {
8870  const char
8871  *p;
8872 
8873  file=fdopen(unique_file,"w");
8874  if (file == (FILE *) NULL)
8875  {
8876  XNoticeWidget(display,windows,"Unable to edit image comment",
8877  image_info->filename);
8878  break;
8879  }
8880  for (p=value; *p != '\0'; p++)
8881  (void) fputc((int) *p,file);
8882  (void) fputc('\n',file);
8883  (void) fclose(file);
8884  }
8885  XSetCursorState(display,windows,MagickTrue);
8886  XCheckRefreshWindows(display,windows);
8887  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8888  &(*image)->exception);
8889  if (status == MagickFalse)
8890  XNoticeWidget(display,windows,"Unable to edit image comment",
8891  (char *) NULL);
8892  else
8893  {
8894  char
8895  *comment;
8896 
8897  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8898  if (comment != (char *) NULL)
8899  {
8900  (void) SetImageProperty(*image,"comment",comment);
8901  (*image)->taint=MagickTrue;
8902  }
8903  }
8904  (void) RelinquishUniqueFileResource(image_info->filename);
8905  XSetCursorState(display,windows,MagickFalse);
8906  break;
8907  }
8908  case LaunchCommand:
8909  {
8910  /*
8911  Launch program.
8912  */
8913  XSetCursorState(display,windows,MagickTrue);
8914  XCheckRefreshWindows(display,windows);
8915  (void) AcquireUniqueFilename(filename);
8916  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8917  filename);
8918  status=WriteImage(image_info,*image);
8919  if (status == MagickFalse)
8920  XNoticeWidget(display,windows,"Unable to launch image editor",
8921  (char *) NULL);
8922  else
8923  {
8924  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8925  CatchException(&(*image)->exception);
8926  XClientMessage(display,windows->image.id,windows->im_protocols,
8927  windows->im_next_image,CurrentTime);
8928  }
8929  (void) RelinquishUniqueFileResource(filename);
8930  XSetCursorState(display,windows,MagickFalse);
8931  break;
8932  }
8933  case RegionofInterestCommand:
8934  {
8935  /*
8936  Apply an image processing technique to a region of interest.
8937  */
8938  (void) XROIImage(display,resource_info,windows,image);
8939  break;
8940  }
8941  case InfoCommand:
8942  break;
8943  case ZoomCommand:
8944  {
8945  /*
8946  Zoom image.
8947  */
8948  if (windows->magnify.mapped != MagickFalse)
8949  (void) XRaiseWindow(display,windows->magnify.id);
8950  else
8951  {
8952  /*
8953  Make magnify image.
8954  */
8955  XSetCursorState(display,windows,MagickTrue);
8956  (void) XMapRaised(display,windows->magnify.id);
8957  XSetCursorState(display,windows,MagickFalse);
8958  }
8959  break;
8960  }
8961  case ShowPreviewCommand:
8962  {
8963  char
8964  **previews;
8965 
8966  Image
8967  *preview_image;
8968 
8969  static char
8970  preview_type[MaxTextExtent] = "Gamma";
8971 
8972  /*
8973  Select preview type from menu.
8974  */
8975  previews=GetCommandOptions(MagickPreviewOptions);
8976  if (previews == (char **) NULL)
8977  break;
8978  XListBrowserWidget(display,windows,&windows->widget,
8979  (const char **) previews,"Preview",
8980  "Select an enhancement, effect, or F/X:",preview_type);
8981  previews=DestroyStringList(previews);
8982  if (*preview_type == '\0')
8983  break;
8984  /*
8985  Show image preview.
8986  */
8987  XSetCursorState(display,windows,MagickTrue);
8988  XCheckRefreshWindows(display,windows);
8989  image_info->preview_type=(PreviewType)
8990  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
8991  image_info->group=(ssize_t) windows->image.id;
8992  (void) DeleteImageProperty(*image,"label");
8993  (void) SetImageProperty(*image,"label","Preview");
8994  (void) AcquireUniqueFilename(filename);
8995  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
8996  filename);
8997  status=WriteImage(image_info,*image);
8998  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8999  preview_image=ReadImage(image_info,&(*image)->exception);
9000  (void) RelinquishUniqueFileResource(filename);
9001  if (preview_image == (Image *) NULL)
9002  break;
9003  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9004  filename);
9005  status=WriteImage(image_info,preview_image);
9006  preview_image=DestroyImage(preview_image);
9007  if (status == MagickFalse)
9008  XNoticeWidget(display,windows,"Unable to show image preview",
9009  (*image)->filename);
9010  XDelay(display,1500);
9011  XSetCursorState(display,windows,MagickFalse);
9012  break;
9013  }
9014  case ShowHistogramCommand:
9015  {
9016  Image
9017  *histogram_image;
9018 
9019  /*
9020  Show image histogram.
9021  */
9022  XSetCursorState(display,windows,MagickTrue);
9023  XCheckRefreshWindows(display,windows);
9024  image_info->group=(ssize_t) windows->image.id;
9025  (void) DeleteImageProperty(*image,"label");
9026  (void) SetImageProperty(*image,"label","Histogram");
9027  (void) AcquireUniqueFilename(filename);
9028  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9029  filename);
9030  status=WriteImage(image_info,*image);
9031  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9032  histogram_image=ReadImage(image_info,&(*image)->exception);
9033  (void) RelinquishUniqueFileResource(filename);
9034  if (histogram_image == (Image *) NULL)
9035  break;
9036  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9037  "show:%s",filename);
9038  status=WriteImage(image_info,histogram_image);
9039  histogram_image=DestroyImage(histogram_image);
9040  if (status == MagickFalse)
9041  XNoticeWidget(display,windows,"Unable to show histogram",
9042  (*image)->filename);
9043  XDelay(display,1500);
9044  XSetCursorState(display,windows,MagickFalse);
9045  break;
9046  }
9047  case ShowMatteCommand:
9048  {
9049  Image
9050  *matte_image;
9051 
9052  if ((*image)->matte == MagickFalse)
9053  {
9054  XNoticeWidget(display,windows,
9055  "Image does not have any matte information",(*image)->filename);
9056  break;
9057  }
9058  /*
9059  Show image matte.
9060  */
9061  XSetCursorState(display,windows,MagickTrue);
9062  XCheckRefreshWindows(display,windows);
9063  image_info->group=(ssize_t) windows->image.id;
9064  (void) DeleteImageProperty(*image,"label");
9065  (void) SetImageProperty(*image,"label","Matte");
9066  (void) AcquireUniqueFilename(filename);
9067  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9068  filename);
9069  status=WriteImage(image_info,*image);
9070  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9071  matte_image=ReadImage(image_info,&(*image)->exception);
9072  (void) RelinquishUniqueFileResource(filename);
9073  if (matte_image == (Image *) NULL)
9074  break;
9075  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9076  filename);
9077  status=WriteImage(image_info,matte_image);
9078  matte_image=DestroyImage(matte_image);
9079  if (status == MagickFalse)
9080  XNoticeWidget(display,windows,"Unable to show matte",
9081  (*image)->filename);
9082  XDelay(display,1500);
9083  XSetCursorState(display,windows,MagickFalse);
9084  break;
9085  }
9086  case BackgroundCommand:
9087  {
9088  /*
9089  Background image.
9090  */
9091  status=XBackgroundImage(display,resource_info,windows,image);
9092  if (status == MagickFalse)
9093  break;
9094  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9095  if (nexus != (Image *) NULL)
9096  XClientMessage(display,windows->image.id,windows->im_protocols,
9097  windows->im_next_image,CurrentTime);
9098  break;
9099  }
9100  case SlideShowCommand:
9101  {
9102  static char
9103  delay[MaxTextExtent] = "5";
9104 
9105  /*
9106  Display next image after pausing.
9107  */
9108  (void) XDialogWidget(display,windows,"Slide Show",
9109  "Pause how many 1/100ths of a second between images:",delay);
9110  if (*delay == '\0')
9111  break;
9112  resource_info->delay=StringToUnsignedLong(delay);
9113  XClientMessage(display,windows->image.id,windows->im_protocols,
9114  windows->im_next_image,CurrentTime);
9115  break;
9116  }
9117  case PreferencesCommand:
9118  {
9119  /*
9120  Set user preferences.
9121  */
9122  status=XPreferencesWidget(display,resource_info,windows);
9123  if (status == MagickFalse)
9124  break;
9125  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9126  if (nexus != (Image *) NULL)
9127  XClientMessage(display,windows->image.id,windows->im_protocols,
9128  windows->im_next_image,CurrentTime);
9129  break;
9130  }
9131  case HelpCommand:
9132  {
9133  /*
9134  User requested help.
9135  */
9136  XTextViewHelp(display,resource_info,windows,MagickFalse,
9137  "Help Viewer - Display",DisplayHelp);
9138  break;
9139  }
9140  case BrowseDocumentationCommand:
9141  {
9142  Atom
9143  mozilla_atom;
9144 
9145  Window
9146  mozilla_window,
9147  root_window;
9148 
9149  /*
9150  Browse the ImageMagick documentation.
9151  */
9152  root_window=XRootWindow(display,XDefaultScreen(display));
9153  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9154  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9155  if (mozilla_window != (Window) NULL)
9156  {
9157  char
9158  command[MaxTextExtent];
9159 
9160  /*
9161  Display documentation using Netscape remote control.
9162  */
9163  (void) FormatLocaleString(command,MaxTextExtent,
9164  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9165  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9166  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9167  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9168  XSetCursorState(display,windows,MagickFalse);
9169  break;
9170  }
9171  XSetCursorState(display,windows,MagickTrue);
9172  XCheckRefreshWindows(display,windows);
9173  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9174  &(*image)->exception);
9175  if (status == MagickFalse)
9176  XNoticeWidget(display,windows,"Unable to browse documentation",
9177  (char *) NULL);
9178  XDelay(display,1500);
9179  XSetCursorState(display,windows,MagickFalse);
9180  break;
9181  }
9182  case VersionCommand:
9183  {
9184  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9185  GetMagickCopyright());
9186  break;
9187  }
9188  case SaveToUndoBufferCommand:
9189  break;
9190  default:
9191  {
9192  (void) XBell(display,0);
9193  break;
9194  }
9195  }
9196  image_info=DestroyImageInfo(image_info);
9197  return(nexus);
9198 }
9199 
9200 /*
9201 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9202 % %
9203 % %
9204 % %
9205 + X M a g n i f y I m a g e %
9206 % %
9207 % %
9208 % %
9209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9210 %
9211 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9212 % The magnified portion is displayed in a separate window.
9213 %
9214 % The format of the XMagnifyImage method is:
9215 %
9216 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9217 %
9218 % A description of each parameter follows:
9219 %
9220 % o display: Specifies a connection to an X server; returned from
9221 % XOpenDisplay.
9222 %
9223 % o windows: Specifies a pointer to a XWindows structure.
9224 %
9225 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9226 % the entire image is refreshed.
9227 %
9228 */
9229 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9230 {
9231  char
9232  text[MaxTextExtent];
9233 
9234  int
9235  x,
9236  y;
9237 
9238  size_t
9239  state;
9240 
9241  /*
9242  Update magnified image until the mouse button is released.
9243  */
9244  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9245  state=DefaultState;
9246  x=event->xbutton.x;
9247  y=event->xbutton.y;
9248  windows->magnify.x=(int) windows->image.x+x;
9249  windows->magnify.y=(int) windows->image.y+y;
9250  do
9251  {
9252  /*
9253  Map and unmap Info widget as text cursor crosses its boundaries.
9254  */
9255  if (windows->info.mapped != MagickFalse)
9256  {
9257  if ((x < (int) (windows->info.x+windows->info.width)) &&
9258  (y < (int) (windows->info.y+windows->info.height)))
9259  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9260  }
9261  else
9262  if ((x > (int) (windows->info.x+windows->info.width)) ||
9263  (y > (int) (windows->info.y+windows->info.height)))
9264  (void) XMapWindow(display,windows->info.id);
9265  if (windows->info.mapped != MagickFalse)
9266  {
9267  /*
9268  Display pointer position.
9269  */
9270  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9271  windows->magnify.x,windows->magnify.y);
9272  XInfoWidget(display,windows,text);
9273  }
9274  /*
9275  Wait for next event.
9276  */
9277  XScreenEvent(display,windows,event);
9278  switch (event->type)
9279  {
9280  case ButtonPress:
9281  break;
9282  case ButtonRelease:
9283  {
9284  /*
9285  User has finished magnifying image.
9286  */
9287  x=event->xbutton.x;
9288  y=event->xbutton.y;
9289  state|=ExitState;
9290  break;
9291  }
9292  case Expose:
9293  break;
9294  case MotionNotify:
9295  {
9296  x=event->xmotion.x;
9297  y=event->xmotion.y;
9298  break;
9299  }
9300  default:
9301  break;
9302  }
9303  /*
9304  Check boundary conditions.
9305  */
9306  if (x < 0)
9307  x=0;
9308  else
9309  if (x >= (int) windows->image.width)
9310  x=(int) windows->image.width-1;
9311  if (y < 0)
9312  y=0;
9313  else
9314  if (y >= (int) windows->image.height)
9315  y=(int) windows->image.height-1;
9316  } while ((state & ExitState) == 0);
9317  /*
9318  Display magnified image.
9319  */
9320  XSetCursorState(display,windows,MagickFalse);
9321 }
9322 
9323 /*
9324 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9325 % %
9326 % %
9327 % %
9328 + X M a g n i f y W i n d o w C o m m a n d %
9329 % %
9330 % %
9331 % %
9332 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9333 %
9334 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9335 % pixel as specified by the key symbol.
9336 %
9337 % The format of the XMagnifyWindowCommand method is:
9338 %
9339 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9340 % const MagickStatusType state,const KeySym key_symbol)
9341 %
9342 % A description of each parameter follows:
9343 %
9344 % o display: Specifies a connection to an X server; returned from
9345 % XOpenDisplay.
9346 %
9347 % o windows: Specifies a pointer to a XWindows structure.
9348 %
9349 % o state: key mask.
9350 %
9351 % o key_symbol: Specifies a KeySym which indicates which side of the image
9352 % to trim.
9353 %
9354 */
9355 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9356  const MagickStatusType state,const KeySym key_symbol)
9357 {
9358  unsigned int
9359  quantum;
9360 
9361  /*
9362  User specified a magnify factor or position.
9363  */
9364  quantum=1;
9365  if ((state & Mod1Mask) != 0)
9366  quantum=10;
9367  switch ((int) key_symbol)
9368  {
9369  case QuitCommand:
9370  {
9371  (void) XWithdrawWindow(display,windows->magnify.id,
9372  windows->magnify.screen);
9373  break;
9374  }
9375  case XK_Home:
9376  case XK_KP_Home:
9377  {
9378  windows->magnify.x=(int) windows->image.width/2;
9379  windows->magnify.y=(int) windows->image.height/2;
9380  break;
9381  }
9382  case XK_Left:
9383  case XK_KP_Left:
9384  {
9385  if (windows->magnify.x > 0)
9386  windows->magnify.x-=quantum;
9387  break;
9388  }
9389  case XK_Up:
9390  case XK_KP_Up:
9391  {
9392  if (windows->magnify.y > 0)
9393  windows->magnify.y-=quantum;
9394  break;
9395  }
9396  case XK_Right:
9397  case XK_KP_Right:
9398  {
9399  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9400  windows->magnify.x+=quantum;
9401  break;
9402  }
9403  case XK_Down:
9404  case XK_KP_Down:
9405  {
9406  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9407  windows->magnify.y+=quantum;
9408  break;
9409  }
9410  case XK_0:
9411  case XK_1:
9412  case XK_2:
9413  case XK_3:
9414  case XK_4:
9415  case XK_5:
9416  case XK_6:
9417  case XK_7:
9418  case XK_8:
9419  case XK_9:
9420  {
9421  windows->magnify.data=(key_symbol-XK_0);
9422  break;
9423  }
9424  case XK_KP_0:
9425  case XK_KP_1:
9426  case XK_KP_2:
9427  case XK_KP_3:
9428  case XK_KP_4:
9429  case XK_KP_5:
9430  case XK_KP_6:
9431  case XK_KP_7:
9432  case XK_KP_8:
9433  case XK_KP_9:
9434  {
9435  windows->magnify.data=(key_symbol-XK_KP_0);
9436  break;
9437  }
9438  default:
9439  break;
9440  }
9441  XMakeMagnifyImage(display,windows);
9442 }
9443 
9444 /*
9445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446 % %
9447 % %
9448 % %
9449 + X M a k e P a n I m a g e %
9450 % %
9451 % %
9452 % %
9453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454 %
9455 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9456 % icon window.
9457 %
9458 % The format of the XMakePanImage method is:
9459 %
9460 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9461 % XWindows *windows,Image *image)
9462 %
9463 % A description of each parameter follows:
9464 %
9465 % o display: Specifies a connection to an X server; returned from
9466 % XOpenDisplay.
9467 %
9468 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9469 %
9470 % o windows: Specifies a pointer to a XWindows structure.
9471 %
9472 % o image: the image.
9473 %
9474 */
9475 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9476  XWindows *windows,Image *image)
9477 {
9478  MagickStatusType
9479  status;
9480 
9481  /*
9482  Create and display image for panning icon.
9483  */
9484  XSetCursorState(display,windows,MagickTrue);
9485  XCheckRefreshWindows(display,windows);
9486  windows->pan.x=(int) windows->image.x;
9487  windows->pan.y=(int) windows->image.y;
9488  status=XMakeImage(display,resource_info,&windows->pan,image,
9489  windows->pan.width,windows->pan.height);
9490  if (status == MagickFalse)
9491  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9492  image->exception.description);
9493  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9494  windows->pan.pixmap);
9495  (void) XClearWindow(display,windows->pan.id);
9496  XDrawPanRectangle(display,windows);
9497  XSetCursorState(display,windows,MagickFalse);
9498 }
9499 
9500 /*
9501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9502 % %
9503 % %
9504 % %
9505 + X M a t t a E d i t I m a g e %
9506 % %
9507 % %
9508 % %
9509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9510 %
9511 % XMatteEditImage() allows the user to interactively change the Matte channel
9512 % of an image. If the image is PseudoClass it is promoted to DirectClass
9513 % before the matte information is stored.
9514 %
9515 % The format of the XMatteEditImage method is:
9516 %
9517 % MagickBooleanType XMatteEditImage(Display *display,
9518 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9519 %
9520 % A description of each parameter follows:
9521 %
9522 % o display: Specifies a connection to an X server; returned from
9523 % XOpenDisplay.
9524 %
9525 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9526 %
9527 % o windows: Specifies a pointer to a XWindows structure.
9528 %
9529 % o image: the image; returned from ReadImage.
9530 %
9531 */
9532 static MagickBooleanType XMatteEditImage(Display *display,
9533  XResourceInfo *resource_info,XWindows *windows,Image **image)
9534 {
9535  const char
9536  *const MatteEditMenu[] =
9537  {
9538  "Method",
9539  "Border Color",
9540  "Fuzz",
9541  "Matte Value",
9542  "Undo",
9543  "Help",
9544  "Dismiss",
9545  (char *) NULL
9546  };
9547 
9548  static char
9549  matte[MaxTextExtent] = "0";
9550 
9551  static const ModeType
9552  MatteEditCommands[] =
9553  {
9554  MatteEditMethod,
9555  MatteEditBorderCommand,
9556  MatteEditFuzzCommand,
9557  MatteEditValueCommand,
9558  MatteEditUndoCommand,
9559  MatteEditHelpCommand,
9560  MatteEditDismissCommand
9561  };
9562 
9563  static PaintMethod
9564  method = PointMethod;
9565 
9566  static XColor
9567  border_color = { 0, 0, 0, 0, 0, 0 };
9568 
9569  char
9570  command[MaxTextExtent],
9571  text[MaxTextExtent] = "";
9572 
9573  Cursor
9574  cursor;
9575 
9576  int
9577  entry,
9578  id,
9579  x,
9580  x_offset,
9581  y,
9582  y_offset;
9583 
9584  int
9585  i;
9586 
9587  PixelPacket
9588  *q;
9589 
9590  unsigned int
9591  height,
9592  width;
9593 
9594  size_t
9595  state;
9596 
9597  XEvent
9598  event;
9599 
9600  /*
9601  Map Command widget.
9602  */
9603  (void) CloneString(&windows->command.name,"Matte Edit");
9604  windows->command.data=4;
9605  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9606  (void) XMapRaised(display,windows->command.id);
9607  XClientMessage(display,windows->image.id,windows->im_protocols,
9608  windows->im_update_widget,CurrentTime);
9609  /*
9610  Make cursor.
9611  */
9612  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9613  resource_info->background_color,resource_info->foreground_color);
9614  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9615  /*
9616  Track pointer until button 1 is pressed.
9617  */
9618  XQueryPosition(display,windows->image.id,&x,&y);
9619  (void) XSelectInput(display,windows->image.id,
9620  windows->image.attributes.event_mask | PointerMotionMask);
9621  state=DefaultState;
9622  do
9623  {
9624  if (windows->info.mapped != MagickFalse)
9625  {
9626  /*
9627  Display pointer position.
9628  */
9629  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9630  x+windows->image.x,y+windows->image.y);
9631  XInfoWidget(display,windows,text);
9632  }
9633  /*
9634  Wait for next event.
9635  */
9636  XScreenEvent(display,windows,&event);
9637  if (event.xany.window == windows->command.id)
9638  {
9639  /*
9640  Select a command from the Command widget.
9641  */
9642  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9643  if (id < 0)
9644  {
9645  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9646  continue;
9647  }
9648  switch (MatteEditCommands[id])
9649  {
9650  case MatteEditMethod:
9651  {
9652  char
9653  **methods;
9654 
9655  /*
9656  Select a method from the pop-up menu.
9657  */
9658  methods=GetCommandOptions(MagickMethodOptions);
9659  if (methods == (char **) NULL)
9660  break;
9661  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9662  (const char **) methods,command);
9663  if (entry >= 0)
9664  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9665  MagickFalse,methods[entry]);
9666  methods=DestroyStringList(methods);
9667  break;
9668  }
9669  case MatteEditBorderCommand:
9670  {
9671  const char
9672  *ColorMenu[MaxNumberPens];
9673 
9674  int
9675  pen_number;
9676 
9677  /*
9678  Initialize menu selections.
9679  */
9680  for (i=0; i < (int) (MaxNumberPens-2); i++)
9681  ColorMenu[i]=resource_info->pen_colors[i];
9682  ColorMenu[MaxNumberPens-2]="Browser...";
9683  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9684  /*
9685  Select a pen color from the pop-up menu.
9686  */
9687  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9688  (const char **) ColorMenu,command);
9689  if (pen_number < 0)
9690  break;
9691  if (pen_number == (MaxNumberPens-2))
9692  {
9693  static char
9694  color_name[MaxTextExtent] = "gray";
9695 
9696  /*
9697  Select a pen color from a dialog.
9698  */
9699  resource_info->pen_colors[pen_number]=color_name;
9700  XColorBrowserWidget(display,windows,"Select",color_name);
9701  if (*color_name == '\0')
9702  break;
9703  }
9704  /*
9705  Set border color.
9706  */
9707  (void) XParseColor(display,windows->map_info->colormap,
9708  resource_info->pen_colors[pen_number],&border_color);
9709  break;
9710  }
9711  case MatteEditFuzzCommand:
9712  {
9713  const char
9714  *const FuzzMenu[] =
9715  {
9716  "0%",
9717  "2%",
9718  "5%",
9719  "10%",
9720  "15%",
9721  "Dialog...",
9722  (char *) NULL,
9723  };
9724 
9725  static char
9726  fuzz[MaxTextExtent];
9727 
9728  /*
9729  Select a command from the pop-up menu.
9730  */
9731  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9732  command);
9733  if (entry < 0)
9734  break;
9735  if (entry != 5)
9736  {
9737  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9738  QuantumRange+1.0);
9739  break;
9740  }
9741  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9742  (void) XDialogWidget(display,windows,"Ok",
9743  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9744  if (*fuzz == '\0')
9745  break;
9746  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9747  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9748  1.0);
9749  break;
9750  }
9751  case MatteEditValueCommand:
9752  {
9753  const char
9754  *const MatteMenu[] =
9755  {
9756  "Opaque",
9757  "Transparent",
9758  "Dialog...",
9759  (char *) NULL,
9760  };
9761 
9762  static char
9763  message[MaxTextExtent];
9764 
9765  /*
9766  Select a command from the pop-up menu.
9767  */
9768  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9769  command);
9770  if (entry < 0)
9771  break;
9772  if (entry != 2)
9773  {
9774  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9775  OpaqueOpacity);
9776  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9777  (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9778  (Quantum) TransparentOpacity);
9779  break;
9780  }
9781  (void) FormatLocaleString(message,MaxTextExtent,
9782  "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9783  QuantumRange);
9784  (void) XDialogWidget(display,windows,"Matte",message,matte);
9785  if (*matte == '\0')
9786  break;
9787  break;
9788  }
9789  case MatteEditUndoCommand:
9790  {
9791  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9792  image);
9793  break;
9794  }
9795  case MatteEditHelpCommand:
9796  {
9797  XTextViewHelp(display,resource_info,windows,MagickFalse,
9798  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9799  break;
9800  }
9801  case MatteEditDismissCommand:
9802  {
9803  /*
9804  Prematurely exit.
9805  */
9806  state|=EscapeState;
9807  state|=ExitState;
9808  break;
9809  }
9810  default:
9811  break;
9812  }
9813  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9814  continue;
9815  }
9816  switch (event.type)
9817  {
9818  case ButtonPress:
9819  {
9820  if (event.xbutton.button != Button1)
9821  break;
9822  if ((event.xbutton.window != windows->image.id) &&
9823  (event.xbutton.window != windows->magnify.id))
9824  break;
9825  /*
9826  Update matte data.
9827  */
9828  x=event.xbutton.x;
9829  y=event.xbutton.y;
9830  (void) XMagickCommand(display,resource_info,windows,
9831  SaveToUndoBufferCommand,image);
9832  state|=UpdateConfigurationState;
9833  break;
9834  }
9835  case ButtonRelease:
9836  {
9837  if (event.xbutton.button != Button1)
9838  break;
9839  if ((event.xbutton.window != windows->image.id) &&
9840  (event.xbutton.window != windows->magnify.id))
9841  break;
9842  /*
9843  Update colormap information.
9844  */
9845  x=event.xbutton.x;
9846  y=event.xbutton.y;
9847  XConfigureImageColormap(display,resource_info,windows,*image);
9848  (void) XConfigureImage(display,resource_info,windows,*image);
9849  XInfoWidget(display,windows,text);
9850  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9851  state&=(~UpdateConfigurationState);
9852  break;
9853  }
9854  case Expose:
9855  break;
9856  case KeyPress:
9857  {
9858  char
9859  command[MaxTextExtent];
9860 
9861  KeySym
9862  key_symbol;
9863 
9864  if (event.xkey.window == windows->magnify.id)
9865  {
9866  Window
9867  window;
9868 
9869  window=windows->magnify.id;
9870  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9871  }
9872  if (event.xkey.window != windows->image.id)
9873  break;
9874  /*
9875  Respond to a user key press.
9876  */
9877  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9878  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9879  switch ((int) key_symbol)
9880  {
9881  case XK_Escape:
9882  case XK_F20:
9883  {
9884  /*
9885  Prematurely exit.
9886  */
9887  state|=ExitState;
9888  break;
9889  }
9890  case XK_F1:
9891  case XK_Help:
9892  {
9893  XTextViewHelp(display,resource_info,windows,MagickFalse,
9894  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9895  break;
9896  }
9897  default:
9898  {
9899  (void) XBell(display,0);
9900  break;
9901  }
9902  }
9903  break;
9904  }
9905  case MotionNotify:
9906  {
9907  /*
9908  Map and unmap Info widget as cursor crosses its boundaries.
9909  */
9910  x=event.xmotion.x;
9911  y=event.xmotion.y;
9912  if (windows->info.mapped != MagickFalse)
9913  {
9914  if ((x < (int) (windows->info.x+windows->info.width)) &&
9915  (y < (int) (windows->info.y+windows->info.height)))
9916  (void) XWithdrawWindow(display,windows->info.id,
9917  windows->info.screen);
9918  }
9919  else
9920  if ((x > (int) (windows->info.x+windows->info.width)) ||
9921  (y > (int) (windows->info.y+windows->info.height)))
9922  (void) XMapWindow(display,windows->info.id);
9923  break;
9924  }
9925  default:
9926  break;
9927  }
9928  if (event.xany.window == windows->magnify.id)
9929  {
9930  x=windows->magnify.x-windows->image.x;
9931  y=windows->magnify.y-windows->image.y;
9932  }
9933  x_offset=x;
9934  y_offset=y;
9935  if ((state & UpdateConfigurationState) != 0)
9936  {
9937  CacheView
9938  *image_view;
9939 
9941  *exception;
9942 
9943  int
9944  x,
9945  y;
9946 
9947  /*
9948  Matte edit is relative to image configuration.
9949  */
9950  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9951  MagickTrue);
9952  XPutPixel(windows->image.ximage,x_offset,y_offset,
9953  windows->pixel_info->background_color.pixel);
9954  width=(unsigned int) (*image)->columns;
9955  height=(unsigned int) (*image)->rows;
9956  x=0;
9957  y=0;
9958  if (windows->image.crop_geometry != (char *) NULL)
9959  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9960  &width,&height);
9961  x_offset=(int)
9962  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9963  y_offset=(int)
9964  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9965  if ((x_offset < 0) || (y_offset < 0))
9966  continue;
9967  if ((x_offset >= (int) (*image)->columns) ||
9968  (y_offset >= (int) (*image)->rows))
9969  continue;
9970  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9971  return(MagickFalse);
9972  (*image)->matte=MagickTrue;
9973  exception=(&(*image)->exception);
9974  image_view=AcquireAuthenticCacheView(*image,exception);
9975  switch (method)
9976  {
9977  case PointMethod:
9978  default:
9979  {
9980  /*
9981  Update matte information using point algorithm.
9982  */
9983  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9984  (ssize_t) y_offset,1,1,exception);
9985  if (q == (PixelPacket *) NULL)
9986  break;
9987  q->opacity=(Quantum) StringToLong(matte);
9988  (void) SyncCacheViewAuthenticPixels(image_view,exception);
9989  break;
9990  }
9991  case ReplaceMethod:
9992  {
9993  PixelPacket
9994  target;
9995 
9996  /*
9997  Update matte information using replace algorithm.
9998  */
9999  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10000  (ssize_t) y_offset,&target,exception);
10001  for (y=0; y < (int) (*image)->rows; y++)
10002  {
10003  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10004  (*image)->columns,1,&(*image)->exception);
10005  if (q == (PixelPacket *) NULL)
10006  break;
10007  for (x=0; x < (int) (*image)->columns; x++)
10008  {
10009  if (IsColorSimilar(*image,q,&target))
10010  q->opacity=(Quantum) StringToLong(matte);
10011  q++;
10012  }
10013  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10014  break;
10015  }
10016  break;
10017  }
10018  case FloodfillMethod:
10019  case FillToBorderMethod:
10020  {
10021  DrawInfo
10022  *draw_info;
10023 
10025  target;
10026 
10027  /*
10028  Update matte information using floodfill algorithm.
10029  */
10030  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10031  (ssize_t) y_offset,&target,exception);
10032  if (method == FillToBorderMethod)
10033  {
10034  target.red=(MagickRealType)
10035  ScaleShortToQuantum(border_color.red);
10036  target.green=(MagickRealType)
10037  ScaleShortToQuantum(border_color.green);
10038  target.blue=(MagickRealType)
10039  ScaleShortToQuantum(border_color.blue);
10040  }
10041  draw_info=CloneDrawInfo(resource_info->image_info,
10042  (DrawInfo *) NULL);
10043  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10044  (char **) NULL));
10045  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10046  (ssize_t) x_offset,(ssize_t) y_offset,
10047  method == FloodfillMethod ? MagickFalse : MagickTrue);
10048  draw_info=DestroyDrawInfo(draw_info);
10049  break;
10050  }
10051  case ResetMethod:
10052  {
10053  /*
10054  Update matte information using reset algorithm.
10055  */
10056  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10057  return(MagickFalse);
10058  for (y=0; y < (int) (*image)->rows; y++)
10059  {
10060  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10061  (*image)->columns,1,exception);
10062  if (q == (PixelPacket *) NULL)
10063  break;
10064  for (x=0; x < (int) (*image)->columns; x++)
10065  {
10066  q->opacity=(Quantum) StringToLong(matte);
10067  q++;
10068  }
10069  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10070  break;
10071  }
10072  if (StringToLong(matte) == OpaqueOpacity)
10073  (*image)->matte=MagickFalse;
10074  break;
10075  }
10076  }
10077  image_view=DestroyCacheView(image_view);
10078  state&=(~UpdateConfigurationState);
10079  }
10080  } while ((state & ExitState) == 0);
10081  (void) XSelectInput(display,windows->image.id,
10082  windows->image.attributes.event_mask);
10083  XSetCursorState(display,windows,MagickFalse);
10084  (void) XFreeCursor(display,cursor);
10085  return(MagickTrue);
10086 }
10087 
10088 /*
10089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10090 % %
10091 % %
10092 % %
10093 + X O p e n I m a g e %
10094 % %
10095 % %
10096 % %
10097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10098 %
10099 % XOpenImage() loads an image from a file.
10100 %
10101 % The format of the XOpenImage method is:
10102 %
10103 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10104 % XWindows *windows,const unsigned int command)
10105 %
10106 % A description of each parameter follows:
10107 %
10108 % o display: Specifies a connection to an X server; returned from
10109 % XOpenDisplay.
10110 %
10111 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10112 %
10113 % o windows: Specifies a pointer to a XWindows structure.
10114 %
10115 % o command: A value other than zero indicates that the file is selected
10116 % from the command line argument list.
10117 %
10118 */
10119 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10120  XWindows *windows,const MagickBooleanType command)
10121 {
10122  const MagickInfo
10123  *magick_info;
10124 
10126  *exception;
10127 
10128  Image
10129  *nexus;
10130 
10131  ImageInfo
10132  *image_info;
10133 
10134  static char
10135  filename[MaxTextExtent] = "\0";
10136 
10137  /*
10138  Request file name from user.
10139  */
10140  if (command == MagickFalse)
10141  XFileBrowserWidget(display,windows,"Open",filename);
10142  else
10143  {
10144  char
10145  **filelist,
10146  **files;
10147 
10148  int
10149  count,
10150  status;
10151 
10152  int
10153  i,
10154  j;
10155 
10156  /*
10157  Select next image from the command line.
10158  */
10159  status=XGetCommand(display,windows->image.id,&files,&count);
10160  if (status == 0)
10161  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10162  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10163  if (filelist == (char **) NULL)
10164  {
10165  (void) XFreeStringList(files);
10166  ThrowXWindowException(ResourceLimitError,
10167  "MemoryAllocationFailed","...");
10168  return((Image *) NULL);
10169  }
10170  j=0;
10171  for (i=1; i < count; i++)
10172  if (*files[i] != '-')
10173  filelist[j++]=files[i];
10174  filelist[j]=(char *) NULL;
10175  XListBrowserWidget(display,windows,&windows->widget,
10176  (const char **) filelist,"Load","Select Image to Load:",filename);
10177  filelist=(char **) RelinquishMagickMemory(filelist);
10178  (void) XFreeStringList(files);
10179  }
10180  if (*filename == '\0')
10181  return((Image *) NULL);
10182  image_info=CloneImageInfo(resource_info->image_info);
10183  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10184  (void *) NULL);
10185  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10186  exception=AcquireExceptionInfo();
10187  (void) SetImageInfo(image_info,0,exception);
10188  if (LocaleCompare(image_info->magick,"X") == 0)
10189  {
10190  char
10191  seconds[MaxTextExtent];
10192 
10193  /*
10194  User may want to delay the X server screen grab.
10195  */
10196  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10197  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10198  seconds);
10199  if (*seconds == '\0')
10200  return((Image *) NULL);
10201  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10202  }
10203  magick_info=GetMagickInfo(image_info->magick,exception);
10204  if ((magick_info != (const MagickInfo *) NULL) &&
10205  (magick_info->raw != MagickFalse))
10206  {
10207  char
10208  geometry[MaxTextExtent];
10209 
10210  /*
10211  Request image size from the user.
10212  */
10213  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10214  if (image_info->size != (char *) NULL)
10215  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10216  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10217  geometry);
10218  (void) CloneString(&image_info->size,geometry);
10219  }
10220  /*
10221  Load the image.
10222  */
10223  XSetCursorState(display,windows,MagickTrue);
10224  XCheckRefreshWindows(display,windows);
10225  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10226  nexus=ReadImage(image_info,exception);
10227  CatchException(exception);
10228  XSetCursorState(display,windows,MagickFalse);
10229  if (nexus != (Image *) NULL)
10230  XClientMessage(display,windows->image.id,windows->im_protocols,
10231  windows->im_next_image,CurrentTime);
10232  else
10233  {
10234  char
10235  *text,
10236  **textlist;
10237 
10238  /*
10239  Unknown image format.
10240  */
10241  text=FileToString(filename,~0UL,exception);
10242  if (text == (char *) NULL)
10243  return((Image *) NULL);
10244  textlist=StringToList(text);
10245  if (textlist != (char **) NULL)
10246  {
10247  char
10248  title[MaxTextExtent];
10249 
10250  int
10251  i;
10252 
10253  (void) FormatLocaleString(title,MaxTextExtent,
10254  "Unknown format: %s",filename);
10255  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10256  (const char **) textlist);
10257  for (i=0; textlist[i] != (char *) NULL; i++)
10258  textlist[i]=DestroyString(textlist[i]);
10259  textlist=(char **) RelinquishMagickMemory(textlist);
10260  }
10261  text=DestroyString(text);
10262  }
10263  exception=DestroyExceptionInfo(exception);
10264  image_info=DestroyImageInfo(image_info);
10265  return(nexus);
10266 }
10267 
10268 /*
10269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10270 % %
10271 % %
10272 % %
10273 + X P a n I m a g e %
10274 % %
10275 % %
10276 % %
10277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10278 %
10279 % XPanImage() pans the image until the mouse button is released.
10280 %
10281 % The format of the XPanImage method is:
10282 %
10283 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10284 %
10285 % A description of each parameter follows:
10286 %
10287 % o display: Specifies a connection to an X server; returned from
10288 % XOpenDisplay.
10289 %
10290 % o windows: Specifies a pointer to a XWindows structure.
10291 %
10292 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10293 % the entire image is refreshed.
10294 %
10295 */
10296 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10297 {
10298  char
10299  text[MaxTextExtent];
10300 
10301  Cursor
10302  cursor;
10303 
10304  MagickRealType
10305  x_factor,
10306  y_factor;
10307 
10309  pan_info;
10310 
10311  size_t
10312  state;
10313 
10314  /*
10315  Define cursor.
10316  */
10317  if ((windows->image.ximage->width > (int) windows->image.width) &&
10318  (windows->image.ximage->height > (int) windows->image.height))
10319  cursor=XCreateFontCursor(display,XC_fleur);
10320  else
10321  if (windows->image.ximage->width > (int) windows->image.width)
10322  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10323  else
10324  if (windows->image.ximage->height > (int) windows->image.height)
10325  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10326  else
10327  cursor=XCreateFontCursor(display,XC_arrow);
10328  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10329  /*
10330  Pan image as pointer moves until the mouse button is released.
10331  */
10332  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10333  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10334  pan_info.width=windows->pan.width*windows->image.width/
10335  windows->image.ximage->width;
10336  pan_info.height=windows->pan.height*windows->image.height/
10337  windows->image.ximage->height;
10338  pan_info.x=0;
10339  pan_info.y=0;
10340  state=UpdateConfigurationState;
10341  do
10342  {
10343  switch (event->type)
10344  {
10345  case ButtonPress:
10346  {
10347  /*
10348  User choose an initial pan location.
10349  */
10350  pan_info.x=(ssize_t) event->xbutton.x;
10351  pan_info.y=(ssize_t) event->xbutton.y;
10352  state|=UpdateConfigurationState;
10353  break;
10354  }
10355  case ButtonRelease:
10356  {
10357  /*
10358  User has finished panning the image.
10359  */
10360  pan_info.x=(ssize_t) event->xbutton.x;
10361  pan_info.y=(ssize_t) event->xbutton.y;
10362  state|=UpdateConfigurationState | ExitState;
10363  break;
10364  }
10365  case MotionNotify:
10366  {
10367  pan_info.x=(ssize_t) event->xmotion.x;
10368  pan_info.y=(ssize_t) event->xmotion.y;
10369  state|=UpdateConfigurationState;
10370  }
10371  default:
10372  break;
10373  }
10374  if ((state & UpdateConfigurationState) != 0)
10375  {
10376  /*
10377  Check boundary conditions.
10378  */
10379  if (pan_info.x < (ssize_t) (pan_info.width/2))
10380  pan_info.x=0;
10381  else
10382  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10383  if (pan_info.x < 0)
10384  pan_info.x=0;
10385  else
10386  if ((int) (pan_info.x+windows->image.width) >
10387  windows->image.ximage->width)
10388  pan_info.x=(ssize_t)
10389  (windows->image.ximage->width-windows->image.width);
10390  if (pan_info.y < (ssize_t) (pan_info.height/2))
10391  pan_info.y=0;
10392  else
10393  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10394  if (pan_info.y < 0)
10395  pan_info.y=0;
10396  else
10397  if ((int) (pan_info.y+windows->image.height) >
10398  windows->image.ximage->height)
10399  pan_info.y=(ssize_t)
10400  (windows->image.ximage->height-windows->image.height);
10401  if ((windows->image.x != (int) pan_info.x) ||
10402  (windows->image.y != (int) pan_info.y))
10403  {
10404  /*
10405  Display image pan offset.
10406  */
10407  windows->image.x=(int) pan_info.x;
10408  windows->image.y=(int) pan_info.y;
10409  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10410  windows->image.width,windows->image.height,windows->image.x,
10411  windows->image.y);
10412  XInfoWidget(display,windows,text);
10413  /*
10414  Refresh Image window.
10415  */
10416  XDrawPanRectangle(display,windows);
10417  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10418  }
10419  state&=(~UpdateConfigurationState);
10420  }
10421  /*
10422  Wait for next event.
10423  */
10424  if ((state & ExitState) == 0)
10425  XScreenEvent(display,windows,event);
10426  } while ((state & ExitState) == 0);
10427  /*
10428  Restore cursor.
10429  */
10430  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10431  (void) XFreeCursor(display,cursor);
10432  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10433 }
10434 
10435 /*
10436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10437 % %
10438 % %
10439 % %
10440 + X P a s t e I m a g e %
10441 % %
10442 % %
10443 % %
10444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10445 %
10446 % XPasteImage() pastes an image previously saved with XCropImage in the X
10447 % window image at a location the user chooses with the pointer.
10448 %
10449 % The format of the XPasteImage method is:
10450 %
10451 % MagickBooleanType XPasteImage(Display *display,
10452 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10453 %
10454 % A description of each parameter follows:
10455 %
10456 % o display: Specifies a connection to an X server; returned from
10457 % XOpenDisplay.
10458 %
10459 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10460 %
10461 % o windows: Specifies a pointer to a XWindows structure.
10462 %
10463 % o image: the image; returned from ReadImage.
10464 %
10465 */
10466 static MagickBooleanType XPasteImage(Display *display,
10467  XResourceInfo *resource_info,XWindows *windows,Image *image)
10468 {
10469  const char
10470  *const PasteMenu[] =
10471  {
10472  "Operator",
10473  "Help",
10474  "Dismiss",
10475  (char *) NULL
10476  };
10477 
10478  static const ModeType
10479  PasteCommands[] =
10480  {
10481  PasteOperatorsCommand,
10482  PasteHelpCommand,
10483  PasteDismissCommand
10484  };
10485 
10486  static CompositeOperator
10487  compose = CopyCompositeOp;
10488 
10489  char
10490  text[MaxTextExtent];
10491 
10492  Cursor
10493  cursor;
10494 
10495  Image
10496  *paste_image;
10497 
10498  int
10499  entry,
10500  id,
10501  x,
10502  y;
10503 
10504  MagickRealType
10505  scale_factor;
10506 
10508  highlight_info,
10509  paste_info;
10510 
10511  unsigned int
10512  height,
10513  width;
10514 
10515  size_t
10516  state;
10517 
10518  XEvent
10519  event;
10520 
10521  /*
10522  Copy image.
10523  */
10524  if (resource_info->copy_image == (Image *) NULL)
10525  return(MagickFalse);
10526  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10527  &image->exception);
10528  if (paste_image == (Image *) NULL)
10529  return(MagickFalse);
10530  /*
10531  Map Command widget.
10532  */
10533  (void) CloneString(&windows->command.name,"Paste");
10534  windows->command.data=1;
10535  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10536  (void) XMapRaised(display,windows->command.id);
10537  XClientMessage(display,windows->image.id,windows->im_protocols,
10538  windows->im_update_widget,CurrentTime);
10539  /*
10540  Track pointer until button 1 is pressed.
10541  */
10542  XSetCursorState(display,windows,MagickFalse);
10543  XQueryPosition(display,windows->image.id,&x,&y);
10544  (void) XSelectInput(display,windows->image.id,
10545  windows->image.attributes.event_mask | PointerMotionMask);
10546  paste_info.x=(ssize_t) windows->image.x+x;
10547  paste_info.y=(ssize_t) windows->image.y+y;
10548  paste_info.width=0;
10549  paste_info.height=0;
10550  cursor=XCreateFontCursor(display,XC_ul_angle);
10551  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10552  state=DefaultState;
10553  do
10554  {
10555  if (windows->info.mapped != MagickFalse)
10556  {
10557  /*
10558  Display pointer position.
10559  */
10560  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10561  (long) paste_info.x,(long) paste_info.y);
10562  XInfoWidget(display,windows,text);
10563  }
10564  highlight_info=paste_info;
10565  highlight_info.x=paste_info.x-windows->image.x;
10566  highlight_info.y=paste_info.y-windows->image.y;
10567  XHighlightRectangle(display,windows->image.id,
10568  windows->image.highlight_context,&highlight_info);
10569  /*
10570  Wait for next event.
10571  */
10572  XScreenEvent(display,windows,&event);
10573  XHighlightRectangle(display,windows->image.id,
10574  windows->image.highlight_context,&highlight_info);
10575  if (event.xany.window == windows->command.id)
10576  {
10577  /*
10578  Select a command from the Command widget.
10579  */
10580  id=XCommandWidget(display,windows,PasteMenu,&event);
10581  if (id < 0)
10582  continue;
10583  switch (PasteCommands[id])
10584  {
10585  case PasteOperatorsCommand:
10586  {
10587  char
10588  command[MaxTextExtent],
10589  **operators;
10590 
10591  /*
10592  Select a command from the pop-up menu.
10593  */
10594  operators=GetCommandOptions(MagickComposeOptions);
10595  if (operators == (char **) NULL)
10596  break;
10597  entry=XMenuWidget(display,windows,PasteMenu[id],
10598  (const char **) operators,command);
10599  if (entry >= 0)
10600  compose=(CompositeOperator) ParseCommandOption(
10601  MagickComposeOptions,MagickFalse,operators[entry]);
10602  operators=DestroyStringList(operators);
10603  break;
10604  }
10605  case PasteHelpCommand:
10606  {
10607  XTextViewHelp(display,resource_info,windows,MagickFalse,
10608  "Help Viewer - Image Composite",ImagePasteHelp);
10609  break;
10610  }
10611  case PasteDismissCommand:
10612  {
10613  /*
10614  Prematurely exit.
10615  */
10616  state|=EscapeState;
10617  state|=ExitState;
10618  break;
10619  }
10620  default:
10621  break;
10622  }
10623  continue;
10624  }
10625  switch (event.type)
10626  {
10627  case ButtonPress:
10628  {
10629  if (resource_info->debug != MagickFalse)
10630  (void) LogMagickEvent(X11Event,GetMagickModule(),
10631  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10632  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10633  if (event.xbutton.button != Button1)
10634  break;
10635  if (event.xbutton.window != windows->image.id)
10636  break;
10637  /*
10638  Paste rectangle is relative to image configuration.
10639  */
10640  width=(unsigned int) image->columns;
10641  height=(unsigned int) image->rows;
10642  x=0;
10643  y=0;
10644  if (windows->image.crop_geometry != (char *) NULL)
10645  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10646  &width,&height);
10647  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10648  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10649  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10650  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10651  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10652  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10653  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10654  break;
10655  }
10656  case ButtonRelease:
10657  {
10658  if (resource_info->debug != MagickFalse)
10659  (void) LogMagickEvent(X11Event,GetMagickModule(),
10660  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10661  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10662  if (event.xbutton.button != Button1)
10663  break;
10664  if (event.xbutton.window != windows->image.id)
10665  break;
10666  if ((paste_info.width != 0) && (paste_info.height != 0))
10667  {
10668  /*
10669  User has selected the location of the paste image.
10670  */
10671  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10672  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10673  state|=ExitState;
10674  }
10675  break;
10676  }
10677  case Expose:
10678  break;
10679  case KeyPress:
10680  {
10681  char
10682  command[MaxTextExtent];
10683 
10684  KeySym
10685  key_symbol;
10686 
10687  int
10688  length;
10689 
10690  if (event.xkey.window != windows->image.id)
10691  break;
10692  /*
10693  Respond to a user key press.
10694  */
10695  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10696  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10697  *(command+length)='\0';
10698  if (resource_info->debug != MagickFalse)
10699  (void) LogMagickEvent(X11Event,GetMagickModule(),
10700  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10701  switch ((int) key_symbol)
10702  {
10703  case XK_Escape:
10704  case XK_F20:
10705  {
10706  /*
10707  Prematurely exit.
10708  */
10709  paste_image=DestroyImage(paste_image);
10710  state|=EscapeState;
10711  state|=ExitState;
10712  break;
10713  }
10714  case XK_F1:
10715  case XK_Help:
10716  {
10717  (void) XSetFunction(display,windows->image.highlight_context,
10718  GXcopy);
10719  XTextViewHelp(display,resource_info,windows,MagickFalse,
10720  "Help Viewer - Image Composite",ImagePasteHelp);
10721  (void) XSetFunction(display,windows->image.highlight_context,
10722  GXinvert);
10723  break;
10724  }
10725  default:
10726  {
10727  (void) XBell(display,0);
10728  break;
10729  }
10730  }
10731  break;
10732  }
10733  case MotionNotify:
10734  {
10735  /*
10736  Map and unmap Info widget as text cursor crosses its boundaries.
10737  */
10738  x=event.xmotion.x;
10739  y=event.xmotion.y;
10740  if (windows->info.mapped != MagickFalse)
10741  {
10742  if ((x < (int) (windows->info.x+windows->info.width)) &&
10743  (y < (int) (windows->info.y+windows->info.height)))
10744  (void) XWithdrawWindow(display,windows->info.id,
10745  windows->info.screen);
10746  }
10747  else
10748  if ((x > (int) (windows->info.x+windows->info.width)) ||
10749  (y > (int) (windows->info.y+windows->info.height)))
10750  (void) XMapWindow(display,windows->info.id);
10751  paste_info.x=(ssize_t) windows->image.x+x;
10752  paste_info.y=(ssize_t) windows->image.y+y;
10753  break;
10754  }
10755  default:
10756  {
10757  if (resource_info->debug != MagickFalse)
10758  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10759  event.type);
10760  break;
10761  }
10762  }
10763  } while ((state & ExitState) == 0);
10764  (void) XSelectInput(display,windows->image.id,
10765  windows->image.attributes.event_mask);
10766  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10767  XSetCursorState(display,windows,MagickFalse);
10768  (void) XFreeCursor(display,cursor);
10769  if ((state & EscapeState) != 0)
10770  return(MagickTrue);
10771  /*
10772  Image pasting is relative to image configuration.
10773  */
10774  XSetCursorState(display,windows,MagickTrue);
10775  XCheckRefreshWindows(display,windows);
10776  width=(unsigned int) image->columns;
10777  height=(unsigned int) image->rows;
10778  x=0;
10779  y=0;
10780  if (windows->image.crop_geometry != (char *) NULL)
10781  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10782  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10783  paste_info.x+=x;
10784  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10785  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10786  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10787  paste_info.y+=y;
10788  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10789  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10790  /*
10791  Paste image with X Image window.
10792  */
10793  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10794  paste_image=DestroyImage(paste_image);
10795  XSetCursorState(display,windows,MagickFalse);
10796  /*
10797  Update image colormap.
10798  */
10799  XConfigureImageColormap(display,resource_info,windows,image);
10800  (void) XConfigureImage(display,resource_info,windows,image);
10801  return(MagickTrue);
10802 }
10803 
10804 /*
10805 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10806 % %
10807 % %
10808 % %
10809 + X P r i n t I m a g e %
10810 % %
10811 % %
10812 % %
10813 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10814 %
10815 % XPrintImage() prints an image to a Postscript printer.
10816 %
10817 % The format of the XPrintImage method is:
10818 %
10819 % MagickBooleanType XPrintImage(Display *display,
10820 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10821 %
10822 % A description of each parameter follows:
10823 %
10824 % o display: Specifies a connection to an X server; returned from
10825 % XOpenDisplay.
10826 %
10827 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10828 %
10829 % o windows: Specifies a pointer to a XWindows structure.
10830 %
10831 % o image: the image.
10832 %
10833 */
10834 static MagickBooleanType XPrintImage(Display *display,
10835  XResourceInfo *resource_info,XWindows *windows,Image *image)
10836 {
10837  char
10838  filename[MaxTextExtent],
10839  geometry[MaxTextExtent];
10840 
10841  const char
10842  *const PageSizes[] =
10843  {
10844  "Letter",
10845  "Tabloid",
10846  "Ledger",
10847  "Legal",
10848  "Statement",
10849  "Executive",
10850  "A3",
10851  "A4",
10852  "A5",
10853  "B4",
10854  "B5",
10855  "Folio",
10856  "Quarto",
10857  "10x14",
10858  (char *) NULL
10859  };
10860 
10861  Image
10862  *print_image;
10863 
10864  ImageInfo
10865  *image_info;
10866 
10867  MagickStatusType
10868  status;
10869 
10870  /*
10871  Request Postscript page geometry from user.
10872  */
10873  image_info=CloneImageInfo(resource_info->image_info);
10874  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10875  if (image_info->page != (char *) NULL)
10876  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10877  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10878  "Select Postscript Page Geometry:",geometry);
10879  if (*geometry == '\0')
10880  return(MagickTrue);
10881  image_info->page=GetPageGeometry(geometry);
10882  /*
10883  Apply image transforms.
10884  */
10885  XSetCursorState(display,windows,MagickTrue);
10886  XCheckRefreshWindows(display,windows);
10887  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10888  if (print_image == (Image *) NULL)
10889  return(MagickFalse);
10890  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10891  windows->image.ximage->width,windows->image.ximage->height);
10892  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10893  /*
10894  Print image.
10895  */
10896  (void) AcquireUniqueFilename(filename);
10897  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10898  filename);
10899  status=WriteImage(image_info,print_image);
10900  (void) RelinquishUniqueFileResource(filename);
10901  print_image=DestroyImage(print_image);
10902  image_info=DestroyImageInfo(image_info);
10903  XSetCursorState(display,windows,MagickFalse);
10904  return(status != 0 ? MagickTrue : MagickFalse);
10905 }
10906 
10907 /*
10908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10909 % %
10910 % %
10911 % %
10912 + X R O I I m a g e %
10913 % %
10914 % %
10915 % %
10916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10917 %
10918 % XROIImage() applies an image processing technique to a region of interest.
10919 %
10920 % The format of the XROIImage method is:
10921 %
10922 % MagickBooleanType XROIImage(Display *display,
10923 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10924 %
10925 % A description of each parameter follows:
10926 %
10927 % o display: Specifies a connection to an X server; returned from
10928 % XOpenDisplay.
10929 %
10930 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10931 %
10932 % o windows: Specifies a pointer to a XWindows structure.
10933 %
10934 % o image: the image; returned from ReadImage.
10935 %
10936 */
10937 static MagickBooleanType XROIImage(Display *display,
10938  XResourceInfo *resource_info,XWindows *windows,Image **image)
10939 {
10940 #define ApplyMenus 7
10941 
10942  const char
10943  *const ROIMenu[] =
10944  {
10945  "Help",
10946  "Dismiss",
10947  (char *) NULL
10948  },
10949  *const ApplyMenu[] =
10950  {
10951  "File",
10952  "Edit",
10953  "Transform",
10954  "Enhance",
10955  "Effects",
10956  "F/X",
10957  "Miscellany",
10958  "Help",
10959  "Dismiss",
10960  (char *) NULL
10961  },
10962  *const FileMenu[] =
10963  {
10964  "Save...",
10965  "Print...",
10966  (char *) NULL
10967  },
10968  *const EditMenu[] =
10969  {
10970  "Undo",
10971  "Redo",
10972  (char *) NULL
10973  },
10974  *const TransformMenu[] =
10975  {
10976  "Flop",
10977  "Flip",
10978  "Rotate Right",
10979  "Rotate Left",
10980  (char *) NULL
10981  },
10982  *const EnhanceMenu[] =
10983  {
10984  "Hue...",
10985  "Saturation...",
10986  "Brightness...",
10987  "Gamma...",
10988  "Spiff",
10989  "Dull",
10990  "Contrast Stretch...",
10991  "Sigmoidal Contrast...",
10992  "Normalize",
10993  "Equalize",
10994  "Negate",
10995  "Grayscale",
10996  "Map...",
10997  "Quantize...",
10998  (char *) NULL
10999  },
11000  *const EffectsMenu[] =
11001  {
11002  "Despeckle",
11003  "Emboss",
11004  "Reduce Noise",
11005  "Add Noise",
11006  "Sharpen...",
11007  "Blur...",
11008  "Threshold...",
11009  "Edge Detect...",
11010  "Spread...",
11011  "Shade...",
11012  "Raise...",
11013  "Segment...",
11014  (char *) NULL
11015  },
11016  *const FXMenu[] =
11017  {
11018  "Solarize...",
11019  "Sepia Tone...",
11020  "Swirl...",
11021  "Implode...",
11022  "Vignette...",
11023  "Wave...",
11024  "Oil Paint...",
11025  "Charcoal Draw...",
11026  (char *) NULL
11027  },
11028  *const MiscellanyMenu[] =
11029  {
11030  "Image Info",
11031  "Zoom Image",
11032  "Show Preview...",
11033  "Show Histogram",
11034  "Show Matte",
11035  (char *) NULL
11036  };
11037 
11038  const char
11039  *const *Menus[ApplyMenus] =
11040  {
11041  FileMenu,
11042  EditMenu,
11043  TransformMenu,
11044  EnhanceMenu,
11045  EffectsMenu,
11046  FXMenu,
11047  MiscellanyMenu
11048  };
11049 
11050  static const CommandType
11051  ApplyCommands[] =
11052  {
11053  NullCommand,
11054  NullCommand,
11055  NullCommand,
11056  NullCommand,
11057  NullCommand,
11058  NullCommand,
11059  NullCommand,
11060  HelpCommand,
11061  QuitCommand
11062  },
11063  FileCommands[] =
11064  {
11065  SaveCommand,
11066  PrintCommand
11067  },
11068  EditCommands[] =
11069  {
11070  UndoCommand,
11071  RedoCommand
11072  },
11073  TransformCommands[] =
11074  {
11075  FlopCommand,
11076  FlipCommand,
11077  RotateRightCommand,
11078  RotateLeftCommand
11079  },
11080  EnhanceCommands[] =
11081  {
11082  HueCommand,
11083  SaturationCommand,
11084  BrightnessCommand,
11085  GammaCommand,
11086  SpiffCommand,
11087  DullCommand,
11088  ContrastStretchCommand,
11089  SigmoidalContrastCommand,
11090  NormalizeCommand,
11091  EqualizeCommand,
11092  NegateCommand,
11093  GrayscaleCommand,
11094  MapCommand,
11095  QuantizeCommand
11096  },
11097  EffectsCommands[] =
11098  {
11099  DespeckleCommand,
11100  EmbossCommand,
11101  ReduceNoiseCommand,
11102  AddNoiseCommand,
11103  SharpenCommand,
11104  BlurCommand,
11105  EdgeDetectCommand,
11106  SpreadCommand,
11107  ShadeCommand,
11108  RaiseCommand,
11109  SegmentCommand
11110  },
11111  FXCommands[] =
11112  {
11113  SolarizeCommand,
11114  SepiaToneCommand,
11115  SwirlCommand,
11116  ImplodeCommand,
11117  VignetteCommand,
11118  WaveCommand,
11119  OilPaintCommand,
11120  CharcoalDrawCommand
11121  },
11122  MiscellanyCommands[] =
11123  {
11124  InfoCommand,
11125  ZoomCommand,
11126  ShowPreviewCommand,
11127  ShowHistogramCommand,
11128  ShowMatteCommand
11129  },
11130  ROICommands[] =
11131  {
11132  ROIHelpCommand,
11133  ROIDismissCommand
11134  };
11135 
11136  static const CommandType
11137  *Commands[ApplyMenus] =
11138  {
11139  FileCommands,
11140  EditCommands,
11141  TransformCommands,
11142  EnhanceCommands,
11143  EffectsCommands,
11144  FXCommands,
11145  MiscellanyCommands
11146  };
11147 
11148  char
11149  command[MaxTextExtent],
11150  text[MaxTextExtent];
11151 
11152  CommandType
11153  command_type;
11154 
11155  Cursor
11156  cursor;
11157 
11158  Image
11159  *roi_image;
11160 
11161  int
11162  entry,
11163  id,
11164  x,
11165  y;
11166 
11167  MagickRealType
11168  scale_factor;
11169 
11170  MagickProgressMonitor
11171  progress_monitor;
11172 
11174  crop_info,
11175  highlight_info,
11176  roi_info;
11177 
11178  unsigned int
11179  height,
11180  width;
11181 
11182  size_t
11183  state;
11184 
11185  XEvent
11186  event;
11187 
11188  /*
11189  Map Command widget.
11190  */
11191  (void) CloneString(&windows->command.name,"ROI");
11192  windows->command.data=0;
11193  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11194  (void) XMapRaised(display,windows->command.id);
11195  XClientMessage(display,windows->image.id,windows->im_protocols,
11196  windows->im_update_widget,CurrentTime);
11197  /*
11198  Track pointer until button 1 is pressed.
11199  */
11200  XQueryPosition(display,windows->image.id,&x,&y);
11201  (void) XSelectInput(display,windows->image.id,
11202  windows->image.attributes.event_mask | PointerMotionMask);
11203  crop_info.width=0;
11204  crop_info.height=0;
11205  crop_info.x=0;
11206  crop_info.y=0;
11207  roi_info.x=(ssize_t) windows->image.x+x;
11208  roi_info.y=(ssize_t) windows->image.y+y;
11209  roi_info.width=0;
11210  roi_info.height=0;
11211  cursor=XCreateFontCursor(display,XC_fleur);
11212  state=DefaultState;
11213  do
11214  {
11215  if (windows->info.mapped != MagickFalse)
11216  {
11217  /*
11218  Display pointer position.
11219  */
11220  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11221  (long) roi_info.x,(long) roi_info.y);
11222  XInfoWidget(display,windows,text);
11223  }
11224  /*
11225  Wait for next event.
11226  */
11227  XScreenEvent(display,windows,&event);
11228  if (event.xany.window == windows->command.id)
11229  {
11230  /*
11231  Select a command from the Command widget.
11232  */
11233  id=XCommandWidget(display,windows,ROIMenu,&event);
11234  if (id < 0)
11235  continue;
11236  switch (ROICommands[id])
11237  {
11238  case ROIHelpCommand:
11239  {
11240  XTextViewHelp(display,resource_info,windows,MagickFalse,
11241  "Help Viewer - Region of Interest",ImageROIHelp);
11242  break;
11243  }
11244  case ROIDismissCommand:
11245  {
11246  /*
11247  Prematurely exit.
11248  */
11249  state|=EscapeState;
11250  state|=ExitState;
11251  break;
11252  }
11253  default:
11254  break;
11255  }
11256  continue;
11257  }
11258  switch (event.type)
11259  {
11260  case ButtonPress:
11261  {
11262  if (event.xbutton.button != Button1)
11263  break;
11264  if (event.xbutton.window != windows->image.id)
11265  break;
11266  /*
11267  Note first corner of region of interest rectangle-- exit loop.
11268  */
11269  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11270  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11271  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11272  state|=ExitState;
11273  break;
11274  }
11275  case ButtonRelease:
11276  break;
11277  case Expose:
11278  break;
11279  case KeyPress:
11280  {
11281  KeySym
11282  key_symbol;
11283 
11284  if (event.xkey.window != windows->image.id)
11285  break;
11286  /*
11287  Respond to a user key press.
11288  */
11289  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11290  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11291  switch ((int) key_symbol)
11292  {
11293  case XK_Escape:
11294  case XK_F20:
11295  {
11296  /*
11297  Prematurely exit.
11298  */
11299  state|=EscapeState;
11300  state|=ExitState;
11301  break;
11302  }
11303  case XK_F1:
11304  case XK_Help:
11305  {
11306  XTextViewHelp(display,resource_info,windows,MagickFalse,
11307  "Help Viewer - Region of Interest",ImageROIHelp);
11308  break;
11309  }
11310  default:
11311  {
11312  (void) XBell(display,0);
11313  break;
11314  }
11315  }
11316  break;
11317  }
11318  case MotionNotify:
11319  {
11320  /*
11321  Map and unmap Info widget as text cursor crosses its boundaries.
11322  */
11323  x=event.xmotion.x;
11324  y=event.xmotion.y;
11325  if (windows->info.mapped != MagickFalse)
11326  {
11327  if ((x < (int) (windows->info.x+windows->info.width)) &&
11328  (y < (int) (windows->info.y+windows->info.height)))
11329  (void) XWithdrawWindow(display,windows->info.id,
11330  windows->info.screen);
11331  }
11332  else
11333  if ((x > (int) (windows->info.x+windows->info.width)) ||
11334  (y > (int) (windows->info.y+windows->info.height)))
11335  (void) XMapWindow(display,windows->info.id);
11336  roi_info.x=(ssize_t) windows->image.x+x;
11337  roi_info.y=(ssize_t) windows->image.y+y;
11338  break;
11339  }
11340  default:
11341  break;
11342  }
11343  } while ((state & ExitState) == 0);
11344  (void) XSelectInput(display,windows->image.id,
11345  windows->image.attributes.event_mask);
11346  if ((state & EscapeState) != 0)
11347  {
11348  /*
11349  User want to exit without region of interest.
11350  */
11351  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11352  (void) XFreeCursor(display,cursor);
11353  return(MagickTrue);
11354  }
11355  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11356  do
11357  {
11358  /*
11359  Size rectangle as pointer moves until the mouse button is released.
11360  */
11361  x=(int) roi_info.x;
11362  y=(int) roi_info.y;
11363  roi_info.width=0;
11364  roi_info.height=0;
11365  state=DefaultState;
11366  do
11367  {
11368  highlight_info=roi_info;
11369  highlight_info.x=roi_info.x-windows->image.x;
11370  highlight_info.y=roi_info.y-windows->image.y;
11371  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11372  {
11373  /*
11374  Display info and draw region of interest rectangle.
11375  */
11376  if (windows->info.mapped == MagickFalse)
11377  (void) XMapWindow(display,windows->info.id);
11378  (void) FormatLocaleString(text,MaxTextExtent,
11379  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11380  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11381  XInfoWidget(display,windows,text);
11382  XHighlightRectangle(display,windows->image.id,
11383  windows->image.highlight_context,&highlight_info);
11384  }
11385  else
11386  if (windows->info.mapped != MagickFalse)
11387  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11388  /*
11389  Wait for next event.
11390  */
11391  XScreenEvent(display,windows,&event);
11392  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11393  XHighlightRectangle(display,windows->image.id,
11394  windows->image.highlight_context,&highlight_info);
11395  switch (event.type)
11396  {
11397  case ButtonPress:
11398  {
11399  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11401  break;
11402  }
11403  case ButtonRelease:
11404  {
11405  /*
11406  User has committed to region of interest rectangle.
11407  */
11408  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11409  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11410  XSetCursorState(display,windows,MagickFalse);
11411  state|=ExitState;
11412  if (LocaleCompare(windows->command.name,"Apply") == 0)
11413  break;
11414  (void) CloneString(&windows->command.name,"Apply");
11415  windows->command.data=ApplyMenus;
11416  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11417  break;
11418  }
11419  case Expose:
11420  break;
11421  case MotionNotify:
11422  {
11423  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11424  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11425  }
11426  default:
11427  break;
11428  }
11429  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11430  ((state & ExitState) != 0))
11431  {
11432  /*
11433  Check boundary conditions.
11434  */
11435  if (roi_info.x < 0)
11436  roi_info.x=0;
11437  else
11438  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11439  roi_info.x=(ssize_t) windows->image.ximage->width;
11440  if ((int) roi_info.x < x)
11441  roi_info.width=(unsigned int) (x-roi_info.x);
11442  else
11443  {
11444  roi_info.width=(unsigned int) (roi_info.x-x);
11445  roi_info.x=(ssize_t) x;
11446  }
11447  if (roi_info.y < 0)
11448  roi_info.y=0;
11449  else
11450  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11451  roi_info.y=(ssize_t) windows->image.ximage->height;
11452  if ((int) roi_info.y < y)
11453  roi_info.height=(unsigned int) (y-roi_info.y);
11454  else
11455  {
11456  roi_info.height=(unsigned int) (roi_info.y-y);
11457  roi_info.y=(ssize_t) y;
11458  }
11459  }
11460  } while ((state & ExitState) == 0);
11461  /*
11462  Wait for user to grab a corner of the rectangle or press return.
11463  */
11464  state=DefaultState;
11465  command_type=NullCommand;
11466  (void) XMapWindow(display,windows->info.id);
11467  do
11468  {
11469  if (windows->info.mapped != MagickFalse)
11470  {
11471  /*
11472  Display pointer position.
11473  */
11474  (void) FormatLocaleString(text,MaxTextExtent,
11475  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11476  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11477  XInfoWidget(display,windows,text);
11478  }
11479  highlight_info=roi_info;
11480  highlight_info.x=roi_info.x-windows->image.x;
11481  highlight_info.y=roi_info.y-windows->image.y;
11482  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11483  {
11484  state|=EscapeState;
11485  state|=ExitState;
11486  break;
11487  }
11488  if ((state & UpdateRegionState) != 0)
11489  {
11490  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11491  switch (command_type)
11492  {
11493  case UndoCommand:
11494  case RedoCommand:
11495  {
11496  (void) XMagickCommand(display,resource_info,windows,command_type,
11497  image);
11498  break;
11499  }
11500  default:
11501  {
11502  /*
11503  Region of interest is relative to image configuration.
11504  */
11505  progress_monitor=SetImageProgressMonitor(*image,
11506  (MagickProgressMonitor) NULL,(*image)->client_data);
11507  crop_info=roi_info;
11508  width=(unsigned int) (*image)->columns;
11509  height=(unsigned int) (*image)->rows;
11510  x=0;
11511  y=0;
11512  if (windows->image.crop_geometry != (char *) NULL)
11513  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11514  &width,&height);
11515  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11516  crop_info.x+=x;
11517  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11518  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11519  scale_factor=(MagickRealType)
11520  height/windows->image.ximage->height;
11521  crop_info.y+=y;
11522  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11523  crop_info.height=(unsigned int)
11524  (scale_factor*crop_info.height+0.5);
11525  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11526  (void) SetImageProgressMonitor(*image,progress_monitor,
11527  (*image)->client_data);
11528  if (roi_image == (Image *) NULL)
11529  continue;
11530  /*
11531  Apply image processing technique to the region of interest.
11532  */
11533  windows->image.orphan=MagickTrue;
11534  (void) XMagickCommand(display,resource_info,windows,command_type,
11535  &roi_image);
11536  progress_monitor=SetImageProgressMonitor(*image,
11537  (MagickProgressMonitor) NULL,(*image)->client_data);
11538  (void) XMagickCommand(display,resource_info,windows,
11539  SaveToUndoBufferCommand,image);
11540  windows->image.orphan=MagickFalse;
11541  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11542  crop_info.x,crop_info.y);
11543  roi_image=DestroyImage(roi_image);
11544  (void) SetImageProgressMonitor(*image,progress_monitor,
11545  (*image)->client_data);
11546  break;
11547  }
11548  }
11549  if (command_type != InfoCommand)
11550  {
11551  XConfigureImageColormap(display,resource_info,windows,*image);
11552  (void) XConfigureImage(display,resource_info,windows,*image);
11553  }
11554  XCheckRefreshWindows(display,windows);
11555  XInfoWidget(display,windows,text);
11556  (void) XSetFunction(display,windows->image.highlight_context,
11557  GXinvert);
11558  state&=(~UpdateRegionState);
11559  }
11560  XHighlightRectangle(display,windows->image.id,
11561  windows->image.highlight_context,&highlight_info);
11562  XScreenEvent(display,windows,&event);
11563  if (event.xany.window == windows->command.id)
11564  {
11565  /*
11566  Select a command from the Command widget.
11567  */
11568  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11569  command_type=NullCommand;
11570  id=XCommandWidget(display,windows,ApplyMenu,&event);
11571  if (id >= 0)
11572  {
11573  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11574  command_type=ApplyCommands[id];
11575  if (id < ApplyMenus)
11576  {
11577  /*
11578  Select a command from a pop-up menu.
11579  */
11580  entry=XMenuWidget(display,windows,ApplyMenu[id],
11581  (const char **) Menus[id],command);
11582  if (entry >= 0)
11583  {
11584  (void) CopyMagickString(command,Menus[id][entry],
11585  MaxTextExtent);
11586  command_type=Commands[id][entry];
11587  }
11588  }
11589  }
11590  (void) XSetFunction(display,windows->image.highlight_context,
11591  GXinvert);
11592  XHighlightRectangle(display,windows->image.id,
11593  windows->image.highlight_context,&highlight_info);
11594  if (command_type == HelpCommand)
11595  {
11596  (void) XSetFunction(display,windows->image.highlight_context,
11597  GXcopy);
11598  XTextViewHelp(display,resource_info,windows,MagickFalse,
11599  "Help Viewer - Region of Interest",ImageROIHelp);
11600  (void) XSetFunction(display,windows->image.highlight_context,
11601  GXinvert);
11602  continue;
11603  }
11604  if (command_type == QuitCommand)
11605  {
11606  /*
11607  exit.
11608  */
11609  state|=EscapeState;
11610  state|=ExitState;
11611  continue;
11612  }
11613  if (command_type != NullCommand)
11614  state|=UpdateRegionState;
11615  continue;
11616  }
11617  XHighlightRectangle(display,windows->image.id,
11618  windows->image.highlight_context,&highlight_info);
11619  switch (event.type)
11620  {
11621  case ButtonPress:
11622  {
11623  x=windows->image.x;
11624  y=windows->image.y;
11625  if (event.xbutton.button != Button1)
11626  break;
11627  if (event.xbutton.window != windows->image.id)
11628  break;
11629  x=windows->image.x+event.xbutton.x;
11630  y=windows->image.y+event.xbutton.y;
11631  if ((x < (int) (roi_info.x+RoiDelta)) &&
11632  (x > (int) (roi_info.x-RoiDelta)) &&
11633  (y < (int) (roi_info.y+RoiDelta)) &&
11634  (y > (int) (roi_info.y-RoiDelta)))
11635  {
11636  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11637  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11638  state|=UpdateConfigurationState;
11639  break;
11640  }
11641  if ((x < (int) (roi_info.x+RoiDelta)) &&
11642  (x > (int) (roi_info.x-RoiDelta)) &&
11643  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11644  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11645  {
11646  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11647  state|=UpdateConfigurationState;
11648  break;
11649  }
11650  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11651  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11652  (y < (int) (roi_info.y+RoiDelta)) &&
11653  (y > (int) (roi_info.y-RoiDelta)))
11654  {
11655  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11656  state|=UpdateConfigurationState;
11657  break;
11658  }
11659  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11660  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11661  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11662  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11663  {
11664  state|=UpdateConfigurationState;
11665  break;
11666  }
11667  }
11668  case ButtonRelease:
11669  {
11670  if (event.xbutton.window == windows->pan.id)
11671  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11672  (highlight_info.y != crop_info.y-windows->image.y))
11673  XHighlightRectangle(display,windows->image.id,
11674  windows->image.highlight_context,&highlight_info);
11675  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11676  event.xbutton.time);
11677  break;
11678  }
11679  case Expose:
11680  {
11681  if (event.xexpose.window == windows->image.id)
11682  if (event.xexpose.count == 0)
11683  {
11684  event.xexpose.x=(int) highlight_info.x;
11685  event.xexpose.y=(int) highlight_info.y;
11686  event.xexpose.width=(int) highlight_info.width;
11687  event.xexpose.height=(int) highlight_info.height;
11688  XRefreshWindow(display,&windows->image,&event);
11689  }
11690  if (event.xexpose.window == windows->info.id)
11691  if (event.xexpose.count == 0)
11692  XInfoWidget(display,windows,text);
11693  break;
11694  }
11695  case KeyPress:
11696  {
11697  KeySym
11698  key_symbol;
11699 
11700  if (event.xkey.window != windows->image.id)
11701  break;
11702  /*
11703  Respond to a user key press.
11704  */
11705  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11706  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11707  switch ((int) key_symbol)
11708  {
11709  case XK_Shift_L:
11710  case XK_Shift_R:
11711  break;
11712  case XK_Escape:
11713  case XK_F20:
11714  state|=EscapeState;
11715  case XK_Return:
11716  {
11717  state|=ExitState;
11718  break;
11719  }
11720  case XK_Home:
11721  case XK_KP_Home:
11722  {
11723  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11724  roi_info.y=(ssize_t) (windows->image.height/2L-
11725  roi_info.height/2L);
11726  break;
11727  }
11728  case XK_Left:
11729  case XK_KP_Left:
11730  {
11731  roi_info.x--;
11732  break;
11733  }
11734  case XK_Up:
11735  case XK_KP_Up:
11736  case XK_Next:
11737  {
11738  roi_info.y--;
11739  break;
11740  }
11741  case XK_Right:
11742  case XK_KP_Right:
11743  {
11744  roi_info.x++;
11745  break;
11746  }
11747  case XK_Prior:
11748  case XK_Down:
11749  case XK_KP_Down:
11750  {
11751  roi_info.y++;
11752  break;
11753  }
11754  case XK_F1:
11755  case XK_Help:
11756  {
11757  (void) XSetFunction(display,windows->image.highlight_context,
11758  GXcopy);
11759  XTextViewHelp(display,resource_info,windows,MagickFalse,
11760  "Help Viewer - Region of Interest",ImageROIHelp);
11761  (void) XSetFunction(display,windows->image.highlight_context,
11762  GXinvert);
11763  break;
11764  }
11765  default:
11766  {
11767  command_type=XImageWindowCommand(display,resource_info,windows,
11768  event.xkey.state,key_symbol,image);
11769  if (command_type != NullCommand)
11770  state|=UpdateRegionState;
11771  break;
11772  }
11773  }
11774  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11775  event.xkey.time);
11776  break;
11777  }
11778  case KeyRelease:
11779  break;
11780  case MotionNotify:
11781  {
11782  if (event.xbutton.window != windows->image.id)
11783  break;
11784  /*
11785  Map and unmap Info widget as text cursor crosses its boundaries.
11786  */
11787  x=event.xmotion.x;
11788  y=event.xmotion.y;
11789  if (windows->info.mapped != MagickFalse)
11790  {
11791  if ((x < (int) (windows->info.x+windows->info.width)) &&
11792  (y < (int) (windows->info.y+windows->info.height)))
11793  (void) XWithdrawWindow(display,windows->info.id,
11794  windows->info.screen);
11795  }
11796  else
11797  if ((x > (int) (windows->info.x+windows->info.width)) ||
11798  (y > (int) (windows->info.y+windows->info.height)))
11799  (void) XMapWindow(display,windows->info.id);
11800  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11801  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11802  break;
11803  }
11804  case SelectionRequest:
11805  {
11806  XSelectionEvent
11807  notify;
11808 
11809  XSelectionRequestEvent
11810  *request;
11811 
11812  /*
11813  Set primary selection.
11814  */
11815  (void) FormatLocaleString(text,MaxTextExtent,
11816  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11817  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11818  request=(&(event.xselectionrequest));
11819  (void) XChangeProperty(request->display,request->requestor,
11820  request->property,request->target,8,PropModeReplace,
11821  (unsigned char *) text,(int) strlen(text));
11822  notify.type=SelectionNotify;
11823  notify.display=request->display;
11824  notify.requestor=request->requestor;
11825  notify.selection=request->selection;
11826  notify.target=request->target;
11827  notify.time=request->time;
11828  if (request->property == None)
11829  notify.property=request->target;
11830  else
11831  notify.property=request->property;
11832  (void) XSendEvent(request->display,request->requestor,False,0,
11833  (XEvent *) &notify);
11834  }
11835  default:
11836  break;
11837  }
11838  if ((state & UpdateConfigurationState) != 0)
11839  {
11840  (void) XPutBackEvent(display,&event);
11841  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11842  break;
11843  }
11844  } while ((state & ExitState) == 0);
11845  } while ((state & ExitState) == 0);
11846  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11847  XSetCursorState(display,windows,MagickFalse);
11848  if ((state & EscapeState) != 0)
11849  return(MagickTrue);
11850  return(MagickTrue);
11851 }
11852 
11853 /*
11854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11855 % %
11856 % %
11857 % %
11858 + X R o t a t e I m a g e %
11859 % %
11860 % %
11861 % %
11862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11863 %
11864 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11865 % rotation angle is computed from the slope of a line drawn by the user.
11866 %
11867 % The format of the XRotateImage method is:
11868 %
11869 % MagickBooleanType XRotateImage(Display *display,
11870 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11871 % Image **image)
11872 %
11873 % A description of each parameter follows:
11874 %
11875 % o display: Specifies a connection to an X server; returned from
11876 % XOpenDisplay.
11877 %
11878 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11879 %
11880 % o windows: Specifies a pointer to a XWindows structure.
11881 %
11882 % o degrees: Specifies the number of degrees to rotate the image.
11883 %
11884 % o image: the image.
11885 %
11886 */
11887 static MagickBooleanType XRotateImage(Display *display,
11888  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11889 {
11890  const char
11891  *const RotateMenu[] =
11892  {
11893  "Pixel Color",
11894  "Direction",
11895  "Help",
11896  "Dismiss",
11897  (char *) NULL
11898  };
11899 
11900  static ModeType
11901  direction = HorizontalRotateCommand;
11902 
11903  static const ModeType
11904  DirectionCommands[] =
11905  {
11906  HorizontalRotateCommand,
11907  VerticalRotateCommand
11908  },
11909  RotateCommands[] =
11910  {
11911  RotateColorCommand,
11912  RotateDirectionCommand,
11913  RotateHelpCommand,
11914  RotateDismissCommand
11915  };
11916 
11917  static unsigned int
11918  pen_id = 0;
11919 
11920  char
11921  command[MaxTextExtent],
11922  text[MaxTextExtent];
11923 
11924  Image
11925  *rotate_image;
11926 
11927  int
11928  id,
11929  x,
11930  y;
11931 
11932  MagickRealType
11933  normalized_degrees;
11934 
11935  int
11936  i;
11937 
11938  unsigned int
11939  height,
11940  rotations,
11941  width;
11942 
11943  if (degrees == 0.0)
11944  {
11945  unsigned int
11946  distance;
11947 
11948  size_t
11949  state;
11950 
11951  XEvent
11952  event;
11953 
11954  XSegment
11955  rotate_info;
11956 
11957  /*
11958  Map Command widget.
11959  */
11960  (void) CloneString(&windows->command.name,"Rotate");
11961  windows->command.data=2;
11962  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11963  (void) XMapRaised(display,windows->command.id);
11964  XClientMessage(display,windows->image.id,windows->im_protocols,
11965  windows->im_update_widget,CurrentTime);
11966  /*
11967  Wait for first button press.
11968  */
11969  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11970  XQueryPosition(display,windows->image.id,&x,&y);
11971  rotate_info.x1=x;
11972  rotate_info.y1=y;
11973  rotate_info.x2=x;
11974  rotate_info.y2=y;
11975  state=DefaultState;
11976  do
11977  {
11978  XHighlightLine(display,windows->image.id,
11979  windows->image.highlight_context,&rotate_info);
11980  /*
11981  Wait for next event.
11982  */
11983  XScreenEvent(display,windows,&event);
11984  XHighlightLine(display,windows->image.id,
11985  windows->image.highlight_context,&rotate_info);
11986  if (event.xany.window == windows->command.id)
11987  {
11988  /*
11989  Select a command from the Command widget.
11990  */
11991  id=XCommandWidget(display,windows,RotateMenu,&event);
11992  if (id < 0)
11993  continue;
11994  (void) XSetFunction(display,windows->image.highlight_context,
11995  GXcopy);
11996  switch (RotateCommands[id])
11997  {
11998  case RotateColorCommand:
11999  {
12000  const char
12001  *ColorMenu[MaxNumberPens];
12002 
12003  int
12004  pen_number;
12005 
12006  XColor
12007  color;
12008 
12009  /*
12010  Initialize menu selections.
12011  */
12012  for (i=0; i < (int) (MaxNumberPens-2); i++)
12013  ColorMenu[i]=resource_info->pen_colors[i];
12014  ColorMenu[MaxNumberPens-2]="Browser...";
12015  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12016  /*
12017  Select a pen color from the pop-up menu.
12018  */
12019  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12020  (const char **) ColorMenu,command);
12021  if (pen_number < 0)
12022  break;
12023  if (pen_number == (MaxNumberPens-2))
12024  {
12025  static char
12026  color_name[MaxTextExtent] = "gray";
12027 
12028  /*
12029  Select a pen color from a dialog.
12030  */
12031  resource_info->pen_colors[pen_number]=color_name;
12032  XColorBrowserWidget(display,windows,"Select",color_name);
12033  if (*color_name == '\0')
12034  break;
12035  }
12036  /*
12037  Set pen color.
12038  */
12039  (void) XParseColor(display,windows->map_info->colormap,
12040  resource_info->pen_colors[pen_number],&color);
12041  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12042  (unsigned int) MaxColors,&color);
12043  windows->pixel_info->pen_colors[pen_number]=color;
12044  pen_id=(unsigned int) pen_number;
12045  break;
12046  }
12047  case RotateDirectionCommand:
12048  {
12049  const char
12050  *const Directions[] =
12051  {
12052  "horizontal",
12053  "vertical",
12054  (char *) NULL,
12055  };
12056 
12057  /*
12058  Select a command from the pop-up menu.
12059  */
12060  id=XMenuWidget(display,windows,RotateMenu[id],
12061  Directions,command);
12062  if (id >= 0)
12063  direction=DirectionCommands[id];
12064  break;
12065  }
12066  case RotateHelpCommand:
12067  {
12068  XTextViewHelp(display,resource_info,windows,MagickFalse,
12069  "Help Viewer - Image Rotation",ImageRotateHelp);
12070  break;
12071  }
12072  case RotateDismissCommand:
12073  {
12074  /*
12075  Prematurely exit.
12076  */
12077  state|=EscapeState;
12078  state|=ExitState;
12079  break;
12080  }
12081  default:
12082  break;
12083  }
12084  (void) XSetFunction(display,windows->image.highlight_context,
12085  GXinvert);
12086  continue;
12087  }
12088  switch (event.type)
12089  {
12090  case ButtonPress:
12091  {
12092  if (event.xbutton.button != Button1)
12093  break;
12094  if (event.xbutton.window != windows->image.id)
12095  break;
12096  /*
12097  exit loop.
12098  */
12099  (void) XSetFunction(display,windows->image.highlight_context,
12100  GXcopy);
12101  rotate_info.x1=event.xbutton.x;
12102  rotate_info.y1=event.xbutton.y;
12103  state|=ExitState;
12104  break;
12105  }
12106  case ButtonRelease:
12107  break;
12108  case Expose:
12109  break;
12110  case KeyPress:
12111  {
12112  char
12113  command[MaxTextExtent];
12114 
12115  KeySym
12116  key_symbol;
12117 
12118  if (event.xkey.window != windows->image.id)
12119  break;
12120  /*
12121  Respond to a user key press.
12122  */
12123  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12124  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12125  switch ((int) key_symbol)
12126  {
12127  case XK_Escape:
12128  case XK_F20:
12129  {
12130  /*
12131  Prematurely exit.
12132  */
12133  state|=EscapeState;
12134  state|=ExitState;
12135  break;
12136  }
12137  case XK_F1:
12138  case XK_Help:
12139  {
12140  (void) XSetFunction(display,windows->image.highlight_context,
12141  GXcopy);
12142  XTextViewHelp(display,resource_info,windows,MagickFalse,
12143  "Help Viewer - Image Rotation",ImageRotateHelp);
12144  (void) XSetFunction(display,windows->image.highlight_context,
12145  GXinvert);
12146  break;
12147  }
12148  default:
12149  {
12150  (void) XBell(display,0);
12151  break;
12152  }
12153  }
12154  break;
12155  }
12156  case MotionNotify:
12157  {
12158  rotate_info.x1=event.xmotion.x;
12159  rotate_info.y1=event.xmotion.y;
12160  }
12161  }
12162  rotate_info.x2=rotate_info.x1;
12163  rotate_info.y2=rotate_info.y1;
12164  if (direction == HorizontalRotateCommand)
12165  rotate_info.x2+=32;
12166  else
12167  rotate_info.y2-=32;
12168  } while ((state & ExitState) == 0);
12169  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12170  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12171  if ((state & EscapeState) != 0)
12172  return(MagickTrue);
12173  /*
12174  Draw line as pointer moves until the mouse button is released.
12175  */
12176  distance=0;
12177  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12178  state=DefaultState;
12179  do
12180  {
12181  if (distance > 9)
12182  {
12183  /*
12184  Display info and draw rotation line.
12185  */
12186  if (windows->info.mapped == MagickFalse)
12187  (void) XMapWindow(display,windows->info.id);
12188  (void) FormatLocaleString(text,MaxTextExtent," %g",
12189  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12190  XInfoWidget(display,windows,text);
12191  XHighlightLine(display,windows->image.id,
12192  windows->image.highlight_context,&rotate_info);
12193  }
12194  else
12195  if (windows->info.mapped != MagickFalse)
12196  (void) XWithdrawWindow(display,windows->info.id,
12197  windows->info.screen);
12198  /*
12199  Wait for next event.
12200  */
12201  XScreenEvent(display,windows,&event);
12202  if (distance > 9)
12203  XHighlightLine(display,windows->image.id,
12204  windows->image.highlight_context,&rotate_info);
12205  switch (event.type)
12206  {
12207  case ButtonPress:
12208  break;
12209  case ButtonRelease:
12210  {
12211  /*
12212  User has committed to rotation line.
12213  */
12214  rotate_info.x2=event.xbutton.x;
12215  rotate_info.y2=event.xbutton.y;
12216  state|=ExitState;
12217  break;
12218  }
12219  case Expose:
12220  break;
12221  case MotionNotify:
12222  {
12223  rotate_info.x2=event.xmotion.x;
12224  rotate_info.y2=event.xmotion.y;
12225  }
12226  default:
12227  break;
12228  }
12229  /*
12230  Check boundary conditions.
12231  */
12232  if (rotate_info.x2 < 0)
12233  rotate_info.x2=0;
12234  else
12235  if (rotate_info.x2 > (int) windows->image.width)
12236  rotate_info.x2=(short) windows->image.width;
12237  if (rotate_info.y2 < 0)
12238  rotate_info.y2=0;
12239  else
12240  if (rotate_info.y2 > (int) windows->image.height)
12241  rotate_info.y2=(short) windows->image.height;
12242  /*
12243  Compute rotation angle from the slope of the line.
12244  */
12245  degrees=0.0;
12246  distance=(unsigned int)
12247  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12248  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12249  if (distance > 9)
12250  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12251  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12252  } while ((state & ExitState) == 0);
12253  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12254  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12255  if (distance <= 9)
12256  return(MagickTrue);
12257  }
12258  if (direction == VerticalRotateCommand)
12259  degrees-=90.0;
12260  if (degrees == 0.0)
12261  return(MagickTrue);
12262  /*
12263  Rotate image.
12264  */
12265  normalized_degrees=degrees;
12266  while (normalized_degrees < -45.0)
12267  normalized_degrees+=360.0;
12268  for (rotations=0; normalized_degrees > 45.0; rotations++)
12269  normalized_degrees-=90.0;
12270  if (normalized_degrees != 0.0)
12271  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12272  XSetCursorState(display,windows,MagickTrue);
12273  XCheckRefreshWindows(display,windows);
12274  (*image)->background_color.red=ScaleShortToQuantum(
12275  windows->pixel_info->pen_colors[pen_id].red);
12276  (*image)->background_color.green=ScaleShortToQuantum(
12277  windows->pixel_info->pen_colors[pen_id].green);
12278  (*image)->background_color.blue=ScaleShortToQuantum(
12279  windows->pixel_info->pen_colors[pen_id].blue);
12280  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12281  XSetCursorState(display,windows,MagickFalse);
12282  if (rotate_image == (Image *) NULL)
12283  return(MagickFalse);
12284  *image=DestroyImage(*image);
12285  *image=rotate_image;
12286  if (windows->image.crop_geometry != (char *) NULL)
12287  {
12288  /*
12289  Rotate crop geometry.
12290  */
12291  width=(unsigned int) (*image)->columns;
12292  height=(unsigned int) (*image)->rows;
12293  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12294  switch (rotations % 4)
12295  {
12296  default:
12297  case 0:
12298  break;
12299  case 1:
12300  {
12301  /*
12302  Rotate 90 degrees.
12303  */
12304  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12305  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12306  (int) height-y,x);
12307  break;
12308  }
12309  case 2:
12310  {
12311  /*
12312  Rotate 180 degrees.
12313  */
12314  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12315  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12316  break;
12317  }
12318  case 3:
12319  {
12320  /*
12321  Rotate 270 degrees.
12322  */
12323  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12324  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12325  break;
12326  }
12327  }
12328  }
12329  if (windows->image.orphan != MagickFalse)
12330  return(MagickTrue);
12331  if (normalized_degrees != 0.0)
12332  {
12333  /*
12334  Update image colormap.
12335  */
12336  windows->image.window_changes.width=(int) (*image)->columns;
12337  windows->image.window_changes.height=(int) (*image)->rows;
12338  if (windows->image.crop_geometry != (char *) NULL)
12339  {
12340  /*
12341  Obtain dimensions of image from crop geometry.
12342  */
12343  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12344  &width,&height);
12345  windows->image.window_changes.width=(int) width;
12346  windows->image.window_changes.height=(int) height;
12347  }
12348  XConfigureImageColormap(display,resource_info,windows,*image);
12349  }
12350  else
12351  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12352  {
12353  windows->image.window_changes.width=windows->image.ximage->height;
12354  windows->image.window_changes.height=windows->image.ximage->width;
12355  }
12356  /*
12357  Update image configuration.
12358  */
12359  (void) XConfigureImage(display,resource_info,windows,*image);
12360  return(MagickTrue);
12361 }
12362 
12363 /*
12364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12365 % %
12366 % %
12367 % %
12368 + X S a v e I m a g e %
12369 % %
12370 % %
12371 % %
12372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12373 %
12374 % XSaveImage() saves an image to a file.
12375 %
12376 % The format of the XSaveImage method is:
12377 %
12378 % MagickBooleanType XSaveImage(Display *display,
12379 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12380 %
12381 % A description of each parameter follows:
12382 %
12383 % o display: Specifies a connection to an X server; returned from
12384 % XOpenDisplay.
12385 %
12386 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12387 %
12388 % o windows: Specifies a pointer to a XWindows structure.
12389 %
12390 % o image: the image.
12391 %
12392 */
12393 static MagickBooleanType XSaveImage(Display *display,
12394  XResourceInfo *resource_info,XWindows *windows,Image *image)
12395 {
12396  char
12397  filename[MaxTextExtent],
12398  geometry[MaxTextExtent];
12399 
12400  Image
12401  *save_image;
12402 
12403  ImageInfo
12404  *image_info;
12405 
12406  MagickStatusType
12407  status;
12408 
12409  /*
12410  Request file name from user.
12411  */
12412  if (resource_info->write_filename != (char *) NULL)
12413  (void) CopyMagickString(filename,resource_info->write_filename,
12414  MaxTextExtent);
12415  else
12416  {
12417  char
12418  path[MaxTextExtent];
12419 
12420  int
12421  status;
12422 
12423  GetPathComponent(image->filename,HeadPath,path);
12424  GetPathComponent(image->filename,TailPath,filename);
12425  if (*path != '\0')
12426  {
12427  status=chdir(path);
12428  if (status == -1)
12429  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12430  FileOpenError,"UnableToOpenFile","%s",path);
12431  }
12432  }
12433  XFileBrowserWidget(display,windows,"Save",filename);
12434  if (*filename == '\0')
12435  return(MagickTrue);
12436  if (IsPathAccessible(filename) != MagickFalse)
12437  {
12438  int
12439  status;
12440 
12441  /*
12442  File exists-- seek user's permission before overwriting.
12443  */
12444  status=XConfirmWidget(display,windows,"Overwrite",filename);
12445  if (status <= 0)
12446  return(MagickTrue);
12447  }
12448  image_info=CloneImageInfo(resource_info->image_info);
12449  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12450  (void) SetImageInfo(image_info,1,&image->exception);
12451  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12452  (LocaleCompare(image_info->magick,"JPG") == 0))
12453  {
12454  char
12455  quality[MaxTextExtent];
12456 
12457  int
12458  status;
12459 
12460  /*
12461  Request JPEG quality from user.
12462  */
12463  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12464  image->quality);
12465  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12466  quality);
12467  if (*quality == '\0')
12468  return(MagickTrue);
12469  image->quality=StringToUnsignedLong(quality);
12470  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12471  }
12472  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12473  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12474  (LocaleCompare(image_info->magick,"PS") == 0) ||
12475  (LocaleCompare(image_info->magick,"PS2") == 0))
12476  {
12477  char
12478  geometry[MaxTextExtent];
12479 
12480  const char
12481  *const PageSizes[] =
12482  {
12483  "Letter",
12484  "Tabloid",
12485  "Ledger",
12486  "Legal",
12487  "Statement",
12488  "Executive",
12489  "A3",
12490  "A4",
12491  "A5",
12492  "B4",
12493  "B5",
12494  "Folio",
12495  "Quarto",
12496  "10x14",
12497  (char *) NULL
12498  };
12499 
12500  /*
12501  Request page geometry from user.
12502  */
12503  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12504  if (LocaleCompare(image_info->magick,"PDF") == 0)
12505  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12506  if (image_info->page != (char *) NULL)
12507  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12508  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12509  "Select page geometry:",geometry);
12510  if (*geometry != '\0')
12511  image_info->page=GetPageGeometry(geometry);
12512  }
12513  /*
12514  Apply image transforms.
12515  */
12516  XSetCursorState(display,windows,MagickTrue);
12517  XCheckRefreshWindows(display,windows);
12518  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12519  if (save_image == (Image *) NULL)
12520  return(MagickFalse);
12521  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12522  windows->image.ximage->width,windows->image.ximage->height);
12523  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12524  /*
12525  Write image.
12526  */
12527  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12528  status=WriteImage(image_info,save_image);
12529  if (status != MagickFalse)
12530  image->taint=MagickFalse;
12531  save_image=DestroyImage(save_image);
12532  image_info=DestroyImageInfo(image_info);
12533  XSetCursorState(display,windows,MagickFalse);
12534  return(status != 0 ? MagickTrue : MagickFalse);
12535 }
12536 
12537 /*
12538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12539 % %
12540 % %
12541 % %
12542 + X S c r e e n E v e n t %
12543 % %
12544 % %
12545 % %
12546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12547 %
12548 % XScreenEvent() handles global events associated with the Pan and Magnify
12549 % windows.
12550 %
12551 % The format of the XScreenEvent function is:
12552 %
12553 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12554 %
12555 % A description of each parameter follows:
12556 %
12557 % o display: Specifies a pointer to the Display structure; returned from
12558 % XOpenDisplay.
12559 %
12560 % o windows: Specifies a pointer to a XWindows structure.
12561 %
12562 % o event: Specifies a pointer to a X11 XEvent structure.
12563 %
12564 %
12565 */
12566 
12567 #if defined(__cplusplus) || defined(c_plusplus)
12568 extern "C" {
12569 #endif
12570 
12571 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12572 {
12573  XWindows
12574  *windows;
12575 
12576  magick_unreferenced(display);
12577 
12578  windows=(XWindows *) data;
12579  if ((event->type == ClientMessage) &&
12580  (event->xclient.window == windows->image.id))
12581  return(MagickFalse);
12582  return(MagickTrue);
12583 }
12584 
12585 #if defined(__cplusplus) || defined(c_plusplus)
12586 }
12587 #endif
12588 
12589 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12590 {
12591  int
12592  x,
12593  y;
12594 
12595  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12596  if (event->xany.window == windows->command.id)
12597  return;
12598  switch (event->type)
12599  {
12600  case ButtonPress:
12601  case ButtonRelease:
12602  {
12603  if ((event->xbutton.button == Button3) &&
12604  (event->xbutton.state & Mod1Mask))
12605  {
12606  /*
12607  Convert Alt-Button3 to Button2.
12608  */
12609  event->xbutton.button=Button2;
12610  event->xbutton.state&=(~Mod1Mask);
12611  }
12612  if (event->xbutton.window == windows->backdrop.id)
12613  {
12614  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12615  event->xbutton.time);
12616  break;
12617  }
12618  if (event->xbutton.window == windows->pan.id)
12619  {
12620  XPanImage(display,windows,event);
12621  break;
12622  }
12623  if (event->xbutton.window == windows->image.id)
12624  if (event->xbutton.button == Button2)
12625  {
12626  /*
12627  Update magnified image.
12628  */
12629  x=event->xbutton.x;
12630  y=event->xbutton.y;
12631  if (x < 0)
12632  x=0;
12633  else
12634  if (x >= (int) windows->image.width)
12635  x=(int) (windows->image.width-1);
12636  windows->magnify.x=(int) windows->image.x+x;
12637  if (y < 0)
12638  y=0;
12639  else
12640  if (y >= (int) windows->image.height)
12641  y=(int) (windows->image.height-1);
12642  windows->magnify.y=windows->image.y+y;
12643  if (windows->magnify.mapped == MagickFalse)
12644  (void) XMapRaised(display,windows->magnify.id);
12645  XMakeMagnifyImage(display,windows);
12646  if (event->type == ButtonRelease)
12647  (void) XWithdrawWindow(display,windows->info.id,
12648  windows->info.screen);
12649  break;
12650  }
12651  break;
12652  }
12653  case ClientMessage:
12654  {
12655  /*
12656  If client window delete message, exit.
12657  */
12658  if (event->xclient.message_type != windows->wm_protocols)
12659  break;
12660  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12661  break;
12662  if (event->xclient.window == windows->magnify.id)
12663  {
12664  (void) XWithdrawWindow(display,windows->magnify.id,
12665  windows->magnify.screen);
12666  break;
12667  }
12668  break;
12669  }
12670  case ConfigureNotify:
12671  {
12672  if (event->xconfigure.window == windows->magnify.id)
12673  {
12674  unsigned int
12675  magnify;
12676 
12677  /*
12678  Magnify window has a new configuration.
12679  */
12680  windows->magnify.width=(unsigned int) event->xconfigure.width;
12681  windows->magnify.height=(unsigned int) event->xconfigure.height;
12682  if (windows->magnify.mapped == MagickFalse)
12683  break;
12684  magnify=1;
12685  while ((int) magnify <= event->xconfigure.width)
12686  magnify<<=1;
12687  while ((int) magnify <= event->xconfigure.height)
12688  magnify<<=1;
12689  magnify>>=1;
12690  if (((int) magnify != event->xconfigure.width) ||
12691  ((int) magnify != event->xconfigure.height))
12692  {
12693  XWindowChanges
12694  window_changes;
12695 
12696  window_changes.width=(int) magnify;
12697  window_changes.height=(int) magnify;
12698  (void) XReconfigureWMWindow(display,windows->magnify.id,
12699  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12700  &window_changes);
12701  break;
12702  }
12703  XMakeMagnifyImage(display,windows);
12704  break;
12705  }
12706  break;
12707  }
12708  case Expose:
12709  {
12710  if (event->xexpose.window == windows->image.id)
12711  {
12712  XRefreshWindow(display,&windows->image,event);
12713  break;
12714  }
12715  if (event->xexpose.window == windows->pan.id)
12716  if (event->xexpose.count == 0)
12717  {
12718  XDrawPanRectangle(display,windows);
12719  break;
12720  }
12721  if (event->xexpose.window == windows->magnify.id)
12722  if (event->xexpose.count == 0)
12723  {
12724  XMakeMagnifyImage(display,windows);
12725  break;
12726  }
12727  break;
12728  }
12729  case KeyPress:
12730  {
12731  char
12732  command[MaxTextExtent];
12733 
12734  KeySym
12735  key_symbol;
12736 
12737  if (event->xkey.window != windows->magnify.id)
12738  break;
12739  /*
12740  Respond to a user key press.
12741  */
12742  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12743  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12744  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12745  break;
12746  }
12747  case MapNotify:
12748  {
12749  if (event->xmap.window == windows->magnify.id)
12750  {
12751  windows->magnify.mapped=MagickTrue;
12752  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12753  break;
12754  }
12755  if (event->xmap.window == windows->info.id)
12756  {
12757  windows->info.mapped=MagickTrue;
12758  break;
12759  }
12760  break;
12761  }
12762  case MotionNotify:
12763  {
12764  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12765  if (event->xmotion.window == windows->image.id)
12766  if (windows->magnify.mapped != MagickFalse)
12767  {
12768  /*
12769  Update magnified image.
12770  */
12771  x=event->xmotion.x;
12772  y=event->xmotion.y;
12773  if (x < 0)
12774  x=0;
12775  else
12776  if (x >= (int) windows->image.width)
12777  x=(int) (windows->image.width-1);
12778  windows->magnify.x=(int) windows->image.x+x;
12779  if (y < 0)
12780  y=0;
12781  else
12782  if (y >= (int) windows->image.height)
12783  y=(int) (windows->image.height-1);
12784  windows->magnify.y=windows->image.y+y;
12785  XMakeMagnifyImage(display,windows);
12786  }
12787  break;
12788  }
12789  case UnmapNotify:
12790  {
12791  if (event->xunmap.window == windows->magnify.id)
12792  {
12793  windows->magnify.mapped=MagickFalse;
12794  break;
12795  }
12796  if (event->xunmap.window == windows->info.id)
12797  {
12798  windows->info.mapped=MagickFalse;
12799  break;
12800  }
12801  break;
12802  }
12803  default:
12804  break;
12805  }
12806 }
12807 
12808 /*
12809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12810 % %
12811 % %
12812 % %
12813 + X S e t C r o p G e o m e t r y %
12814 % %
12815 % %
12816 % %
12817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12818 %
12819 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12820 % and translates it to a cropping geometry relative to the image.
12821 %
12822 % The format of the XSetCropGeometry method is:
12823 %
12824 % void XSetCropGeometry(Display *display,XWindows *windows,
12825 % RectangleInfo *crop_info,Image *image)
12826 %
12827 % A description of each parameter follows:
12828 %
12829 % o display: Specifies a connection to an X server; returned from
12830 % XOpenDisplay.
12831 %
12832 % o windows: Specifies a pointer to a XWindows structure.
12833 %
12834 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12835 % Image window to crop.
12836 %
12837 % o image: the image.
12838 %
12839 */
12840 static void XSetCropGeometry(Display *display,XWindows *windows,
12841  RectangleInfo *crop_info,Image *image)
12842 {
12843  char
12844  text[MaxTextExtent];
12845 
12846  int
12847  x,
12848  y;
12849 
12850  MagickRealType
12851  scale_factor;
12852 
12853  unsigned int
12854  height,
12855  width;
12856 
12857  if (windows->info.mapped != MagickFalse)
12858  {
12859  /*
12860  Display info on cropping rectangle.
12861  */
12862  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12863  (double) crop_info->width,(double) crop_info->height,(double)
12864  crop_info->x,(double) crop_info->y);
12865  XInfoWidget(display,windows,text);
12866  }
12867  /*
12868  Cropping geometry is relative to any previous crop geometry.
12869  */
12870  x=0;
12871  y=0;
12872  width=(unsigned int) image->columns;
12873  height=(unsigned int) image->rows;
12874  if (windows->image.crop_geometry != (char *) NULL)
12875  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12876  else
12877  windows->image.crop_geometry=AcquireString((char *) NULL);
12878  /*
12879  Define the crop geometry string from the cropping rectangle.
12880  */
12881  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12882  if (crop_info->x > 0)
12883  x+=(int) (scale_factor*crop_info->x+0.5);
12884  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12885  if (width == 0)
12886  width=1;
12887  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12888  if (crop_info->y > 0)
12889  y+=(int) (scale_factor*crop_info->y+0.5);
12890  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12891  if (height == 0)
12892  height=1;
12893  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12894  "%ux%u%+d%+d",width,height,x,y);
12895 }
12896 
12897 /*
12898 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12899 % %
12900 % %
12901 % %
12902 + X T i l e I m a g e %
12903 % %
12904 % %
12905 % %
12906 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12907 %
12908 % XTileImage() loads or deletes a selected tile from a visual image directory.
12909 % The load or delete command is chosen from a menu.
12910 %
12911 % The format of the XTileImage method is:
12912 %
12913 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12914 % XWindows *windows,Image *image,XEvent *event)
12915 %
12916 % A description of each parameter follows:
12917 %
12918 % o tile_image: XTileImage reads or deletes the tile image
12919 % and returns it. A null image is returned if an error occurs.
12920 %
12921 % o display: Specifies a connection to an X server; returned from
12922 % XOpenDisplay.
12923 %
12924 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12925 %
12926 % o windows: Specifies a pointer to a XWindows structure.
12927 %
12928 % o image: the image; returned from ReadImage.
12929 %
12930 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12931 % the entire image is refreshed.
12932 %
12933 */
12934 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12935  XWindows *windows,Image *image,XEvent *event)
12936 {
12937  const char
12938  *const VerbMenu[] =
12939  {
12940  "Load",
12941  "Next",
12942  "Former",
12943  "Delete",
12944  "Update",
12945  (char *) NULL,
12946  };
12947 
12948  static const ModeType
12949  TileCommands[] =
12950  {
12951  TileLoadCommand,
12952  TileNextCommand,
12953  TileFormerCommand,
12954  TileDeleteCommand,
12955  TileUpdateCommand
12956  };
12957 
12958  char
12959  command[MaxTextExtent],
12960  filename[MaxTextExtent];
12961 
12962  Image
12963  *tile_image;
12964 
12965  int
12966  id,
12967  status,
12968  tile,
12969  x,
12970  y;
12971 
12972  MagickRealType
12973  scale_factor;
12974 
12975  char
12976  *p,
12977  *q;
12978 
12979  int
12980  i;
12981 
12982  unsigned int
12983  height,
12984  width;
12985 
12986  /*
12987  Tile image is relative to montage image configuration.
12988  */
12989  x=0;
12990  y=0;
12991  width=(unsigned int) image->columns;
12992  height=(unsigned int) image->rows;
12993  if (windows->image.crop_geometry != (char *) NULL)
12994  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12995  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12996  event->xbutton.x+=windows->image.x;
12997  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12998  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12999  event->xbutton.y+=windows->image.y;
13000  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13001  /*
13002  Determine size and location of each tile in the visual image directory.
13003  */
13004  width=(unsigned int) image->columns;
13005  height=(unsigned int) image->rows;
13006  x=0;
13007  y=0;
13008  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13009  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13010  (event->xbutton.x-x)/width;
13011  if (tile < 0)
13012  {
13013  /*
13014  Button press is outside any tile.
13015  */
13016  (void) XBell(display,0);
13017  return((Image *) NULL);
13018  }
13019  /*
13020  Determine file name from the tile directory.
13021  */
13022  p=image->directory;
13023  for (i=tile; (i != 0) && (*p != '\0'); )
13024  {
13025  if (*p == '\xff')
13026  i--;
13027  p++;
13028  }
13029  if (*p == '\0')
13030  {
13031  /*
13032  Button press is outside any tile.
13033  */
13034  (void) XBell(display,0);
13035  return((Image *) NULL);
13036  }
13037  /*
13038  Select a command from the pop-up menu.
13039  */
13040  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13041  if (id < 0)
13042  return((Image *) NULL);
13043  q=p;
13044  while ((*q != '\xff') && (*q != '\0'))
13045  q++;
13046  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13047  /*
13048  Perform command for the selected tile.
13049  */
13050  XSetCursorState(display,windows,MagickTrue);
13051  XCheckRefreshWindows(display,windows);
13052  tile_image=NewImageList();
13053  switch (TileCommands[id])
13054  {
13055  case TileLoadCommand:
13056  {
13057  /*
13058  Load tile image.
13059  */
13060  XCheckRefreshWindows(display,windows);
13061  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13062  MaxTextExtent);
13063  (void) CopyMagickString(resource_info->image_info->filename,filename,
13064  MaxTextExtent);
13065  tile_image=ReadImage(resource_info->image_info,&image->exception);
13066  CatchException(&image->exception);
13067  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13068  break;
13069  }
13070  case TileNextCommand:
13071  {
13072  /*
13073  Display next image.
13074  */
13075  XClientMessage(display,windows->image.id,windows->im_protocols,
13076  windows->im_next_image,CurrentTime);
13077  break;
13078  }
13079  case TileFormerCommand:
13080  {
13081  /*
13082  Display former image.
13083  */
13084  XClientMessage(display,windows->image.id,windows->im_protocols,
13085  windows->im_former_image,CurrentTime);
13086  break;
13087  }
13088  case TileDeleteCommand:
13089  {
13090  /*
13091  Delete tile image.
13092  */
13093  if (IsPathAccessible(filename) == MagickFalse)
13094  {
13095  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13096  break;
13097  }
13098  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13099  if (status <= 0)
13100  break;
13101  status=ShredFile(filename);
13102  status|=remove_utf8(filename);
13103  if (status != MagickFalse)
13104  {
13105  XNoticeWidget(display,windows,"Unable to delete image file:",
13106  filename);
13107  break;
13108  }
13109  }
13110  case TileUpdateCommand:
13111  {
13113  *exception;
13114 
13115  int
13116  x_offset,
13117  y_offset;
13118 
13119  PixelPacket
13120  pixel;
13121 
13122  int
13123  j;
13124 
13125  PixelPacket
13126  *s;
13127 
13128  /*
13129  Ensure all the images exist.
13130  */
13131  tile=0;
13132  for (p=image->directory; *p != '\0'; p++)
13133  {
13134  CacheView
13135  *image_view;
13136 
13137  q=p;
13138  while ((*q != '\xff') && (*q != '\0'))
13139  q++;
13140  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13141  p=q;
13142  if (IsPathAccessible(filename) != MagickFalse)
13143  {
13144  tile++;
13145  continue;
13146  }
13147  /*
13148  Overwrite tile with background color.
13149  */
13150  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13151  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13152  exception=(&image->exception);
13153  image_view=AcquireAuthenticCacheView(image,exception);
13154  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13155  for (i=0; i < (int) height; i++)
13156  {
13157  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13158  y_offset+i,width,1,exception);
13159  if (s == (PixelPacket *) NULL)
13160  break;
13161  for (j=0; j < (int) width; j++)
13162  *s++=pixel;
13163  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13164  break;
13165  }
13166  image_view=DestroyCacheView(image_view);
13167  tile++;
13168  }
13169  windows->image.window_changes.width=(int) image->columns;
13170  windows->image.window_changes.height=(int) image->rows;
13171  XConfigureImageColormap(display,resource_info,windows,image);
13172  (void) XConfigureImage(display,resource_info,windows,image);
13173  break;
13174  }
13175  default:
13176  break;
13177  }
13178  XSetCursorState(display,windows,MagickFalse);
13179  return(tile_image);
13180 }
13181 
13182 /*
13183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13184 % %
13185 % %
13186 % %
13187 + X T r a n s l a t e I m a g e %
13188 % %
13189 % %
13190 % %
13191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13192 %
13193 % XTranslateImage() translates the image within an Image window by one pixel
13194 % as specified by the key symbol. If the image has a `montage string the
13195 % translation is respect to the width and height contained within the string.
13196 %
13197 % The format of the XTranslateImage method is:
13198 %
13199 % void XTranslateImage(Display *display,XWindows *windows,
13200 % Image *image,const KeySym key_symbol)
13201 %
13202 % A description of each parameter follows:
13203 %
13204 % o display: Specifies a connection to an X server; returned from
13205 % XOpenDisplay.
13206 %
13207 % o windows: Specifies a pointer to a XWindows structure.
13208 %
13209 % o image: the image.
13210 %
13211 % o key_symbol: Specifies a KeySym which indicates which side of the image
13212 % to trim.
13213 %
13214 */
13215 static void XTranslateImage(Display *display,XWindows *windows,
13216  Image *image,const KeySym key_symbol)
13217 {
13218  char
13219  text[MaxTextExtent];
13220 
13221  int
13222  x,
13223  y;
13224 
13225  unsigned int
13226  x_offset,
13227  y_offset;
13228 
13229  /*
13230  User specified a pan position offset.
13231  */
13232  x_offset=windows->image.width;
13233  y_offset=windows->image.height;
13234  if (image->montage != (char *) NULL)
13235  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13236  switch ((int) key_symbol)
13237  {
13238  case XK_Home:
13239  case XK_KP_Home:
13240  {
13241  windows->image.x=(int) windows->image.width/2;
13242  windows->image.y=(int) windows->image.height/2;
13243  break;
13244  }
13245  case XK_Left:
13246  case XK_KP_Left:
13247  {
13248  windows->image.x-=x_offset;
13249  break;
13250  }
13251  case XK_Next:
13252  case XK_Up:
13253  case XK_KP_Up:
13254  {
13255  windows->image.y-=y_offset;
13256  break;
13257  }
13258  case XK_Right:
13259  case XK_KP_Right:
13260  {
13261  windows->image.x+=x_offset;
13262  break;
13263  }
13264  case XK_Prior:
13265  case XK_Down:
13266  case XK_KP_Down:
13267  {
13268  windows->image.y+=y_offset;
13269  break;
13270  }
13271  default:
13272  return;
13273  }
13274  /*
13275  Check boundary conditions.
13276  */
13277  if (windows->image.x < 0)
13278  windows->image.x=0;
13279  else
13280  if ((int) (windows->image.x+windows->image.width) >
13281  windows->image.ximage->width)
13282  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13283  if (windows->image.y < 0)
13284  windows->image.y=0;
13285  else
13286  if ((int) (windows->image.y+windows->image.height) >
13287  windows->image.ximage->height)
13288  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13289  /*
13290  Refresh Image window.
13291  */
13292  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13293  windows->image.width,windows->image.height,windows->image.x,
13294  windows->image.y);
13295  XInfoWidget(display,windows,text);
13296  XCheckRefreshWindows(display,windows);
13297  XDrawPanRectangle(display,windows);
13298  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13299  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13300 }
13301 
13302 /*
13303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13304 % %
13305 % %
13306 % %
13307 + X T r i m I m a g e %
13308 % %
13309 % %
13310 % %
13311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13312 %
13313 % XTrimImage() trims the edges from the Image window.
13314 %
13315 % The format of the XTrimImage method is:
13316 %
13317 % MagickBooleanType XTrimImage(Display *display,
13318 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13319 %
13320 % A description of each parameter follows:
13321 %
13322 % o display: Specifies a connection to an X server; returned from
13323 % XOpenDisplay.
13324 %
13325 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13326 %
13327 % o windows: Specifies a pointer to a XWindows structure.
13328 %
13329 % o image: the image.
13330 %
13331 */
13332 static MagickBooleanType XTrimImage(Display *display,
13333  XResourceInfo *resource_info,XWindows *windows,Image *image)
13334 {
13336  trim_info;
13337 
13338  int
13339  x,
13340  y;
13341 
13342  size_t
13343  background,
13344  pixel;
13345 
13346  /*
13347  Trim edges from image.
13348  */
13349  XSetCursorState(display,windows,MagickTrue);
13350  XCheckRefreshWindows(display,windows);
13351  /*
13352  Crop the left edge.
13353  */
13354  background=XGetPixel(windows->image.ximage,0,0);
13355  trim_info.width=(size_t) windows->image.ximage->width;
13356  for (x=0; x < windows->image.ximage->width; x++)
13357  {
13358  for (y=0; y < windows->image.ximage->height; y++)
13359  {
13360  pixel=XGetPixel(windows->image.ximage,x,y);
13361  if (pixel != background)
13362  break;
13363  }
13364  if (y < windows->image.ximage->height)
13365  break;
13366  }
13367  trim_info.x=(ssize_t) x;
13368  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13369  {
13370  XSetCursorState(display,windows,MagickFalse);
13371  return(MagickFalse);
13372  }
13373  /*
13374  Crop the right edge.
13375  */
13376  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13377  for (x=windows->image.ximage->width-1; x != 0; x--)
13378  {
13379  for (y=0; y < windows->image.ximage->height; y++)
13380  {
13381  pixel=XGetPixel(windows->image.ximage,x,y);
13382  if (pixel != background)
13383  break;
13384  }
13385  if (y < windows->image.ximage->height)
13386  break;
13387  }
13388  trim_info.width=(size_t) (x-trim_info.x+1);
13389  /*
13390  Crop the top edge.
13391  */
13392  background=XGetPixel(windows->image.ximage,0,0);
13393  trim_info.height=(size_t) windows->image.ximage->height;
13394  for (y=0; y < windows->image.ximage->height; y++)
13395  {
13396  for (x=0; x < windows->image.ximage->width; x++)
13397  {
13398  pixel=XGetPixel(windows->image.ximage,x,y);
13399  if (pixel != background)
13400  break;
13401  }
13402  if (x < windows->image.ximage->width)
13403  break;
13404  }
13405  trim_info.y=(ssize_t) y;
13406  /*
13407  Crop the bottom edge.
13408  */
13409  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13410  for (y=windows->image.ximage->height-1; y != 0; y--)
13411  {
13412  for (x=0; x < windows->image.ximage->width; x++)
13413  {
13414  pixel=XGetPixel(windows->image.ximage,x,y);
13415  if (pixel != background)
13416  break;
13417  }
13418  if (x < windows->image.ximage->width)
13419  break;
13420  }
13421  trim_info.height=(size_t) y-trim_info.y+1;
13422  if (((unsigned int) trim_info.width != windows->image.width) ||
13423  ((unsigned int) trim_info.height != windows->image.height))
13424  {
13425  /*
13426  Reconfigure Image window as defined by the trimming rectangle.
13427  */
13428  XSetCropGeometry(display,windows,&trim_info,image);
13429  windows->image.window_changes.width=(int) trim_info.width;
13430  windows->image.window_changes.height=(int) trim_info.height;
13431  (void) XConfigureImage(display,resource_info,windows,image);
13432  }
13433  XSetCursorState(display,windows,MagickFalse);
13434  return(MagickTrue);
13435 }
13436 
13437 /*
13438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13439 % %
13440 % %
13441 % %
13442 + X V i s u a l D i r e c t o r y I m a g e %
13443 % %
13444 % %
13445 % %
13446 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13447 %
13448 % XVisualDirectoryImage() creates a Visual Image Directory.
13449 %
13450 % The format of the XVisualDirectoryImage method is:
13451 %
13452 % Image *XVisualDirectoryImage(Display *display,
13453 % XResourceInfo *resource_info,XWindows *windows)
13454 %
13455 % A description of each parameter follows:
13456 %
13457 % o nexus: Method XVisualDirectoryImage returns a visual image
13458 % directory if it can be created successfully. Otherwise a null image
13459 % is returned.
13460 %
13461 % o display: Specifies a connection to an X server; returned from
13462 % XOpenDisplay.
13463 %
13464 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13465 %
13466 % o windows: Specifies a pointer to a XWindows structure.
13467 %
13468 */
13469 static Image *XVisualDirectoryImage(Display *display,
13470  XResourceInfo *resource_info,XWindows *windows)
13471 {
13472 #define TileImageTag "Scale/Image"
13473 #define XClientName "montage"
13474 
13475  char
13476  **filelist;
13477 
13479  *exception;
13480 
13481  Image
13482  *images,
13483  *montage_image,
13484  *next_image,
13485  *thumbnail_image;
13486 
13487  ImageInfo
13488  *read_info;
13489 
13490  int
13491  number_files;
13492 
13493  MagickBooleanType
13494  backdrop;
13495 
13496  MagickStatusType
13497  status;
13498 
13499  MontageInfo
13500  *montage_info;
13501 
13503  geometry;
13504 
13505  int
13506  i;
13507 
13508  static char
13509  filename[MaxTextExtent] = "\0",
13510  filenames[MaxTextExtent] = "*";
13511 
13512  XResourceInfo
13513  background_resources;
13514 
13515  /*
13516  Request file name from user.
13517  */
13518  XFileBrowserWidget(display,windows,"Directory",filenames);
13519  if (*filenames == '\0')
13520  return((Image *) NULL);
13521  /*
13522  Expand the filenames.
13523  */
13524  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13525  if (filelist == (char **) NULL)
13526  {
13527  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13528  filenames);
13529  return((Image *) NULL);
13530  }
13531  number_files=1;
13532  filelist[0]=filenames;
13533  status=ExpandFilenames(&number_files,&filelist);
13534  if ((status == MagickFalse) || (number_files == 0))
13535  {
13536  if (number_files == 0)
13537  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13538  else
13539  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13540  filenames);
13541  return((Image *) NULL);
13542  }
13543  /*
13544  Set image background resources.
13545  */
13546  background_resources=(*resource_info);
13547  background_resources.window_id=AcquireString("");
13548  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13549  "0x%lx",windows->image.id);
13550  background_resources.backdrop=MagickTrue;
13551  /*
13552  Read each image and convert them to a tile.
13553  */
13554  backdrop=(windows->visual_info->klass == TrueColor) ||
13555  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13556  read_info=CloneImageInfo(resource_info->image_info);
13557  (void) SetImageOption(read_info,"jpeg:size","120x120");
13558  (void) CloneString(&read_info->size,DefaultTileGeometry);
13559  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13560  (void *) NULL);
13561  images=NewImageList();
13562  exception=AcquireExceptionInfo();
13563  XSetCursorState(display,windows,MagickTrue);
13564  XCheckRefreshWindows(display,windows);
13565  for (i=0; i < (int) number_files; i++)
13566  {
13567  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13568  filelist[i]=DestroyString(filelist[i]);
13569  *read_info->magick='\0';
13570  next_image=ReadImage(read_info,exception);
13571  CatchException(exception);
13572  if (next_image != (Image *) NULL)
13573  {
13574  (void) DeleteImageProperty(next_image,"label");
13575  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13576  read_info,next_image,DefaultTileLabel));
13577  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13578  exception);
13579  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13580  geometry.height,exception);
13581  if (thumbnail_image != (Image *) NULL)
13582  {
13583  next_image=DestroyImage(next_image);
13584  next_image=thumbnail_image;
13585  }
13586  if (backdrop)
13587  {
13588  (void) XDisplayBackgroundImage(display,&background_resources,
13589  next_image);
13590  XSetCursorState(display,windows,MagickTrue);
13591  }
13592  AppendImageToList(&images,next_image);
13593  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13594  {
13595  MagickBooleanType
13596  proceed;
13597 
13598  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13599  (MagickSizeType) number_files);
13600  if (proceed == MagickFalse)
13601  break;
13602  }
13603  }
13604  }
13605  exception=DestroyExceptionInfo(exception);
13606  filelist=(char **) RelinquishMagickMemory(filelist);
13607  if (images == (Image *) NULL)
13608  {
13609  read_info=DestroyImageInfo(read_info);
13610  XSetCursorState(display,windows,MagickFalse);
13611  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13612  return((Image *) NULL);
13613  }
13614  /*
13615  Create the Visual Image Directory.
13616  */
13617  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13618  montage_info->pointsize=10;
13619  if (resource_info->font != (char *) NULL)
13620  (void) CloneString(&montage_info->font,resource_info->font);
13621  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13622  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13623  images),&images->exception);
13624  images=DestroyImageList(images);
13625  montage_info=DestroyMontageInfo(montage_info);
13626  read_info=DestroyImageInfo(read_info);
13627  XSetCursorState(display,windows,MagickFalse);
13628  if (montage_image == (Image *) NULL)
13629  return(montage_image);
13630  XClientMessage(display,windows->image.id,windows->im_protocols,
13631  windows->im_next_image,CurrentTime);
13632  return(montage_image);
13633 }
13634 
13635 /*
13636 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13637 % %
13638 % %
13639 % %
13640 % X D i s p l a y B a c k g r o u n d I m a g e %
13641 % %
13642 % %
13643 % %
13644 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13645 %
13646 % XDisplayBackgroundImage() displays an image in the background of a window.
13647 %
13648 % The format of the XDisplayBackgroundImage method is:
13649 %
13650 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13651 % XResourceInfo *resource_info,Image *image)
13652 %
13653 % A description of each parameter follows:
13654 %
13655 % o display: Specifies a connection to an X server; returned from
13656 % XOpenDisplay.
13657 %
13658 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13659 %
13660 % o image: the image.
13661 %
13662 */
13663 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13664  XResourceInfo *resource_info,Image *image)
13665 {
13666  char
13667  geometry[MaxTextExtent],
13668  visual_type[MaxTextExtent];
13669 
13670  int
13671  height,
13672  status,
13673  width;
13674 
13676  geometry_info;
13677 
13678  static XPixelInfo
13679  pixel;
13680 
13681  static XStandardColormap
13682  *map_info;
13683 
13684  static XVisualInfo
13685  *visual_info = (XVisualInfo *) NULL;
13686 
13687  static XWindowInfo
13688  window_info;
13689 
13690  size_t
13691  delay;
13692 
13693  Window
13694  root_window;
13695 
13696  XGCValues
13697  context_values;
13698 
13699  XResourceInfo
13700  resources;
13701 
13702  XWindowAttributes
13703  window_attributes;
13704 
13705  /*
13706  Determine target window.
13707  */
13708  assert(image != (Image *) NULL);
13709  assert(image->signature == MagickCoreSignature);
13710  if (IsEventLogging() != MagickFalse)
13711  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13712  resources=(*resource_info);
13713  window_info.id=(Window) NULL;
13714  root_window=XRootWindow(display,XDefaultScreen(display));
13715  if (LocaleCompare(resources.window_id,"root") == 0)
13716  window_info.id=root_window;
13717  else
13718  {
13719  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13720  window_info.id=XWindowByID(display,root_window,
13721  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13722  if (window_info.id == (Window) NULL)
13723  window_info.id=XWindowByName(display,root_window,resources.window_id);
13724  }
13725  if (window_info.id == (Window) NULL)
13726  {
13727  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13728  resources.window_id);
13729  }
13730  /*
13731  Determine window visual id.
13732  */
13733  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13734  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13735  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13736  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13737  if (status != 0)
13738  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13739  XVisualIDFromVisual(window_attributes.visual));
13740  if (visual_info == (XVisualInfo *) NULL)
13741  {
13742  /*
13743  Allocate standard colormap.
13744  */
13745  map_info=XAllocStandardColormap();
13746  if (map_info == (XStandardColormap *) NULL)
13747  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13748  image->filename);
13749  map_info->colormap=(Colormap) NULL;
13750  pixel.pixels=(unsigned long *) NULL;
13751  /*
13752  Initialize visual info.
13753  */
13754  resources.map_type=(char *) NULL;
13755  resources.visual_type=visual_type;
13756  visual_info=XBestVisualInfo(display,map_info,&resources);
13757  if (visual_info == (XVisualInfo *) NULL)
13758  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13759  resources.visual_type);
13760  /*
13761  Initialize window info.
13762  */
13763  window_info.ximage=(XImage *) NULL;
13764  window_info.matte_image=(XImage *) NULL;
13765  window_info.pixmap=(Pixmap) NULL;
13766  window_info.matte_pixmap=(Pixmap) NULL;
13767  }
13768  /*
13769  Free previous root colors.
13770  */
13771  if (window_info.id == root_window)
13772  (void) XDestroyWindowColors(display,root_window);
13773  /*
13774  Initialize Standard Colormap.
13775  */
13776  resources.colormap=SharedColormap;
13777  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13778  /*
13779  Graphic context superclass.
13780  */
13781  context_values.background=pixel.foreground_color.pixel;
13782  context_values.foreground=pixel.background_color.pixel;
13783  pixel.annotate_context=XCreateGC(display,window_info.id,
13784  (size_t) (GCBackground | GCForeground),&context_values);
13785  if (pixel.annotate_context == (GC) NULL)
13786  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13787  image->filename);
13788  /*
13789  Initialize Image window attributes.
13790  */
13791  window_info.name=AcquireString("\0");
13792  window_info.icon_name=AcquireString("\0");
13793  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13794  &resources,&window_info);
13795  /*
13796  Create the X image.
13797  */
13798  window_info.width=(unsigned int) image->columns;
13799  window_info.height=(unsigned int) image->rows;
13800  if ((image->columns != window_info.width) ||
13801  (image->rows != window_info.height))
13802  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13803  image->filename);
13804  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13805  window_attributes.width,window_attributes.height);
13806  geometry_info.width=window_info.width;
13807  geometry_info.height=window_info.height;
13808  geometry_info.x=(ssize_t) window_info.x;
13809  geometry_info.y=(ssize_t) window_info.y;
13810  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13811  &geometry_info.width,&geometry_info.height);
13812  window_info.width=(unsigned int) geometry_info.width;
13813  window_info.height=(unsigned int) geometry_info.height;
13814  window_info.x=(int) geometry_info.x;
13815  window_info.y=(int) geometry_info.y;
13816  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13817  window_info.height);
13818  if (status == MagickFalse)
13819  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13820  image->filename);
13821  window_info.x=0;
13822  window_info.y=0;
13823  if (resource_info->debug != MagickFalse)
13824  {
13825  (void) LogMagickEvent(X11Event,GetMagickModule(),
13826  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13827  (double) image->columns,(double) image->rows);
13828  if (image->colors != 0)
13829  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13830  image->colors);
13831  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13832  }
13833  /*
13834  Adjust image dimensions as specified by backdrop or geometry options.
13835  */
13836  width=(int) window_info.width;
13837  height=(int) window_info.height;
13838  if (resources.backdrop != MagickFalse)
13839  {
13840  /*
13841  Center image on window.
13842  */
13843  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13844  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13845  width=window_attributes.width;
13846  height=window_attributes.height;
13847  }
13848  if ((resources.image_geometry != (char *) NULL) &&
13849  (*resources.image_geometry != '\0'))
13850  {
13851  char
13852  default_geometry[MaxTextExtent];
13853 
13854  int
13855  flags,
13856  gravity;
13857 
13858  XSizeHints
13859  *size_hints;
13860 
13861  /*
13862  User specified geometry.
13863  */
13864  size_hints=XAllocSizeHints();
13865  if (size_hints == (XSizeHints *) NULL)
13866  ThrowXWindowFatalException(ResourceLimitFatalError,
13867  "MemoryAllocationFailed",image->filename);
13868  size_hints->flags=0L;
13869  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13870  width,height);
13871  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13872  default_geometry,window_info.border_width,size_hints,&window_info.x,
13873  &window_info.y,&width,&height,&gravity);
13874  if (flags & (XValue | YValue))
13875  {
13876  width=window_attributes.width;
13877  height=window_attributes.height;
13878  }
13879  (void) XFree((void *) size_hints);
13880  }
13881  /*
13882  Create the X pixmap.
13883  */
13884  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13885  (unsigned int) height,window_info.depth);
13886  if (window_info.pixmap == (Pixmap) NULL)
13887  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13888  image->filename);
13889  /*
13890  Display pixmap on the window.
13891  */
13892  if (((unsigned int) width > window_info.width) ||
13893  ((unsigned int) height > window_info.height))
13894  (void) XFillRectangle(display,window_info.pixmap,
13895  window_info.annotate_context,0,0,(unsigned int) width,
13896  (unsigned int) height);
13897  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13898  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13899  window_info.width,(unsigned int) window_info.height);
13900  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13901  (void) XClearWindow(display,window_info.id);
13902  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13903  XDelay(display,delay == 0UL ? 10UL : delay);
13904  (void) XSync(display,MagickFalse);
13905  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13906 }
13907 
13908 /*
13909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13910 % %
13911 % %
13912 % %
13913 + X D i s p l a y I m a g e %
13914 % %
13915 % %
13916 % %
13917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13918 %
13919 % XDisplayImage() displays an image via X11. A new image is created and
13920 % returned if the user interactively transforms the displayed image.
13921 %
13922 % The format of the XDisplayImage method is:
13923 %
13924 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13925 % char **argv,int argc,Image **image,size_t *state)
13926 %
13927 % A description of each parameter follows:
13928 %
13929 % o nexus: Method XDisplayImage returns an image when the
13930 % user chooses 'Open Image' from the command menu or picks a tile
13931 % from the image directory. Otherwise a null image is returned.
13932 %
13933 % o display: Specifies a connection to an X server; returned from
13934 % XOpenDisplay.
13935 %
13936 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13937 %
13938 % o argv: Specifies the application's argument list.
13939 %
13940 % o argc: Specifies the number of arguments.
13941 %
13942 % o image: Specifies an address to an address of an Image structure;
13943 %
13944 */
13945 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13946  char **argv,int argc,Image **image,size_t *state)
13947 {
13948 #define MagnifySize 256 /* must be a power of 2 */
13949 #define MagickMenus 10
13950 #define MagickTitle "Commands"
13951 
13952  const char
13953  *const CommandMenu[] =
13954  {
13955  "File",
13956  "Edit",
13957  "View",
13958  "Transform",
13959  "Enhance",
13960  "Effects",
13961  "F/X",
13962  "Image Edit",
13963  "Miscellany",
13964  "Help",
13965  (char *) NULL
13966  },
13967  *const FileMenu[] =
13968  {
13969  "Open...",
13970  "Next",
13971  "Former",
13972  "Select...",
13973  "Save...",
13974  "Print...",
13975  "Delete...",
13976  "New...",
13977  "Visual Directory...",
13978  "Quit",
13979  (char *) NULL
13980  },
13981  *const EditMenu[] =
13982  {
13983  "Undo",
13984  "Redo",
13985  "Cut",
13986  "Copy",
13987  "Paste",
13988  (char *) NULL
13989  },
13990  *const ViewMenu[] =
13991  {
13992  "Half Size",
13993  "Original Size",
13994  "Double Size",
13995  "Resize...",
13996  "Apply",
13997  "Refresh",
13998  "Restore",
13999  (char *) NULL
14000  },
14001  *const TransformMenu[] =
14002  {
14003  "Crop",
14004  "Chop",
14005  "Flop",
14006  "Flip",
14007  "Rotate Right",
14008  "Rotate Left",
14009  "Rotate...",
14010  "Shear...",
14011  "Roll...",
14012  "Trim Edges",
14013  (char *) NULL
14014  },
14015  *const EnhanceMenu[] =
14016  {
14017  "Hue...",
14018  "Saturation...",
14019  "Brightness...",
14020  "Gamma...",
14021  "Spiff",
14022  "Dull",
14023  "Contrast Stretch...",
14024  "Sigmoidal Contrast...",
14025  "Normalize",
14026  "Equalize",
14027  "Negate",
14028  "Grayscale",
14029  "Map...",
14030  "Quantize...",
14031  (char *) NULL
14032  },
14033  *const EffectsMenu[] =
14034  {
14035  "Despeckle",
14036  "Emboss",
14037  "Reduce Noise",
14038  "Add Noise...",
14039  "Sharpen...",
14040  "Blur...",
14041  "Threshold...",
14042  "Edge Detect...",
14043  "Spread...",
14044  "Shade...",
14045  "Raise...",
14046  "Segment...",
14047  (char *) NULL
14048  },
14049  *const FXMenu[] =
14050  {
14051  "Solarize...",
14052  "Sepia Tone...",
14053  "Swirl...",
14054  "Implode...",
14055  "Vignette...",
14056  "Wave...",
14057  "Oil Paint...",
14058  "Charcoal Draw...",
14059  (char *) NULL
14060  },
14061  *const ImageEditMenu[] =
14062  {
14063  "Annotate...",
14064  "Draw...",
14065  "Color...",
14066  "Matte...",
14067  "Composite...",
14068  "Add Border...",
14069  "Add Frame...",
14070  "Comment...",
14071  "Launch...",
14072  "Region of Interest...",
14073  (char *) NULL
14074  },
14075  *const MiscellanyMenu[] =
14076  {
14077  "Image Info",
14078  "Zoom Image",
14079  "Show Preview...",
14080  "Show Histogram",
14081  "Show Matte",
14082  "Background...",
14083  "Slide Show...",
14084  "Preferences...",
14085  (char *) NULL
14086  },
14087  *const HelpMenu[] =
14088  {
14089  "Overview",
14090  "Browse Documentation",
14091  "About Display",
14092  (char *) NULL
14093  },
14094  *const ShortCutsMenu[] =
14095  {
14096  "Next",
14097  "Former",
14098  "Open...",
14099  "Save...",
14100  "Print...",
14101  "Undo",
14102  "Restore",
14103  "Image Info",
14104  "Quit",
14105  (char *) NULL
14106  },
14107  *const VirtualMenu[] =
14108  {
14109  "Image Info",
14110  "Print",
14111  "Next",
14112  "Quit",
14113  (char *) NULL
14114  };
14115 
14116  const char
14117  *const *Menus[MagickMenus] =
14118  {
14119  FileMenu,
14120  EditMenu,
14121  ViewMenu,
14122  TransformMenu,
14123  EnhanceMenu,
14124  EffectsMenu,
14125  FXMenu,
14126  ImageEditMenu,
14127  MiscellanyMenu,
14128  HelpMenu
14129  };
14130 
14131  static CommandType
14132  CommandMenus[] =
14133  {
14134  NullCommand,
14135  NullCommand,
14136  NullCommand,
14137  NullCommand,
14138  NullCommand,
14139  NullCommand,
14140  NullCommand,
14141  NullCommand,
14142  NullCommand,
14143  NullCommand,
14144  },
14145  FileCommands[] =
14146  {
14147  OpenCommand,
14148  NextCommand,
14149  FormerCommand,
14150  SelectCommand,
14151  SaveCommand,
14152  PrintCommand,
14153  DeleteCommand,
14154  NewCommand,
14155  VisualDirectoryCommand,
14156  QuitCommand
14157  },
14158  EditCommands[] =
14159  {
14160  UndoCommand,
14161  RedoCommand,
14162  CutCommand,
14163  CopyCommand,
14164  PasteCommand
14165  },
14166  ViewCommands[] =
14167  {
14168  HalfSizeCommand,
14169  OriginalSizeCommand,
14170  DoubleSizeCommand,
14171  ResizeCommand,
14172  ApplyCommand,
14173  RefreshCommand,
14174  RestoreCommand
14175  },
14176  TransformCommands[] =
14177  {
14178  CropCommand,
14179  ChopCommand,
14180  FlopCommand,
14181  FlipCommand,
14182  RotateRightCommand,
14183  RotateLeftCommand,
14184  RotateCommand,
14185  ShearCommand,
14186  RollCommand,
14187  TrimCommand
14188  },
14189  EnhanceCommands[] =
14190  {
14191  HueCommand,
14192  SaturationCommand,
14193  BrightnessCommand,
14194  GammaCommand,
14195  SpiffCommand,
14196  DullCommand,
14197  ContrastStretchCommand,
14198  SigmoidalContrastCommand,
14199  NormalizeCommand,
14200  EqualizeCommand,
14201  NegateCommand,
14202  GrayscaleCommand,
14203  MapCommand,
14204  QuantizeCommand
14205  },
14206  EffectsCommands[] =
14207  {
14208  DespeckleCommand,
14209  EmbossCommand,
14210  ReduceNoiseCommand,
14211  AddNoiseCommand,
14212  SharpenCommand,
14213  BlurCommand,
14214  ThresholdCommand,
14215  EdgeDetectCommand,
14216  SpreadCommand,
14217  ShadeCommand,
14218  RaiseCommand,
14219  SegmentCommand
14220  },
14221  FXCommands[] =
14222  {
14223  SolarizeCommand,
14224  SepiaToneCommand,
14225  SwirlCommand,
14226  ImplodeCommand,
14227  VignetteCommand,
14228  WaveCommand,
14229  OilPaintCommand,
14230  CharcoalDrawCommand
14231  },
14232  ImageEditCommands[] =
14233  {
14234  AnnotateCommand,
14235  DrawCommand,
14236  ColorCommand,
14237  MatteCommand,
14238  CompositeCommand,
14239  AddBorderCommand,
14240  AddFrameCommand,
14241  CommentCommand,
14242  LaunchCommand,
14243  RegionofInterestCommand
14244  },
14245  MiscellanyCommands[] =
14246  {
14247  InfoCommand,
14248  ZoomCommand,
14249  ShowPreviewCommand,
14250  ShowHistogramCommand,
14251  ShowMatteCommand,
14252  BackgroundCommand,
14253  SlideShowCommand,
14254  PreferencesCommand
14255  },
14256  HelpCommands[] =
14257  {
14258  HelpCommand,
14259  BrowseDocumentationCommand,
14260  VersionCommand
14261  },
14262  ShortCutsCommands[] =
14263  {
14264  NextCommand,
14265  FormerCommand,
14266  OpenCommand,
14267  SaveCommand,
14268  PrintCommand,
14269  UndoCommand,
14270  RestoreCommand,
14271  InfoCommand,
14272  QuitCommand
14273  },
14274  VirtualCommands[] =
14275  {
14276  InfoCommand,
14277  PrintCommand,
14278  NextCommand,
14279  QuitCommand
14280  };
14281 
14282  static CommandType
14283  *Commands[MagickMenus] =
14284  {
14285  FileCommands,
14286  EditCommands,
14287  ViewCommands,
14288  TransformCommands,
14289  EnhanceCommands,
14290  EffectsCommands,
14291  FXCommands,
14292  ImageEditCommands,
14293  MiscellanyCommands,
14294  HelpCommands
14295  };
14296 
14297  char
14298  command[MaxTextExtent],
14299  *directory,
14300  geometry[MaxTextExtent],
14301  resource_name[MaxTextExtent];
14302 
14303  CommandType
14304  command_type;
14305 
14306  Image
14307  *display_image,
14308  *nexus;
14309 
14310  int
14311  entry,
14312  id;
14313 
14314  KeySym
14315  key_symbol;
14316 
14317  MagickStatusType
14318  context_mask,
14319  status;
14320 
14322  geometry_info;
14323 
14324  int
14325  i;
14326 
14327  static char
14328  working_directory[MaxTextExtent];
14329 
14330  static XPoint
14331  vid_info;
14332 
14333  static XWindowInfo
14334  *magick_windows[MaxXWindows];
14335 
14336  static unsigned int
14337  number_windows;
14338 
14339  struct stat
14340  attributes;
14341 
14342  time_t
14343  timer,
14344  timestamp,
14345  update_time;
14346 
14347  unsigned int
14348  height,
14349  width;
14350 
14351  size_t
14352  delay;
14353 
14354  WarningHandler
14355  warning_handler;
14356 
14357  Window
14358  root_window;
14359 
14360  XClassHint
14361  *class_hints;
14362 
14363  XEvent
14364  event;
14365 
14366  XFontStruct
14367  *font_info;
14368 
14369  XGCValues
14370  context_values;
14371 
14372  XPixelInfo
14373  *icon_pixel,
14374  *pixel;
14375 
14376  XResourceInfo
14377  *icon_resources;
14378 
14379  XStandardColormap
14380  *icon_map,
14381  *map_info;
14382 
14383  XVisualInfo
14384  *icon_visual,
14385  *visual_info;
14386 
14387  XWindowChanges
14388  window_changes;
14389 
14390  XWindows
14391  *windows;
14392 
14393  XWMHints
14394  *manager_hints;
14395 
14396  assert(image != (Image **) NULL);
14397  assert((*image)->signature == MagickCoreSignature);
14398  if (IsEventLogging() != MagickFalse)
14399  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14400  display_image=(*image);
14401  warning_handler=(WarningHandler) NULL;
14402  windows=XSetWindows((XWindows *) ~0);
14403  if (windows != (XWindows *) NULL)
14404  {
14405  int
14406  status;
14407 
14408  if (*working_directory == '\0')
14409  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14410  status=chdir(working_directory);
14411  if (status == -1)
14412  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14413  FileOpenError,"UnableToOpenFile","%s",working_directory);
14414  warning_handler=resource_info->display_warnings ?
14415  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14416  warning_handler=resource_info->display_warnings ?
14417  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14418  }
14419  else
14420  {
14421  /*
14422  Allocate windows structure.
14423  */
14424  resource_info->colors=display_image->colors;
14425  windows=XSetWindows(XInitializeWindows(display,resource_info));
14426  if (windows == (XWindows *) NULL)
14427  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14428  (*image)->filename);
14429  /*
14430  Initialize window id's.
14431  */
14432  number_windows=0;
14433  magick_windows[number_windows++]=(&windows->icon);
14434  magick_windows[number_windows++]=(&windows->backdrop);
14435  magick_windows[number_windows++]=(&windows->image);
14436  magick_windows[number_windows++]=(&windows->info);
14437  magick_windows[number_windows++]=(&windows->command);
14438  magick_windows[number_windows++]=(&windows->widget);
14439  magick_windows[number_windows++]=(&windows->popup);
14440  magick_windows[number_windows++]=(&windows->magnify);
14441  magick_windows[number_windows++]=(&windows->pan);
14442  for (i=0; i < (int) number_windows; i++)
14443  magick_windows[i]->id=(Window) NULL;
14444  vid_info.x=0;
14445  vid_info.y=0;
14446  }
14447  /*
14448  Initialize font info.
14449  */
14450  if (windows->font_info != (XFontStruct *) NULL)
14451  (void) XFreeFont(display,windows->font_info);
14452  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14453  if (windows->font_info == (XFontStruct *) NULL)
14454  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14455  resource_info->font);
14456  /*
14457  Initialize Standard Colormap.
14458  */
14459  map_info=windows->map_info;
14460  icon_map=windows->icon_map;
14461  visual_info=windows->visual_info;
14462  icon_visual=windows->icon_visual;
14463  pixel=windows->pixel_info;
14464  icon_pixel=windows->icon_pixel;
14465  font_info=windows->font_info;
14466  icon_resources=windows->icon_resources;
14467  class_hints=windows->class_hints;
14468  manager_hints=windows->manager_hints;
14469  root_window=XRootWindow(display,visual_info->screen);
14470  nexus=NewImageList();
14471  if (resource_info->debug != MagickFalse)
14472  {
14473  (void) LogMagickEvent(X11Event,GetMagickModule(),
14474  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14475  (double) display_image->scene,(double) display_image->columns,
14476  (double) display_image->rows);
14477  if (display_image->colors != 0)
14478  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14479  display_image->colors);
14480  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14481  display_image->magick);
14482  }
14483  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14484  map_info,pixel);
14485  display_image->taint=MagickFalse;
14486  /*
14487  Initialize graphic context.
14488  */
14489  windows->context.id=(Window) NULL;
14490  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14491  resource_info,&windows->context);
14492  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14493  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14494  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14495  class_hints->res_class[0]);
14496  manager_hints->flags=InputHint | StateHint;
14497  manager_hints->input=MagickFalse;
14498  manager_hints->initial_state=WithdrawnState;
14499  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14500  &windows->context);
14501  if (resource_info->debug != MagickFalse)
14502  (void) LogMagickEvent(X11Event,GetMagickModule(),
14503  "Window id: 0x%lx (context)",windows->context.id);
14504  context_values.background=pixel->background_color.pixel;
14505  context_values.font=font_info->fid;
14506  context_values.foreground=pixel->foreground_color.pixel;
14507  context_values.graphics_exposures=MagickFalse;
14508  context_mask=(MagickStatusType)
14509  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14510  if (pixel->annotate_context != (GC) NULL)
14511  (void) XFreeGC(display,pixel->annotate_context);
14512  pixel->annotate_context=XCreateGC(display,windows->context.id,
14513  context_mask,&context_values);
14514  if (pixel->annotate_context == (GC) NULL)
14515  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14516  display_image->filename);
14517  context_values.background=pixel->depth_color.pixel;
14518  if (pixel->widget_context != (GC) NULL)
14519  (void) XFreeGC(display,pixel->widget_context);
14520  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14521  &context_values);
14522  if (pixel->widget_context == (GC) NULL)
14523  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14524  display_image->filename);
14525  context_values.background=pixel->foreground_color.pixel;
14526  context_values.foreground=pixel->background_color.pixel;
14527  context_values.plane_mask=context_values.background ^
14528  context_values.foreground;
14529  if (pixel->highlight_context != (GC) NULL)
14530  (void) XFreeGC(display,pixel->highlight_context);
14531  pixel->highlight_context=XCreateGC(display,windows->context.id,
14532  (size_t) (context_mask | GCPlaneMask),&context_values);
14533  if (pixel->highlight_context == (GC) NULL)
14534  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14535  display_image->filename);
14536  (void) XDestroyWindow(display,windows->context.id);
14537  /*
14538  Initialize icon window.
14539  */
14540  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14541  icon_resources,&windows->icon);
14542  windows->icon.geometry=resource_info->icon_geometry;
14543  XBestIconSize(display,&windows->icon,display_image);
14544  windows->icon.attributes.colormap=XDefaultColormap(display,
14545  icon_visual->screen);
14546  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14547  manager_hints->flags=InputHint | StateHint;
14548  manager_hints->input=MagickFalse;
14549  manager_hints->initial_state=IconicState;
14550  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14551  &windows->icon);
14552  if (resource_info->debug != MagickFalse)
14553  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14554  windows->icon.id);
14555  /*
14556  Initialize graphic context for icon window.
14557  */
14558  if (icon_pixel->annotate_context != (GC) NULL)
14559  (void) XFreeGC(display,icon_pixel->annotate_context);
14560  context_values.background=icon_pixel->background_color.pixel;
14561  context_values.foreground=icon_pixel->foreground_color.pixel;
14562  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14563  (size_t) (GCBackground | GCForeground),&context_values);
14564  if (icon_pixel->annotate_context == (GC) NULL)
14565  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14566  display_image->filename);
14567  windows->icon.annotate_context=icon_pixel->annotate_context;
14568  /*
14569  Initialize Image window.
14570  */
14571  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14572  &windows->image);
14573  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14574  if (resource_info->use_shared_memory == MagickFalse)
14575  windows->image.shared_memory=MagickFalse;
14576  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14577  {
14578  char
14579  *title;
14580 
14581  title=InterpretImageProperties(resource_info->image_info,display_image,
14582  resource_info->title);
14583  (void) CloneString(&windows->image.name,title);
14584  (void) CloneString(&windows->image.icon_name,title);
14585  title=DestroyString(title);
14586  }
14587  else
14588  {
14589  char
14590  filename[MaxTextExtent],
14591  window_name[MaxTextExtent];
14592 
14593  /*
14594  Window name is the base of the filename.
14595  */
14596  GetPathComponent(display_image->magick_filename,TailPath,filename);
14597  if (display_image->scene == 0)
14598  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14599  MagickPackageName,filename);
14600  else
14601  (void) FormatLocaleString(window_name,MaxTextExtent,
14602  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14603  (double) display_image->scene,(double) GetImageListLength(
14604  display_image));
14605  (void) CloneString(&windows->image.name,window_name);
14606  (void) CloneString(&windows->image.icon_name,filename);
14607  }
14608  if (resource_info->immutable)
14609  windows->image.immutable=MagickTrue;
14610  windows->image.use_pixmap=resource_info->use_pixmap;
14611  windows->image.geometry=resource_info->image_geometry;
14612  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14613  XDisplayWidth(display,visual_info->screen),
14614  XDisplayHeight(display,visual_info->screen));
14615  geometry_info.width=display_image->columns;
14616  geometry_info.height=display_image->rows;
14617  geometry_info.x=0;
14618  geometry_info.y=0;
14619  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14620  &geometry_info.width,&geometry_info.height);
14621  windows->image.width=(unsigned int) geometry_info.width;
14622  windows->image.height=(unsigned int) geometry_info.height;
14623  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14624  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14625  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14626  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14627  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14628  resource_info,&windows->backdrop);
14629  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14630  {
14631  /*
14632  Initialize backdrop window.
14633  */
14634  windows->backdrop.x=0;
14635  windows->backdrop.y=0;
14636  (void) CloneString(&windows->backdrop.name,"Backdrop");
14637  windows->backdrop.flags=(size_t) (USSize | USPosition);
14638  windows->backdrop.width=(unsigned int)
14639  XDisplayWidth(display,visual_info->screen);
14640  windows->backdrop.height=(unsigned int)
14641  XDisplayHeight(display,visual_info->screen);
14642  windows->backdrop.border_width=0;
14643  windows->backdrop.immutable=MagickTrue;
14644  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14645  ButtonReleaseMask;
14646  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14647  StructureNotifyMask;
14648  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14649  manager_hints->icon_window=windows->icon.id;
14650  manager_hints->input=MagickTrue;
14651  manager_hints->initial_state=resource_info->iconic ? IconicState :
14652  NormalState;
14653  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14654  &windows->backdrop);
14655  if (resource_info->debug != MagickFalse)
14656  (void) LogMagickEvent(X11Event,GetMagickModule(),
14657  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14658  (void) XMapWindow(display,windows->backdrop.id);
14659  (void) XClearWindow(display,windows->backdrop.id);
14660  if (windows->image.id != (Window) NULL)
14661  {
14662  (void) XDestroyWindow(display,windows->image.id);
14663  windows->image.id=(Window) NULL;
14664  }
14665  /*
14666  Position image in the center the backdrop.
14667  */
14668  windows->image.flags|=USPosition;
14669  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14670  (windows->image.width/2);
14671  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14672  (windows->image.height/2);
14673  }
14674  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14675  manager_hints->icon_window=windows->icon.id;
14676  manager_hints->input=MagickTrue;
14677  manager_hints->initial_state=resource_info->iconic ? IconicState :
14678  NormalState;
14679  if (windows->group_leader.id != (Window) NULL)
14680  {
14681  /*
14682  Follow the leader.
14683  */
14684  manager_hints->flags|=WindowGroupHint;
14685  manager_hints->window_group=windows->group_leader.id;
14686  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14687  if (resource_info->debug != MagickFalse)
14688  (void) LogMagickEvent(X11Event,GetMagickModule(),
14689  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14690  }
14691  XMakeWindow(display,
14692  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14693  argv,argc,class_hints,manager_hints,&windows->image);
14694  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14695  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14696  if (windows->group_leader.id != (Window) NULL)
14697  (void) XSetTransientForHint(display,windows->image.id,
14698  windows->group_leader.id);
14699  if (resource_info->debug != MagickFalse)
14700  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14701  windows->image.id);
14702  /*
14703  Initialize Info widget.
14704  */
14705  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14706  &windows->info);
14707  (void) CloneString(&windows->info.name,"Info");
14708  (void) CloneString(&windows->info.icon_name,"Info");
14709  windows->info.border_width=1;
14710  windows->info.x=2;
14711  windows->info.y=2;
14712  windows->info.flags|=PPosition;
14713  windows->info.attributes.win_gravity=UnmapGravity;
14714  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14715  StructureNotifyMask;
14716  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14717  manager_hints->input=MagickFalse;
14718  manager_hints->initial_state=NormalState;
14719  manager_hints->window_group=windows->image.id;
14720  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14721  &windows->info);
14722  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14723  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14724  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14725  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14726  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14727  if (windows->image.mapped != MagickFalse)
14728  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14729  if (resource_info->debug != MagickFalse)
14730  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14731  windows->info.id);
14732  /*
14733  Initialize Command widget.
14734  */
14735  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14736  resource_info,&windows->command);
14737  windows->command.data=MagickMenus;
14738  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14739  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14740  resource_info->client_name);
14741  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14742  resource_name,"geometry",(char *) NULL);
14743  (void) CloneString(&windows->command.name,MagickTitle);
14744  windows->command.border_width=0;
14745  windows->command.flags|=PPosition;
14746  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14747  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14748  OwnerGrabButtonMask | StructureNotifyMask;
14749  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14750  manager_hints->input=MagickTrue;
14751  manager_hints->initial_state=NormalState;
14752  manager_hints->window_group=windows->image.id;
14753  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14754  &windows->command);
14755  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14756  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14757  HighlightHeight);
14758  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14759  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14760  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14761  if (windows->command.mapped != MagickFalse)
14762  (void) XMapRaised(display,windows->command.id);
14763  if (resource_info->debug != MagickFalse)
14764  (void) LogMagickEvent(X11Event,GetMagickModule(),
14765  "Window id: 0x%lx (command)",windows->command.id);
14766  /*
14767  Initialize Widget window.
14768  */
14769  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14770  resource_info,&windows->widget);
14771  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14772  resource_info->client_name);
14773  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14774  resource_name,"geometry",(char *) NULL);
14775  windows->widget.border_width=0;
14776  windows->widget.flags|=PPosition;
14777  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14778  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14779  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14780  StructureNotifyMask;
14781  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14782  manager_hints->input=MagickTrue;
14783  manager_hints->initial_state=NormalState;
14784  manager_hints->window_group=windows->image.id;
14785  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14786  &windows->widget);
14787  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14788  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14789  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14790  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14791  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14792  if (resource_info->debug != MagickFalse)
14793  (void) LogMagickEvent(X11Event,GetMagickModule(),
14794  "Window id: 0x%lx (widget)",windows->widget.id);
14795  /*
14796  Initialize popup window.
14797  */
14798  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14799  resource_info,&windows->popup);
14800  windows->popup.border_width=0;
14801  windows->popup.flags|=PPosition;
14802  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14803  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14804  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14805  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14806  manager_hints->input=MagickTrue;
14807  manager_hints->initial_state=NormalState;
14808  manager_hints->window_group=windows->image.id;
14809  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14810  &windows->popup);
14811  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14812  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14813  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14814  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14815  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14816  if (resource_info->debug != MagickFalse)
14817  (void) LogMagickEvent(X11Event,GetMagickModule(),
14818  "Window id: 0x%lx (pop up)",windows->popup.id);
14819  /*
14820  Initialize Magnify window and cursor.
14821  */
14822  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14823  resource_info,&windows->magnify);
14824  if (resource_info->use_shared_memory == MagickFalse)
14825  windows->magnify.shared_memory=MagickFalse;
14826  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14827  resource_info->client_name);
14828  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14829  resource_name,"geometry",(char *) NULL);
14830  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14831  resource_info->magnify);
14832  if (windows->magnify.cursor != (Cursor) NULL)
14833  (void) XFreeCursor(display,windows->magnify.cursor);
14834  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14835  map_info->colormap,resource_info->background_color,
14836  resource_info->foreground_color);
14837  if (windows->magnify.cursor == (Cursor) NULL)
14838  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14839  display_image->filename);
14840  windows->magnify.width=MagnifySize;
14841  windows->magnify.height=MagnifySize;
14842  windows->magnify.flags|=PPosition;
14843  windows->magnify.min_width=MagnifySize;
14844  windows->magnify.min_height=MagnifySize;
14845  windows->magnify.width_inc=MagnifySize;
14846  windows->magnify.height_inc=MagnifySize;
14847  windows->magnify.data=resource_info->magnify;
14848  windows->magnify.attributes.cursor=windows->magnify.cursor;
14849  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14850  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14851  StructureNotifyMask;
14852  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14853  manager_hints->input=MagickTrue;
14854  manager_hints->initial_state=NormalState;
14855  manager_hints->window_group=windows->image.id;
14856  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14857  &windows->magnify);
14858  if (resource_info->debug != MagickFalse)
14859  (void) LogMagickEvent(X11Event,GetMagickModule(),
14860  "Window id: 0x%lx (magnify)",windows->magnify.id);
14861  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14862  /*
14863  Initialize panning window.
14864  */
14865  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14866  resource_info,&windows->pan);
14867  (void) CloneString(&windows->pan.name,"Pan Icon");
14868  windows->pan.width=windows->icon.width;
14869  windows->pan.height=windows->icon.height;
14870  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14871  resource_info->client_name);
14872  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14873  resource_name,"geometry",(char *) NULL);
14874  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14875  &windows->pan.width,&windows->pan.height);
14876  windows->pan.flags|=PPosition;
14877  windows->pan.immutable=MagickTrue;
14878  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14879  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14880  StructureNotifyMask;
14881  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14882  manager_hints->input=MagickFalse;
14883  manager_hints->initial_state=NormalState;
14884  manager_hints->window_group=windows->image.id;
14885  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14886  &windows->pan);
14887  if (resource_info->debug != MagickFalse)
14888  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14889  windows->pan.id);
14890  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14891  if (windows->info.mapped != MagickFalse)
14892  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14893  if ((windows->image.mapped == MagickFalse) ||
14894  (windows->backdrop.id != (Window) NULL))
14895  (void) XMapWindow(display,windows->image.id);
14896  /*
14897  Set our progress monitor and warning handlers.
14898  */
14899  if (warning_handler == (WarningHandler) NULL)
14900  {
14901  warning_handler=resource_info->display_warnings ?
14902  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14903  warning_handler=resource_info->display_warnings ?
14904  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14905  }
14906  /*
14907  Initialize Image and Magnify X images.
14908  */
14909  windows->image.x=0;
14910  windows->image.y=0;
14911  windows->magnify.shape=MagickFalse;
14912  width=(unsigned int) display_image->columns;
14913  height=(unsigned int) display_image->rows;
14914  if ((display_image->columns != width) || (display_image->rows != height))
14915  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14916  display_image->filename);
14917  status=XMakeImage(display,resource_info,&windows->image,display_image,
14918  width,height);
14919  if (status == MagickFalse)
14920  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14921  display_image->filename);
14922  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14923  windows->magnify.width,windows->magnify.height);
14924  if (status == MagickFalse)
14925  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14926  display_image->filename);
14927  if (windows->magnify.mapped != MagickFalse)
14928  (void) XMapRaised(display,windows->magnify.id);
14929  if (windows->pan.mapped != MagickFalse)
14930  (void) XMapRaised(display,windows->pan.id);
14931  windows->image.window_changes.width=(int) display_image->columns;
14932  windows->image.window_changes.height=(int) display_image->rows;
14933  (void) XConfigureImage(display,resource_info,windows,display_image);
14934  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14935  (void) XSync(display,MagickFalse);
14936  /*
14937  Respond to events.
14938  */
14939  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14940  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14941  update_time=0;
14942  if (resource_info->update != MagickFalse)
14943  {
14944  MagickBooleanType
14945  status;
14946 
14947  /*
14948  Determine when file data was last modified.
14949  */
14950  status=GetPathAttributes(display_image->filename,&attributes);
14951  if (status != MagickFalse)
14952  update_time=attributes.st_mtime;
14953  }
14954  *state&=(~FormerImageState);
14955  *state&=(~MontageImageState);
14956  *state&=(~NextImageState);
14957  do
14958  {
14959  /*
14960  Handle a window event.
14961  */
14962  if (windows->image.mapped != MagickFalse)
14963  if ((display_image->delay != 0) || (resource_info->update != 0))
14964  {
14965  if (timer < GetMagickTime())
14966  {
14967  if (resource_info->update == MagickFalse)
14968  *state|=NextImageState | ExitState;
14969  else
14970  {
14971  MagickBooleanType
14972  status;
14973 
14974  /*
14975  Determine if image file was modified.
14976  */
14977  status=GetPathAttributes(display_image->filename,&attributes);
14978  if (status != MagickFalse)
14979  if (update_time != attributes.st_mtime)
14980  {
14981  /*
14982  Redisplay image.
14983  */
14984  (void) FormatLocaleString(
14985  resource_info->image_info->filename,MaxTextExtent,
14986  "%s:%s",display_image->magick,
14987  display_image->filename);
14988  nexus=ReadImage(resource_info->image_info,
14989  &display_image->exception);
14990  if (nexus != (Image *) NULL)
14991  *state|=NextImageState | ExitState;
14992  }
14993  delay=display_image->delay/MagickMax(
14994  display_image->ticks_per_second,1L);
14995  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14996  }
14997  }
14998  if (XEventsQueued(display,QueuedAfterFlush) == 0)
14999  {
15000  /*
15001  Do not block if delay > 0.
15002  */
15003  XDelay(display,SuspendTime << 2);
15004  continue;
15005  }
15006  }
15007  timestamp=GetMagickTime();
15008  (void) XNextEvent(display,&event);
15009  if (windows->image.stasis == MagickFalse)
15010  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15011  MagickTrue : MagickFalse;
15012  if (windows->magnify.stasis == MagickFalse)
15013  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15014  MagickTrue : MagickFalse;
15015  if (event.xany.window == windows->command.id)
15016  {
15017  /*
15018  Select a command from the Command widget.
15019  */
15020  id=XCommandWidget(display,windows,CommandMenu,&event);
15021  if (id < 0)
15022  continue;
15023  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15024  command_type=CommandMenus[id];
15025  if (id < MagickMenus)
15026  {
15027  /*
15028  Select a command from a pop-up menu.
15029  */
15030  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15031  command);
15032  if (entry < 0)
15033  continue;
15034  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15035  command_type=Commands[id][entry];
15036  }
15037  if (command_type != NullCommand)
15038  nexus=XMagickCommand(display,resource_info,windows,command_type,
15039  &display_image);
15040  continue;
15041  }
15042  switch (event.type)
15043  {
15044  case ButtonPress:
15045  {
15046  if (resource_info->debug != MagickFalse)
15047  (void) LogMagickEvent(X11Event,GetMagickModule(),
15048  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15049  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15050  if ((event.xbutton.button == Button3) &&
15051  (event.xbutton.state & Mod1Mask))
15052  {
15053  /*
15054  Convert Alt-Button3 to Button2.
15055  */
15056  event.xbutton.button=Button2;
15057  event.xbutton.state&=(~Mod1Mask);
15058  }
15059  if (event.xbutton.window == windows->backdrop.id)
15060  {
15061  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15062  event.xbutton.time);
15063  break;
15064  }
15065  if (event.xbutton.window == windows->image.id)
15066  {
15067  switch (event.xbutton.button)
15068  {
15069  case Button1:
15070  {
15071  if (resource_info->immutable)
15072  {
15073  /*
15074  Select a command from the Virtual menu.
15075  */
15076  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15077  command);
15078  if (entry >= 0)
15079  nexus=XMagickCommand(display,resource_info,windows,
15080  VirtualCommands[entry],&display_image);
15081  break;
15082  }
15083  /*
15084  Map/unmap Command widget.
15085  */
15086  if (windows->command.mapped != MagickFalse)
15087  (void) XWithdrawWindow(display,windows->command.id,
15088  windows->command.screen);
15089  else
15090  {
15091  (void) XCommandWidget(display,windows,CommandMenu,
15092  (XEvent *) NULL);
15093  (void) XMapRaised(display,windows->command.id);
15094  }
15095  break;
15096  }
15097  case Button2:
15098  {
15099  /*
15100  User pressed the image magnify button.
15101  */
15102  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15103  &display_image);
15104  XMagnifyImage(display,windows,&event);
15105  break;
15106  }
15107  case Button3:
15108  {
15109  if (resource_info->immutable)
15110  {
15111  /*
15112  Select a command from the Virtual menu.
15113  */
15114  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15115  command);
15116  if (entry >= 0)
15117  nexus=XMagickCommand(display,resource_info,windows,
15118  VirtualCommands[entry],&display_image);
15119  break;
15120  }
15121  if (display_image->montage != (char *) NULL)
15122  {
15123  /*
15124  Open or delete a tile from a visual image directory.
15125  */
15126  nexus=XTileImage(display,resource_info,windows,
15127  display_image,&event);
15128  if (nexus != (Image *) NULL)
15129  *state|=MontageImageState | NextImageState | ExitState;
15130  vid_info.x=(short int) windows->image.x;
15131  vid_info.y=(short int) windows->image.y;
15132  break;
15133  }
15134  /*
15135  Select a command from the Short Cuts menu.
15136  */
15137  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15138  command);
15139  if (entry >= 0)
15140  nexus=XMagickCommand(display,resource_info,windows,
15141  ShortCutsCommands[entry],&display_image);
15142  break;
15143  }
15144  case Button4:
15145  {
15146  /*
15147  Wheel up.
15148  */
15149  XTranslateImage(display,windows,*image,XK_Up);
15150  break;
15151  }
15152  case Button5:
15153  {
15154  /*
15155  Wheel down.
15156  */
15157  XTranslateImage(display,windows,*image,XK_Down);
15158  break;
15159  }
15160  default:
15161  break;
15162  }
15163  break;
15164  }
15165  if (event.xbutton.window == windows->magnify.id)
15166  {
15167  const char
15168  *const MagnifyMenu[] =
15169  {
15170  "2",
15171  "4",
15172  "5",
15173  "6",
15174  "7",
15175  "8",
15176  "9",
15177  "3",
15178  (char *) NULL,
15179  };
15180 
15181  int
15182  factor;
15183 
15184  static KeySym
15185  MagnifyCommands[] =
15186  {
15187  XK_2,
15188  XK_4,
15189  XK_5,
15190  XK_6,
15191  XK_7,
15192  XK_8,
15193  XK_9,
15194  XK_3
15195  };
15196 
15197  /*
15198  Select a magnify factor from the pop-up menu.
15199  */
15200  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15201  if (factor >= 0)
15202  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15203  break;
15204  }
15205  if (event.xbutton.window == windows->pan.id)
15206  {
15207  switch (event.xbutton.button)
15208  {
15209  case Button4:
15210  {
15211  /*
15212  Wheel up.
15213  */
15214  XTranslateImage(display,windows,*image,XK_Up);
15215  break;
15216  }
15217  case Button5:
15218  {
15219  /*
15220  Wheel down.
15221  */
15222  XTranslateImage(display,windows,*image,XK_Down);
15223  break;
15224  }
15225  default:
15226  {
15227  XPanImage(display,windows,&event);
15228  break;
15229  }
15230  }
15231  break;
15232  }
15233  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15234  1L);
15235  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15236  break;
15237  }
15238  case ButtonRelease:
15239  {
15240  if (resource_info->debug != MagickFalse)
15241  (void) LogMagickEvent(X11Event,GetMagickModule(),
15242  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15243  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15244  break;
15245  }
15246  case ClientMessage:
15247  {
15248  if (resource_info->debug != MagickFalse)
15249  (void) LogMagickEvent(X11Event,GetMagickModule(),
15250  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15251  event.xclient.message_type,event.xclient.format,(unsigned long)
15252  event.xclient.data.l[0]);
15253  if (event.xclient.message_type == windows->im_protocols)
15254  {
15255  if (*event.xclient.data.l == (long) windows->im_update_widget)
15256  {
15257  (void) CloneString(&windows->command.name,MagickTitle);
15258  windows->command.data=MagickMenus;
15259  (void) XCommandWidget(display,windows,CommandMenu,
15260  (XEvent *) NULL);
15261  break;
15262  }
15263  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15264  {
15265  /*
15266  Update graphic context and window colormap.
15267  */
15268  for (i=0; i < (int) number_windows; i++)
15269  {
15270  if (magick_windows[i]->id == windows->icon.id)
15271  continue;
15272  context_values.background=pixel->background_color.pixel;
15273  context_values.foreground=pixel->foreground_color.pixel;
15274  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15275  context_mask,&context_values);
15276  (void) XChangeGC(display,magick_windows[i]->widget_context,
15277  context_mask,&context_values);
15278  context_values.background=pixel->foreground_color.pixel;
15279  context_values.foreground=pixel->background_color.pixel;
15280  context_values.plane_mask=context_values.background ^
15281  context_values.foreground;
15282  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15283  (size_t) (context_mask | GCPlaneMask),
15284  &context_values);
15285  magick_windows[i]->attributes.background_pixel=
15286  pixel->background_color.pixel;
15287  magick_windows[i]->attributes.border_pixel=
15288  pixel->border_color.pixel;
15289  magick_windows[i]->attributes.colormap=map_info->colormap;
15290  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15291  (unsigned long) magick_windows[i]->mask,
15292  &magick_windows[i]->attributes);
15293  }
15294  if (windows->pan.mapped != MagickFalse)
15295  {
15296  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15297  windows->pan.pixmap);
15298  (void) XClearWindow(display,windows->pan.id);
15299  XDrawPanRectangle(display,windows);
15300  }
15301  if (windows->backdrop.id != (Window) NULL)
15302  (void) XInstallColormap(display,map_info->colormap);
15303  break;
15304  }
15305  if (*event.xclient.data.l == (long) windows->im_former_image)
15306  {
15307  *state|=FormerImageState | ExitState;
15308  break;
15309  }
15310  if (*event.xclient.data.l == (long) windows->im_next_image)
15311  {
15312  *state|=NextImageState | ExitState;
15313  break;
15314  }
15315  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15316  {
15317  *state|=RetainColorsState;
15318  break;
15319  }
15320  if (*event.xclient.data.l == (long) windows->im_exit)
15321  {
15322  *state|=ExitState;
15323  break;
15324  }
15325  break;
15326  }
15327  if (event.xclient.message_type == windows->dnd_protocols)
15328  {
15329  Atom
15330  selection,
15331  type;
15332 
15333  int
15334  format,
15335  status;
15336 
15337  unsigned char
15338  *data;
15339 
15340  unsigned long
15341  after,
15342  length;
15343 
15344  /*
15345  Display image named by the Drag-and-Drop selection.
15346  */
15347  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15348  break;
15349  selection=XInternAtom(display,"DndSelection",MagickFalse);
15350  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15351  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15352  &length,&after,&data);
15353  if ((status != Success) || (length == 0))
15354  break;
15355  if (*event.xclient.data.l == 2)
15356  {
15357  /*
15358  Offix DND.
15359  */
15360  (void) CopyMagickString(resource_info->image_info->filename,
15361  (char *) data,MaxTextExtent);
15362  }
15363  else
15364  {
15365  /*
15366  XDND.
15367  */
15368  if (strncmp((char *) data, "file:", 5) != 0)
15369  {
15370  (void) XFree((void *) data);
15371  break;
15372  }
15373  (void) CopyMagickString(resource_info->image_info->filename,
15374  ((char *) data)+5,MaxTextExtent);
15375  }
15376  nexus=ReadImage(resource_info->image_info,
15377  &display_image->exception);
15378  CatchException(&display_image->exception);
15379  if (nexus != (Image *) NULL)
15380  *state|=NextImageState | ExitState;
15381  (void) XFree((void *) data);
15382  break;
15383  }
15384  /*
15385  If client window delete message, exit.
15386  */
15387  if (event.xclient.message_type != windows->wm_protocols)
15388  break;
15389  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15390  break;
15391  (void) XWithdrawWindow(display,event.xclient.window,
15392  visual_info->screen);
15393  if (event.xclient.window == windows->image.id)
15394  {
15395  *state|=ExitState;
15396  break;
15397  }
15398  if (event.xclient.window == windows->pan.id)
15399  {
15400  /*
15401  Restore original image size when pan window is deleted.
15402  */
15403  windows->image.window_changes.width=windows->image.ximage->width;
15404  windows->image.window_changes.height=windows->image.ximage->height;
15405  (void) XConfigureImage(display,resource_info,windows,
15406  display_image);
15407  }
15408  break;
15409  }
15410  case ConfigureNotify:
15411  {
15412  if (resource_info->debug != MagickFalse)
15413  (void) LogMagickEvent(X11Event,GetMagickModule(),
15414  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15415  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15416  event.xconfigure.y,event.xconfigure.send_event);
15417  if (event.xconfigure.window == windows->image.id)
15418  {
15419  /*
15420  Image window has a new configuration.
15421  */
15422  if (event.xconfigure.send_event != 0)
15423  {
15424  XWindowChanges
15425  window_changes;
15426 
15427  /*
15428  Position the transient windows relative of the Image window.
15429  */
15430  if (windows->command.geometry == (char *) NULL)
15431  if (windows->command.mapped == MagickFalse)
15432  {
15433  windows->command.x=event.xconfigure.x-
15434  windows->command.width-25;
15435  windows->command.y=event.xconfigure.y;
15436  XConstrainWindowPosition(display,&windows->command);
15437  window_changes.x=windows->command.x;
15438  window_changes.y=windows->command.y;
15439  (void) XReconfigureWMWindow(display,windows->command.id,
15440  windows->command.screen,(unsigned int) (CWX | CWY),
15441  &window_changes);
15442  }
15443  if (windows->widget.geometry == (char *) NULL)
15444  if (windows->widget.mapped == MagickFalse)
15445  {
15446  windows->widget.x=event.xconfigure.x+
15447  event.xconfigure.width/10;
15448  windows->widget.y=event.xconfigure.y+
15449  event.xconfigure.height/10;
15450  XConstrainWindowPosition(display,&windows->widget);
15451  window_changes.x=windows->widget.x;
15452  window_changes.y=windows->widget.y;
15453  (void) XReconfigureWMWindow(display,windows->widget.id,
15454  windows->widget.screen,(unsigned int) (CWX | CWY),
15455  &window_changes);
15456  }
15457  if (windows->magnify.geometry == (char *) NULL)
15458  if (windows->magnify.mapped == MagickFalse)
15459  {
15460  windows->magnify.x=event.xconfigure.x+
15461  event.xconfigure.width+25;
15462  windows->magnify.y=event.xconfigure.y;
15463  XConstrainWindowPosition(display,&windows->magnify);
15464  window_changes.x=windows->magnify.x;
15465  window_changes.y=windows->magnify.y;
15466  (void) XReconfigureWMWindow(display,windows->magnify.id,
15467  windows->magnify.screen,(unsigned int) (CWX | CWY),
15468  &window_changes);
15469  }
15470  if (windows->pan.geometry == (char *) NULL)
15471  if (windows->pan.mapped == MagickFalse)
15472  {
15473  windows->pan.x=event.xconfigure.x+
15474  event.xconfigure.width+25;
15475  windows->pan.y=event.xconfigure.y+
15476  windows->magnify.height+50;
15477  XConstrainWindowPosition(display,&windows->pan);
15478  window_changes.x=windows->pan.x;
15479  window_changes.y=windows->pan.y;
15480  (void) XReconfigureWMWindow(display,windows->pan.id,
15481  windows->pan.screen,(unsigned int) (CWX | CWY),
15482  &window_changes);
15483  }
15484  }
15485  if ((event.xconfigure.width == (int) windows->image.width) &&
15486  (event.xconfigure.height == (int) windows->image.height))
15487  break;
15488  windows->image.width=(unsigned int) event.xconfigure.width;
15489  windows->image.height=(unsigned int) event.xconfigure.height;
15490  windows->image.x=0;
15491  windows->image.y=0;
15492  if (display_image->montage != (char *) NULL)
15493  {
15494  windows->image.x=vid_info.x;
15495  windows->image.y=vid_info.y;
15496  }
15497  if ((windows->image.mapped != MagickFalse) &&
15498  (windows->image.stasis != MagickFalse))
15499  {
15500  /*
15501  Update image window configuration.
15502  */
15503  windows->image.window_changes.width=event.xconfigure.width;
15504  windows->image.window_changes.height=event.xconfigure.height;
15505  (void) XConfigureImage(display,resource_info,windows,
15506  display_image);
15507  }
15508  /*
15509  Update pan window configuration.
15510  */
15511  if ((event.xconfigure.width < windows->image.ximage->width) ||
15512  (event.xconfigure.height < windows->image.ximage->height))
15513  {
15514  (void) XMapRaised(display,windows->pan.id);
15515  XDrawPanRectangle(display,windows);
15516  }
15517  else
15518  if (windows->pan.mapped != MagickFalse)
15519  (void) XWithdrawWindow(display,windows->pan.id,
15520  windows->pan.screen);
15521  break;
15522  }
15523  if (event.xconfigure.window == windows->magnify.id)
15524  {
15525  unsigned int
15526  magnify;
15527 
15528  /*
15529  Magnify window has a new configuration.
15530  */
15531  windows->magnify.width=(unsigned int) event.xconfigure.width;
15532  windows->magnify.height=(unsigned int) event.xconfigure.height;
15533  if (windows->magnify.mapped == MagickFalse)
15534  break;
15535  magnify=1;
15536  while ((int) magnify <= event.xconfigure.width)
15537  magnify<<=1;
15538  while ((int) magnify <= event.xconfigure.height)
15539  magnify<<=1;
15540  magnify>>=1;
15541  if (((int) magnify != event.xconfigure.width) ||
15542  ((int) magnify != event.xconfigure.height))
15543  {
15544  window_changes.width=(int) magnify;
15545  window_changes.height=(int) magnify;
15546  (void) XReconfigureWMWindow(display,windows->magnify.id,
15547  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15548  &window_changes);
15549  break;
15550  }
15551  if ((windows->magnify.mapped != MagickFalse) &&
15552  (windows->magnify.stasis != MagickFalse))
15553  {
15554  status=XMakeImage(display,resource_info,&windows->magnify,
15555  display_image,windows->magnify.width,windows->magnify.height);
15556  XMakeMagnifyImage(display,windows);
15557  }
15558  break;
15559  }
15560  if ((windows->magnify.mapped != MagickFalse) &&
15561  (event.xconfigure.window == windows->pan.id))
15562  {
15563  /*
15564  Pan icon window has a new configuration.
15565  */
15566  if (event.xconfigure.send_event != 0)
15567  {
15568  windows->pan.x=event.xconfigure.x;
15569  windows->pan.y=event.xconfigure.y;
15570  }
15571  windows->pan.width=(unsigned int) event.xconfigure.width;
15572  windows->pan.height=(unsigned int) event.xconfigure.height;
15573  break;
15574  }
15575  if (event.xconfigure.window == windows->icon.id)
15576  {
15577  /*
15578  Icon window has a new configuration.
15579  */
15580  windows->icon.width=(unsigned int) event.xconfigure.width;
15581  windows->icon.height=(unsigned int) event.xconfigure.height;
15582  break;
15583  }
15584  break;
15585  }
15586  case DestroyNotify:
15587  {
15588  /*
15589  Group leader has exited.
15590  */
15591  if (resource_info->debug != MagickFalse)
15592  (void) LogMagickEvent(X11Event,GetMagickModule(),
15593  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15594  if (event.xdestroywindow.window == windows->group_leader.id)
15595  {
15596  *state|=ExitState;
15597  break;
15598  }
15599  break;
15600  }
15601  case EnterNotify:
15602  {
15603  /*
15604  Selectively install colormap.
15605  */
15606  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15607  if (event.xcrossing.mode != NotifyUngrab)
15608  XInstallColormap(display,map_info->colormap);
15609  break;
15610  }
15611  case Expose:
15612  {
15613  if (resource_info->debug != MagickFalse)
15614  (void) LogMagickEvent(X11Event,GetMagickModule(),
15615  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15616  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15617  event.xexpose.y);
15618  /*
15619  Refresh windows that are now exposed.
15620  */
15621  if ((event.xexpose.window == windows->image.id) &&
15622  (windows->image.mapped != MagickFalse))
15623  {
15624  XRefreshWindow(display,&windows->image,&event);
15625  delay=display_image->delay/MagickMax(
15626  display_image->ticks_per_second,1L);
15627  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15628  break;
15629  }
15630  if ((event.xexpose.window == windows->magnify.id) &&
15631  (windows->magnify.mapped != MagickFalse))
15632  {
15633  XMakeMagnifyImage(display,windows);
15634  break;
15635  }
15636  if (event.xexpose.window == windows->pan.id)
15637  {
15638  XDrawPanRectangle(display,windows);
15639  break;
15640  }
15641  if (event.xexpose.window == windows->icon.id)
15642  {
15643  XRefreshWindow(display,&windows->icon,&event);
15644  break;
15645  }
15646  break;
15647  }
15648  case KeyPress:
15649  {
15650  int
15651  length;
15652 
15653  /*
15654  Respond to a user key press.
15655  */
15656  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15657  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15658  *(command+length)='\0';
15659  if (resource_info->debug != MagickFalse)
15660  (void) LogMagickEvent(X11Event,GetMagickModule(),
15661  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15662  key_symbol,command);
15663  if (event.xkey.window == windows->image.id)
15664  {
15665  command_type=XImageWindowCommand(display,resource_info,windows,
15666  event.xkey.state,key_symbol,&display_image);
15667  if (command_type != NullCommand)
15668  nexus=XMagickCommand(display,resource_info,windows,command_type,
15669  &display_image);
15670  }
15671  if (event.xkey.window == windows->magnify.id)
15672  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15673  if (event.xkey.window == windows->pan.id)
15674  {
15675  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15676  (void) XWithdrawWindow(display,windows->pan.id,
15677  windows->pan.screen);
15678  else
15679  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15680  XTextViewHelp(display,resource_info,windows,MagickFalse,
15681  "Help Viewer - Image Pan",ImagePanHelp);
15682  else
15683  XTranslateImage(display,windows,*image,key_symbol);
15684  }
15685  delay=display_image->delay/MagickMax(
15686  display_image->ticks_per_second,1L);
15687  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15688  break;
15689  }
15690  case KeyRelease:
15691  {
15692  /*
15693  Respond to a user key release.
15694  */
15695  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15696  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15697  if (resource_info->debug != MagickFalse)
15698  (void) LogMagickEvent(X11Event,GetMagickModule(),
15699  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15700  break;
15701  }
15702  case LeaveNotify:
15703  {
15704  /*
15705  Selectively uninstall colormap.
15706  */
15707  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15708  if (event.xcrossing.mode != NotifyUngrab)
15709  XUninstallColormap(display,map_info->colormap);
15710  break;
15711  }
15712  case MapNotify:
15713  {
15714  if (resource_info->debug != MagickFalse)
15715  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15716  event.xmap.window);
15717  if (event.xmap.window == windows->backdrop.id)
15718  {
15719  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15720  CurrentTime);
15721  windows->backdrop.mapped=MagickTrue;
15722  break;
15723  }
15724  if (event.xmap.window == windows->image.id)
15725  {
15726  if (windows->backdrop.id != (Window) NULL)
15727  (void) XInstallColormap(display,map_info->colormap);
15728  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15729  {
15730  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15731  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15732  }
15733  if (((int) windows->image.width < windows->image.ximage->width) ||
15734  ((int) windows->image.height < windows->image.ximage->height))
15735  (void) XMapRaised(display,windows->pan.id);
15736  windows->image.mapped=MagickTrue;
15737  break;
15738  }
15739  if (event.xmap.window == windows->magnify.id)
15740  {
15741  XMakeMagnifyImage(display,windows);
15742  windows->magnify.mapped=MagickTrue;
15743  (void) XWithdrawWindow(display,windows->info.id,
15744  windows->info.screen);
15745  break;
15746  }
15747  if (event.xmap.window == windows->pan.id)
15748  {
15749  XMakePanImage(display,resource_info,windows,display_image);
15750  windows->pan.mapped=MagickTrue;
15751  break;
15752  }
15753  if (event.xmap.window == windows->info.id)
15754  {
15755  windows->info.mapped=MagickTrue;
15756  break;
15757  }
15758  if (event.xmap.window == windows->icon.id)
15759  {
15760  MagickBooleanType
15761  taint;
15762 
15763  /*
15764  Create an icon image.
15765  */
15766  taint=display_image->taint;
15767  XMakeStandardColormap(display,icon_visual,icon_resources,
15768  display_image,icon_map,icon_pixel);
15769  (void) XMakeImage(display,icon_resources,&windows->icon,
15770  display_image,windows->icon.width,windows->icon.height);
15771  display_image->taint=taint;
15772  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15773  windows->icon.pixmap);
15774  (void) XClearWindow(display,windows->icon.id);
15775  (void) XWithdrawWindow(display,windows->info.id,
15776  windows->info.screen);
15777  windows->icon.mapped=MagickTrue;
15778  break;
15779  }
15780  if (event.xmap.window == windows->command.id)
15781  {
15782  windows->command.mapped=MagickTrue;
15783  break;
15784  }
15785  if (event.xmap.window == windows->popup.id)
15786  {
15787  windows->popup.mapped=MagickTrue;
15788  break;
15789  }
15790  if (event.xmap.window == windows->widget.id)
15791  {
15792  windows->widget.mapped=MagickTrue;
15793  break;
15794  }
15795  break;
15796  }
15797  case MappingNotify:
15798  {
15799  (void) XRefreshKeyboardMapping(&event.xmapping);
15800  break;
15801  }
15802  case NoExpose:
15803  break;
15804  case PropertyNotify:
15805  {
15806  Atom
15807  type;
15808 
15809  int
15810  format,
15811  status;
15812 
15813  unsigned char
15814  *data;
15815 
15816  unsigned long
15817  after,
15818  length;
15819 
15820  if (resource_info->debug != MagickFalse)
15821  (void) LogMagickEvent(X11Event,GetMagickModule(),
15822  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15823  event.xproperty.atom,event.xproperty.state);
15824  if (event.xproperty.atom != windows->im_remote_command)
15825  break;
15826  /*
15827  Display image named by the remote command protocol.
15828  */
15829  status=XGetWindowProperty(display,event.xproperty.window,
15830  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15831  AnyPropertyType,&type,&format,&length,&after,&data);
15832  if ((status != Success) || (length == 0))
15833  break;
15834  if (LocaleCompare((char *) data,"-quit") == 0)
15835  {
15836  XClientMessage(display,windows->image.id,windows->im_protocols,
15837  windows->im_exit,CurrentTime);
15838  (void) XFree((void *) data);
15839  break;
15840  }
15841  (void) CopyMagickString(resource_info->image_info->filename,
15842  (char *) data,MaxTextExtent);
15843  (void) XFree((void *) data);
15844  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15845  CatchException(&display_image->exception);
15846  if (nexus != (Image *) NULL)
15847  *state|=NextImageState | ExitState;
15848  break;
15849  }
15850  case ReparentNotify:
15851  {
15852  if (resource_info->debug != MagickFalse)
15853  (void) LogMagickEvent(X11Event,GetMagickModule(),
15854  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15855  event.xreparent.window);
15856  break;
15857  }
15858  case UnmapNotify:
15859  {
15860  if (resource_info->debug != MagickFalse)
15861  (void) LogMagickEvent(X11Event,GetMagickModule(),
15862  "Unmap Notify: 0x%lx",event.xunmap.window);
15863  if (event.xunmap.window == windows->backdrop.id)
15864  {
15865  windows->backdrop.mapped=MagickFalse;
15866  break;
15867  }
15868  if (event.xunmap.window == windows->image.id)
15869  {
15870  windows->image.mapped=MagickFalse;
15871  break;
15872  }
15873  if (event.xunmap.window == windows->magnify.id)
15874  {
15875  windows->magnify.mapped=MagickFalse;
15876  break;
15877  }
15878  if (event.xunmap.window == windows->pan.id)
15879  {
15880  windows->pan.mapped=MagickFalse;
15881  break;
15882  }
15883  if (event.xunmap.window == windows->info.id)
15884  {
15885  windows->info.mapped=MagickFalse;
15886  break;
15887  }
15888  if (event.xunmap.window == windows->icon.id)
15889  {
15890  if (map_info->colormap == icon_map->colormap)
15891  XConfigureImageColormap(display,resource_info,windows,
15892  display_image);
15893  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15894  icon_pixel);
15895  windows->icon.mapped=MagickFalse;
15896  break;
15897  }
15898  if (event.xunmap.window == windows->command.id)
15899  {
15900  windows->command.mapped=MagickFalse;
15901  break;
15902  }
15903  if (event.xunmap.window == windows->popup.id)
15904  {
15905  if (windows->backdrop.id != (Window) NULL)
15906  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15907  CurrentTime);
15908  windows->popup.mapped=MagickFalse;
15909  break;
15910  }
15911  if (event.xunmap.window == windows->widget.id)
15912  {
15913  if (windows->backdrop.id != (Window) NULL)
15914  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15915  CurrentTime);
15916  windows->widget.mapped=MagickFalse;
15917  break;
15918  }
15919  break;
15920  }
15921  default:
15922  {
15923  if (resource_info->debug != MagickFalse)
15924  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15925  event.type);
15926  break;
15927  }
15928  }
15929  } while (!(*state & ExitState));
15930  if ((*state & ExitState) == 0)
15931  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15932  &display_image);
15933  else
15934  if (resource_info->confirm_edit != MagickFalse)
15935  {
15936  /*
15937  Query user if image has changed.
15938  */
15939  if ((resource_info->immutable == MagickFalse) &&
15940  (display_image->taint != MagickFalse))
15941  {
15942  int
15943  status;
15944 
15945  status=XConfirmWidget(display,windows,"Your image changed.",
15946  "Do you want to save it");
15947  if (status == 0)
15948  *state&=(~ExitState);
15949  else
15950  if (status > 0)
15951  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15952  &display_image);
15953  }
15954  }
15955  if ((windows->visual_info->klass == GrayScale) ||
15956  (windows->visual_info->klass == PseudoColor) ||
15957  (windows->visual_info->klass == DirectColor))
15958  {
15959  /*
15960  Withdraw pan and Magnify window.
15961  */
15962  if (windows->info.mapped != MagickFalse)
15963  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15964  if (windows->magnify.mapped != MagickFalse)
15965  (void) XWithdrawWindow(display,windows->magnify.id,
15966  windows->magnify.screen);
15967  if (windows->command.mapped != MagickFalse)
15968  (void) XWithdrawWindow(display,windows->command.id,
15969  windows->command.screen);
15970  }
15971  if (windows->pan.mapped != MagickFalse)
15972  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15973  if (resource_info->backdrop == MagickFalse)
15974  if (windows->backdrop.mapped)
15975  {
15976  (void) XWithdrawWindow(display,windows->backdrop.id,
15977  windows->backdrop.screen);
15978  (void) XDestroyWindow(display,windows->backdrop.id);
15979  windows->backdrop.id=(Window) NULL;
15980  (void) XWithdrawWindow(display,windows->image.id,
15981  windows->image.screen);
15982  (void) XDestroyWindow(display,windows->image.id);
15983  windows->image.id=(Window) NULL;
15984  }
15985  XSetCursorState(display,windows,MagickTrue);
15986  XCheckRefreshWindows(display,windows);
15987  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15988  *state&=(~ExitState);
15989  if (*state & ExitState)
15990  {
15991  /*
15992  Free Standard Colormap.
15993  */
15994  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15995  if (resource_info->map_type == (char *) NULL)
15996  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15997  /*
15998  Free X resources.
15999  */
16000  if (resource_info->copy_image != (Image *) NULL)
16001  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16002  DestroyXResources();
16003  }
16004  (void) XSync(display,MagickFalse);
16005  /*
16006  Restore our progress monitor and warning handlers.
16007  */
16008  (void) SetErrorHandler(warning_handler);
16009  (void) SetWarningHandler(warning_handler);
16010  /*
16011  Change to home directory.
16012  */
16013  directory=getcwd(working_directory,MaxTextExtent);
16014  (void) directory;
16015  {
16016  int
16017  status;
16018 
16019  if (*resource_info->home_directory == '\0')
16020  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16021  status=chdir(resource_info->home_directory);
16022  if (status == -1)
16023  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16024  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16025  }
16026  *image=display_image;
16027  return(nexus);
16028 }
16029 #else
16030 
16031 /*
16032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16033 % %
16034 % %
16035 % %
16036 + D i s p l a y I m a g e s %
16037 % %
16038 % %
16039 % %
16040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16041 %
16042 % DisplayImages() displays an image sequence to any X window screen. It
16043 % returns a value other than 0 if successful. Check the exception member
16044 % of image to determine the reason for any failure.
16045 %
16046 % The format of the DisplayImages method is:
16047 %
16048 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16049 % Image *images)
16050 %
16051 % A description of each parameter follows:
16052 %
16053 % o image_info: the image info.
16054 %
16055 % o image: the image.
16056 %
16057 */
16058 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16059  Image *image)
16060 {
16061  assert(image_info != (const ImageInfo *) NULL);
16062  assert(image_info->signature == MagickCoreSignature);
16063  assert(image != (Image *) NULL);
16064  assert(image->signature == MagickCoreSignature);
16065  if (IsEventLogging() != MagickFalse)
16066  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16067  (void) image_info;
16068  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16069  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16070  image->filename);
16071  return(MagickFalse);
16072 }
16073 
16074 /*
16075 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16076 % %
16077 % %
16078 % %
16079 + R e m o t e D i s p l a y C o m m a n d %
16080 % %
16081 % %
16082 % %
16083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16084 %
16085 % RemoteDisplayCommand() encourages a remote display program to display the
16086 % specified image filename.
16087 %
16088 % The format of the RemoteDisplayCommand method is:
16089 %
16090 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16091 % const char *window,const char *filename,ExceptionInfo *exception)
16092 %
16093 % A description of each parameter follows:
16094 %
16095 % o image_info: the image info.
16096 %
16097 % o window: Specifies the name or id of an X window.
16098 %
16099 % o filename: the name of the image filename to display.
16100 %
16101 % o exception: return any errors or warnings in this structure.
16102 %
16103 */
16104 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16105  const char *window,const char *filename,ExceptionInfo *exception)
16106 {
16107  assert(image_info != (const ImageInfo *) NULL);
16108  assert(image_info->signature == MagickCoreSignature);
16109  assert(filename != (char *) NULL);
16110  if (IsEventLogging() != MagickFalse)
16111  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16112  (void) window;
16113  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16114  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16115  return(MagickFalse);
16116 }
16117 #endif
Definition: image.h:152