MagickCore  6.9.13-44
Convert, Edit, Or Compose Bitmap Images
display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % DDDD IIIII SSSSS PPPP L AAA Y Y %
7 % D D I SS P P L A A Y Y %
8 % D D I SSS PPPP L AAAAA Y %
9 % D D I SS P L A A Y %
10 % DDDD IIIII SSSSS P LLLLL A A Y %
11 % %
12 % %
13 % MagickCore Methods to Interactively Display and Edit an Image %
14 % %
15 % Software Design %
16 % Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/license/ %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 
39 /*
40  Include declarations.
41 */
42 #include "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 the 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 Transparent. 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 the 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 } DisplayCommand;
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 DisplayCommand
1536  XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1537  const MagickStatusType,KeySym,Image **);
1538 
1539 static Image
1540  *XMagickCommand(Display *,XResourceInfo *,XWindows *,const DisplayCommand,
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 DisplayCommand,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  size_t
1615  state;
1616 
1617  ssize_t
1618  i;
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  *p,
1807  text[MaxTextExtent];
1808 
1809  const char
1810  *ColorMenu[MaxNumberPens+1];
1811 
1812  Cursor
1813  cursor;
1814 
1815  GC
1816  annotate_context;
1817 
1818  int
1819  id,
1820  pen_number,
1821  status,
1822  x,
1823  y;
1824 
1825  KeySym
1826  key_symbol;
1827 
1828  size_t
1829  state;
1830 
1831  ssize_t
1832  i;
1833 
1834  unsigned int
1835  height,
1836  width;
1837 
1838  XAnnotateInfo
1839  *annotate_info,
1840  *previous_info;
1841 
1842  XColor
1843  color;
1844 
1845  XFontStruct
1846  *font_info;
1847 
1848  XEvent
1849  event,
1850  text_event;
1851 
1852  /*
1853  Map Command widget.
1854  */
1855  (void) CloneString(&windows->command.name,"Annotate");
1856  windows->command.data=4;
1857  (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1858  (void) XMapRaised(display,windows->command.id);
1859  XClientMessage(display,windows->image.id,windows->im_protocols,
1860  windows->im_update_widget,CurrentTime);
1861  /*
1862  Track pointer until button 1 is pressed.
1863  */
1864  XQueryPosition(display,windows->image.id,&x,&y);
1865  (void) XSelectInput(display,windows->image.id,
1866  windows->image.attributes.event_mask | PointerMotionMask);
1867  cursor=XCreateFontCursor(display,XC_left_side);
1868  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1869  state=DefaultState;
1870  do
1871  {
1872  if (windows->info.mapped != MagickFalse)
1873  {
1874  /*
1875  Display pointer position.
1876  */
1877  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1878  x+windows->image.x,y+windows->image.y);
1879  XInfoWidget(display,windows,text);
1880  }
1881  /*
1882  Wait for next event.
1883  */
1884  XScreenEvent(display,windows,&event);
1885  if (event.xany.window == windows->command.id)
1886  {
1887  /*
1888  Select a command from the Command widget.
1889  */
1890  id=XCommandWidget(display,windows,AnnotateMenu,&event);
1891  (void) XCheckDefineCursor(display,windows->image.id,cursor);
1892  if (id < 0)
1893  continue;
1894  switch (AnnotateCommands[id])
1895  {
1896  case AnnotateNameCommand:
1897  {
1898  const char
1899  *FontMenu[MaxNumberFonts];
1900 
1901  int
1902  font_number;
1903 
1904  /*
1905  Initialize menu selections.
1906  */
1907  for (i=0; i < MaxNumberFonts; i++)
1908  FontMenu[i]=resource_info->font_name[i];
1909  FontMenu[MaxNumberFonts-2]="Browser...";
1910  FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1911  /*
1912  Select a font name from the pop-up menu.
1913  */
1914  font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1915  (const char **) FontMenu,command);
1916  if (font_number < 0)
1917  break;
1918  if (font_number == (MaxNumberFonts-2))
1919  {
1920  static char
1921  font_name[MaxTextExtent] = "fixed";
1922 
1923  /*
1924  Select a font name from a browser.
1925  */
1926  resource_info->font_name[font_number]=font_name;
1927  XFontBrowserWidget(display,windows,"Select",font_name);
1928  if (*font_name == '\0')
1929  break;
1930  }
1931  /*
1932  Initialize font info.
1933  */
1934  font_info=XLoadQueryFont(display,resource_info->font_name[
1935  font_number]);
1936  if (font_info == (XFontStruct *) NULL)
1937  {
1938  XNoticeWidget(display,windows,"Unable to load font:",
1939  resource_info->font_name[font_number]);
1940  break;
1941  }
1942  font_id=(unsigned int) font_number;
1943  (void) XFreeFont(display,font_info);
1944  break;
1945  }
1946  case AnnotateFontColorCommand:
1947  {
1948  /*
1949  Initialize menu selections.
1950  */
1951  for (i=0; i < (int) (MaxNumberPens-2); i++)
1952  ColorMenu[i]=resource_info->pen_colors[i];
1953  ColorMenu[MaxNumberPens-2]="transparent";
1954  ColorMenu[MaxNumberPens-1]="Browser...";
1955  ColorMenu[MaxNumberPens]=(const char *) NULL;
1956  /*
1957  Select a pen color from the pop-up menu.
1958  */
1959  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1960  (const char **) ColorMenu,command);
1961  if (pen_number < 0)
1962  break;
1963  transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1964  MagickFalse;
1965  if (transparent_pen != MagickFalse)
1966  break;
1967  if (pen_number == (MaxNumberPens-1))
1968  {
1969  static char
1970  color_name[MaxTextExtent] = "gray";
1971 
1972  /*
1973  Select a pen color from a dialog.
1974  */
1975  resource_info->pen_colors[pen_number]=color_name;
1976  XColorBrowserWidget(display,windows,"Select",color_name);
1977  if (*color_name == '\0')
1978  break;
1979  }
1980  /*
1981  Set pen color.
1982  */
1983  (void) XParseColor(display,windows->map_info->colormap,
1984  resource_info->pen_colors[pen_number],&color);
1985  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
1986  (unsigned int) MaxColors,&color);
1987  windows->pixel_info->pen_colors[pen_number]=color;
1988  pen_id=(unsigned int) pen_number;
1989  break;
1990  }
1991  case AnnotateBackgroundColorCommand:
1992  {
1993  /*
1994  Initialize menu selections.
1995  */
1996  for (i=0; i < (int) (MaxNumberPens-2); i++)
1997  ColorMenu[i]=resource_info->pen_colors[i];
1998  ColorMenu[MaxNumberPens-2]="transparent";
1999  ColorMenu[MaxNumberPens-1]="Browser...";
2000  ColorMenu[MaxNumberPens]=(const char *) NULL;
2001  /*
2002  Select a pen color from the pop-up menu.
2003  */
2004  pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2005  (const char **) ColorMenu,command);
2006  if (pen_number < 0)
2007  break;
2008  transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2009  MagickFalse;
2010  if (transparent_box != MagickFalse)
2011  break;
2012  if (pen_number == (MaxNumberPens-1))
2013  {
2014  static char
2015  color_name[MaxTextExtent] = "gray";
2016 
2017  /*
2018  Select a pen color from a dialog.
2019  */
2020  resource_info->pen_colors[pen_number]=color_name;
2021  XColorBrowserWidget(display,windows,"Select",color_name);
2022  if (*color_name == '\0')
2023  break;
2024  }
2025  /*
2026  Set pen color.
2027  */
2028  (void) XParseColor(display,windows->map_info->colormap,
2029  resource_info->pen_colors[pen_number],&color);
2030  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2031  (unsigned int) MaxColors,&color);
2032  windows->pixel_info->pen_colors[pen_number]=color;
2033  box_id=(unsigned int) pen_number;
2034  break;
2035  }
2036  case AnnotateRotateCommand:
2037  {
2038  int
2039  entry;
2040 
2041  const char
2042  *const RotateMenu[] =
2043  {
2044  "-90",
2045  "-45",
2046  "-30",
2047  "0",
2048  "30",
2049  "45",
2050  "90",
2051  "180",
2052  "Dialog...",
2053  (char *) NULL,
2054  };
2055 
2056  static char
2057  angle[MaxTextExtent] = "30.0";
2058 
2059  /*
2060  Select a command from the pop-up menu.
2061  */
2062  entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2063  command);
2064  if (entry < 0)
2065  break;
2066  if (entry != 8)
2067  {
2068  degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2069  break;
2070  }
2071  (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2072  angle);
2073  if (*angle == '\0')
2074  break;
2075  degrees=StringToDouble(angle,(char **) NULL);
2076  break;
2077  }
2078  case AnnotateHelpCommand:
2079  {
2080  XTextViewHelp(display,resource_info,windows,MagickFalse,
2081  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2082  break;
2083  }
2084  case AnnotateDismissCommand:
2085  {
2086  /*
2087  Prematurely exit.
2088  */
2089  state|=EscapeState;
2090  state|=ExitState;
2091  break;
2092  }
2093  default:
2094  break;
2095  }
2096  continue;
2097  }
2098  switch (event.type)
2099  {
2100  case ButtonPress:
2101  {
2102  if (event.xbutton.button != Button1)
2103  break;
2104  if (event.xbutton.window != windows->image.id)
2105  break;
2106  /*
2107  Change to text entering mode.
2108  */
2109  x=event.xbutton.x;
2110  y=event.xbutton.y;
2111  state|=ExitState;
2112  break;
2113  }
2114  case ButtonRelease:
2115  break;
2116  case Expose:
2117  break;
2118  case KeyPress:
2119  {
2120  if (event.xkey.window != windows->image.id)
2121  break;
2122  /*
2123  Respond to a user key press.
2124  */
2125  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2126  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2127  switch ((int) key_symbol)
2128  {
2129  case XK_Escape:
2130  case XK_F20:
2131  {
2132  /*
2133  Prematurely exit.
2134  */
2135  state|=EscapeState;
2136  state|=ExitState;
2137  break;
2138  }
2139  case XK_F1:
2140  case XK_Help:
2141  {
2142  XTextViewHelp(display,resource_info,windows,MagickFalse,
2143  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144  break;
2145  }
2146  default:
2147  {
2148  (void) XBell(display,0);
2149  break;
2150  }
2151  }
2152  break;
2153  }
2154  case MotionNotify:
2155  {
2156  /*
2157  Map and unmap Info widget as cursor crosses its boundaries.
2158  */
2159  x=event.xmotion.x;
2160  y=event.xmotion.y;
2161  if (windows->info.mapped != MagickFalse)
2162  {
2163  if ((x < (int) (windows->info.x+windows->info.width)) &&
2164  (y < (int) (windows->info.y+windows->info.height)))
2165  (void) XWithdrawWindow(display,windows->info.id,
2166  windows->info.screen);
2167  }
2168  else
2169  if ((x > (int) (windows->info.x+windows->info.width)) ||
2170  (y > (int) (windows->info.y+windows->info.height)))
2171  (void) XMapWindow(display,windows->info.id);
2172  break;
2173  }
2174  default:
2175  break;
2176  }
2177  } while ((state & ExitState) == 0);
2178  (void) XSelectInput(display,windows->image.id,
2179  windows->image.attributes.event_mask);
2180  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2181  if ((state & EscapeState) != 0)
2182  return(MagickTrue);
2183  /*
2184  Set font info and check boundary conditions.
2185  */
2186  font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2187  if (font_info == (XFontStruct *) NULL)
2188  {
2189  XNoticeWidget(display,windows,"Unable to load font:",
2190  resource_info->font_name[font_id]);
2191  font_info=windows->font_info;
2192  }
2193  if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2194  x=(int) windows->image.width-font_info->max_bounds.width;
2195  if (y < (int) (font_info->ascent+font_info->descent))
2196  y=(int) font_info->ascent+font_info->descent;
2197  if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2198  ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2199  return(MagickFalse);
2200  /*
2201  Initialize annotate structure.
2202  */
2203  annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2204  if (annotate_info == (XAnnotateInfo *) NULL)
2205  return(MagickFalse);
2206  XGetAnnotateInfo(annotate_info);
2207  annotate_info->x=x;
2208  annotate_info->y=y;
2209  if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2210  annotate_info->stencil=OpaqueStencil;
2211  else
2212  if (transparent_box == MagickFalse)
2213  annotate_info->stencil=BackgroundStencil;
2214  else
2215  annotate_info->stencil=ForegroundStencil;
2216  annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2217  annotate_info->degrees=degrees;
2218  annotate_info->font_info=font_info;
2219  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2220  windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2221  sizeof(*annotate_info->text));
2222  if (annotate_info->text == (char *) NULL)
2223  return(MagickFalse);
2224  /*
2225  Create cursor and set graphic context.
2226  */
2227  cursor=XCreateFontCursor(display,XC_pencil);
2228  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2229  annotate_context=windows->image.annotate_context;
2230  (void) XSetFont(display,annotate_context,font_info->fid);
2231  (void) XSetBackground(display,annotate_context,
2232  windows->pixel_info->pen_colors[box_id].pixel);
2233  (void) XSetForeground(display,annotate_context,
2234  windows->pixel_info->pen_colors[pen_id].pixel);
2235  /*
2236  Begin annotating the image with text.
2237  */
2238  (void) CloneString(&windows->command.name,"Text");
2239  windows->command.data=0;
2240  (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2241  state=DefaultState;
2242  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2243  text_event.xexpose.width=(int) font_info->max_bounds.width;
2244  text_event.xexpose.height=font_info->max_bounds.ascent+
2245  font_info->max_bounds.descent;
2246  p=annotate_info->text;
2247  do
2248  {
2249  /*
2250  Display text cursor.
2251  */
2252  *p='\0';
2253  (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2254  /*
2255  Wait for next event.
2256  */
2257  XScreenEvent(display,windows,&event);
2258  if (event.xany.window == windows->command.id)
2259  {
2260  /*
2261  Select a command from the Command widget.
2262  */
2263  (void) XSetBackground(display,annotate_context,
2264  windows->pixel_info->background_color.pixel);
2265  (void) XSetForeground(display,annotate_context,
2266  windows->pixel_info->foreground_color.pixel);
2267  id=XCommandWidget(display,windows,AnnotateMenu,&event);
2268  (void) XSetBackground(display,annotate_context,
2269  windows->pixel_info->pen_colors[box_id].pixel);
2270  (void) XSetForeground(display,annotate_context,
2271  windows->pixel_info->pen_colors[pen_id].pixel);
2272  if (id < 0)
2273  continue;
2274  switch (TextCommands[id])
2275  {
2276  case TextHelpCommand:
2277  {
2278  XTextViewHelp(display,resource_info,windows,MagickFalse,
2279  "Help Viewer - Image Annotation",ImageAnnotateHelp);
2280  (void) XCheckDefineCursor(display,windows->image.id,cursor);
2281  break;
2282  }
2283  case TextApplyCommand:
2284  {
2285  /*
2286  Finished annotating.
2287  */
2288  annotate_info->width=(unsigned int) XTextWidth(font_info,
2289  annotate_info->text,(int) strlen(annotate_info->text));
2290  XRefreshWindow(display,&windows->image,&text_event);
2291  state|=ExitState;
2292  break;
2293  }
2294  default:
2295  break;
2296  }
2297  continue;
2298  }
2299  /*
2300  Erase text cursor.
2301  */
2302  text_event.xexpose.x=x;
2303  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2304  (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2305  (unsigned int) text_event.xexpose.width,(unsigned int)
2306  text_event.xexpose.height,MagickFalse);
2307  XRefreshWindow(display,&windows->image,&text_event);
2308  switch (event.type)
2309  {
2310  case ButtonPress:
2311  {
2312  if (event.xbutton.window != windows->image.id)
2313  break;
2314  if (event.xbutton.button == Button2)
2315  {
2316  /*
2317  Request primary selection.
2318  */
2319  (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2320  windows->image.id,CurrentTime);
2321  break;
2322  }
2323  break;
2324  }
2325  case Expose:
2326  {
2327  if (event.xexpose.count == 0)
2328  {
2329  XAnnotateInfo
2330  *text_info;
2331 
2332  /*
2333  Refresh Image window.
2334  */
2335  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2336  text_info=annotate_info;
2337  while (text_info != (XAnnotateInfo *) NULL)
2338  {
2339  if (annotate_info->stencil == ForegroundStencil)
2340  (void) XDrawString(display,windows->image.id,annotate_context,
2341  text_info->x,text_info->y,text_info->text,
2342  (int) strlen(text_info->text));
2343  else
2344  (void) XDrawImageString(display,windows->image.id,
2345  annotate_context,text_info->x,text_info->y,text_info->text,
2346  (int) strlen(text_info->text));
2347  text_info=text_info->previous;
2348  }
2349  (void) XDrawString(display,windows->image.id,annotate_context,
2350  x,y,"_",1);
2351  }
2352  break;
2353  }
2354  case KeyPress:
2355  {
2356  int
2357  length;
2358 
2359  if (event.xkey.window != windows->image.id)
2360  break;
2361  /*
2362  Respond to a user key press.
2363  */
2364  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2365  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2366  *(command+length)='\0';
2367  if (((event.xkey.state & ControlMask) != 0) ||
2368  ((event.xkey.state & Mod1Mask) != 0))
2369  state|=ModifierState;
2370  if ((state & ModifierState) != 0)
2371  switch ((int) key_symbol)
2372  {
2373  case XK_u:
2374  case XK_U:
2375  {
2376  key_symbol=DeleteCommand;
2377  break;
2378  }
2379  default:
2380  break;
2381  }
2382  switch ((int) key_symbol)
2383  {
2384  case XK_BackSpace:
2385  {
2386  /*
2387  Erase one character.
2388  */
2389  if (p == annotate_info->text)
2390  {
2391  if (annotate_info->previous == (XAnnotateInfo *) NULL)
2392  break;
2393  else
2394  {
2395  /*
2396  Go to end of the previous line of text.
2397  */
2398  annotate_info=annotate_info->previous;
2399  p=annotate_info->text;
2400  x=annotate_info->x+annotate_info->width;
2401  y=annotate_info->y;
2402  if (annotate_info->width != 0)
2403  p+=(ptrdiff_t) strlen(annotate_info->text);
2404  break;
2405  }
2406  }
2407  p--;
2408  x-=XTextWidth(font_info,p,1);
2409  text_event.xexpose.x=x;
2410  text_event.xexpose.y=y-font_info->max_bounds.ascent;
2411  XRefreshWindow(display,&windows->image,&text_event);
2412  break;
2413  }
2414  case XK_bracketleft:
2415  {
2416  key_symbol=XK_Escape;
2417  break;
2418  }
2419  case DeleteCommand:
2420  {
2421  /*
2422  Erase the entire line of text.
2423  */
2424  while (p != annotate_info->text)
2425  {
2426  p--;
2427  x-=XTextWidth(font_info,p,1);
2428  text_event.xexpose.x=x;
2429  XRefreshWindow(display,&windows->image,&text_event);
2430  }
2431  break;
2432  }
2433  case XK_Escape:
2434  case XK_F20:
2435  {
2436  /*
2437  Finished annotating.
2438  */
2439  annotate_info->width=(unsigned int) XTextWidth(font_info,
2440  annotate_info->text,(int) strlen(annotate_info->text));
2441  XRefreshWindow(display,&windows->image,&text_event);
2442  state|=ExitState;
2443  break;
2444  }
2445  default:
2446  {
2447  /*
2448  Draw a single character on the Image window.
2449  */
2450  if ((state & ModifierState) != 0)
2451  break;
2452  if (*command == '\0')
2453  break;
2454  *p=(*command);
2455  if (annotate_info->stencil == ForegroundStencil)
2456  (void) XDrawString(display,windows->image.id,annotate_context,
2457  x,y,p,1);
2458  else
2459  (void) XDrawImageString(display,windows->image.id,
2460  annotate_context,x,y,p,1);
2461  x+=XTextWidth(font_info,p,1);
2462  p++;
2463  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2464  break;
2465  magick_fallthrough;
2466  }
2467  case XK_Return:
2468  case XK_KP_Enter:
2469  {
2470  /*
2471  Advance to the next line of text.
2472  */
2473  *p='\0';
2474  annotate_info->width=(unsigned int) XTextWidth(font_info,
2475  annotate_info->text,(int) strlen(annotate_info->text));
2476  if (annotate_info->next != (XAnnotateInfo *) NULL)
2477  {
2478  /*
2479  Line of text already exists.
2480  */
2481  annotate_info=annotate_info->next;
2482  x=annotate_info->x;
2483  y=annotate_info->y;
2484  p=annotate_info->text;
2485  break;
2486  }
2487  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2488  sizeof(*annotate_info->next));
2489  if (annotate_info->next == (XAnnotateInfo *) NULL)
2490  return(MagickFalse);
2491  *annotate_info->next=(*annotate_info);
2492  annotate_info->next->previous=annotate_info;
2493  annotate_info=annotate_info->next;
2494  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2495  windows->image.width/MagickMax((ssize_t)
2496  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2497  if (annotate_info->text == (char *) NULL)
2498  return(MagickFalse);
2499  annotate_info->y+=annotate_info->height;
2500  if (annotate_info->y > (int) windows->image.height)
2501  annotate_info->y=(int) annotate_info->height;
2502  annotate_info->next=(XAnnotateInfo *) NULL;
2503  x=annotate_info->x;
2504  y=annotate_info->y;
2505  p=annotate_info->text;
2506  break;
2507  }
2508  }
2509  break;
2510  }
2511  case KeyRelease:
2512  {
2513  /*
2514  Respond to a user key release.
2515  */
2516  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2517  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2518  state&=(~ModifierState);
2519  break;
2520  }
2521  case SelectionNotify:
2522  {
2523  Atom
2524  type;
2525 
2526  int
2527  format;
2528 
2529  unsigned char
2530  *data;
2531 
2532  unsigned long
2533  after,
2534  length;
2535 
2536  /*
2537  Obtain response from primary selection.
2538  */
2539  if (event.xselection.property == (Atom) None)
2540  break;
2541  status=XGetWindowProperty(display,event.xselection.requestor,
2542  event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2543  &type,&format,&length,&after,&data);
2544  if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2545  (length == 0))
2546  break;
2547  /*
2548  Annotate Image window with primary selection.
2549  */
2550  for (i=0; i < (ssize_t) length; i++)
2551  {
2552  if ((char) data[i] != '\n')
2553  {
2554  /*
2555  Draw a single character on the Image window.
2556  */
2557  *p=(char) data[i];
2558  (void) XDrawString(display,windows->image.id,annotate_context,
2559  x,y,p,1);
2560  x+=XTextWidth(font_info,p,1);
2561  p++;
2562  if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2563  continue;
2564  }
2565  /*
2566  Advance to the next line of text.
2567  */
2568  *p='\0';
2569  annotate_info->width=(unsigned int) XTextWidth(font_info,
2570  annotate_info->text,(int) strlen(annotate_info->text));
2571  if (annotate_info->next != (XAnnotateInfo *) NULL)
2572  {
2573  /*
2574  Line of text already exists.
2575  */
2576  annotate_info=annotate_info->next;
2577  x=annotate_info->x;
2578  y=annotate_info->y;
2579  p=annotate_info->text;
2580  continue;
2581  }
2582  annotate_info->next=(XAnnotateInfo *) AcquireQuantumMemory(1,
2583  sizeof(*annotate_info->next));
2584  if (annotate_info->next == (XAnnotateInfo *) NULL)
2585  return(MagickFalse);
2586  *annotate_info->next=(*annotate_info);
2587  annotate_info->next->previous=annotate_info;
2588  annotate_info=annotate_info->next;
2589  annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2590  windows->image.width/MagickMax((ssize_t)
2591  font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2592  if (annotate_info->text == (char *) NULL)
2593  return(MagickFalse);
2594  annotate_info->y+=annotate_info->height;
2595  if (annotate_info->y > (int) windows->image.height)
2596  annotate_info->y=(int) annotate_info->height;
2597  annotate_info->next=(XAnnotateInfo *) NULL;
2598  x=annotate_info->x;
2599  y=annotate_info->y;
2600  p=annotate_info->text;
2601  }
2602  (void) XFree((void *) data);
2603  break;
2604  }
2605  default:
2606  break;
2607  }
2608  } while ((state & ExitState) == 0);
2609  (void) XFreeCursor(display,cursor);
2610  /*
2611  Annotation is relative to image configuration.
2612  */
2613  width=(unsigned int) image->columns;
2614  height=(unsigned int) image->rows;
2615  x=0;
2616  y=0;
2617  if (windows->image.crop_geometry != (char *) NULL)
2618  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2619  /*
2620  Initialize annotated image.
2621  */
2622  XSetCursorState(display,windows,MagickTrue);
2623  XCheckRefreshWindows(display,windows);
2624  while (annotate_info != (XAnnotateInfo *) NULL)
2625  {
2626  if (annotate_info->width == 0)
2627  {
2628  /*
2629  No text on this line-- go to the next line of text.
2630  */
2631  previous_info=annotate_info->previous;
2632  annotate_info->text=(char *)
2633  RelinquishMagickMemory(annotate_info->text);
2634  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2635  annotate_info=previous_info;
2636  continue;
2637  }
2638  /*
2639  Determine pixel index for box and pen color.
2640  */
2641  windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2642  if (windows->pixel_info->colors != 0)
2643  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2644  if (windows->pixel_info->pixels[i] ==
2645  windows->pixel_info->pen_colors[box_id].pixel)
2646  {
2647  windows->pixel_info->box_index=(unsigned short) i;
2648  break;
2649  }
2650  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2651  if (windows->pixel_info->colors != 0)
2652  for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2653  if (windows->pixel_info->pixels[i] ==
2654  windows->pixel_info->pen_colors[pen_id].pixel)
2655  {
2656  windows->pixel_info->pen_index=(unsigned short) i;
2657  break;
2658  }
2659  /*
2660  Define the annotate geometry string.
2661  */
2662  annotate_info->x=(int)
2663  width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2664  annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2665  windows->image.y)/windows->image.ximage->height;
2666  (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2667  "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2668  height*annotate_info->height/windows->image.ximage->height,
2669  annotate_info->x+x,annotate_info->y+y);
2670  /*
2671  Annotate image with text.
2672  */
2673  status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2674  if (status == 0)
2675  return(MagickFalse);
2676  /*
2677  Free up memory.
2678  */
2679  previous_info=annotate_info->previous;
2680  annotate_info->text=DestroyString(annotate_info->text);
2681  annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2682  annotate_info=previous_info;
2683  }
2684  (void) XSetForeground(display,annotate_context,
2685  windows->pixel_info->foreground_color.pixel);
2686  (void) XSetBackground(display,annotate_context,
2687  windows->pixel_info->background_color.pixel);
2688  (void) XSetFont(display,annotate_context,windows->font_info->fid);
2689  XSetCursorState(display,windows,MagickFalse);
2690  (void) XFreeFont(display,font_info);
2691  /*
2692  Update image configuration.
2693  */
2694  XConfigureImageColormap(display,resource_info,windows,image);
2695  (void) XConfigureImage(display,resource_info,windows,image);
2696  return(MagickTrue);
2697 }
2698 
2699 /*
2700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2701 % %
2702 % %
2703 % %
2704 + X B a c k g r o u n d I m a g e %
2705 % %
2706 % %
2707 % %
2708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2709 %
2710 % XBackgroundImage() displays the image in the background of a window.
2711 %
2712 % The format of the XBackgroundImage method is:
2713 %
2714 % MagickBooleanType XBackgroundImage(Display *display,
2715 % XResourceInfo *resource_info,XWindows *windows,Image **image)
2716 %
2717 % A description of each parameter follows:
2718 %
2719 % o display: Specifies a connection to an X server; returned from
2720 % XOpenDisplay.
2721 %
2722 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2723 %
2724 % o windows: Specifies a pointer to a XWindows structure.
2725 %
2726 % o image: the image.
2727 %
2728 */
2729 static MagickBooleanType XBackgroundImage(Display *display,
2730  XResourceInfo *resource_info,XWindows *windows,Image **image)
2731 {
2732 #define BackgroundImageTag "Background/Image"
2733 
2734  int
2735  status;
2736 
2737  static char
2738  window_id[MaxTextExtent] = "root";
2739 
2740  XResourceInfo
2741  background_resources;
2742 
2743  /*
2744  Put image in background.
2745  */
2746  status=XDialogWidget(display,windows,"Background",
2747  "Enter window id (id 0x00 selects window with pointer):",window_id);
2748  if (*window_id == '\0')
2749  return(MagickFalse);
2750  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2751  XInfoWidget(display,windows,BackgroundImageTag);
2752  XSetCursorState(display,windows,MagickTrue);
2753  XCheckRefreshWindows(display,windows);
2754  background_resources=(*resource_info);
2755  background_resources.window_id=window_id;
2756  background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2757  status=XDisplayBackgroundImage(display,&background_resources,*image);
2758  if (status != MagickFalse)
2759  XClientMessage(display,windows->image.id,windows->im_protocols,
2760  windows->im_retain_colors,CurrentTime);
2761  XSetCursorState(display,windows,MagickFalse);
2762  (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2763  return(MagickTrue);
2764 }
2765 
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 % %
2769 % %
2770 % %
2771 + X C h o p I m a g e %
2772 % %
2773 % %
2774 % %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 % XChopImage() chops the X image.
2778 %
2779 % The format of the XChopImage method is:
2780 %
2781 % MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2782 % XWindows *windows,Image **image)
2783 %
2784 % A description of each parameter follows:
2785 %
2786 % o display: Specifies a connection to an X server; returned from
2787 % XOpenDisplay.
2788 %
2789 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 % o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 % o image: the image.
2794 %
2795 */
2796 static MagickBooleanType XChopImage(Display *display,
2797  XResourceInfo *resource_info,XWindows *windows,Image **image)
2798 {
2799  const char
2800  *const ChopMenu[] =
2801  {
2802  "Direction",
2803  "Help",
2804  "Dismiss",
2805  (char *) NULL
2806  };
2807 
2808  static ModeType
2809  direction = HorizontalChopCommand;
2810 
2811  static const ModeType
2812  ChopCommands[] =
2813  {
2814  ChopDirectionCommand,
2815  ChopHelpCommand,
2816  ChopDismissCommand
2817  },
2818  DirectionCommands[] =
2819  {
2820  HorizontalChopCommand,
2821  VerticalChopCommand
2822  };
2823 
2824  char
2825  text[MaxTextExtent];
2826 
2827  Image
2828  *chop_image;
2829 
2830  int
2831  id,
2832  x,
2833  y;
2834 
2835  MagickRealType
2836  scale_factor;
2837 
2839  chop_info;
2840 
2841  unsigned int
2842  distance,
2843  height,
2844  width;
2845 
2846  size_t
2847  state;
2848 
2849  XEvent
2850  event;
2851 
2852  XSegment
2853  segment_info;
2854 
2855  /*
2856  Map Command widget.
2857  */
2858  (void) CloneString(&windows->command.name,"Chop");
2859  windows->command.data=1;
2860  (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2861  (void) XMapRaised(display,windows->command.id);
2862  XClientMessage(display,windows->image.id,windows->im_protocols,
2863  windows->im_update_widget,CurrentTime);
2864  /*
2865  Track pointer until button 1 is pressed.
2866  */
2867  XQueryPosition(display,windows->image.id,&x,&y);
2868  (void) XSelectInput(display,windows->image.id,
2869  windows->image.attributes.event_mask | PointerMotionMask);
2870  state=DefaultState;
2871  (void) memset(&segment_info,0,sizeof(segment_info));
2872  do
2873  {
2874  if (windows->info.mapped != MagickFalse)
2875  {
2876  /*
2877  Display pointer position.
2878  */
2879  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2880  x+windows->image.x,y+windows->image.y);
2881  XInfoWidget(display,windows,text);
2882  }
2883  /*
2884  Wait for next event.
2885  */
2886  XScreenEvent(display,windows,&event);
2887  if (event.xany.window == windows->command.id)
2888  {
2889  /*
2890  Select a command from the Command widget.
2891  */
2892  id=XCommandWidget(display,windows,ChopMenu,&event);
2893  if (id < 0)
2894  continue;
2895  switch (ChopCommands[id])
2896  {
2897  case ChopDirectionCommand:
2898  {
2899  char
2900  command[MaxTextExtent];
2901 
2902  const char
2903  *const Directions[] =
2904  {
2905  "horizontal",
2906  "vertical",
2907  (char *) NULL,
2908  };
2909 
2910  /*
2911  Select a command from the pop-up menu.
2912  */
2913  id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2914  if (id >= 0)
2915  direction=DirectionCommands[id];
2916  break;
2917  }
2918  case ChopHelpCommand:
2919  {
2920  XTextViewHelp(display,resource_info,windows,MagickFalse,
2921  "Help Viewer - Image Chop",ImageChopHelp);
2922  break;
2923  }
2924  case ChopDismissCommand:
2925  {
2926  /*
2927  Prematurely exit.
2928  */
2929  state|=EscapeState;
2930  state|=ExitState;
2931  break;
2932  }
2933  default:
2934  break;
2935  }
2936  continue;
2937  }
2938  switch (event.type)
2939  {
2940  case ButtonPress:
2941  {
2942  if (event.xbutton.button != Button1)
2943  break;
2944  if (event.xbutton.window != windows->image.id)
2945  break;
2946  /*
2947  User has committed to start point of chopping line.
2948  */
2949  segment_info.x1=(short int) event.xbutton.x;
2950  segment_info.x2=(short int) event.xbutton.x;
2951  segment_info.y1=(short int) event.xbutton.y;
2952  segment_info.y2=(short int) event.xbutton.y;
2953  state|=ExitState;
2954  break;
2955  }
2956  case ButtonRelease:
2957  break;
2958  case Expose:
2959  break;
2960  case KeyPress:
2961  {
2962  char
2963  command[MaxTextExtent];
2964 
2965  KeySym
2966  key_symbol;
2967 
2968  if (event.xkey.window != windows->image.id)
2969  break;
2970  /*
2971  Respond to a user key press.
2972  */
2973  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2974  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2975  switch ((int) key_symbol)
2976  {
2977  case XK_Escape:
2978  case XK_F20:
2979  {
2980  /*
2981  Prematurely exit.
2982  */
2983  state|=EscapeState;
2984  state|=ExitState;
2985  break;
2986  }
2987  case XK_F1:
2988  case XK_Help:
2989  {
2990  (void) XSetFunction(display,windows->image.highlight_context,
2991  GXcopy);
2992  XTextViewHelp(display,resource_info,windows,MagickFalse,
2993  "Help Viewer - Image Chop",ImageChopHelp);
2994  (void) XSetFunction(display,windows->image.highlight_context,
2995  GXinvert);
2996  break;
2997  }
2998  default:
2999  {
3000  (void) XBell(display,0);
3001  break;
3002  }
3003  }
3004  break;
3005  }
3006  case MotionNotify:
3007  {
3008  /*
3009  Map and unmap Info widget as text cursor crosses its boundaries.
3010  */
3011  x=event.xmotion.x;
3012  y=event.xmotion.y;
3013  if (windows->info.mapped != MagickFalse)
3014  {
3015  if ((x < (int) (windows->info.x+windows->info.width)) &&
3016  (y < (int) (windows->info.y+windows->info.height)))
3017  (void) XWithdrawWindow(display,windows->info.id,
3018  windows->info.screen);
3019  }
3020  else
3021  if ((x > (int) (windows->info.x+windows->info.width)) ||
3022  (y > (int) (windows->info.y+windows->info.height)))
3023  (void) XMapWindow(display,windows->info.id);
3024  }
3025  }
3026  } while ((state & ExitState) == 0);
3027  (void) XSelectInput(display,windows->image.id,
3028  windows->image.attributes.event_mask);
3029  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3030  if ((state & EscapeState) != 0)
3031  return(MagickTrue);
3032  /*
3033  Draw line as pointer moves until the mouse button is released.
3034  */
3035  chop_info.width=0;
3036  chop_info.height=0;
3037  chop_info.x=0;
3038  chop_info.y=0;
3039  distance=0;
3040  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3041  state=DefaultState;
3042  do
3043  {
3044  if (distance > 9)
3045  {
3046  /*
3047  Display info and draw chopping line.
3048  */
3049  if (windows->info.mapped == MagickFalse)
3050  (void) XMapWindow(display,windows->info.id);
3051  (void) FormatLocaleString(text,MaxTextExtent,
3052  " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3053  chop_info.height,(double) chop_info.x,(double) chop_info.y);
3054  XInfoWidget(display,windows,text);
3055  XHighlightLine(display,windows->image.id,
3056  windows->image.highlight_context,&segment_info);
3057  }
3058  else
3059  if (windows->info.mapped != MagickFalse)
3060  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3061  /*
3062  Wait for next event.
3063  */
3064  XScreenEvent(display,windows,&event);
3065  if (distance > 9)
3066  XHighlightLine(display,windows->image.id,
3067  windows->image.highlight_context,&segment_info);
3068  switch (event.type)
3069  {
3070  case ButtonPress:
3071  {
3072  segment_info.x2=(short int) event.xmotion.x;
3073  segment_info.y2=(short int) event.xmotion.y;
3074  break;
3075  }
3076  case ButtonRelease:
3077  {
3078  /*
3079  User has committed to chopping line.
3080  */
3081  segment_info.x2=(short int) event.xbutton.x;
3082  segment_info.y2=(short int) event.xbutton.y;
3083  state|=ExitState;
3084  break;
3085  }
3086  case Expose:
3087  break;
3088  case MotionNotify:
3089  {
3090  segment_info.x2=(short int) event.xmotion.x;
3091  segment_info.y2=(short int) event.xmotion.y;
3092  }
3093  default:
3094  break;
3095  }
3096  /*
3097  Check boundary conditions.
3098  */
3099  if (segment_info.x2 < 0)
3100  segment_info.x2=0;
3101  else
3102  if (segment_info.x2 > windows->image.ximage->width)
3103  segment_info.x2=windows->image.ximage->width;
3104  if (segment_info.y2 < 0)
3105  segment_info.y2=0;
3106  else
3107  if (segment_info.y2 > windows->image.ximage->height)
3108  segment_info.y2=windows->image.ximage->height;
3109  distance=(unsigned int)
3110  (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3111  ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3112  /*
3113  Compute chopping geometry.
3114  */
3115  if (direction == HorizontalChopCommand)
3116  {
3117  chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3118  chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3119  chop_info.height=0;
3120  chop_info.y=0;
3121  if (segment_info.x1 > (int) segment_info.x2)
3122  {
3123  chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3124  chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3125  }
3126  }
3127  else
3128  {
3129  chop_info.width=0;
3130  chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3131  chop_info.x=0;
3132  chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3133  if (segment_info.y1 > segment_info.y2)
3134  {
3135  chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3136  chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3137  }
3138  }
3139  } while ((state & ExitState) == 0);
3140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3141  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3142  if (distance <= 9)
3143  return(MagickTrue);
3144  /*
3145  Image chopping is relative to image configuration.
3146  */
3147  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3148  XSetCursorState(display,windows,MagickTrue);
3149  XCheckRefreshWindows(display,windows);
3150  windows->image.window_changes.width=windows->image.ximage->width-
3151  (unsigned int) chop_info.width;
3152  windows->image.window_changes.height=windows->image.ximage->height-
3153  (unsigned int) chop_info.height;
3154  width=(unsigned int) (*image)->columns;
3155  height=(unsigned int) (*image)->rows;
3156  x=0;
3157  y=0;
3158  if (windows->image.crop_geometry != (char *) NULL)
3159  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3160  scale_factor=(MagickRealType) width/windows->image.ximage->width;
3161  chop_info.x+=x;
3162  chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3163  chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3164  scale_factor=(MagickRealType) height/windows->image.ximage->height;
3165  chop_info.y+=y;
3166  chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3167  chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3168  /*
3169  Chop image.
3170  */
3171  chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3172  XSetCursorState(display,windows,MagickFalse);
3173  if (chop_image == (Image *) NULL)
3174  return(MagickFalse);
3175  *image=DestroyImage(*image);
3176  *image=chop_image;
3177  /*
3178  Update image configuration.
3179  */
3180  XConfigureImageColormap(display,resource_info,windows,*image);
3181  (void) XConfigureImage(display,resource_info,windows,*image);
3182  return(MagickTrue);
3183 }
3184 
3185 /*
3186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3187 % %
3188 % %
3189 % %
3190 + X C o l o r E d i t I m a g e %
3191 % %
3192 % %
3193 % %
3194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3195 %
3196 % XColorEditImage() allows the user to interactively change the color of one
3197 % pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3198 %
3199 % The format of the XColorEditImage method is:
3200 %
3201 % MagickBooleanType XColorEditImage(Display *display,
3202 % XResourceInfo *resource_info,XWindows *windows,Image **image)
3203 %
3204 % A description of each parameter follows:
3205 %
3206 % o display: Specifies a connection to an X server; returned from
3207 % XOpenDisplay.
3208 %
3209 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3210 %
3211 % o windows: Specifies a pointer to a XWindows structure.
3212 %
3213 % o image: the image; returned from ReadImage.
3214 %
3215 */
3216 
3217 
3218 static MagickBooleanType XColorEditImage(Display *display,
3219  XResourceInfo *resource_info,XWindows *windows,Image **image)
3220 {
3221  const char
3222  *const ColorEditMenu[] =
3223  {
3224  "Method",
3225  "Pixel Color",
3226  "Border Color",
3227  "Fuzz",
3228  "Undo",
3229  "Help",
3230  "Dismiss",
3231  (char *) NULL
3232  };
3233 
3234  static const ModeType
3235  ColorEditCommands[] =
3236  {
3237  ColorEditMethodCommand,
3238  ColorEditColorCommand,
3239  ColorEditBorderCommand,
3240  ColorEditFuzzCommand,
3241  ColorEditUndoCommand,
3242  ColorEditHelpCommand,
3243  ColorEditDismissCommand
3244  };
3245 
3246  static PaintMethod
3247  method = PointMethod;
3248 
3249  static unsigned int
3250  pen_id = 0;
3251 
3252  static XColor
3253  border_color = { 0, 0, 0, 0, 0, 0 };
3254 
3255  char
3256  command[MaxTextExtent] = "",
3257  text[MaxTextExtent] = "";
3258 
3259  Cursor
3260  cursor;
3261 
3263  *exception;
3264 
3265  int
3266  entry,
3267  id,
3268  x,
3269  x_offset,
3270  y,
3271  y_offset;
3272 
3273  PixelPacket
3274  *q;
3275 
3276  size_t
3277  state;
3278 
3279  ssize_t
3280  i;
3281 
3282  unsigned int
3283  height,
3284  width;
3285 
3286  XColor
3287  color;
3288 
3289  XEvent
3290  event;
3291 
3292  /*
3293  Map Command widget.
3294  */
3295  (void) CloneString(&windows->command.name,"Color Edit");
3296  windows->command.data=4;
3297  (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3298  (void) XMapRaised(display,windows->command.id);
3299  XClientMessage(display,windows->image.id,windows->im_protocols,
3300  windows->im_update_widget,CurrentTime);
3301  /*
3302  Make cursor.
3303  */
3304  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3305  resource_info->background_color,resource_info->foreground_color);
3306  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3307  /*
3308  Track pointer until button 1 is pressed.
3309  */
3310  XQueryPosition(display,windows->image.id,&x,&y);
3311  (void) XSelectInput(display,windows->image.id,
3312  windows->image.attributes.event_mask | PointerMotionMask);
3313  state=DefaultState;
3314  do
3315  {
3316  if (windows->info.mapped != MagickFalse)
3317  {
3318  /*
3319  Display pointer position.
3320  */
3321  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3322  x+windows->image.x,y+windows->image.y);
3323  XInfoWidget(display,windows,text);
3324  }
3325  /*
3326  Wait for next event.
3327  */
3328  XScreenEvent(display,windows,&event);
3329  if (event.xany.window == windows->command.id)
3330  {
3331  /*
3332  Select a command from the Command widget.
3333  */
3334  id=XCommandWidget(display,windows,ColorEditMenu,&event);
3335  if (id < 0)
3336  {
3337  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3338  continue;
3339  }
3340  switch (ColorEditCommands[id])
3341  {
3342  case ColorEditMethodCommand:
3343  {
3344  char
3345  **methods;
3346 
3347  /*
3348  Select a method from the pop-up menu.
3349  */
3350  methods=(char **) GetCommandOptions(MagickMethodOptions);
3351  if (methods == (char **) NULL)
3352  break;
3353  entry=XMenuWidget(display,windows,ColorEditMenu[id],
3354  (const char **) methods,command);
3355  if (entry >= 0)
3356  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3357  MagickFalse,methods[entry]);
3358  methods=DestroyStringList(methods);
3359  break;
3360  }
3361  case ColorEditColorCommand:
3362  {
3363  const char
3364  *ColorMenu[MaxNumberPens];
3365 
3366  int
3367  pen_number;
3368 
3369  /*
3370  Initialize menu selections.
3371  */
3372  for (i=0; i < (int) (MaxNumberPens-2); i++)
3373  ColorMenu[i]=resource_info->pen_colors[i];
3374  ColorMenu[MaxNumberPens-2]="Browser...";
3375  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3376  /*
3377  Select a pen color from the pop-up menu.
3378  */
3379  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3380  (const char **) ColorMenu,command);
3381  if (pen_number < 0)
3382  break;
3383  if (pen_number == (MaxNumberPens-2))
3384  {
3385  static char
3386  color_name[MaxTextExtent] = "gray";
3387 
3388  /*
3389  Select a pen color from a dialog.
3390  */
3391  resource_info->pen_colors[pen_number]=color_name;
3392  XColorBrowserWidget(display,windows,"Select",color_name);
3393  if (*color_name == '\0')
3394  break;
3395  }
3396  /*
3397  Set pen color.
3398  */
3399  (void) XParseColor(display,windows->map_info->colormap,
3400  resource_info->pen_colors[pen_number],&color);
3401  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3402  (unsigned int) MaxColors,&color);
3403  windows->pixel_info->pen_colors[pen_number]=color;
3404  pen_id=(unsigned int) pen_number;
3405  break;
3406  }
3407  case ColorEditBorderCommand:
3408  {
3409  const char
3410  *ColorMenu[MaxNumberPens];
3411 
3412  int
3413  pen_number;
3414 
3415  /*
3416  Initialize menu selections.
3417  */
3418  for (i=0; i < (int) (MaxNumberPens-2); i++)
3419  ColorMenu[i]=resource_info->pen_colors[i];
3420  ColorMenu[MaxNumberPens-2]="Browser...";
3421  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3422  /*
3423  Select a pen color from the pop-up menu.
3424  */
3425  pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3426  (const char **) ColorMenu,command);
3427  if (pen_number < 0)
3428  break;
3429  if (pen_number == (MaxNumberPens-2))
3430  {
3431  static char
3432  color_name[MaxTextExtent] = "gray";
3433 
3434  /*
3435  Select a pen color from a dialog.
3436  */
3437  resource_info->pen_colors[pen_number]=color_name;
3438  XColorBrowserWidget(display,windows,"Select",color_name);
3439  if (*color_name == '\0')
3440  break;
3441  }
3442  /*
3443  Set border color.
3444  */
3445  (void) XParseColor(display,windows->map_info->colormap,
3446  resource_info->pen_colors[pen_number],&border_color);
3447  break;
3448  }
3449  case ColorEditFuzzCommand:
3450  {
3451  static char
3452  fuzz[MaxTextExtent];
3453 
3454  static const char
3455  *FuzzMenu[] =
3456  {
3457  "0%",
3458  "2%",
3459  "5%",
3460  "10%",
3461  "15%",
3462  "Dialog...",
3463  (char *) NULL,
3464  };
3465 
3466  /*
3467  Select a command from the pop-up menu.
3468  */
3469  entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3470  command);
3471  if (entry < 0)
3472  break;
3473  if (entry != 5)
3474  {
3475  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3476  QuantumRange+1.0);
3477  break;
3478  }
3479  (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3480  (void) XDialogWidget(display,windows,"Ok",
3481  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3482  if (*fuzz == '\0')
3483  break;
3484  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3485  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3486  1.0);
3487  break;
3488  }
3489  case ColorEditUndoCommand:
3490  {
3491  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3492  image);
3493  break;
3494  }
3495  case ColorEditHelpCommand:
3496  default:
3497  {
3498  XTextViewHelp(display,resource_info,windows,MagickFalse,
3499  "Help Viewer - Image Annotation",ImageColorEditHelp);
3500  break;
3501  }
3502  case ColorEditDismissCommand:
3503  {
3504  /*
3505  Prematurely exit.
3506  */
3507  state|=EscapeState;
3508  state|=ExitState;
3509  break;
3510  }
3511  }
3512  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3513  continue;
3514  }
3515  switch (event.type)
3516  {
3517  case ButtonPress:
3518  {
3519  if (event.xbutton.button != Button1)
3520  break;
3521  if ((event.xbutton.window != windows->image.id) &&
3522  (event.xbutton.window != windows->magnify.id))
3523  break;
3524  /*
3525  exit loop.
3526  */
3527  x=event.xbutton.x;
3528  y=event.xbutton.y;
3529  (void) XMagickCommand(display,resource_info,windows,
3530  SaveToUndoBufferCommand,image);
3531  state|=UpdateConfigurationState;
3532  break;
3533  }
3534  case ButtonRelease:
3535  {
3536  if (event.xbutton.button != Button1)
3537  break;
3538  if ((event.xbutton.window != windows->image.id) &&
3539  (event.xbutton.window != windows->magnify.id))
3540  break;
3541  /*
3542  Update colormap information.
3543  */
3544  x=event.xbutton.x;
3545  y=event.xbutton.y;
3546  XConfigureImageColormap(display,resource_info,windows,*image);
3547  (void) XConfigureImage(display,resource_info,windows,*image);
3548  XInfoWidget(display,windows,text);
3549  (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550  state&=(~UpdateConfigurationState);
3551  break;
3552  }
3553  case Expose:
3554  break;
3555  case KeyPress:
3556  {
3557  KeySym
3558  key_symbol;
3559 
3560  if (event.xkey.window == windows->magnify.id)
3561  {
3562  Window
3563  window;
3564 
3565  window=windows->magnify.id;
3566  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3567  }
3568  if (event.xkey.window != windows->image.id)
3569  break;
3570  /*
3571  Respond to a user key press.
3572  */
3573  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3574  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3575  switch ((int) key_symbol)
3576  {
3577  case XK_Escape:
3578  case XK_F20:
3579  {
3580  /*
3581  Prematurely exit.
3582  */
3583  state|=ExitState;
3584  break;
3585  }
3586  case XK_F1:
3587  case XK_Help:
3588  {
3589  XTextViewHelp(display,resource_info,windows,MagickFalse,
3590  "Help Viewer - Image Annotation",ImageColorEditHelp);
3591  break;
3592  }
3593  default:
3594  {
3595  (void) XBell(display,0);
3596  break;
3597  }
3598  }
3599  break;
3600  }
3601  case MotionNotify:
3602  {
3603  /*
3604  Map and unmap Info widget as cursor crosses its boundaries.
3605  */
3606  x=event.xmotion.x;
3607  y=event.xmotion.y;
3608  if (windows->info.mapped != MagickFalse)
3609  {
3610  if ((x < (int) (windows->info.x+windows->info.width)) &&
3611  (y < (int) (windows->info.y+windows->info.height)))
3612  (void) XWithdrawWindow(display,windows->info.id,
3613  windows->info.screen);
3614  }
3615  else
3616  if ((x > (int) (windows->info.x+windows->info.width)) ||
3617  (y > (int) (windows->info.y+windows->info.height)))
3618  (void) XMapWindow(display,windows->info.id);
3619  break;
3620  }
3621  default:
3622  break;
3623  }
3624  if (event.xany.window == windows->magnify.id)
3625  {
3626  x=windows->magnify.x-windows->image.x;
3627  y=windows->magnify.y-windows->image.y;
3628  }
3629  x_offset=x;
3630  y_offset=y;
3631  if ((state & UpdateConfigurationState) != 0)
3632  {
3633  CacheView
3634  *image_view;
3635 
3636  int
3637  x,
3638  y;
3639 
3640  /*
3641  Pixel edit is relative to image configuration.
3642  */
3643  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3644  MagickTrue);
3645  color=windows->pixel_info->pen_colors[pen_id];
3646  XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3647  width=(unsigned int) (*image)->columns;
3648  height=(unsigned int) (*image)->rows;
3649  x=0;
3650  y=0;
3651  if (windows->image.crop_geometry != (char *) NULL)
3652  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3653  &width,&height);
3654  x_offset=(int)
3655  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3656  y_offset=(int)
3657  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3658  if ((x_offset < 0) || (y_offset < 0))
3659  continue;
3660  if ((x_offset >= (int) (*image)->columns) ||
3661  (y_offset >= (int) (*image)->rows))
3662  continue;
3663  exception=(&(*image)->exception);
3664  image_view=AcquireAuthenticCacheView(*image,exception);
3665  switch (method)
3666  {
3667  case PointMethod:
3668  default:
3669  {
3670  /*
3671  Update color information using point algorithm.
3672  */
3673  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3674  return(MagickFalse);
3675  q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3676  (ssize_t)y_offset,1,1,exception);
3677  if (q == (PixelPacket *) NULL)
3678  break;
3679  q->red=ScaleShortToQuantum(color.red);
3680  q->green=ScaleShortToQuantum(color.green);
3681  q->blue=ScaleShortToQuantum(color.blue);
3682  (void) SyncCacheViewAuthenticPixels(image_view,
3683  &(*image)->exception);
3684  break;
3685  }
3686  case ReplaceMethod:
3687  {
3688  PixelPacket
3689  target;
3690 
3691  /*
3692  Update color information using replace algorithm.
3693  */
3694  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3695  (ssize_t) y_offset,&target,&(*image)->exception);
3696  if ((*image)->storage_class == DirectClass)
3697  {
3698  for (y=0; y < (int) (*image)->rows; y++)
3699  {
3700  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3701  (*image)->columns,1,exception);
3702  if (q == (PixelPacket *) NULL)
3703  break;
3704  for (x=0; x < (int) (*image)->columns; x++)
3705  {
3706  if (IsColorSimilar(*image,q,&target) != MagickFalse)
3707  {
3708  q->red=ScaleShortToQuantum(color.red);
3709  q->green=ScaleShortToQuantum(color.green);
3710  q->blue=ScaleShortToQuantum(color.blue);
3711  }
3712  q++;
3713  }
3714  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3715  break;
3716  }
3717  }
3718  else
3719  {
3720  for (i=0; i < (ssize_t) (*image)->colors; i++)
3721  if (IsColorSimilar(*image,(*image)->colormap+i,&target) != MagickFalse)
3722  {
3723  (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3724  (*image)->colormap[i].green=ScaleShortToQuantum(
3725  color.green);
3726  (*image)->colormap[i].blue=ScaleShortToQuantum(
3727  color.blue);
3728  }
3729  (void) SyncImage(*image);
3730  }
3731  break;
3732  }
3733  case FloodfillMethod:
3734  case FillToBorderMethod:
3735  {
3736  DrawInfo
3737  *draw_info;
3738 
3740  target;
3741 
3742  /*
3743  Update color information using floodfill algorithm.
3744  */
3745  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3746  (ssize_t) y_offset,&target,exception);
3747  if (method == FillToBorderMethod)
3748  {
3749  target.red=(MagickRealType)
3750  ScaleShortToQuantum(border_color.red);
3751  target.green=(MagickRealType)
3752  ScaleShortToQuantum(border_color.green);
3753  target.blue=(MagickRealType)
3754  ScaleShortToQuantum(border_color.blue);
3755  }
3756  draw_info=CloneDrawInfo(resource_info->image_info,
3757  (DrawInfo *) NULL);
3758  (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3759  &draw_info->fill,exception);
3760  (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3761  (ssize_t) x_offset,(ssize_t) y_offset,
3762  method == FloodfillMethod ? MagickFalse : MagickTrue);
3763  draw_info=DestroyDrawInfo(draw_info);
3764  break;
3765  }
3766  case ResetMethod:
3767  {
3768  /*
3769  Update color information using reset algorithm.
3770  */
3771  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3772  return(MagickFalse);
3773  for (y=0; y < (int) (*image)->rows; y++)
3774  {
3775  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776  (*image)->columns,1,exception);
3777  if (q == (PixelPacket *) NULL)
3778  break;
3779  for (x=0; x < (int) (*image)->columns; x++)
3780  {
3781  q->red=ScaleShortToQuantum(color.red);
3782  q->green=ScaleShortToQuantum(color.green);
3783  q->blue=ScaleShortToQuantum(color.blue);
3784  q++;
3785  }
3786  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3787  break;
3788  }
3789  break;
3790  }
3791  }
3792  image_view=DestroyCacheView(image_view);
3793  state&=(~UpdateConfigurationState);
3794  }
3795  } while ((state & ExitState) == 0);
3796  (void) XSelectInput(display,windows->image.id,
3797  windows->image.attributes.event_mask);
3798  XSetCursorState(display,windows,MagickFalse);
3799  (void) XFreeCursor(display,cursor);
3800  return(MagickTrue);
3801 }
3802 
3803 /*
3804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3805 % %
3806 % %
3807 % %
3808 + X C o m p o s i t e I m a g e %
3809 % %
3810 % %
3811 % %
3812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3813 %
3814 % XCompositeImage() requests an image name from the user, reads the image and
3815 % composites it with the X window image at a location the user chooses with
3816 % the pointer.
3817 %
3818 % The format of the XCompositeImage method is:
3819 %
3820 % MagickBooleanType XCompositeImage(Display *display,
3821 % XResourceInfo *resource_info,XWindows *windows,Image *image)
3822 %
3823 % A description of each parameter follows:
3824 %
3825 % o display: Specifies a connection to an X server; returned from
3826 % XOpenDisplay.
3827 %
3828 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3829 %
3830 % o windows: Specifies a pointer to a XWindows structure.
3831 %
3832 % o image: the image; returned from ReadImage.
3833 %
3834 */
3835 static MagickBooleanType XCompositeImage(Display *display,
3836  XResourceInfo *resource_info,XWindows *windows,Image *image)
3837 {
3838  const char
3839  *const CompositeMenu[] =
3840  {
3841  "Operators",
3842  "Dissolve",
3843  "Displace",
3844  "Help",
3845  "Dismiss",
3846  (char *) NULL
3847  };
3848 
3849  static char
3850  displacement_geometry[MaxTextExtent] = "30x30",
3851  filename[MaxTextExtent] = "\0";
3852 
3853  static CompositeOperator
3854  compose = CopyCompositeOp;
3855 
3856  static const ModeType
3857  CompositeCommands[] =
3858  {
3859  CompositeOperatorsCommand,
3860  CompositeDissolveCommand,
3861  CompositeDisplaceCommand,
3862  CompositeHelpCommand,
3863  CompositeDismissCommand
3864  };
3865 
3866  char
3867  text[MaxTextExtent];
3868 
3869  Cursor
3870  cursor;
3871 
3872  Image
3873  *composite_image;
3874 
3875  int
3876  entry,
3877  id,
3878  x,
3879  y;
3880 
3881  MagickRealType
3882  blend,
3883  scale_factor;
3884 
3886  highlight_info,
3887  composite_info;
3888 
3889  unsigned int
3890  height,
3891  width;
3892 
3893  size_t
3894  state;
3895 
3896  XEvent
3897  event;
3898 
3899  /*
3900  Request image file name from user.
3901  */
3902  XFileBrowserWidget(display,windows,"Composite",filename);
3903  if (*filename == '\0')
3904  return(MagickTrue);
3905  /*
3906  Read image.
3907  */
3908  XSetCursorState(display,windows,MagickTrue);
3909  XCheckRefreshWindows(display,windows);
3910  (void) CopyMagickString(resource_info->image_info->filename,filename,
3911  MaxTextExtent);
3912  composite_image=ReadImage(resource_info->image_info,&image->exception);
3913  CatchException(&image->exception);
3914  XSetCursorState(display,windows,MagickFalse);
3915  if (composite_image == (Image *) NULL)
3916  return(MagickFalse);
3917  /*
3918  Map Command widget.
3919  */
3920  (void) CloneString(&windows->command.name,"Composite");
3921  windows->command.data=1;
3922  (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3923  (void) XMapRaised(display,windows->command.id);
3924  XClientMessage(display,windows->image.id,windows->im_protocols,
3925  windows->im_update_widget,CurrentTime);
3926  /*
3927  Track pointer until button 1 is pressed.
3928  */
3929  XQueryPosition(display,windows->image.id,&x,&y);
3930  (void) XSelectInput(display,windows->image.id,
3931  windows->image.attributes.event_mask | PointerMotionMask);
3932  composite_info.x=(ssize_t) windows->image.x+x;
3933  composite_info.y=(ssize_t) windows->image.y+y;
3934  composite_info.width=0;
3935  composite_info.height=0;
3936  cursor=XCreateFontCursor(display,XC_ul_angle);
3937  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3938  blend=0.0;
3939  state=DefaultState;
3940  do
3941  {
3942  if (windows->info.mapped != MagickFalse)
3943  {
3944  /*
3945  Display pointer position.
3946  */
3947  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3948  (long) composite_info.x,(long) composite_info.y);
3949  XInfoWidget(display,windows,text);
3950  }
3951  highlight_info=composite_info;
3952  highlight_info.x=composite_info.x-windows->image.x;
3953  highlight_info.y=composite_info.y-windows->image.y;
3954  XHighlightRectangle(display,windows->image.id,
3955  windows->image.highlight_context,&highlight_info);
3956  /*
3957  Wait for next event.
3958  */
3959  XScreenEvent(display,windows,&event);
3960  XHighlightRectangle(display,windows->image.id,
3961  windows->image.highlight_context,&highlight_info);
3962  if (event.xany.window == windows->command.id)
3963  {
3964  /*
3965  Select a command from the Command widget.
3966  */
3967  id=XCommandWidget(display,windows,CompositeMenu,&event);
3968  if (id < 0)
3969  continue;
3970  switch (CompositeCommands[id])
3971  {
3972  case CompositeOperatorsCommand:
3973  {
3974  char
3975  command[MaxTextExtent],
3976  **operators;
3977 
3978  /*
3979  Select a command from the pop-up menu.
3980  */
3981  operators=GetCommandOptions(MagickComposeOptions);
3982  if (operators == (char **) NULL)
3983  break;
3984  entry=XMenuWidget(display,windows,CompositeMenu[id],
3985  (const char **) operators,command);
3986  if (entry >= 0)
3987  compose=(CompositeOperator) ParseCommandOption(
3988  MagickComposeOptions,MagickFalse,operators[entry]);
3989  operators=DestroyStringList(operators);
3990  break;
3991  }
3992  case CompositeDissolveCommand:
3993  {
3994  static char
3995  factor[MaxTextExtent] = "20.0";
3996 
3997  /*
3998  Dissolve the two images a given percent.
3999  */
4000  (void) XSetFunction(display,windows->image.highlight_context,
4001  GXcopy);
4002  (void) XDialogWidget(display,windows,"Dissolve",
4003  "Enter the blend factor (0.0 - 99.9%):",factor);
4004  (void) XSetFunction(display,windows->image.highlight_context,
4005  GXinvert);
4006  if (*factor == '\0')
4007  break;
4008  blend=StringToDouble(factor,(char **) NULL);
4009  compose=DissolveCompositeOp;
4010  break;
4011  }
4012  case CompositeDisplaceCommand:
4013  {
4014  /*
4015  Get horizontal and vertical scale displacement geometry.
4016  */
4017  (void) XSetFunction(display,windows->image.highlight_context,
4018  GXcopy);
4019  (void) XDialogWidget(display,windows,"Displace",
4020  "Enter the horizontal and vertical scale:",displacement_geometry);
4021  (void) XSetFunction(display,windows->image.highlight_context,
4022  GXinvert);
4023  if (*displacement_geometry == '\0')
4024  break;
4025  compose=DisplaceCompositeOp;
4026  break;
4027  }
4028  case CompositeHelpCommand:
4029  {
4030  (void) XSetFunction(display,windows->image.highlight_context,
4031  GXcopy);
4032  XTextViewHelp(display,resource_info,windows,MagickFalse,
4033  "Help Viewer - Image Composite",ImageCompositeHelp);
4034  (void) XSetFunction(display,windows->image.highlight_context,
4035  GXinvert);
4036  break;
4037  }
4038  case CompositeDismissCommand:
4039  {
4040  /*
4041  Prematurely exit.
4042  */
4043  state|=EscapeState;
4044  state|=ExitState;
4045  break;
4046  }
4047  default:
4048  break;
4049  }
4050  continue;
4051  }
4052  switch (event.type)
4053  {
4054  case ButtonPress:
4055  {
4056  if (resource_info->debug != MagickFalse)
4057  (void) LogMagickEvent(X11Event,GetMagickModule(),
4058  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4059  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4060  if (event.xbutton.button != Button1)
4061  break;
4062  if (event.xbutton.window != windows->image.id)
4063  break;
4064  /*
4065  Change cursor.
4066  */
4067  composite_info.width=composite_image->columns;
4068  composite_info.height=composite_image->rows;
4069  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4070  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4071  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4072  break;
4073  }
4074  case ButtonRelease:
4075  {
4076  if (resource_info->debug != MagickFalse)
4077  (void) LogMagickEvent(X11Event,GetMagickModule(),
4078  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4079  event.xbutton.button,event.xbutton.x,event.xbutton.y);
4080  if (event.xbutton.button != Button1)
4081  break;
4082  if (event.xbutton.window != windows->image.id)
4083  break;
4084  if ((composite_info.width != 0) && (composite_info.height != 0))
4085  {
4086  /*
4087  User has selected the location of the composite image.
4088  */
4089  composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4090  composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4091  state|=ExitState;
4092  }
4093  break;
4094  }
4095  case Expose:
4096  break;
4097  case KeyPress:
4098  {
4099  char
4100  command[MaxTextExtent];
4101 
4102  KeySym
4103  key_symbol;
4104 
4105  int
4106  length;
4107 
4108  if (event.xkey.window != windows->image.id)
4109  break;
4110  /*
4111  Respond to a user key press.
4112  */
4113  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4114  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4115  *(command+length)='\0';
4116  if (resource_info->debug != MagickFalse)
4117  (void) LogMagickEvent(X11Event,GetMagickModule(),
4118  "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4119  switch ((int) key_symbol)
4120  {
4121  case XK_Escape:
4122  case XK_F20:
4123  {
4124  /*
4125  Prematurely exit.
4126  */
4127  composite_image=DestroyImage(composite_image);
4128  state|=EscapeState;
4129  state|=ExitState;
4130  break;
4131  }
4132  case XK_F1:
4133  case XK_Help:
4134  {
4135  (void) XSetFunction(display,windows->image.highlight_context,
4136  GXcopy);
4137  XTextViewHelp(display,resource_info,windows,MagickFalse,
4138  "Help Viewer - Image Composite",ImageCompositeHelp);
4139  (void) XSetFunction(display,windows->image.highlight_context,
4140  GXinvert);
4141  break;
4142  }
4143  default:
4144  {
4145  (void) XBell(display,0);
4146  break;
4147  }
4148  }
4149  break;
4150  }
4151  case MotionNotify:
4152  {
4153  /*
4154  Map and unmap Info widget as text cursor crosses its boundaries.
4155  */
4156  x=event.xmotion.x;
4157  y=event.xmotion.y;
4158  if (windows->info.mapped != MagickFalse)
4159  {
4160  if ((x < (int) (windows->info.x+windows->info.width)) &&
4161  (y < (int) (windows->info.y+windows->info.height)))
4162  (void) XWithdrawWindow(display,windows->info.id,
4163  windows->info.screen);
4164  }
4165  else
4166  if ((x > (int) (windows->info.x+windows->info.width)) ||
4167  (y > (int) (windows->info.y+windows->info.height)))
4168  (void) XMapWindow(display,windows->info.id);
4169  composite_info.x=(ssize_t) windows->image.x+x;
4170  composite_info.y=(ssize_t) windows->image.y+y;
4171  break;
4172  }
4173  default:
4174  {
4175  if (resource_info->debug != MagickFalse)
4176  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4177  event.type);
4178  break;
4179  }
4180  }
4181  } while ((state & ExitState) == 0);
4182  (void) XSelectInput(display,windows->image.id,
4183  windows->image.attributes.event_mask);
4184  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4185  XSetCursorState(display,windows,MagickFalse);
4186  (void) XFreeCursor(display,cursor);
4187  if ((state & EscapeState) != 0)
4188  return(MagickTrue);
4189  /*
4190  Image compositing is relative to image configuration.
4191  */
4192  XSetCursorState(display,windows,MagickTrue);
4193  XCheckRefreshWindows(display,windows);
4194  width=(unsigned int) image->columns;
4195  height=(unsigned int) image->rows;
4196  x=0;
4197  y=0;
4198  if (windows->image.crop_geometry != (char *) NULL)
4199  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4200  scale_factor=(MagickRealType) width/windows->image.ximage->width;
4201  composite_info.x+=x;
4202  composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4203  composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4204  scale_factor=(MagickRealType) height/windows->image.ximage->height;
4205  composite_info.y+=y;
4206  composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4207  composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4208  if ((composite_info.width != composite_image->columns) ||
4209  (composite_info.height != composite_image->rows))
4210  {
4211  Image
4212  *resize_image;
4213 
4214  /*
4215  Scale composite image.
4216  */
4217  resize_image=ResizeImage(composite_image,composite_info.width,
4218  composite_info.height,composite_image->filter,composite_image->blur,
4219  &image->exception);
4220  composite_image=DestroyImage(composite_image);
4221  if (resize_image == (Image *) NULL)
4222  {
4223  XSetCursorState(display,windows,MagickFalse);
4224  return(MagickFalse);
4225  }
4226  composite_image=resize_image;
4227  }
4228  if (compose == DisplaceCompositeOp)
4229  (void) SetImageArtifact(composite_image,"compose:args",
4230  displacement_geometry);
4231  if (blend != 0.0)
4232  {
4233  CacheView
4234  *image_view;
4235 
4237  *exception;
4238 
4239  int
4240  y;
4241 
4242  Quantum
4243  opacity;
4244 
4245  int
4246  x;
4247 
4248  PixelPacket
4249  *q;
4250 
4251  /*
4252  Create mattes for blending.
4253  */
4254  (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4255  opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4256  ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4257  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4258  return(MagickFalse);
4259  image->matte=MagickTrue;
4260  exception=(&image->exception);
4261  image_view=AcquireAuthenticCacheView(image,exception);
4262  for (y=0; y < (int) image->rows; y++)
4263  {
4264  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4265  exception);
4266  if (q == (PixelPacket *) NULL)
4267  break;
4268  for (x=0; x < (int) image->columns; x++)
4269  {
4270  q->opacity=opacity;
4271  q++;
4272  }
4273  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4274  break;
4275  }
4276  image_view=DestroyCacheView(image_view);
4277  }
4278  /*
4279  Composite image with X Image window.
4280  */
4281  (void) CompositeImage(image,compose,composite_image,composite_info.x,
4282  composite_info.y);
4283  composite_image=DestroyImage(composite_image);
4284  XSetCursorState(display,windows,MagickFalse);
4285  /*
4286  Update image configuration.
4287  */
4288  XConfigureImageColormap(display,resource_info,windows,image);
4289  (void) XConfigureImage(display,resource_info,windows,image);
4290  return(MagickTrue);
4291 }
4292 
4293 /*
4294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4295 % %
4296 % %
4297 % %
4298 + X C o n f i g u r e I m a g e %
4299 % %
4300 % %
4301 % %
4302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4303 %
4304 % XConfigureImage() creates a new X image. It also notifies the window
4305 % manager of the new image size and configures the transient widows.
4306 %
4307 % The format of the XConfigureImage method is:
4308 %
4309 % MagickBooleanType XConfigureImage(Display *display,
4310 % XResourceInfo *resource_info,XWindows *windows,Image *image)
4311 %
4312 % A description of each parameter follows:
4313 %
4314 % o display: Specifies a connection to an X server; returned from
4315 % XOpenDisplay.
4316 %
4317 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4318 %
4319 % o windows: Specifies a pointer to a XWindows structure.
4320 %
4321 % o image: the image.
4322 %
4323 %
4324 */
4325 static MagickBooleanType XConfigureImage(Display *display,
4326  XResourceInfo *resource_info,XWindows *windows,Image *image)
4327 {
4328  char
4329  geometry[MaxTextExtent];
4330 
4331  MagickStatusType
4332  status;
4333 
4334  size_t
4335  mask,
4336  height,
4337  width;
4338 
4339  ssize_t
4340  x,
4341  y;
4342 
4343  XSizeHints
4344  *size_hints;
4345 
4346  XWindowChanges
4347  window_changes;
4348 
4349  /*
4350  Dismiss if window dimensions are zero.
4351  */
4352  width=(unsigned int) windows->image.window_changes.width;
4353  height=(unsigned int) windows->image.window_changes.height;
4354  if (resource_info->debug != MagickFalse)
4355  (void) LogMagickEvent(X11Event,GetMagickModule(),
4356  "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4357  windows->image.ximage->height,(double) width,(double) height);
4358  if ((width*height) == 0)
4359  return(MagickTrue);
4360  x=0;
4361  y=0;
4362  /*
4363  Resize image to fit Image window dimensions.
4364  */
4365  XSetCursorState(display,windows,MagickTrue);
4366  (void) XFlush(display);
4367  if (((int) width != windows->image.ximage->width) ||
4368  ((int) height != windows->image.ximage->height))
4369  image->taint=MagickTrue;
4370  windows->magnify.x=(int)
4371  width*windows->magnify.x/windows->image.ximage->width;
4372  windows->magnify.y=(int)
4373  height*windows->magnify.y/windows->image.ximage->height;
4374  windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4375  windows->image.y=(int)
4376  (height*windows->image.y/windows->image.ximage->height);
4377  status=XMakeImage(display,resource_info,&windows->image,image,
4378  (unsigned int) width,(unsigned int) height);
4379  if (status == MagickFalse)
4380  XNoticeWidget(display,windows,"Unable to configure X image:",
4381  windows->image.name);
4382  /*
4383  Notify window manager of the new configuration.
4384  */
4385  if (resource_info->image_geometry != (char *) NULL)
4386  (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4387  resource_info->image_geometry);
4388  else
4389  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4390  XDisplayWidth(display,windows->image.screen),
4391  XDisplayHeight(display,windows->image.screen));
4392  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4393  window_changes.width=(int) width;
4394  if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4395  window_changes.width=XDisplayWidth(display,windows->image.screen);
4396  window_changes.height=(int) height;
4397  if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4398  window_changes.height=XDisplayHeight(display,windows->image.screen);
4399  mask=(size_t) (CWWidth | CWHeight);
4400  if (resource_info->backdrop)
4401  {
4402  mask|=CWX | CWY;
4403  window_changes.x=(int)
4404  ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4405  window_changes.y=(int)
4406  ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4407  }
4408  (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4409  (unsigned int) mask,&window_changes);
4410  (void) XClearWindow(display,windows->image.id);
4411  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4412  /*
4413  Update Magnify window configuration.
4414  */
4415  if (windows->magnify.mapped != MagickFalse)
4416  XMakeMagnifyImage(display,windows);
4417  windows->pan.crop_geometry=windows->image.crop_geometry;
4418  XBestIconSize(display,&windows->pan,image);
4419  while (((windows->pan.width << 1) < MaxIconSize) &&
4420  ((windows->pan.height << 1) < MaxIconSize))
4421  {
4422  windows->pan.width<<=1;
4423  windows->pan.height<<=1;
4424  }
4425  if (windows->pan.geometry != (char *) NULL)
4426  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4427  &windows->pan.width,&windows->pan.height);
4428  window_changes.width=(int) windows->pan.width;
4429  window_changes.height=(int) windows->pan.height;
4430  size_hints=XAllocSizeHints();
4431  if (size_hints != (XSizeHints *) NULL)
4432  {
4433  /*
4434  Set new size hints.
4435  */
4436  size_hints->flags=PSize | PMinSize | PMaxSize;
4437  size_hints->width=window_changes.width;
4438  size_hints->height=window_changes.height;
4439  size_hints->min_width=size_hints->width;
4440  size_hints->min_height=size_hints->height;
4441  size_hints->max_width=size_hints->width;
4442  size_hints->max_height=size_hints->height;
4443  (void) XSetNormalHints(display,windows->pan.id,size_hints);
4444  (void) XFree((void *) size_hints);
4445  }
4446  (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4447  (unsigned int) (CWWidth | CWHeight),&window_changes);
4448  /*
4449  Update icon window configuration.
4450  */
4451  windows->icon.crop_geometry=windows->image.crop_geometry;
4452  XBestIconSize(display,&windows->icon,image);
4453  window_changes.width=(int) windows->icon.width;
4454  window_changes.height=(int) windows->icon.height;
4455  (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4456  (unsigned int) (CWWidth | CWHeight),&window_changes);
4457  XSetCursorState(display,windows,MagickFalse);
4458  return(status != 0 ? MagickTrue : MagickFalse);
4459 }
4460 
4461 /*
4462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463 % %
4464 % %
4465 % %
4466 + X C r o p I m a g e %
4467 % %
4468 % %
4469 % %
4470 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4471 %
4472 % XCropImage() allows the user to select a region of the image and crop, copy,
4473 % or cut it. For copy or cut, the image can subsequently be composited onto
4474 % the image with XPasteImage.
4475 %
4476 % The format of the XCropImage method is:
4477 %
4478 % MagickBooleanType XCropImage(Display *display,
4479 % XResourceInfo *resource_info,XWindows *windows,Image *image,
4480 % const ClipboardMode mode)
4481 %
4482 % A description of each parameter follows:
4483 %
4484 % o display: Specifies a connection to an X server; returned from
4485 % XOpenDisplay.
4486 %
4487 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4488 %
4489 % o windows: Specifies a pointer to a XWindows structure.
4490 %
4491 % o image: the image; returned from ReadImage.
4492 %
4493 % o mode: This unsigned value specified whether the image should be
4494 % cropped, copied, or cut.
4495 %
4496 */
4497 static MagickBooleanType XCropImage(Display *display,
4498  XResourceInfo *resource_info,XWindows *windows,Image *image,
4499  const ClipboardMode mode)
4500 {
4501  static const char
4502  *CropModeMenu[] =
4503  {
4504  "Help",
4505  "Dismiss",
4506  (char *) NULL
4507  },
4508  *RectifyModeMenu[] =
4509  {
4510  "Crop",
4511  "Help",
4512  "Dismiss",
4513  (char *) NULL
4514  };
4515 
4516  static const ModeType
4517  CropCommands[] =
4518  {
4519  CropHelpCommand,
4520  CropDismissCommand
4521  },
4522  RectifyCommands[] =
4523  {
4524  RectifyCopyCommand,
4525  RectifyHelpCommand,
4526  RectifyDismissCommand
4527  };
4528 
4529  CacheView
4530  *image_view;
4531 
4532  char
4533  command[MaxTextExtent],
4534  text[MaxTextExtent];
4535 
4536  Cursor
4537  cursor;
4538 
4540  *exception;
4541 
4542  int
4543  id,
4544  x,
4545  y;
4546 
4547  KeySym
4548  key_symbol;
4549 
4550  Image
4551  *crop_image;
4552 
4553  MagickRealType
4554  scale_factor;
4555 
4557  crop_info,
4558  highlight_info;
4559 
4560  PixelPacket
4561  *q;
4562 
4563  unsigned int
4564  height,
4565  width;
4566 
4567  size_t
4568  state;
4569 
4570  XEvent
4571  event;
4572 
4573  /*
4574  Map Command widget.
4575  */
4576  switch (mode)
4577  {
4578  case CopyMode:
4579  {
4580  (void) CloneString(&windows->command.name,"Copy");
4581  break;
4582  }
4583  case CropMode:
4584  {
4585  (void) CloneString(&windows->command.name,"Crop");
4586  break;
4587  }
4588  case CutMode:
4589  {
4590  (void) CloneString(&windows->command.name,"Cut");
4591  break;
4592  }
4593  }
4594  RectifyModeMenu[0]=windows->command.name;
4595  windows->command.data=0;
4596  (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4597  (void) XMapRaised(display,windows->command.id);
4598  XClientMessage(display,windows->image.id,windows->im_protocols,
4599  windows->im_update_widget,CurrentTime);
4600  /*
4601  Track pointer until button 1 is pressed.
4602  */
4603  XQueryPosition(display,windows->image.id,&x,&y);
4604  (void) XSelectInput(display,windows->image.id,
4605  windows->image.attributes.event_mask | PointerMotionMask);
4606  crop_info.x=(ssize_t) windows->image.x+x;
4607  crop_info.y=(ssize_t) windows->image.y+y;
4608  crop_info.width=0;
4609  crop_info.height=0;
4610  cursor=XCreateFontCursor(display,XC_fleur);
4611  state=DefaultState;
4612  do
4613  {
4614  if (windows->info.mapped != MagickFalse)
4615  {
4616  /*
4617  Display pointer position.
4618  */
4619  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4620  (long) crop_info.x,(long) crop_info.y);
4621  XInfoWidget(display,windows,text);
4622  }
4623  /*
4624  Wait for next event.
4625  */
4626  XScreenEvent(display,windows,&event);
4627  if (event.xany.window == windows->command.id)
4628  {
4629  /*
4630  Select a command from the Command widget.
4631  */
4632  id=XCommandWidget(display,windows,CropModeMenu,&event);
4633  if (id < 0)
4634  continue;
4635  switch (CropCommands[id])
4636  {
4637  case CropHelpCommand:
4638  {
4639  switch (mode)
4640  {
4641  case CopyMode:
4642  {
4643  XTextViewHelp(display,resource_info,windows,MagickFalse,
4644  "Help Viewer - Image Copy",ImageCopyHelp);
4645  break;
4646  }
4647  case CropMode:
4648  {
4649  XTextViewHelp(display,resource_info,windows,MagickFalse,
4650  "Help Viewer - Image Crop",ImageCropHelp);
4651  break;
4652  }
4653  case CutMode:
4654  {
4655  XTextViewHelp(display,resource_info,windows,MagickFalse,
4656  "Help Viewer - Image Cut",ImageCutHelp);
4657  break;
4658  }
4659  }
4660  break;
4661  }
4662  case CropDismissCommand:
4663  {
4664  /*
4665  Prematurely exit.
4666  */
4667  state|=EscapeState;
4668  state|=ExitState;
4669  break;
4670  }
4671  default:
4672  break;
4673  }
4674  continue;
4675  }
4676  switch (event.type)
4677  {
4678  case ButtonPress:
4679  {
4680  if (event.xbutton.button != Button1)
4681  break;
4682  if (event.xbutton.window != windows->image.id)
4683  break;
4684  /*
4685  Note first corner of cropping rectangle-- exit loop.
4686  */
4687  (void) XCheckDefineCursor(display,windows->image.id,cursor);
4688  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4689  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4690  state|=ExitState;
4691  break;
4692  }
4693  case ButtonRelease:
4694  break;
4695  case Expose:
4696  break;
4697  case KeyPress:
4698  {
4699  if (event.xkey.window != windows->image.id)
4700  break;
4701  /*
4702  Respond to a user key press.
4703  */
4704  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4705  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4706  switch ((int) key_symbol)
4707  {
4708  case XK_Escape:
4709  case XK_F20:
4710  {
4711  /*
4712  Prematurely exit.
4713  */
4714  state|=EscapeState;
4715  state|=ExitState;
4716  break;
4717  }
4718  case XK_F1:
4719  case XK_Help:
4720  {
4721  switch (mode)
4722  {
4723  case CopyMode:
4724  {
4725  XTextViewHelp(display,resource_info,windows,MagickFalse,
4726  "Help Viewer - Image Copy",ImageCopyHelp);
4727  break;
4728  }
4729  case CropMode:
4730  {
4731  XTextViewHelp(display,resource_info,windows,MagickFalse,
4732  "Help Viewer - Image Crop",ImageCropHelp);
4733  break;
4734  }
4735  case CutMode:
4736  {
4737  XTextViewHelp(display,resource_info,windows,MagickFalse,
4738  "Help Viewer - Image Cut",ImageCutHelp);
4739  break;
4740  }
4741  }
4742  break;
4743  }
4744  default:
4745  {
4746  (void) XBell(display,0);
4747  break;
4748  }
4749  }
4750  break;
4751  }
4752  case MotionNotify:
4753  {
4754  if (event.xmotion.window != windows->image.id)
4755  break;
4756  /*
4757  Map and unmap Info widget as text cursor crosses its boundaries.
4758  */
4759  x=event.xmotion.x;
4760  y=event.xmotion.y;
4761  if (windows->info.mapped != MagickFalse)
4762  {
4763  if ((x < (int) (windows->info.x+windows->info.width)) &&
4764  (y < (int) (windows->info.y+windows->info.height)))
4765  (void) XWithdrawWindow(display,windows->info.id,
4766  windows->info.screen);
4767  }
4768  else
4769  if ((x > (int) (windows->info.x+windows->info.width)) ||
4770  (y > (int) (windows->info.y+windows->info.height)))
4771  (void) XMapWindow(display,windows->info.id);
4772  crop_info.x=(ssize_t) windows->image.x+x;
4773  crop_info.y=(ssize_t) windows->image.y+y;
4774  break;
4775  }
4776  default:
4777  break;
4778  }
4779  } while ((state & ExitState) == 0);
4780  (void) XSelectInput(display,windows->image.id,
4781  windows->image.attributes.event_mask);
4782  if ((state & EscapeState) != 0)
4783  {
4784  /*
4785  User want to exit without cropping.
4786  */
4787  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4788  (void) XFreeCursor(display,cursor);
4789  return(MagickTrue);
4790  }
4791  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4792  do
4793  {
4794  /*
4795  Size rectangle as pointer moves until the mouse button is released.
4796  */
4797  x=(int) crop_info.x;
4798  y=(int) crop_info.y;
4799  crop_info.width=0;
4800  crop_info.height=0;
4801  state=DefaultState;
4802  do
4803  {
4804  highlight_info=crop_info;
4805  highlight_info.x=crop_info.x-windows->image.x;
4806  highlight_info.y=crop_info.y-windows->image.y;
4807  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4808  {
4809  /*
4810  Display info and draw cropping rectangle.
4811  */
4812  if (windows->info.mapped == MagickFalse)
4813  (void) XMapWindow(display,windows->info.id);
4814  (void) FormatLocaleString(text,MaxTextExtent,
4815  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4816  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4817  XInfoWidget(display,windows,text);
4818  XHighlightRectangle(display,windows->image.id,
4819  windows->image.highlight_context,&highlight_info);
4820  }
4821  else
4822  if (windows->info.mapped != MagickFalse)
4823  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4824  /*
4825  Wait for next event.
4826  */
4827  XScreenEvent(display,windows,&event);
4828  if ((highlight_info.width > 3) && (highlight_info.height > 3))
4829  XHighlightRectangle(display,windows->image.id,
4830  windows->image.highlight_context,&highlight_info);
4831  switch (event.type)
4832  {
4833  case ButtonPress:
4834  {
4835  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4836  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4837  break;
4838  }
4839  case ButtonRelease:
4840  {
4841  /*
4842  User has committed to cropping rectangle.
4843  */
4844  crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4845  crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4846  XSetCursorState(display,windows,MagickFalse);
4847  state|=ExitState;
4848  windows->command.data=0;
4849  (void) XCommandWidget(display,windows,RectifyModeMenu,
4850  (XEvent *) NULL);
4851  break;
4852  }
4853  case Expose:
4854  break;
4855  case MotionNotify:
4856  {
4857  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4858  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4859  }
4860  default:
4861  break;
4862  }
4863  if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4864  ((state & ExitState) != 0))
4865  {
4866  /*
4867  Check boundary conditions.
4868  */
4869  if (crop_info.x < 0)
4870  crop_info.x=0;
4871  else
4872  if (crop_info.x > (ssize_t) windows->image.ximage->width)
4873  crop_info.x=(ssize_t) windows->image.ximage->width;
4874  if ((int) crop_info.x < x)
4875  crop_info.width=(unsigned int) (x-crop_info.x);
4876  else
4877  {
4878  crop_info.width=(unsigned int) (crop_info.x-x);
4879  crop_info.x=(ssize_t) x;
4880  }
4881  if (crop_info.y < 0)
4882  crop_info.y=0;
4883  else
4884  if (crop_info.y > (ssize_t) windows->image.ximage->height)
4885  crop_info.y=(ssize_t) windows->image.ximage->height;
4886  if ((int) crop_info.y < y)
4887  crop_info.height=(unsigned int) (y-crop_info.y);
4888  else
4889  {
4890  crop_info.height=(unsigned int) (crop_info.y-y);
4891  crop_info.y=(ssize_t) y;
4892  }
4893  }
4894  } while ((state & ExitState) == 0);
4895  /*
4896  Wait for user to grab a corner of the rectangle or press return.
4897  */
4898  state=DefaultState;
4899  (void) XMapWindow(display,windows->info.id);
4900  do
4901  {
4902  if (windows->info.mapped != MagickFalse)
4903  {
4904  /*
4905  Display pointer position.
4906  */
4907  (void) FormatLocaleString(text,MaxTextExtent,
4908  " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4909  crop_info.height,(double) crop_info.x,(double) crop_info.y);
4910  XInfoWidget(display,windows,text);
4911  }
4912  highlight_info=crop_info;
4913  highlight_info.x=crop_info.x-windows->image.x;
4914  highlight_info.y=crop_info.y-windows->image.y;
4915  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4916  {
4917  state|=EscapeState;
4918  state|=ExitState;
4919  break;
4920  }
4921  XHighlightRectangle(display,windows->image.id,
4922  windows->image.highlight_context,&highlight_info);
4923  XScreenEvent(display,windows,&event);
4924  if (event.xany.window == windows->command.id)
4925  {
4926  /*
4927  Select a command from the Command widget.
4928  */
4929  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4930  id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4931  (void) XSetFunction(display,windows->image.highlight_context,
4932  GXinvert);
4933  XHighlightRectangle(display,windows->image.id,
4934  windows->image.highlight_context,&highlight_info);
4935  if (id >= 0)
4936  switch (RectifyCommands[id])
4937  {
4938  case RectifyCopyCommand:
4939  {
4940  state|=ExitState;
4941  break;
4942  }
4943  case RectifyHelpCommand:
4944  {
4945  (void) XSetFunction(display,windows->image.highlight_context,
4946  GXcopy);
4947  switch (mode)
4948  {
4949  case CopyMode:
4950  {
4951  XTextViewHelp(display,resource_info,windows,MagickFalse,
4952  "Help Viewer - Image Copy",ImageCopyHelp);
4953  break;
4954  }
4955  case CropMode:
4956  {
4957  XTextViewHelp(display,resource_info,windows,MagickFalse,
4958  "Help Viewer - Image Crop",ImageCropHelp);
4959  break;
4960  }
4961  case CutMode:
4962  {
4963  XTextViewHelp(display,resource_info,windows,MagickFalse,
4964  "Help Viewer - Image Cut",ImageCutHelp);
4965  break;
4966  }
4967  }
4968  (void) XSetFunction(display,windows->image.highlight_context,
4969  GXinvert);
4970  break;
4971  }
4972  case RectifyDismissCommand:
4973  {
4974  /*
4975  Prematurely exit.
4976  */
4977  state|=EscapeState;
4978  state|=ExitState;
4979  break;
4980  }
4981  default:
4982  break;
4983  }
4984  continue;
4985  }
4986  XHighlightRectangle(display,windows->image.id,
4987  windows->image.highlight_context,&highlight_info);
4988  switch (event.type)
4989  {
4990  case ButtonPress:
4991  {
4992  if (event.xbutton.button != Button1)
4993  break;
4994  if (event.xbutton.window != windows->image.id)
4995  break;
4996  x=windows->image.x+event.xbutton.x;
4997  y=windows->image.y+event.xbutton.y;
4998  if ((x < (int) (crop_info.x+RoiDelta)) &&
4999  (x > (int) (crop_info.x-RoiDelta)) &&
5000  (y < (int) (crop_info.y+RoiDelta)) &&
5001  (y > (int) (crop_info.y-RoiDelta)))
5002  {
5003  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5004  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5005  state|=UpdateConfigurationState;
5006  break;
5007  }
5008  if ((x < (int) (crop_info.x+RoiDelta)) &&
5009  (x > (int) (crop_info.x-RoiDelta)) &&
5010  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5011  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5012  {
5013  crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5014  state|=UpdateConfigurationState;
5015  break;
5016  }
5017  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5018  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5019  (y < (int) (crop_info.y+RoiDelta)) &&
5020  (y > (int) (crop_info.y-RoiDelta)))
5021  {
5022  crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5023  state|=UpdateConfigurationState;
5024  break;
5025  }
5026  if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5027  (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5028  (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5029  (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5030  {
5031  state|=UpdateConfigurationState;
5032  break;
5033  }
5034  magick_fallthrough;
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  {
5077  state|=EscapeState;
5078  magick_fallthrough;
5079  }
5080  case XK_Return:
5081  {
5082  state|=ExitState;
5083  break;
5084  }
5085  case XK_Home:
5086  case XK_KP_Home:
5087  {
5088  crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/
5089  2L);
5090  crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/
5091  2L);
5092  break;
5093  }
5094  case XK_Left:
5095  case XK_KP_Left:
5096  {
5097  crop_info.x--;
5098  break;
5099  }
5100  case XK_Up:
5101  case XK_KP_Up:
5102  case XK_Next:
5103  {
5104  crop_info.y--;
5105  break;
5106  }
5107  case XK_Right:
5108  case XK_KP_Right:
5109  {
5110  crop_info.x++;
5111  break;
5112  }
5113  case XK_Prior:
5114  case XK_Down:
5115  case XK_KP_Down:
5116  {
5117  crop_info.y++;
5118  break;
5119  }
5120  case XK_F1:
5121  case XK_Help:
5122  {
5123  (void) XSetFunction(display,windows->image.highlight_context,
5124  GXcopy);
5125  switch (mode)
5126  {
5127  case CopyMode:
5128  {
5129  XTextViewHelp(display,resource_info,windows,MagickFalse,
5130  "Help Viewer - Image Copy",ImageCopyHelp);
5131  break;
5132  }
5133  case CropMode:
5134  {
5135  XTextViewHelp(display,resource_info,windows,MagickFalse,
5136  "Help Viewer - Image Cropg",ImageCropHelp);
5137  break;
5138  }
5139  case CutMode:
5140  {
5141  XTextViewHelp(display,resource_info,windows,MagickFalse,
5142  "Help Viewer - Image Cutg",ImageCutHelp);
5143  break;
5144  }
5145  }
5146  (void) XSetFunction(display,windows->image.highlight_context,
5147  GXinvert);
5148  break;
5149  }
5150  default:
5151  {
5152  (void) XBell(display,0);
5153  break;
5154  }
5155  }
5156  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5157  event.xkey.time);
5158  break;
5159  }
5160  case KeyRelease:
5161  break;
5162  case MotionNotify:
5163  {
5164  if (event.xmotion.window != windows->image.id)
5165  break;
5166  /*
5167  Map and unmap Info widget as text cursor crosses its boundaries.
5168  */
5169  x=event.xmotion.x;
5170  y=event.xmotion.y;
5171  if (windows->info.mapped != MagickFalse)
5172  {
5173  if ((x < (int) (windows->info.x+windows->info.width)) &&
5174  (y < (int) (windows->info.y+windows->info.height)))
5175  (void) XWithdrawWindow(display,windows->info.id,
5176  windows->info.screen);
5177  }
5178  else
5179  if ((x > (int) (windows->info.x+windows->info.width)) ||
5180  (y > (int) (windows->info.y+windows->info.height)))
5181  (void) XMapWindow(display,windows->info.id);
5182  crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5183  crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5184  break;
5185  }
5186  case SelectionRequest:
5187  {
5188  XSelectionEvent
5189  notify;
5190 
5191  XSelectionRequestEvent
5192  *request;
5193 
5194  /*
5195  Set primary selection.
5196  */
5197  (void) FormatLocaleString(text,MaxTextExtent,
5198  "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5199  crop_info.height,(double) crop_info.x,(double) crop_info.y);
5200  request=(&(event.xselectionrequest));
5201  (void) XChangeProperty(request->display,request->requestor,
5202  request->property,request->target,8,PropModeReplace,
5203  (unsigned char *) text,(int) strlen(text));
5204  notify.type=SelectionNotify;
5205  notify.display=request->display;
5206  notify.requestor=request->requestor;
5207  notify.selection=request->selection;
5208  notify.target=request->target;
5209  notify.time=request->time;
5210  if (request->property == None)
5211  notify.property=request->target;
5212  else
5213  notify.property=request->property;
5214  (void) XSendEvent(request->display,request->requestor,False,0,
5215  (XEvent *) &notify);
5216  }
5217  default:
5218  break;
5219  }
5220  if ((state & UpdateConfigurationState) != 0)
5221  {
5222  (void) XPutBackEvent(display,&event);
5223  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5224  break;
5225  }
5226  } while ((state & ExitState) == 0);
5227  } while ((state & ExitState) == 0);
5228  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5229  XSetCursorState(display,windows,MagickFalse);
5230  if ((state & EscapeState) != 0)
5231  return(MagickTrue);
5232  if (mode == CropMode)
5233  if (((int) crop_info.width != windows->image.ximage->width) ||
5234  ((int) crop_info.height != windows->image.ximage->height))
5235  {
5236  /*
5237  Reconfigure Image window as defined by cropping rectangle.
5238  */
5239  XSetCropGeometry(display,windows,&crop_info,image);
5240  windows->image.window_changes.width=(int) crop_info.width;
5241  windows->image.window_changes.height=(int) crop_info.height;
5242  (void) XConfigureImage(display,resource_info,windows,image);
5243  return(MagickTrue);
5244  }
5245  /*
5246  Copy image before applying image transforms.
5247  */
5248  XSetCursorState(display,windows,MagickTrue);
5249  XCheckRefreshWindows(display,windows);
5250  width=(unsigned int) image->columns;
5251  height=(unsigned int) image->rows;
5252  x=0;
5253  y=0;
5254  if (windows->image.crop_geometry != (char *) NULL)
5255  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5256  scale_factor=(MagickRealType) width/windows->image.ximage->width;
5257  crop_info.x+=x;
5258  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5259  crop_info.x+=image->page.x;
5260  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5261  scale_factor=(MagickRealType) height/windows->image.ximage->height;
5262  crop_info.y+=y;
5263  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5264  crop_info.y+=image->page.y;
5265  crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5266  crop_image=CropImage(image,&crop_info,&image->exception);
5267  XSetCursorState(display,windows,MagickFalse);
5268  if (crop_image == (Image *) NULL)
5269  return(MagickFalse);
5270  if (resource_info->copy_image != (Image *) NULL)
5271  resource_info->copy_image=DestroyImage(resource_info->copy_image);
5272  resource_info->copy_image=crop_image;
5273  if (mode == CopyMode)
5274  {
5275  (void) XConfigureImage(display,resource_info,windows,image);
5276  return(MagickTrue);
5277  }
5278  /*
5279  Cut image.
5280  */
5281  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5282  return(MagickFalse);
5283  image->matte=MagickTrue;
5284  exception=(&image->exception);
5285  image_view=AcquireAuthenticCacheView(image,exception);
5286  for (y=0; y < (int) crop_info.height; y++)
5287  {
5288  q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5289  crop_info.width,1,exception);
5290  if (q == (PixelPacket *) NULL)
5291  break;
5292  for (x=0; x < (int) crop_info.width; x++)
5293  {
5294  q->opacity=(Quantum) TransparentOpacity;
5295  q++;
5296  }
5297  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5298  break;
5299  }
5300  image_view=DestroyCacheView(image_view);
5301  /*
5302  Update image configuration.
5303  */
5304  XConfigureImageColormap(display,resource_info,windows,image);
5305  (void) XConfigureImage(display,resource_info,windows,image);
5306  return(MagickTrue);
5307 }
5308 
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 % %
5312 % %
5313 % %
5314 + X D r a w I m a g e %
5315 % %
5316 % %
5317 % %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 % XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321 % the image.
5322 %
5323 % The format of the XDrawEditImage method is:
5324 %
5325 % MagickBooleanType XDrawEditImage(Display *display,
5326 % XResourceInfo *resource_info,XWindows *windows,Image **image)
5327 %
5328 % A description of each parameter follows:
5329 %
5330 % o display: Specifies a connection to an X server; returned from
5331 % XOpenDisplay.
5332 %
5333 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334 %
5335 % o windows: Specifies a pointer to a XWindows structure.
5336 %
5337 % o image: the image.
5338 %
5339 */
5340 static MagickBooleanType XDrawEditImage(Display *display,
5341  XResourceInfo *resource_info,XWindows *windows,Image **image)
5342 {
5343  const char
5344  *const DrawMenu[] =
5345  {
5346  "Element",
5347  "Color",
5348  "Stipple",
5349  "Width",
5350  "Undo",
5351  "Help",
5352  "Dismiss",
5353  (char *) NULL
5354  };
5355 
5356  static ElementType
5357  element = PointElement;
5358 
5359  static const ModeType
5360  DrawCommands[] =
5361  {
5362  DrawElementCommand,
5363  DrawColorCommand,
5364  DrawStippleCommand,
5365  DrawWidthCommand,
5366  DrawUndoCommand,
5367  DrawHelpCommand,
5368  DrawDismissCommand
5369  };
5370 
5371  static Pixmap
5372  stipple = (Pixmap) NULL;
5373 
5374  static unsigned int
5375  pen_id = 0,
5376  line_width = 1;
5377 
5378  char
5379  command[MaxTextExtent],
5380  text[MaxTextExtent];
5381 
5382  Cursor
5383  cursor;
5384 
5385  int
5386  entry,
5387  id,
5388  number_coordinates,
5389  x,
5390  y;
5391 
5392  MagickRealType
5393  degrees;
5394 
5395  MagickStatusType
5396  status;
5397 
5399  rectangle_info;
5400 
5401  int
5402  i;
5403 
5404  unsigned int
5405  distance,
5406  height,
5407  max_coordinates,
5408  width;
5409 
5410  size_t
5411  state;
5412 
5413  Window
5414  root_window;
5415 
5416  XDrawInfo
5417  draw_info;
5418 
5419  XEvent
5420  event;
5421 
5422  XPoint
5423  *coordinate_info;
5424 
5425  XSegment
5426  line_info;
5427 
5428  /*
5429  Allocate polygon info.
5430  */
5431  max_coordinates=2048;
5432  coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433  sizeof(*coordinate_info));
5434  if (coordinate_info == (XPoint *) NULL)
5435  {
5436  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438  return(MagickFalse);
5439  }
5440  /*
5441  Map Command widget.
5442  */
5443  (void) CloneString(&windows->command.name,"Draw");
5444  windows->command.data=4;
5445  (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446  (void) XMapRaised(display,windows->command.id);
5447  XClientMessage(display,windows->image.id,windows->im_protocols,
5448  windows->im_update_widget,CurrentTime);
5449  /*
5450  Wait for first button press.
5451  */
5452  root_window=XRootWindow(display,XDefaultScreen(display));
5453  draw_info.stencil=OpaqueStencil;
5454  status=MagickTrue;
5455  cursor=XCreateFontCursor(display,XC_tcross);
5456  for ( ; ; )
5457  {
5458  XQueryPosition(display,windows->image.id,&x,&y);
5459  (void) XSelectInput(display,windows->image.id,
5460  windows->image.attributes.event_mask | PointerMotionMask);
5461  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462  state=DefaultState;
5463  do
5464  {
5465  if (windows->info.mapped != MagickFalse)
5466  {
5467  /*
5468  Display pointer position.
5469  */
5470  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5471  x+windows->image.x,y+windows->image.y);
5472  XInfoWidget(display,windows,text);
5473  }
5474  /*
5475  Wait for next event.
5476  */
5477  XScreenEvent(display,windows,&event);
5478  if (event.xany.window == windows->command.id)
5479  {
5480  /*
5481  Select a command from the Command widget.
5482  */
5483  id=XCommandWidget(display,windows,DrawMenu,&event);
5484  if (id < 0)
5485  continue;
5486  switch (DrawCommands[id])
5487  {
5488  case DrawElementCommand:
5489  {
5490  const char
5491  *const Elements[] =
5492  {
5493  "point",
5494  "line",
5495  "rectangle",
5496  "fill rectangle",
5497  "circle",
5498  "fill circle",
5499  "ellipse",
5500  "fill ellipse",
5501  "polygon",
5502  "fill polygon",
5503  (char *) NULL,
5504  };
5505 
5506  /*
5507  Select a command from the pop-up menu.
5508  */
5509  element=(ElementType) (XMenuWidget(display,windows,
5510  DrawMenu[id],Elements,command)+1);
5511  break;
5512  }
5513  case DrawColorCommand:
5514  {
5515  const char
5516  *ColorMenu[MaxNumberPens+1];
5517 
5518  int
5519  pen_number;
5520 
5521  MagickBooleanType
5522  transparent;
5523 
5524  XColor
5525  color;
5526 
5527  /*
5528  Initialize menu selections.
5529  */
5530  for (i=0; i < (int) (MaxNumberPens-2); i++)
5531  ColorMenu[i]=resource_info->pen_colors[i];
5532  ColorMenu[MaxNumberPens-2]="transparent";
5533  ColorMenu[MaxNumberPens-1]="Browser...";
5534  ColorMenu[MaxNumberPens]=(char *) NULL;
5535  /*
5536  Select a pen color from the pop-up menu.
5537  */
5538  pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539  (const char **) ColorMenu,command);
5540  if (pen_number < 0)
5541  break;
5542  transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543  MagickFalse;
5544  if (transparent != MagickFalse)
5545  {
5546  draw_info.stencil=TransparentStencil;
5547  break;
5548  }
5549  if (pen_number == (MaxNumberPens-1))
5550  {
5551  static char
5552  color_name[MaxTextExtent] = "gray";
5553 
5554  /*
5555  Select a pen color from a dialog.
5556  */
5557  resource_info->pen_colors[pen_number]=color_name;
5558  XColorBrowserWidget(display,windows,"Select",color_name);
5559  if (*color_name == '\0')
5560  break;
5561  }
5562  /*
5563  Set pen color.
5564  */
5565  (void) XParseColor(display,windows->map_info->colormap,
5566  resource_info->pen_colors[pen_number],&color);
5567  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568  (unsigned int) MaxColors,&color);
5569  windows->pixel_info->pen_colors[pen_number]=color;
5570  pen_id=(unsigned int) pen_number;
5571  draw_info.stencil=OpaqueStencil;
5572  break;
5573  }
5574  case DrawStippleCommand:
5575  {
5576  const char
5577  *StipplesMenu[] =
5578  {
5579  "Brick",
5580  "Diagonal",
5581  "Scales",
5582  "Vertical",
5583  "Wavy",
5584  "Translucent",
5585  "Opaque",
5586  (char *) NULL,
5587  (char *) NULL,
5588  };
5589 
5590  Image
5591  *stipple_image;
5592 
5593  ImageInfo
5594  *image_info;
5595 
5596  int
5597  status;
5598 
5599  static char
5600  filename[MaxTextExtent] = "\0";
5601 
5602  /*
5603  Select a command from the pop-up menu.
5604  */
5605  StipplesMenu[7]="Open...";
5606  entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607  command);
5608  if (entry < 0)
5609  break;
5610  if (stipple != (Pixmap) NULL)
5611  (void) XFreePixmap(display,stipple);
5612  stipple=(Pixmap) NULL;
5613  if (entry != 7)
5614  {
5615  switch (entry)
5616  {
5617  case 0:
5618  {
5619  stipple=XCreateBitmapFromData(display,root_window,
5620  (char *) BricksBitmap,BricksWidth,BricksHeight);
5621  break;
5622  }
5623  case 1:
5624  {
5625  stipple=XCreateBitmapFromData(display,root_window,
5626  (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5627  break;
5628  }
5629  case 2:
5630  {
5631  stipple=XCreateBitmapFromData(display,root_window,
5632  (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5633  break;
5634  }
5635  case 3:
5636  {
5637  stipple=XCreateBitmapFromData(display,root_window,
5638  (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5639  break;
5640  }
5641  case 4:
5642  {
5643  stipple=XCreateBitmapFromData(display,root_window,
5644  (char *) WavyBitmap,WavyWidth,WavyHeight);
5645  break;
5646  }
5647  case 5:
5648  {
5649  stipple=XCreateBitmapFromData(display,root_window,
5650  (char *) HighlightBitmap,HighlightWidth,
5651  HighlightHeight);
5652  break;
5653  }
5654  case 6:
5655  default:
5656  {
5657  stipple=XCreateBitmapFromData(display,root_window,
5658  (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5659  break;
5660  }
5661  }
5662  break;
5663  }
5664  XFileBrowserWidget(display,windows,"Stipple",filename);
5665  if (*filename == '\0')
5666  break;
5667  /*
5668  Read image.
5669  */
5670  XSetCursorState(display,windows,MagickTrue);
5671  XCheckRefreshWindows(display,windows);
5672  image_info=AcquireImageInfo();
5673  (void) CopyMagickString(image_info->filename,filename,
5674  MaxTextExtent);
5675  stipple_image=ReadImage(image_info,&(*image)->exception);
5676  CatchException(&(*image)->exception);
5677  XSetCursorState(display,windows,MagickFalse);
5678  if (stipple_image == (Image *) NULL)
5679  break;
5680  (void) AcquireUniqueFileResource(filename);
5681  (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5682  "xbm:%s",filename);
5683  (void) WriteImage(image_info,stipple_image);
5684  stipple_image=DestroyImage(stipple_image);
5685  image_info=DestroyImageInfo(image_info);
5686  status=XReadBitmapFile(display,root_window,filename,&width,
5687  &height,&stipple,&x,&y);
5688  (void) RelinquishUniqueFileResource(filename);
5689  if ((status != BitmapSuccess) != 0)
5690  XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5691  filename);
5692  break;
5693  }
5694  case DrawWidthCommand:
5695  {
5696  const char
5697  *const WidthsMenu[] =
5698  {
5699  "1",
5700  "2",
5701  "4",
5702  "8",
5703  "16",
5704  "Dialog...",
5705  (char *) NULL,
5706  };
5707 
5708  static char
5709  width[MaxTextExtent] = "0";
5710 
5711  /*
5712  Select a command from the pop-up menu.
5713  */
5714  entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5715  command);
5716  if (entry < 0)
5717  break;
5718  if (entry != 5)
5719  {
5720  line_width=(unsigned int) StringToUnsignedLong(
5721  WidthsMenu[entry]);
5722  break;
5723  }
5724  (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725  width);
5726  if (*width == '\0')
5727  break;
5728  line_width=(unsigned int) StringToUnsignedLong(width);
5729  break;
5730  }
5731  case DrawUndoCommand:
5732  {
5733  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734  image);
5735  break;
5736  }
5737  case DrawHelpCommand:
5738  {
5739  XTextViewHelp(display,resource_info,windows,MagickFalse,
5740  "Help Viewer - Image Rotation",ImageDrawHelp);
5741  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742  break;
5743  }
5744  case DrawDismissCommand:
5745  {
5746  /*
5747  Prematurely exit.
5748  */
5749  state|=EscapeState;
5750  state|=ExitState;
5751  break;
5752  }
5753  default:
5754  break;
5755  }
5756  (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757  continue;
5758  }
5759  switch (event.type)
5760  {
5761  case ButtonPress:
5762  {
5763  if (event.xbutton.button != Button1)
5764  break;
5765  if (event.xbutton.window != windows->image.id)
5766  break;
5767  /*
5768  exit loop.
5769  */
5770  x=event.xbutton.x;
5771  y=event.xbutton.y;
5772  state|=ExitState;
5773  break;
5774  }
5775  case ButtonRelease:
5776  break;
5777  case Expose:
5778  break;
5779  case KeyPress:
5780  {
5781  KeySym
5782  key_symbol;
5783 
5784  if (event.xkey.window != windows->image.id)
5785  break;
5786  /*
5787  Respond to a user key press.
5788  */
5789  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791  switch ((int) key_symbol)
5792  {
5793  case XK_Escape:
5794  case XK_F20:
5795  {
5796  /*
5797  Prematurely exit.
5798  */
5799  state|=EscapeState;
5800  state|=ExitState;
5801  break;
5802  }
5803  case XK_F1:
5804  case XK_Help:
5805  {
5806  XTextViewHelp(display,resource_info,windows,MagickFalse,
5807  "Help Viewer - Image Rotation",ImageDrawHelp);
5808  break;
5809  }
5810  default:
5811  {
5812  (void) XBell(display,0);
5813  break;
5814  }
5815  }
5816  break;
5817  }
5818  case MotionNotify:
5819  {
5820  /*
5821  Map and unmap Info widget as text cursor crosses its boundaries.
5822  */
5823  x=event.xmotion.x;
5824  y=event.xmotion.y;
5825  if (windows->info.mapped != MagickFalse)
5826  {
5827  if ((x < (int) (windows->info.x+windows->info.width)) &&
5828  (y < (int) (windows->info.y+windows->info.height)))
5829  (void) XWithdrawWindow(display,windows->info.id,
5830  windows->info.screen);
5831  }
5832  else
5833  if ((x > (int) (windows->info.x+windows->info.width)) ||
5834  (y > (int) (windows->info.y+windows->info.height)))
5835  (void) XMapWindow(display,windows->info.id);
5836  break;
5837  }
5838  }
5839  } while ((state & ExitState) == 0);
5840  (void) XSelectInput(display,windows->image.id,
5841  windows->image.attributes.event_mask);
5842  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843  if ((state & EscapeState) != 0)
5844  break;
5845  /*
5846  Draw element as pointer moves until the button is released.
5847  */
5848  distance=0;
5849  degrees=0.0;
5850  line_info.x1=x;
5851  line_info.y1=y;
5852  line_info.x2=x;
5853  line_info.y2=y;
5854  rectangle_info.x=(ssize_t) x;
5855  rectangle_info.y=(ssize_t) y;
5856  rectangle_info.width=0;
5857  rectangle_info.height=0;
5858  number_coordinates=1;
5859  coordinate_info->x=x;
5860  coordinate_info->y=y;
5861  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862  state=DefaultState;
5863  do
5864  {
5865  switch (element)
5866  {
5867  case PointElement:
5868  default:
5869  {
5870  if (number_coordinates > 1)
5871  {
5872  (void) XDrawLines(display,windows->image.id,
5873  windows->image.highlight_context,coordinate_info,
5874  number_coordinates,CoordModeOrigin);
5875  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5876  coordinate_info[number_coordinates-1].x,
5877  coordinate_info[number_coordinates-1].y);
5878  XInfoWidget(display,windows,text);
5879  }
5880  break;
5881  }
5882  case LineElement:
5883  {
5884  if (distance > 9)
5885  {
5886  /*
5887  Display angle of the line.
5888  */
5889  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890  line_info.y1),(double) (line_info.x2-line_info.x1)));
5891  (void) FormatLocaleString(text,MaxTextExtent," %g",
5892  (double) degrees);
5893  XInfoWidget(display,windows,text);
5894  XHighlightLine(display,windows->image.id,
5895  windows->image.highlight_context,&line_info);
5896  }
5897  else
5898  if (windows->info.mapped != MagickFalse)
5899  (void) XWithdrawWindow(display,windows->info.id,
5900  windows->info.screen);
5901  break;
5902  }
5903  case RectangleElement:
5904  case FillRectangleElement:
5905  {
5906  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907  {
5908  /*
5909  Display info and draw drawing rectangle.
5910  */
5911  (void) FormatLocaleString(text,MaxTextExtent,
5912  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913  (double) rectangle_info.height,(double) rectangle_info.x,
5914  (double) rectangle_info.y);
5915  XInfoWidget(display,windows,text);
5916  XHighlightRectangle(display,windows->image.id,
5917  windows->image.highlight_context,&rectangle_info);
5918  }
5919  else
5920  if (windows->info.mapped != MagickFalse)
5921  (void) XWithdrawWindow(display,windows->info.id,
5922  windows->info.screen);
5923  break;
5924  }
5925  case CircleElement:
5926  case FillCircleElement:
5927  case EllipseElement:
5928  case FillEllipseElement:
5929  {
5930  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931  {
5932  /*
5933  Display info and draw drawing rectangle.
5934  */
5935  (void) FormatLocaleString(text,MaxTextExtent,
5936  " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937  (double) rectangle_info.height,(double) rectangle_info.x,
5938  (double) rectangle_info.y);
5939  XInfoWidget(display,windows,text);
5940  XHighlightEllipse(display,windows->image.id,
5941  windows->image.highlight_context,&rectangle_info);
5942  }
5943  else
5944  if (windows->info.mapped != MagickFalse)
5945  (void) XWithdrawWindow(display,windows->info.id,
5946  windows->info.screen);
5947  break;
5948  }
5949  case PolygonElement:
5950  case FillPolygonElement:
5951  {
5952  if (number_coordinates > 1)
5953  (void) XDrawLines(display,windows->image.id,
5954  windows->image.highlight_context,coordinate_info,
5955  number_coordinates,CoordModeOrigin);
5956  if (distance > 9)
5957  {
5958  /*
5959  Display angle of the line.
5960  */
5961  degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962  line_info.y1),(double) (line_info.x2-line_info.x1)));
5963  (void) FormatLocaleString(text,MaxTextExtent," %g",
5964  (double) degrees);
5965  XInfoWidget(display,windows,text);
5966  XHighlightLine(display,windows->image.id,
5967  windows->image.highlight_context,&line_info);
5968  }
5969  else
5970  if (windows->info.mapped != MagickFalse)
5971  (void) XWithdrawWindow(display,windows->info.id,
5972  windows->info.screen);
5973  break;
5974  }
5975  }
5976  /*
5977  Wait for next event.
5978  */
5979  XScreenEvent(display,windows,&event);
5980  switch (element)
5981  {
5982  case PointElement:
5983  default:
5984  {
5985  if (number_coordinates > 1)
5986  (void) XDrawLines(display,windows->image.id,
5987  windows->image.highlight_context,coordinate_info,
5988  number_coordinates,CoordModeOrigin);
5989  break;
5990  }
5991  case LineElement:
5992  {
5993  if (distance > 9)
5994  XHighlightLine(display,windows->image.id,
5995  windows->image.highlight_context,&line_info);
5996  break;
5997  }
5998  case RectangleElement:
5999  case FillRectangleElement:
6000  {
6001  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002  XHighlightRectangle(display,windows->image.id,
6003  windows->image.highlight_context,&rectangle_info);
6004  break;
6005  }
6006  case CircleElement:
6007  case FillCircleElement:
6008  case EllipseElement:
6009  case FillEllipseElement:
6010  {
6011  if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012  XHighlightEllipse(display,windows->image.id,
6013  windows->image.highlight_context,&rectangle_info);
6014  break;
6015  }
6016  case PolygonElement:
6017  case FillPolygonElement:
6018  {
6019  if (number_coordinates > 1)
6020  (void) XDrawLines(display,windows->image.id,
6021  windows->image.highlight_context,coordinate_info,
6022  number_coordinates,CoordModeOrigin);
6023  if (distance > 9)
6024  XHighlightLine(display,windows->image.id,
6025  windows->image.highlight_context,&line_info);
6026  break;
6027  }
6028  }
6029  switch (event.type)
6030  {
6031  case ButtonPress:
6032  break;
6033  case ButtonRelease:
6034  {
6035  /*
6036  User has committed to element.
6037  */
6038  line_info.x2=event.xbutton.x;
6039  line_info.y2=event.xbutton.y;
6040  rectangle_info.x=(ssize_t) event.xbutton.x;
6041  rectangle_info.y=(ssize_t) event.xbutton.y;
6042  coordinate_info[number_coordinates].x=event.xbutton.x;
6043  coordinate_info[number_coordinates].y=event.xbutton.y;
6044  if (((element != PolygonElement) &&
6045  (element != FillPolygonElement)) || (distance <= 9))
6046  {
6047  state|=ExitState;
6048  break;
6049  }
6050  number_coordinates++;
6051  if (number_coordinates < (int) max_coordinates)
6052  {
6053  line_info.x1=event.xbutton.x;
6054  line_info.y1=event.xbutton.y;
6055  break;
6056  }
6057  max_coordinates<<=1;
6058  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059  max_coordinates,sizeof(*coordinate_info));
6060  if (coordinate_info == (XPoint *) NULL)
6061  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063  break;
6064  }
6065  case Expose:
6066  break;
6067  case MotionNotify:
6068  {
6069  if (event.xmotion.window != windows->image.id)
6070  break;
6071  if (element != PointElement)
6072  {
6073  line_info.x2=event.xmotion.x;
6074  line_info.y2=event.xmotion.y;
6075  rectangle_info.x=(ssize_t) event.xmotion.x;
6076  rectangle_info.y=(ssize_t) event.xmotion.y;
6077  break;
6078  }
6079  coordinate_info[number_coordinates].x=event.xbutton.x;
6080  coordinate_info[number_coordinates].y=event.xbutton.y;
6081  number_coordinates++;
6082  if (number_coordinates < (int) max_coordinates)
6083  break;
6084  max_coordinates<<=1;
6085  coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086  max_coordinates,sizeof(*coordinate_info));
6087  if (coordinate_info == (XPoint *) NULL)
6088  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089  ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090  break;
6091  }
6092  default:
6093  break;
6094  }
6095  /*
6096  Check boundary conditions.
6097  */
6098  if (line_info.x2 < 0)
6099  line_info.x2=0;
6100  else
6101  if (line_info.x2 > (int) windows->image.width)
6102  line_info.x2=(short) windows->image.width;
6103  if (line_info.y2 < 0)
6104  line_info.y2=0;
6105  else
6106  if (line_info.y2 > (int) windows->image.height)
6107  line_info.y2=(short) windows->image.height;
6108  distance=(unsigned int)
6109  (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110  ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111  if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112  ((state & ExitState) != 0))
6113  {
6114  if (rectangle_info.x < 0)
6115  rectangle_info.x=0;
6116  else
6117  if (rectangle_info.x > (ssize_t) windows->image.width)
6118  rectangle_info.x=(ssize_t) windows->image.width;
6119  if ((int) rectangle_info.x < x)
6120  rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121  else
6122  {
6123  rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124  rectangle_info.x=(ssize_t) x;
6125  }
6126  if (rectangle_info.y < 0)
6127  rectangle_info.y=0;
6128  else
6129  if (rectangle_info.y > (ssize_t) windows->image.height)
6130  rectangle_info.y=(ssize_t) windows->image.height;
6131  if ((int) rectangle_info.y < y)
6132  rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133  else
6134  {
6135  rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136  rectangle_info.y=(ssize_t) y;
6137  }
6138  }
6139  } while ((state & ExitState) == 0);
6140  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141  if ((element == PointElement) || (element == PolygonElement) ||
6142  (element == FillPolygonElement))
6143  {
6144  /*
6145  Determine polygon bounding box.
6146  */
6147  rectangle_info.x=(ssize_t) coordinate_info->x;
6148  rectangle_info.y=(ssize_t) coordinate_info->y;
6149  x=coordinate_info->x;
6150  y=coordinate_info->y;
6151  for (i=1; i < number_coordinates; i++)
6152  {
6153  if (coordinate_info[i].x > x)
6154  x=coordinate_info[i].x;
6155  if (coordinate_info[i].y > y)
6156  y=coordinate_info[i].y;
6157  if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6158  rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6159  if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6160  rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6161  }
6162  rectangle_info.width=(size_t) (x-rectangle_info.x);
6163  rectangle_info.height=(size_t) (y-rectangle_info.y);
6164  for (i=0; i < number_coordinates; i++)
6165  {
6166  coordinate_info[i].x-=rectangle_info.x;
6167  coordinate_info[i].y-=rectangle_info.y;
6168  }
6169  }
6170  else
6171  if (distance <= 9)
6172  continue;
6173  else
6174  if ((element == RectangleElement) ||
6175  (element == CircleElement) || (element == EllipseElement))
6176  {
6177  rectangle_info.width--;
6178  rectangle_info.height--;
6179  }
6180  /*
6181  Drawing is relative to image configuration.
6182  */
6183  draw_info.x=(int) rectangle_info.x;
6184  draw_info.y=(int) rectangle_info.y;
6185  (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186  image);
6187  width=(unsigned int) (*image)->columns;
6188  height=(unsigned int) (*image)->rows;
6189  x=0;
6190  y=0;
6191  if (windows->image.crop_geometry != (char *) NULL)
6192  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193  draw_info.x+=windows->image.x-(line_width/2);
6194  if (draw_info.x < 0)
6195  draw_info.x=0;
6196  draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197  draw_info.y+=windows->image.y-(line_width/2);
6198  if (draw_info.y < 0)
6199  draw_info.y=0;
6200  draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201  draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202  if (draw_info.width > (unsigned int) (*image)->columns)
6203  draw_info.width=(unsigned int) (*image)->columns;
6204  draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205  if (draw_info.height > (unsigned int) (*image)->rows)
6206  draw_info.height=(unsigned int) (*image)->rows;
6207  (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208  width*draw_info.width/windows->image.ximage->width,
6209  height*draw_info.height/windows->image.ximage->height,
6210  draw_info.x+x,draw_info.y+y);
6211  /*
6212  Initialize drawing attributes.
6213  */
6214  draw_info.degrees=0.0;
6215  draw_info.element=element;
6216  draw_info.stipple=stipple;
6217  draw_info.line_width=line_width;
6218  draw_info.line_info=line_info;
6219  if (line_info.x1 > (int) (line_width/2))
6220  draw_info.line_info.x1=(short) line_width/2;
6221  if (line_info.y1 > (int) (line_width/2))
6222  draw_info.line_info.y1=(short) line_width/2;
6223  draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224  draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225  if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226  {
6227  draw_info.line_info.x2=(-draw_info.line_info.x2);
6228  draw_info.line_info.y2=(-draw_info.line_info.y2);
6229  }
6230  if (draw_info.line_info.x2 < 0)
6231  {
6232  draw_info.line_info.x2=(-draw_info.line_info.x2);
6233  Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234  }
6235  if (draw_info.line_info.y2 < 0)
6236  {
6237  draw_info.line_info.y2=(-draw_info.line_info.y2);
6238  Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239  }
6240  draw_info.rectangle_info=rectangle_info;
6241  if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6242  draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243  if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6244  draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245  draw_info.number_coordinates=(unsigned int) number_coordinates;
6246  draw_info.coordinate_info=coordinate_info;
6247  windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248  /*
6249  Draw element on image.
6250  */
6251  XSetCursorState(display,windows,MagickTrue);
6252  XCheckRefreshWindows(display,windows);
6253  status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254  XSetCursorState(display,windows,MagickFalse);
6255  /*
6256  Update image colormap and return to image drawing.
6257  */
6258  XConfigureImageColormap(display,resource_info,windows,*image);
6259  (void) XConfigureImage(display,resource_info,windows,*image);
6260  }
6261  XSetCursorState(display,windows,MagickFalse);
6262  coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263  return(status != 0 ? MagickTrue : MagickFalse);
6264 }
6265 
6266 /*
6267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268 % %
6269 % %
6270 % %
6271 + X D r a w P a n R e c t a n g l e %
6272 % %
6273 % %
6274 % %
6275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276 %
6277 % XDrawPanRectangle() draws a rectangle in the pan window. The pan window
6278 % displays a zoom image and the rectangle shows which portion of the image is
6279 % displayed in the Image window.
6280 %
6281 % The format of the XDrawPanRectangle method is:
6282 %
6283 % XDrawPanRectangle(Display *display,XWindows *windows)
6284 %
6285 % A description of each parameter follows:
6286 %
6287 % o display: Specifies a connection to an X server; returned from
6288 % XOpenDisplay.
6289 %
6290 % o windows: Specifies a pointer to a XWindows structure.
6291 %
6292 */
6293 static void XDrawPanRectangle(Display *display,XWindows *windows)
6294 {
6295  MagickRealType
6296  scale_factor;
6297 
6299  highlight_info;
6300 
6301  /*
6302  Determine dimensions of the panning rectangle.
6303  */
6304  scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305  highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6306  highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307  scale_factor=(MagickRealType)
6308  windows->pan.height/windows->image.ximage->height;
6309  highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6310  highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311  /*
6312  Display the panning rectangle.
6313  */
6314  (void) XClearWindow(display,windows->pan.id);
6315  XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316  &highlight_info);
6317 }
6318 
6319 /*
6320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321 % %
6322 % %
6323 % %
6324 + X I m a g e C a c h e %
6325 % %
6326 % %
6327 % %
6328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329 %
6330 % XImageCache() handles the creation, manipulation, and destruction of the
6331 % image cache (undo and redo buffers).
6332 %
6333 % The format of the XImageCache method is:
6334 %
6335 % void XImageCache(Display *display,XResourceInfo *resource_info,
6336 % XWindows *windows,const DisplayCommand command,Image **image)
6337 %
6338 % A description of each parameter follows:
6339 %
6340 % o display: Specifies a connection to an X server; returned from
6341 % XOpenDisplay.
6342 %
6343 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344 %
6345 % o windows: Specifies a pointer to a XWindows structure.
6346 %
6347 % o command: Specifies a command to perform.
6348 %
6349 % o image: the image; XImageCache may transform the image and return a new
6350 % image pointer.
6351 %
6352 */
6353 static void XImageCache(Display *display,XResourceInfo *resource_info,
6354  XWindows *windows,const DisplayCommand command,Image **image)
6355 {
6356  Image
6357  *cache_image;
6358 
6359  static Image
6360  *redo_image = (Image *) NULL,
6361  *undo_image = (Image *) NULL;
6362 
6363  switch (command)
6364  {
6365  case FreeBuffersCommand:
6366  {
6367  /*
6368  Free memory from the undo and redo cache.
6369  */
6370  while (undo_image != (Image *) NULL)
6371  {
6372  cache_image=undo_image;
6373  undo_image=GetPreviousImageInList(undo_image);
6374  cache_image->list=DestroyImage(cache_image->list);
6375  cache_image=DestroyImage(cache_image);
6376  }
6377  undo_image=NewImageList();
6378  if (redo_image != (Image *) NULL)
6379  redo_image=DestroyImage(redo_image);
6380  redo_image=NewImageList();
6381  return;
6382  }
6383  case UndoCommand:
6384  {
6385  char
6386  image_geometry[MaxTextExtent];
6387 
6388  /*
6389  Undo the last image transformation.
6390  */
6391  if (undo_image == (Image *) NULL)
6392  {
6393  (void) XBell(display,0);
6394  ThrowXWindowException(ImageError,"NoImagesWereFound",
6395  (*image)->filename);
6396  return;
6397  }
6398  cache_image=undo_image;
6399  undo_image=GetPreviousImageInList(undo_image);
6400  windows->image.window_changes.width=(int) cache_image->columns;
6401  windows->image.window_changes.height=(int) cache_image->rows;
6402  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6403  windows->image.ximage->width,windows->image.ximage->height);
6404  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6405  if (windows->image.crop_geometry != (char *) NULL)
6406  windows->image.crop_geometry=(char *)
6407  RelinquishMagickMemory(windows->image.crop_geometry);
6408  windows->image.crop_geometry=cache_image->geometry;
6409  if (redo_image != (Image *) NULL)
6410  redo_image=DestroyImage(redo_image);
6411  redo_image=(*image);
6412  *image=cache_image->list;
6413  cache_image=DestroyImage(cache_image);
6414  if (windows->image.orphan != MagickFalse)
6415  return;
6416  XConfigureImageColormap(display,resource_info,windows,*image);
6417  (void) XConfigureImage(display,resource_info,windows,*image);
6418  return;
6419  }
6420  case CutCommand:
6421  case PasteCommand:
6422  case ApplyCommand:
6423  case HalfSizeCommand:
6424  case OriginalSizeCommand:
6425  case DoubleSizeCommand:
6426  case ResizeCommand:
6427  case TrimCommand:
6428  case CropCommand:
6429  case ChopCommand:
6430  case FlipCommand:
6431  case FlopCommand:
6432  case RotateRightCommand:
6433  case RotateLeftCommand:
6434  case RotateCommand:
6435  case ShearCommand:
6436  case RollCommand:
6437  case NegateCommand:
6438  case ContrastStretchCommand:
6439  case SigmoidalContrastCommand:
6440  case NormalizeCommand:
6441  case EqualizeCommand:
6442  case HueCommand:
6443  case SaturationCommand:
6444  case BrightnessCommand:
6445  case GammaCommand:
6446  case SpiffCommand:
6447  case DullCommand:
6448  case GrayscaleCommand:
6449  case MapCommand:
6450  case QuantizeCommand:
6451  case DespeckleCommand:
6452  case EmbossCommand:
6453  case ReduceNoiseCommand:
6454  case AddNoiseCommand:
6455  case SharpenCommand:
6456  case BlurCommand:
6457  case ThresholdCommand:
6458  case EdgeDetectCommand:
6459  case SpreadCommand:
6460  case ShadeCommand:
6461  case RaiseCommand:
6462  case SegmentCommand:
6463  case SolarizeCommand:
6464  case SepiaToneCommand:
6465  case SwirlCommand:
6466  case ImplodeCommand:
6467  case VignetteCommand:
6468  case WaveCommand:
6469  case OilPaintCommand:
6470  case CharcoalDrawCommand:
6471  case AnnotateCommand:
6472  case AddBorderCommand:
6473  case AddFrameCommand:
6474  case CompositeCommand:
6475  case CommentCommand:
6476  case LaunchCommand:
6477  case RegionOfInterestCommand:
6478  case SaveToUndoBufferCommand:
6479  case RedoCommand:
6480  {
6481  Image
6482  *previous_image;
6483 
6484  ssize_t
6485  bytes;
6486 
6487  bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6488  if (undo_image != (Image *) NULL)
6489  {
6490  /*
6491  Ensure the undo cache has enough memory available.
6492  */
6493  previous_image=undo_image;
6494  while (previous_image != (Image *) NULL)
6495  {
6496  bytes+=previous_image->list->columns*previous_image->list->rows*
6497  sizeof(PixelPacket);
6498  if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6499  {
6500  previous_image=GetPreviousImageInList(previous_image);
6501  continue;
6502  }
6503  bytes-=previous_image->list->columns*previous_image->list->rows*
6504  sizeof(PixelPacket);
6505  if (previous_image == undo_image)
6506  undo_image=NewImageList();
6507  else
6508  previous_image->next->previous=NewImageList();
6509  break;
6510  }
6511  while (previous_image != (Image *) NULL)
6512  {
6513  /*
6514  Delete any excess memory from undo cache.
6515  */
6516  cache_image=previous_image;
6517  previous_image=GetPreviousImageInList(previous_image);
6518  cache_image->list=DestroyImage(cache_image->list);
6519  cache_image=DestroyImage(cache_image);
6520  }
6521  }
6522  if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6523  break;
6524  /*
6525  Save image before transformations are applied.
6526  */
6527  cache_image=AcquireImage((ImageInfo *) NULL);
6528  if (cache_image == (Image *) NULL)
6529  break;
6530  XSetCursorState(display,windows,MagickTrue);
6531  XCheckRefreshWindows(display,windows);
6532  cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6533  XSetCursorState(display,windows,MagickFalse);
6534  if (cache_image->list == (Image *) NULL)
6535  {
6536  cache_image=DestroyImage(cache_image);
6537  break;
6538  }
6539  cache_image->columns=(size_t) windows->image.ximage->width;
6540  cache_image->rows=(size_t) windows->image.ximage->height;
6541  cache_image->geometry=windows->image.crop_geometry;
6542  if (windows->image.crop_geometry != (char *) NULL)
6543  {
6544  cache_image->geometry=AcquireString((char *) NULL);
6545  (void) CopyMagickString(cache_image->geometry,
6546  windows->image.crop_geometry,MaxTextExtent);
6547  }
6548  if (undo_image == (Image *) NULL)
6549  {
6550  undo_image=cache_image;
6551  break;
6552  }
6553  undo_image->next=cache_image;
6554  undo_image->next->previous=undo_image;
6555  undo_image=undo_image->next;
6556  break;
6557  }
6558  default:
6559  break;
6560  }
6561  if (command == RedoCommand)
6562  {
6563  /*
6564  Redo the last image transformation.
6565  */
6566  if (redo_image == (Image *) NULL)
6567  {
6568  (void) XBell(display,0);
6569  return;
6570  }
6571  windows->image.window_changes.width=(int) redo_image->columns;
6572  windows->image.window_changes.height=(int) redo_image->rows;
6573  if (windows->image.crop_geometry != (char *) NULL)
6574  windows->image.crop_geometry=(char *)
6575  RelinquishMagickMemory(windows->image.crop_geometry);
6576  windows->image.crop_geometry=redo_image->geometry;
6577  *image=DestroyImage(*image);
6578  *image=redo_image;
6579  redo_image=NewImageList();
6580  if (windows->image.orphan != MagickFalse)
6581  return;
6582  XConfigureImageColormap(display,resource_info,windows,*image);
6583  (void) XConfigureImage(display,resource_info,windows,*image);
6584  return;
6585  }
6586  if (command != InfoCommand)
6587  return;
6588  /*
6589  Display image info.
6590  */
6591  XSetCursorState(display,windows,MagickTrue);
6592  XCheckRefreshWindows(display,windows);
6593  XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6594  XSetCursorState(display,windows,MagickFalse);
6595 }
6596 
6597 /*
6598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6599 % %
6600 % %
6601 % %
6602 + X I m a g e W i n d o w C o m m a n d %
6603 % %
6604 % %
6605 % %
6606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6607 %
6608 % XImageWindowCommand() makes a transform to the image or Image window as
6609 % specified by a user menu button or keyboard command.
6610 %
6611 % The format of the XMagickCommand method is:
6612 %
6613 % DisplayCommand XImageWindowCommand(Display *display,
6614 % XResourceInfo *resource_info,XWindows *windows,
6615 % const MagickStatusType state,KeySym key_symbol,Image **image)
6616 %
6617 % A description of each parameter follows:
6618 %
6619 % o nexus: Method XImageWindowCommand returns an image when the
6620 % user chooses 'Open Image' from the command menu. Otherwise a null
6621 % image is returned.
6622 %
6623 % o display: Specifies a connection to an X server; returned from
6624 % XOpenDisplay.
6625 %
6626 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6627 %
6628 % o windows: Specifies a pointer to a XWindows structure.
6629 %
6630 % o state: key mask.
6631 %
6632 % o key_symbol: Specifies a command to perform.
6633 %
6634 % o image: the image; XImageWIndowCommand
6635 % may transform the image and return a new image pointer.
6636 %
6637 */
6638 static DisplayCommand XImageWindowCommand(Display *display,
6639  XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6640  KeySym key_symbol,Image **image)
6641 {
6642  static char
6643  delta[MaxTextExtent+1] = "";
6644 
6645  static const char
6646  Digits[] = "01234567890";
6647 
6648  static KeySym
6649  last_symbol = XK_0;
6650 
6651  if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6652  {
6653  size_t
6654  length;
6655 
6656  if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6657  {
6658  *delta='\0';
6659  resource_info->quantum=1;
6660  }
6661  last_symbol=key_symbol;
6662  length=strlen(delta);
6663  if (length < MagickPathExtent)
6664  {
6665  delta[length]=Digits[key_symbol-XK_0];
6666  delta[length+1]='\0';
6667  }
6668  resource_info->quantum=StringToLong(delta);
6669  return(NullCommand);
6670  }
6671  last_symbol=key_symbol;
6672  if (resource_info->immutable)
6673  {
6674  /*
6675  Virtual image window has a restricted command set.
6676  */
6677  switch (key_symbol)
6678  {
6679  case XK_question:
6680  return(InfoCommand);
6681  case XK_p:
6682  case XK_Print:
6683  return(PrintCommand);
6684  case XK_space:
6685  return(NextCommand);
6686  case XK_q:
6687  case XK_Escape:
6688  return(QuitCommand);
6689  default:
6690  break;
6691  }
6692  return(NullCommand);
6693  }
6694  switch ((int) key_symbol)
6695  {
6696  case XK_o:
6697  {
6698  if ((state & ControlMask) == 0)
6699  break;
6700  return(OpenCommand);
6701  }
6702  case XK_space:
6703  return(NextCommand);
6704  case XK_BackSpace:
6705  return(FormerCommand);
6706  case XK_s:
6707  {
6708  if ((state & Mod1Mask) != 0)
6709  return(SwirlCommand);
6710  if ((state & ControlMask) == 0)
6711  return(ShearCommand);
6712  return(SaveCommand);
6713  }
6714  case XK_p:
6715  case XK_Print:
6716  {
6717  if ((state & Mod1Mask) != 0)
6718  return(OilPaintCommand);
6719  if ((state & Mod4Mask) != 0)
6720  return(ColorCommand);
6721  if ((state & ControlMask) == 0)
6722  return(NullCommand);
6723  return(PrintCommand);
6724  }
6725  case XK_d:
6726  {
6727  if ((state & Mod4Mask) != 0)
6728  return(DrawCommand);
6729  if ((state & ControlMask) == 0)
6730  return(NullCommand);
6731  return(DeleteCommand);
6732  }
6733  case XK_Select:
6734  {
6735  if ((state & ControlMask) == 0)
6736  return(NullCommand);
6737  return(SelectCommand);
6738  }
6739  case XK_n:
6740  {
6741  if ((state & ControlMask) == 0)
6742  return(NullCommand);
6743  return(NewCommand);
6744  }
6745  case XK_q:
6746  case XK_Escape:
6747  return(QuitCommand);
6748  case XK_z:
6749  case XK_Undo:
6750  {
6751  if ((state & ControlMask) == 0)
6752  return(NullCommand);
6753  return(UndoCommand);
6754  }
6755  case XK_r:
6756  case XK_Redo:
6757  {
6758  if ((state & ControlMask) == 0)
6759  return(RollCommand);
6760  return(RedoCommand);
6761  }
6762  case XK_x:
6763  {
6764  if ((state & ControlMask) == 0)
6765  return(NullCommand);
6766  return(CutCommand);
6767  }
6768  case XK_c:
6769  {
6770  if ((state & Mod1Mask) != 0)
6771  return(CharcoalDrawCommand);
6772  if ((state & ControlMask) == 0)
6773  return(CropCommand);
6774  return(CopyCommand);
6775  }
6776  case XK_v:
6777  case XK_Insert:
6778  {
6779  if ((state & Mod4Mask) != 0)
6780  return(CompositeCommand);
6781  if ((state & ControlMask) == 0)
6782  return(FlipCommand);
6783  return(PasteCommand);
6784  }
6785  case XK_less:
6786  return(HalfSizeCommand);
6787  case XK_minus:
6788  return(OriginalSizeCommand);
6789  case XK_greater:
6790  return(DoubleSizeCommand);
6791  case XK_percent:
6792  return(ResizeCommand);
6793  case XK_at:
6794  return(RefreshCommand);
6795  case XK_bracketleft:
6796  return(ChopCommand);
6797  case XK_h:
6798  return(FlopCommand);
6799  case XK_slash:
6800  return(RotateRightCommand);
6801  case XK_backslash:
6802  return(RotateLeftCommand);
6803  case XK_asterisk:
6804  return(RotateCommand);
6805  case XK_t:
6806  return(TrimCommand);
6807  case XK_H:
6808  return(HueCommand);
6809  case XK_S:
6810  return(SaturationCommand);
6811  case XK_L:
6812  return(BrightnessCommand);
6813  case XK_G:
6814  return(GammaCommand);
6815  case XK_C:
6816  return(SpiffCommand);
6817  case XK_Z:
6818  return(DullCommand);
6819  case XK_N:
6820  return(NormalizeCommand);
6821  case XK_equal:
6822  return(EqualizeCommand);
6823  case XK_asciitilde:
6824  return(NegateCommand);
6825  case XK_period:
6826  return(GrayscaleCommand);
6827  case XK_numbersign:
6828  return(QuantizeCommand);
6829  case XK_F2:
6830  return(DespeckleCommand);
6831  case XK_F3:
6832  return(EmbossCommand);
6833  case XK_F4:
6834  return(ReduceNoiseCommand);
6835  case XK_F5:
6836  return(AddNoiseCommand);
6837  case XK_F6:
6838  return(SharpenCommand);
6839  case XK_F7:
6840  return(BlurCommand);
6841  case XK_F8:
6842  return(ThresholdCommand);
6843  case XK_F9:
6844  return(EdgeDetectCommand);
6845  case XK_F10:
6846  return(SpreadCommand);
6847  case XK_F11:
6848  return(ShadeCommand);
6849  case XK_F12:
6850  return(RaiseCommand);
6851  case XK_F13:
6852  return(SegmentCommand);
6853  case XK_i:
6854  {
6855  if ((state & Mod1Mask) == 0)
6856  return(NullCommand);
6857  return(ImplodeCommand);
6858  }
6859  case XK_w:
6860  {
6861  if ((state & Mod1Mask) == 0)
6862  return(NullCommand);
6863  return(WaveCommand);
6864  }
6865  case XK_m:
6866  {
6867  if ((state & Mod4Mask) == 0)
6868  return(NullCommand);
6869  return(MatteCommand);
6870  }
6871  case XK_b:
6872  {
6873  if ((state & Mod4Mask) == 0)
6874  return(NullCommand);
6875  return(AddBorderCommand);
6876  }
6877  case XK_f:
6878  {
6879  if ((state & Mod4Mask) == 0)
6880  return(NullCommand);
6881  return(AddFrameCommand);
6882  }
6883  case XK_exclam:
6884  {
6885  if ((state & Mod4Mask) == 0)
6886  return(NullCommand);
6887  return(CommentCommand);
6888  }
6889  case XK_a:
6890  {
6891  if ((state & Mod1Mask) != 0)
6892  return(ApplyCommand);
6893  if ((state & Mod4Mask) != 0)
6894  return(AnnotateCommand);
6895  if ((state & ControlMask) == 0)
6896  return(NullCommand);
6897  return(RegionOfInterestCommand);
6898  }
6899  case XK_question:
6900  return(InfoCommand);
6901  case XK_plus:
6902  return(ZoomCommand);
6903  case XK_P:
6904  {
6905  if ((state & ShiftMask) == 0)
6906  return(NullCommand);
6907  return(ShowPreviewCommand);
6908  }
6909  case XK_Execute:
6910  return(LaunchCommand);
6911  case XK_F1:
6912  return(HelpCommand);
6913  case XK_Find:
6914  return(BrowseDocumentationCommand);
6915  case XK_Menu:
6916  {
6917  (void) XMapRaised(display,windows->command.id);
6918  return(NullCommand);
6919  }
6920  case XK_Next:
6921  case XK_Prior:
6922  case XK_Home:
6923  case XK_KP_Home:
6924  {
6925  XTranslateImage(display,windows,*image,key_symbol);
6926  return(NullCommand);
6927  }
6928  case XK_Up:
6929  case XK_KP_Up:
6930  case XK_Down:
6931  case XK_KP_Down:
6932  case XK_Left:
6933  case XK_KP_Left:
6934  case XK_Right:
6935  case XK_KP_Right:
6936  {
6937  if ((state & Mod1Mask) != 0)
6938  {
6940  crop_info;
6941 
6942  /*
6943  Trim one pixel from edge of image.
6944  */
6945  crop_info.x=0;
6946  crop_info.y=0;
6947  crop_info.width=(size_t) windows->image.ximage->width;
6948  crop_info.height=(size_t) windows->image.ximage->height;
6949  if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6950  {
6951  if (resource_info->quantum >= (int) crop_info.height)
6952  resource_info->quantum=(int) crop_info.height-1;
6953  crop_info.height-=resource_info->quantum;
6954  }
6955  if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6956  {
6957  if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6958  resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6959  crop_info.y+=resource_info->quantum;
6960  crop_info.height-=resource_info->quantum;
6961  }
6962  if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6963  {
6964  if (resource_info->quantum >= (int) crop_info.width)
6965  resource_info->quantum=(int) crop_info.width-1;
6966  crop_info.width-=resource_info->quantum;
6967  }
6968  if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6969  {
6970  if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6971  resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6972  crop_info.x+=resource_info->quantum;
6973  crop_info.width-=resource_info->quantum;
6974  }
6975  if ((int) (windows->image.x+windows->image.width) >
6976  (int) crop_info.width)
6977  windows->image.x=(int) (crop_info.width-windows->image.width);
6978  if ((int) (windows->image.y+windows->image.height) >
6979  (int) crop_info.height)
6980  windows->image.y=(int) (crop_info.height-windows->image.height);
6981  XSetCropGeometry(display,windows,&crop_info,*image);
6982  windows->image.window_changes.width=(int) crop_info.width;
6983  windows->image.window_changes.height=(int) crop_info.height;
6984  (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6985  (void) XConfigureImage(display,resource_info,windows,*image);
6986  return(NullCommand);
6987  }
6988  XTranslateImage(display,windows,*image,key_symbol);
6989  return(NullCommand);
6990  }
6991  default:
6992  return(NullCommand);
6993  }
6994  return(NullCommand);
6995 }
6996 
6997 /*
6998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6999 % %
7000 % %
7001 % %
7002 + X M a g i c k C o m m a n d %
7003 % %
7004 % %
7005 % %
7006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7007 %
7008 % XMagickCommand() makes a transform to the image or Image window as
7009 % specified by a user menu button or keyboard command.
7010 %
7011 % The format of the XMagickCommand method is:
7012 %
7013 % Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7014 % XWindows *windows,const DisplayCommand command,Image **image)
7015 %
7016 % A description of each parameter follows:
7017 %
7018 % o nexus: Method XMagickCommand returns an image when the
7019 % user chooses 'Load Image' from the command menu. Otherwise a null
7020 % image is returned.
7021 %
7022 % o display: Specifies a connection to an X server; returned from
7023 % XOpenDisplay.
7024 %
7025 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7026 %
7027 % o windows: Specifies a pointer to a XWindows structure.
7028 %
7029 % o command: Specifies a command to perform.
7030 %
7031 % o image: the image; XMagickCommand
7032 % may transform the image and return a new image pointer.
7033 %
7034 */
7035 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7036  XWindows *windows,const DisplayCommand command,Image **image)
7037 {
7038  char
7039  filename[MaxTextExtent],
7040  geometry[MaxTextExtent],
7041  modulate_factors[MaxTextExtent];
7042 
7043  GeometryInfo
7044  geometry_info;
7045 
7046  Image
7047  *nexus;
7048 
7049  ImageInfo
7050  *image_info;
7051 
7052  int
7053  x,
7054  y;
7055 
7056  MagickStatusType
7057  flags,
7058  status;
7059 
7060  QuantizeInfo
7061  quantize_info;
7062 
7064  page_geometry;
7065 
7066  int
7067  i;
7068 
7069  static char
7070  color[MaxTextExtent] = "gray";
7071 
7072  unsigned int
7073  height,
7074  width;
7075 
7076  /*
7077  Process user command.
7078  */
7079  XCheckRefreshWindows(display,windows);
7080  XImageCache(display,resource_info,windows,command,image);
7081  nexus=NewImageList();
7082  windows->image.window_changes.width=windows->image.ximage->width;
7083  windows->image.window_changes.height=windows->image.ximage->height;
7084  image_info=CloneImageInfo(resource_info->image_info);
7085  SetGeometryInfo(&geometry_info);
7086  GetQuantizeInfo(&quantize_info);
7087  switch (command)
7088  {
7089  case OpenCommand:
7090  {
7091  /*
7092  Load image.
7093  */
7094  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7095  break;
7096  }
7097  case NextCommand:
7098  {
7099  /*
7100  Display next image.
7101  */
7102  for (i=0; i < resource_info->quantum; i++)
7103  XClientMessage(display,windows->image.id,windows->im_protocols,
7104  windows->im_next_image,CurrentTime);
7105  break;
7106  }
7107  case FormerCommand:
7108  {
7109  /*
7110  Display former image.
7111  */
7112  for (i=0; i < resource_info->quantum; i++)
7113  XClientMessage(display,windows->image.id,windows->im_protocols,
7114  windows->im_former_image,CurrentTime);
7115  break;
7116  }
7117  case SelectCommand:
7118  {
7119  int
7120  status;
7121 
7122  /*
7123  Select image.
7124  */
7125  if (*resource_info->home_directory == '\0')
7126  (void) CopyMagickString(resource_info->home_directory,".",
7127  MaxTextExtent);
7128  status=chdir(resource_info->home_directory);
7129  if (status == -1)
7130  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7131  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7132  nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7133  break;
7134  }
7135  case SaveCommand:
7136  {
7137  /*
7138  Save image.
7139  */
7140  status=XSaveImage(display,resource_info,windows,*image);
7141  if (status == MagickFalse)
7142  {
7143  char
7144  message[MaxTextExtent];
7145 
7146  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7147  (*image)->exception.reason != (char *) NULL ?
7148  (*image)->exception.reason : "",
7149  (*image)->exception.description != (char *) NULL ?
7150  (*image)->exception.description : "");
7151  XNoticeWidget(display,windows,"Unable to save file:",message);
7152  break;
7153  }
7154  break;
7155  }
7156  case PrintCommand:
7157  {
7158  /*
7159  Print image.
7160  */
7161  status=XPrintImage(display,resource_info,windows,*image);
7162  if (status == MagickFalse)
7163  {
7164  char
7165  message[MaxTextExtent];
7166 
7167  (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7168  (*image)->exception.reason != (char *) NULL ?
7169  (*image)->exception.reason : "",
7170  (*image)->exception.description != (char *) NULL ?
7171  (*image)->exception.description : "");
7172  XNoticeWidget(display,windows,"Unable to print file:",message);
7173  break;
7174  }
7175  break;
7176  }
7177  case DeleteCommand:
7178  {
7179  static char
7180  filename[MaxTextExtent] = "\0";
7181 
7182  /*
7183  Delete image file.
7184  */
7185  XFileBrowserWidget(display,windows,"Delete",filename);
7186  if (*filename == '\0')
7187  break;
7188  status=ShredFile(filename);
7189  status|=remove_utf8(filename);
7190  if (status != MagickFalse)
7191  XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7192  break;
7193  }
7194  case NewCommand:
7195  {
7196  int
7197  status;
7198 
7199  static char
7200  color[MaxTextExtent] = "gray",
7201  geometry[MaxTextExtent] = "640x480";
7202 
7203  static const char
7204  *format = "gradient";
7205 
7206  /*
7207  Query user for canvas geometry.
7208  */
7209  status=XDialogWidget(display,windows,"New","Enter image geometry:",
7210  geometry);
7211  if (*geometry == '\0')
7212  break;
7213  if (status == 0)
7214  format="xc";
7215  XColorBrowserWidget(display,windows,"Select",color);
7216  if (*color == '\0')
7217  break;
7218  /*
7219  Create canvas.
7220  */
7221  (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7222  "%s:%s",format,color);
7223  (void) CloneString(&image_info->size,geometry);
7224  nexus=ReadImage(image_info,&(*image)->exception);
7225  CatchException(&(*image)->exception);
7226  XClientMessage(display,windows->image.id,windows->im_protocols,
7227  windows->im_next_image,CurrentTime);
7228  break;
7229  }
7230  case VisualDirectoryCommand:
7231  {
7232  /*
7233  Visual Image directory.
7234  */
7235  nexus=XVisualDirectoryImage(display,resource_info,windows);
7236  break;
7237  }
7238  case QuitCommand:
7239  {
7240  /*
7241  exit program.
7242  */
7243  if (resource_info->confirm_exit == MagickFalse)
7244  XClientMessage(display,windows->image.id,windows->im_protocols,
7245  windows->im_exit,CurrentTime);
7246  else
7247  {
7248  int
7249  status;
7250 
7251  /*
7252  Confirm program exit.
7253  */
7254  status=XConfirmWidget(display,windows,"Do you really want to exit",
7255  resource_info->client_name);
7256  if (status > 0)
7257  XClientMessage(display,windows->image.id,windows->im_protocols,
7258  windows->im_exit,CurrentTime);
7259  }
7260  break;
7261  }
7262  case CutCommand:
7263  {
7264  /*
7265  Cut image.
7266  */
7267  (void) XCropImage(display,resource_info,windows,*image,CutMode);
7268  break;
7269  }
7270  case CopyCommand:
7271  {
7272  /*
7273  Copy image.
7274  */
7275  (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7276  break;
7277  }
7278  case PasteCommand:
7279  {
7280  /*
7281  Paste image.
7282  */
7283  status=XPasteImage(display,resource_info,windows,*image);
7284  if (status == MagickFalse)
7285  {
7286  XNoticeWidget(display,windows,"Unable to paste X image",
7287  (*image)->filename);
7288  break;
7289  }
7290  break;
7291  }
7292  case HalfSizeCommand:
7293  {
7294  /*
7295  Half image size.
7296  */
7297  windows->image.window_changes.width=windows->image.ximage->width/2;
7298  windows->image.window_changes.height=windows->image.ximage->height/2;
7299  (void) XConfigureImage(display,resource_info,windows,*image);
7300  break;
7301  }
7302  case OriginalSizeCommand:
7303  {
7304  /*
7305  Original image size.
7306  */
7307  windows->image.window_changes.width=(int) (*image)->columns;
7308  windows->image.window_changes.height=(int) (*image)->rows;
7309  (void) XConfigureImage(display,resource_info,windows,*image);
7310  break;
7311  }
7312  case DoubleSizeCommand:
7313  {
7314  /*
7315  Double the image size.
7316  */
7317  windows->image.window_changes.width=windows->image.ximage->width << 1;
7318  windows->image.window_changes.height=windows->image.ximage->height << 1;
7319  (void) XConfigureImage(display,resource_info,windows,*image);
7320  break;
7321  }
7322  case ResizeCommand:
7323  {
7324  int
7325  status;
7326 
7327  size_t
7328  height,
7329  width;
7330 
7331  ssize_t
7332  x,
7333  y;
7334 
7335  /*
7336  Resize image.
7337  */
7338  width=(size_t) windows->image.ximage->width;
7339  height=(size_t) windows->image.ximage->height;
7340  x=0;
7341  y=0;
7342  (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7343  (double) width,(double) height);
7344  status=XDialogWidget(display,windows,"Resize",
7345  "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7346  if (*geometry == '\0')
7347  break;
7348  if (status == 0)
7349  (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7350  (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7351  windows->image.window_changes.width=(int) width;
7352  windows->image.window_changes.height=(int) height;
7353  (void) XConfigureImage(display,resource_info,windows,*image);
7354  break;
7355  }
7356  case ApplyCommand:
7357  {
7358  char
7359  image_geometry[MaxTextExtent];
7360 
7361  if ((windows->image.crop_geometry == (char *) NULL) &&
7362  ((int) (*image)->columns == windows->image.ximage->width) &&
7363  ((int) (*image)->rows == windows->image.ximage->height))
7364  break;
7365  /*
7366  Apply size transforms to image.
7367  */
7368  XSetCursorState(display,windows,MagickTrue);
7369  XCheckRefreshWindows(display,windows);
7370  /*
7371  Crop and/or scale displayed image.
7372  */
7373  (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7374  windows->image.ximage->width,windows->image.ximage->height);
7375  (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7376  if (windows->image.crop_geometry != (char *) NULL)
7377  windows->image.crop_geometry=(char *)
7378  RelinquishMagickMemory(windows->image.crop_geometry);
7379  windows->image.x=0;
7380  windows->image.y=0;
7381  XConfigureImageColormap(display,resource_info,windows,*image);
7382  (void) XConfigureImage(display,resource_info,windows,*image);
7383  break;
7384  }
7385  case RefreshCommand:
7386  {
7387  (void) XConfigureImage(display,resource_info,windows,*image);
7388  break;
7389  }
7390  case RestoreCommand:
7391  {
7392  /*
7393  Restore Image window to its original size.
7394  */
7395  if ((windows->image.width == (unsigned int) (*image)->columns) &&
7396  (windows->image.height == (unsigned int) (*image)->rows) &&
7397  (windows->image.crop_geometry == (char *) NULL))
7398  {
7399  (void) XBell(display,0);
7400  break;
7401  }
7402  windows->image.window_changes.width=(int) (*image)->columns;
7403  windows->image.window_changes.height=(int) (*image)->rows;
7404  if (windows->image.crop_geometry != (char *) NULL)
7405  {
7406  windows->image.crop_geometry=(char *)
7407  RelinquishMagickMemory(windows->image.crop_geometry);
7408  windows->image.crop_geometry=(char *) NULL;
7409  windows->image.x=0;
7410  windows->image.y=0;
7411  }
7412  XConfigureImageColormap(display,resource_info,windows,*image);
7413  (void) XConfigureImage(display,resource_info,windows,*image);
7414  break;
7415  }
7416  case CropCommand:
7417  {
7418  /*
7419  Crop image.
7420  */
7421  (void) XCropImage(display,resource_info,windows,*image,CropMode);
7422  break;
7423  }
7424  case ChopCommand:
7425  {
7426  /*
7427  Chop image.
7428  */
7429  status=XChopImage(display,resource_info,windows,image);
7430  if (status == MagickFalse)
7431  {
7432  XNoticeWidget(display,windows,"Unable to cut X image",
7433  (*image)->filename);
7434  break;
7435  }
7436  break;
7437  }
7438  case FlopCommand:
7439  {
7440  Image
7441  *flop_image;
7442 
7443  /*
7444  Flop image scanlines.
7445  */
7446  XSetCursorState(display,windows,MagickTrue);
7447  XCheckRefreshWindows(display,windows);
7448  flop_image=FlopImage(*image,&(*image)->exception);
7449  if (flop_image != (Image *) NULL)
7450  {
7451  *image=DestroyImage(*image);
7452  *image=flop_image;
7453  }
7454  CatchException(&(*image)->exception);
7455  XSetCursorState(display,windows,MagickFalse);
7456  if (windows->image.crop_geometry != (char *) NULL)
7457  {
7458  /*
7459  Flop crop geometry.
7460  */
7461  width=(unsigned int) (*image)->columns;
7462  height=(unsigned int) (*image)->rows;
7463  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7464  &width,&height);
7465  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7466  "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7467  }
7468  if (windows->image.orphan != MagickFalse)
7469  break;
7470  (void) XConfigureImage(display,resource_info,windows,*image);
7471  break;
7472  }
7473  case FlipCommand:
7474  {
7475  Image
7476  *flip_image;
7477 
7478  /*
7479  Flip image scanlines.
7480  */
7481  XSetCursorState(display,windows,MagickTrue);
7482  XCheckRefreshWindows(display,windows);
7483  flip_image=FlipImage(*image,&(*image)->exception);
7484  if (flip_image != (Image *) NULL)
7485  {
7486  *image=DestroyImage(*image);
7487  *image=flip_image;
7488  }
7489  CatchException(&(*image)->exception);
7490  XSetCursorState(display,windows,MagickFalse);
7491  if (windows->image.crop_geometry != (char *) NULL)
7492  {
7493  /*
7494  Flip crop geometry.
7495  */
7496  width=(unsigned int) (*image)->columns;
7497  height=(unsigned int) (*image)->rows;
7498  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7499  &width,&height);
7500  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7501  "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7502  }
7503  if (windows->image.orphan != MagickFalse)
7504  break;
7505  (void) XConfigureImage(display,resource_info,windows,*image);
7506  break;
7507  }
7508  case RotateRightCommand:
7509  {
7510  /*
7511  Rotate image 90 degrees clockwise.
7512  */
7513  status=XRotateImage(display,resource_info,windows,90.0,image);
7514  if (status == MagickFalse)
7515  {
7516  XNoticeWidget(display,windows,"Unable to rotate X image",
7517  (*image)->filename);
7518  break;
7519  }
7520  break;
7521  }
7522  case RotateLeftCommand:
7523  {
7524  /*
7525  Rotate image 90 degrees counter-clockwise.
7526  */
7527  status=XRotateImage(display,resource_info,windows,-90.0,image);
7528  if (status == MagickFalse)
7529  {
7530  XNoticeWidget(display,windows,"Unable to rotate X image",
7531  (*image)->filename);
7532  break;
7533  }
7534  break;
7535  }
7536  case RotateCommand:
7537  {
7538  /*
7539  Rotate image.
7540  */
7541  status=XRotateImage(display,resource_info,windows,0.0,image);
7542  if (status == MagickFalse)
7543  {
7544  XNoticeWidget(display,windows,"Unable to rotate X image",
7545  (*image)->filename);
7546  break;
7547  }
7548  break;
7549  }
7550  case ShearCommand:
7551  {
7552  Image
7553  *shear_image;
7554 
7555  static char
7556  geometry[MaxTextExtent] = "45.0x45.0";
7557 
7558  /*
7559  Query user for shear color and geometry.
7560  */
7561  XColorBrowserWidget(display,windows,"Select",color);
7562  if (*color == '\0')
7563  break;
7564  (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7565  geometry);
7566  if (*geometry == '\0')
7567  break;
7568  /*
7569  Shear image.
7570  */
7571  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7572  XSetCursorState(display,windows,MagickTrue);
7573  XCheckRefreshWindows(display,windows);
7574  (void) QueryColorDatabase(color,&(*image)->background_color,
7575  &(*image)->exception);
7576  flags=ParseGeometry(geometry,&geometry_info);
7577  if ((flags & SigmaValue) == 0)
7578  geometry_info.sigma=geometry_info.rho;
7579  shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7580  &(*image)->exception);
7581  if (shear_image != (Image *) NULL)
7582  {
7583  *image=DestroyImage(*image);
7584  *image=shear_image;
7585  }
7586  CatchException(&(*image)->exception);
7587  XSetCursorState(display,windows,MagickFalse);
7588  if (windows->image.orphan != MagickFalse)
7589  break;
7590  windows->image.window_changes.width=(int) (*image)->columns;
7591  windows->image.window_changes.height=(int) (*image)->rows;
7592  XConfigureImageColormap(display,resource_info,windows,*image);
7593  (void) XConfigureImage(display,resource_info,windows,*image);
7594  break;
7595  }
7596  case RollCommand:
7597  {
7598  Image
7599  *roll_image;
7600 
7601  static char
7602  geometry[MaxTextExtent] = "+2+2";
7603 
7604  /*
7605  Query user for the roll geometry.
7606  */
7607  (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7608  geometry);
7609  if (*geometry == '\0')
7610  break;
7611  /*
7612  Roll image.
7613  */
7614  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7615  XSetCursorState(display,windows,MagickTrue);
7616  XCheckRefreshWindows(display,windows);
7617  (void) ParsePageGeometry(*image,geometry,&page_geometry,
7618  &(*image)->exception);
7619  roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7620  &(*image)->exception);
7621  if (roll_image != (Image *) NULL)
7622  {
7623  *image=DestroyImage(*image);
7624  *image=roll_image;
7625  }
7626  CatchException(&(*image)->exception);
7627  XSetCursorState(display,windows,MagickFalse);
7628  if (windows->image.orphan != MagickFalse)
7629  break;
7630  windows->image.window_changes.width=(int) (*image)->columns;
7631  windows->image.window_changes.height=(int) (*image)->rows;
7632  XConfigureImageColormap(display,resource_info,windows,*image);
7633  (void) XConfigureImage(display,resource_info,windows,*image);
7634  break;
7635  }
7636  case TrimCommand:
7637  {
7638  static char
7639  fuzz[MaxTextExtent];
7640 
7641  /*
7642  Query user for the fuzz factor.
7643  */
7644  (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7645  (*image)->fuzz/((double) QuantumRange+1.0));
7646  (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7647  if (*fuzz == '\0')
7648  break;
7649  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7650  /*
7651  Trim image.
7652  */
7653  status=XTrimImage(display,resource_info,windows,*image);
7654  if (status == MagickFalse)
7655  {
7656  XNoticeWidget(display,windows,"Unable to trim X image",
7657  (*image)->filename);
7658  break;
7659  }
7660  break;
7661  }
7662  case HueCommand:
7663  {
7664  static char
7665  hue_percent[MaxTextExtent] = "110";
7666 
7667  /*
7668  Query user for percent hue change.
7669  */
7670  (void) XDialogWidget(display,windows,"Apply",
7671  "Enter percent change in image hue (0-200):",hue_percent);
7672  if (*hue_percent == '\0')
7673  break;
7674  /*
7675  Vary the image hue.
7676  */
7677  XSetCursorState(display,windows,MagickTrue);
7678  XCheckRefreshWindows(display,windows);
7679  (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7680  (void) ConcatenateMagickString(modulate_factors,hue_percent,
7681  MaxTextExtent);
7682  (void) ModulateImage(*image,modulate_factors);
7683  XSetCursorState(display,windows,MagickFalse);
7684  if (windows->image.orphan != MagickFalse)
7685  break;
7686  XConfigureImageColormap(display,resource_info,windows,*image);
7687  (void) XConfigureImage(display,resource_info,windows,*image);
7688  break;
7689  }
7690  case SaturationCommand:
7691  {
7692  static char
7693  saturation_percent[MaxTextExtent] = "110";
7694 
7695  /*
7696  Query user for percent saturation change.
7697  */
7698  (void) XDialogWidget(display,windows,"Apply",
7699  "Enter percent change in color saturation (0-200):",saturation_percent);
7700  if (*saturation_percent == '\0')
7701  break;
7702  /*
7703  Vary color saturation.
7704  */
7705  XSetCursorState(display,windows,MagickTrue);
7706  XCheckRefreshWindows(display,windows);
7707  (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7708  (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7709  MaxTextExtent);
7710  (void) ModulateImage(*image,modulate_factors);
7711  XSetCursorState(display,windows,MagickFalse);
7712  if (windows->image.orphan != MagickFalse)
7713  break;
7714  XConfigureImageColormap(display,resource_info,windows,*image);
7715  (void) XConfigureImage(display,resource_info,windows,*image);
7716  break;
7717  }
7718  case BrightnessCommand:
7719  {
7720  static char
7721  brightness_percent[MaxTextExtent] = "110";
7722 
7723  /*
7724  Query user for percent brightness change.
7725  */
7726  (void) XDialogWidget(display,windows,"Apply",
7727  "Enter percent change in color brightness (0-200):",brightness_percent);
7728  if (*brightness_percent == '\0')
7729  break;
7730  /*
7731  Vary the color brightness.
7732  */
7733  XSetCursorState(display,windows,MagickTrue);
7734  XCheckRefreshWindows(display,windows);
7735  (void) CopyMagickString(modulate_factors,brightness_percent,
7736  MaxTextExtent);
7737  (void) ModulateImage(*image,modulate_factors);
7738  XSetCursorState(display,windows,MagickFalse);
7739  if (windows->image.orphan != MagickFalse)
7740  break;
7741  XConfigureImageColormap(display,resource_info,windows,*image);
7742  (void) XConfigureImage(display,resource_info,windows,*image);
7743  break;
7744  }
7745  case GammaCommand:
7746  {
7747  static char
7748  factor[MaxTextExtent] = "1.6";
7749 
7750  /*
7751  Query user for gamma value.
7752  */
7753  (void) XDialogWidget(display,windows,"Gamma",
7754  "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7755  if (*factor == '\0')
7756  break;
7757  /*
7758  Gamma correct image.
7759  */
7760  XSetCursorState(display,windows,MagickTrue);
7761  XCheckRefreshWindows(display,windows);
7762  (void) GammaImage(*image,factor);
7763  XSetCursorState(display,windows,MagickFalse);
7764  if (windows->image.orphan != MagickFalse)
7765  break;
7766  XConfigureImageColormap(display,resource_info,windows,*image);
7767  (void) XConfigureImage(display,resource_info,windows,*image);
7768  break;
7769  }
7770  case SpiffCommand:
7771  {
7772  /*
7773  Sharpen the image contrast.
7774  */
7775  XSetCursorState(display,windows,MagickTrue);
7776  XCheckRefreshWindows(display,windows);
7777  (void) ContrastImage(*image,MagickTrue);
7778  XSetCursorState(display,windows,MagickFalse);
7779  if (windows->image.orphan != MagickFalse)
7780  break;
7781  XConfigureImageColormap(display,resource_info,windows,*image);
7782  (void) XConfigureImage(display,resource_info,windows,*image);
7783  break;
7784  }
7785  case DullCommand:
7786  {
7787  /*
7788  Dull the image contrast.
7789  */
7790  XSetCursorState(display,windows,MagickTrue);
7791  XCheckRefreshWindows(display,windows);
7792  (void) ContrastImage(*image,MagickFalse);
7793  XSetCursorState(display,windows,MagickFalse);
7794  if (windows->image.orphan != MagickFalse)
7795  break;
7796  XConfigureImageColormap(display,resource_info,windows,*image);
7797  (void) XConfigureImage(display,resource_info,windows,*image);
7798  break;
7799  }
7800  case ContrastStretchCommand:
7801  {
7802  double
7803  black_point,
7804  white_point;
7805 
7806  static char
7807  levels[MaxTextExtent] = "1%";
7808 
7809  /*
7810  Query user for gamma value.
7811  */
7812  (void) XDialogWidget(display,windows,"Contrast Stretch",
7813  "Enter black and white points:",levels);
7814  if (*levels == '\0')
7815  break;
7816  /*
7817  Contrast stretch image.
7818  */
7819  XSetCursorState(display,windows,MagickTrue);
7820  XCheckRefreshWindows(display,windows);
7821  flags=ParseGeometry(levels,&geometry_info);
7822  black_point=geometry_info.rho;
7823  white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7824  if ((flags & PercentValue) != 0)
7825  {
7826  black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7827  white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7828  }
7829  white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7830  (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7831  white_point);
7832  XSetCursorState(display,windows,MagickFalse);
7833  if (windows->image.orphan != MagickFalse)
7834  break;
7835  XConfigureImageColormap(display,resource_info,windows,*image);
7836  (void) XConfigureImage(display,resource_info,windows,*image);
7837  break;
7838  }
7839  case SigmoidalContrastCommand:
7840  {
7841  static char
7842  levels[MaxTextExtent] = "3x50%";
7843 
7844  /*
7845  Query user for gamma value.
7846  */
7847  (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7848  "Enter contrast and midpoint:",levels);
7849  if (*levels == '\0')
7850  break;
7851  /*
7852  Contrast stretch image.
7853  */
7854  XSetCursorState(display,windows,MagickTrue);
7855  XCheckRefreshWindows(display,windows);
7856  (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7857  XSetCursorState(display,windows,MagickFalse);
7858  if (windows->image.orphan != MagickFalse)
7859  break;
7860  XConfigureImageColormap(display,resource_info,windows,*image);
7861  (void) XConfigureImage(display,resource_info,windows,*image);
7862  break;
7863  }
7864  case NormalizeCommand:
7865  {
7866  /*
7867  Perform histogram normalization on the image.
7868  */
7869  XSetCursorState(display,windows,MagickTrue);
7870  XCheckRefreshWindows(display,windows);
7871  (void) NormalizeImage(*image);
7872  XSetCursorState(display,windows,MagickFalse);
7873  if (windows->image.orphan != MagickFalse)
7874  break;
7875  XConfigureImageColormap(display,resource_info,windows,*image);
7876  (void) XConfigureImage(display,resource_info,windows,*image);
7877  break;
7878  }
7879  case EqualizeCommand:
7880  {
7881  /*
7882  Perform histogram equalization on the image.
7883  */
7884  XSetCursorState(display,windows,MagickTrue);
7885  XCheckRefreshWindows(display,windows);
7886  (void) EqualizeImage(*image);
7887  XSetCursorState(display,windows,MagickFalse);
7888  if (windows->image.orphan != MagickFalse)
7889  break;
7890  XConfigureImageColormap(display,resource_info,windows,*image);
7891  (void) XConfigureImage(display,resource_info,windows,*image);
7892  break;
7893  }
7894  case NegateCommand:
7895  {
7896  /*
7897  Negate colors in image.
7898  */
7899  XSetCursorState(display,windows,MagickTrue);
7900  XCheckRefreshWindows(display,windows);
7901  (void) NegateImage(*image,MagickFalse);
7902  XSetCursorState(display,windows,MagickFalse);
7903  if (windows->image.orphan != MagickFalse)
7904  break;
7905  XConfigureImageColormap(display,resource_info,windows,*image);
7906  (void) XConfigureImage(display,resource_info,windows,*image);
7907  break;
7908  }
7909  case GrayscaleCommand:
7910  {
7911  /*
7912  Convert image to grayscale.
7913  */
7914  XSetCursorState(display,windows,MagickTrue);
7915  XCheckRefreshWindows(display,windows);
7916  (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7917  GrayscaleType : GrayscaleMatteType);
7918  XSetCursorState(display,windows,MagickFalse);
7919  if (windows->image.orphan != MagickFalse)
7920  break;
7921  XConfigureImageColormap(display,resource_info,windows,*image);
7922  (void) XConfigureImage(display,resource_info,windows,*image);
7923  break;
7924  }
7925  case MapCommand:
7926  {
7927  Image
7928  *affinity_image;
7929 
7930  static char
7931  filename[MaxTextExtent] = "\0";
7932 
7933  /*
7934  Request image file name from user.
7935  */
7936  XFileBrowserWidget(display,windows,"Map",filename);
7937  if (*filename == '\0')
7938  break;
7939  /*
7940  Map image.
7941  */
7942  XSetCursorState(display,windows,MagickTrue);
7943  XCheckRefreshWindows(display,windows);
7944  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7945  affinity_image=ReadImage(image_info,&(*image)->exception);
7946  if (affinity_image != (Image *) NULL)
7947  {
7948  (void) RemapImage(&quantize_info,*image,affinity_image);
7949  affinity_image=DestroyImage(affinity_image);
7950  }
7951  CatchException(&(*image)->exception);
7952  XSetCursorState(display,windows,MagickFalse);
7953  if (windows->image.orphan != MagickFalse)
7954  break;
7955  XConfigureImageColormap(display,resource_info,windows,*image);
7956  (void) XConfigureImage(display,resource_info,windows,*image);
7957  break;
7958  }
7959  case QuantizeCommand:
7960  {
7961  int
7962  status;
7963 
7964  static char
7965  colors[MaxTextExtent] = "256";
7966 
7967  /*
7968  Query user for maximum number of colors.
7969  */
7970  status=XDialogWidget(display,windows,"Quantize",
7971  "Maximum number of colors:",colors);
7972  if (*colors == '\0')
7973  break;
7974  /*
7975  Color reduce the image.
7976  */
7977  XSetCursorState(display,windows,MagickTrue);
7978  XCheckRefreshWindows(display,windows);
7979  quantize_info.number_colors=StringToUnsignedLong(colors);
7980  quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7981  (void) QuantizeImage(&quantize_info,*image);
7982  XSetCursorState(display,windows,MagickFalse);
7983  if (windows->image.orphan != MagickFalse)
7984  break;
7985  XConfigureImageColormap(display,resource_info,windows,*image);
7986  (void) XConfigureImage(display,resource_info,windows,*image);
7987  break;
7988  }
7989  case DespeckleCommand:
7990  {
7991  Image
7992  *despeckle_image;
7993 
7994  /*
7995  Despeckle image.
7996  */
7997  XSetCursorState(display,windows,MagickTrue);
7998  XCheckRefreshWindows(display,windows);
7999  despeckle_image=DespeckleImage(*image,&(*image)->exception);
8000  if (despeckle_image != (Image *) NULL)
8001  {
8002  *image=DestroyImage(*image);
8003  *image=despeckle_image;
8004  }
8005  CatchException(&(*image)->exception);
8006  XSetCursorState(display,windows,MagickFalse);
8007  if (windows->image.orphan != MagickFalse)
8008  break;
8009  XConfigureImageColormap(display,resource_info,windows,*image);
8010  (void) XConfigureImage(display,resource_info,windows,*image);
8011  break;
8012  }
8013  case EmbossCommand:
8014  {
8015  Image
8016  *emboss_image;
8017 
8018  static char
8019  radius[MaxTextExtent] = "0.0x1.0";
8020 
8021  /*
8022  Query user for emboss radius.
8023  */
8024  (void) XDialogWidget(display,windows,"Emboss",
8025  "Enter the emboss radius and standard deviation:",radius);
8026  if (*radius == '\0')
8027  break;
8028  /*
8029  Reduce noise in the image.
8030  */
8031  XSetCursorState(display,windows,MagickTrue);
8032  XCheckRefreshWindows(display,windows);
8033  flags=ParseGeometry(radius,&geometry_info);
8034  if ((flags & SigmaValue) == 0)
8035  geometry_info.sigma=1.0;
8036  emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8037  &(*image)->exception);
8038  if (emboss_image != (Image *) NULL)
8039  {
8040  *image=DestroyImage(*image);
8041  *image=emboss_image;
8042  }
8043  CatchException(&(*image)->exception);
8044  XSetCursorState(display,windows,MagickFalse);
8045  if (windows->image.orphan != MagickFalse)
8046  break;
8047  XConfigureImageColormap(display,resource_info,windows,*image);
8048  (void) XConfigureImage(display,resource_info,windows,*image);
8049  break;
8050  }
8051  case ReduceNoiseCommand:
8052  {
8053  Image
8054  *noise_image;
8055 
8056  static char
8057  radius[MaxTextExtent] = "0";
8058 
8059  /*
8060  Query user for noise radius.
8061  */
8062  (void) XDialogWidget(display,windows,"Reduce Noise",
8063  "Enter the noise radius:",radius);
8064  if (*radius == '\0')
8065  break;
8066  /*
8067  Reduce noise in the image.
8068  */
8069  XSetCursorState(display,windows,MagickTrue);
8070  XCheckRefreshWindows(display,windows);
8071  flags=ParseGeometry(radius,&geometry_info);
8072  noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8073  geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8074  if (noise_image != (Image *) NULL)
8075  {
8076  *image=DestroyImage(*image);
8077  *image=noise_image;
8078  }
8079  CatchException(&(*image)->exception);
8080  XSetCursorState(display,windows,MagickFalse);
8081  if (windows->image.orphan != MagickFalse)
8082  break;
8083  XConfigureImageColormap(display,resource_info,windows,*image);
8084  (void) XConfigureImage(display,resource_info,windows,*image);
8085  break;
8086  }
8087  case AddNoiseCommand:
8088  {
8089  char
8090  **noises;
8091 
8092  Image
8093  *noise_image;
8094 
8095  static char
8096  noise_type[MaxTextExtent] = "Gaussian";
8097 
8098  /*
8099  Add noise to the image.
8100  */
8101  noises=GetCommandOptions(MagickNoiseOptions);
8102  if (noises == (char **) NULL)
8103  break;
8104  XListBrowserWidget(display,windows,&windows->widget,
8105  (const char **) noises,"Add Noise",
8106  "Select a type of noise to add to your image:",noise_type);
8107  noises=DestroyStringList(noises);
8108  if (*noise_type == '\0')
8109  break;
8110  XSetCursorState(display,windows,MagickTrue);
8111  XCheckRefreshWindows(display,windows);
8112  noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8113  MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8114  if (noise_image != (Image *) NULL)
8115  {
8116  *image=DestroyImage(*image);
8117  *image=noise_image;
8118  }
8119  CatchException(&(*image)->exception);
8120  XSetCursorState(display,windows,MagickFalse);
8121  if (windows->image.orphan != MagickFalse)
8122  break;
8123  XConfigureImageColormap(display,resource_info,windows,*image);
8124  (void) XConfigureImage(display,resource_info,windows,*image);
8125  break;
8126  }
8127  case SharpenCommand:
8128  {
8129  Image
8130  *sharp_image;
8131 
8132  static char
8133  radius[MaxTextExtent] = "0.0x1.0";
8134 
8135  /*
8136  Query user for sharpen radius.
8137  */
8138  (void) XDialogWidget(display,windows,"Sharpen",
8139  "Enter the sharpen radius and standard deviation:",radius);
8140  if (*radius == '\0')
8141  break;
8142  /*
8143  Sharpen image scanlines.
8144  */
8145  XSetCursorState(display,windows,MagickTrue);
8146  XCheckRefreshWindows(display,windows);
8147  flags=ParseGeometry(radius,&geometry_info);
8148  sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8149  &(*image)->exception);
8150  if (sharp_image != (Image *) NULL)
8151  {
8152  *image=DestroyImage(*image);
8153  *image=sharp_image;
8154  }
8155  CatchException(&(*image)->exception);
8156  XSetCursorState(display,windows,MagickFalse);
8157  if (windows->image.orphan != MagickFalse)
8158  break;
8159  XConfigureImageColormap(display,resource_info,windows,*image);
8160  (void) XConfigureImage(display,resource_info,windows,*image);
8161  break;
8162  }
8163  case BlurCommand:
8164  {
8165  Image
8166  *blur_image;
8167 
8168  static char
8169  radius[MaxTextExtent] = "0.0x1.0";
8170 
8171  /*
8172  Query user for blur radius.
8173  */
8174  (void) XDialogWidget(display,windows,"Blur",
8175  "Enter the blur radius and standard deviation:",radius);
8176  if (*radius == '\0')
8177  break;
8178  /*
8179  Blur an image.
8180  */
8181  XSetCursorState(display,windows,MagickTrue);
8182  XCheckRefreshWindows(display,windows);
8183  flags=ParseGeometry(radius,&geometry_info);
8184  blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8185  &(*image)->exception);
8186  if (blur_image != (Image *) NULL)
8187  {
8188  *image=DestroyImage(*image);
8189  *image=blur_image;
8190  }
8191  CatchException(&(*image)->exception);
8192  XSetCursorState(display,windows,MagickFalse);
8193  if (windows->image.orphan != MagickFalse)
8194  break;
8195  XConfigureImageColormap(display,resource_info,windows,*image);
8196  (void) XConfigureImage(display,resource_info,windows,*image);
8197  break;
8198  }
8199  case ThresholdCommand:
8200  {
8201  double
8202  threshold;
8203 
8204  static char
8205  factor[MaxTextExtent] = "128";
8206 
8207  /*
8208  Query user for threshold value.
8209  */
8210  (void) XDialogWidget(display,windows,"Threshold",
8211  "Enter threshold value:",factor);
8212  if (*factor == '\0')
8213  break;
8214  /*
8215  Gamma correct image.
8216  */
8217  XSetCursorState(display,windows,MagickTrue);
8218  XCheckRefreshWindows(display,windows);
8219  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8220  (void) BilevelImage(*image,threshold);
8221  XSetCursorState(display,windows,MagickFalse);
8222  if (windows->image.orphan != MagickFalse)
8223  break;
8224  XConfigureImageColormap(display,resource_info,windows,*image);
8225  (void) XConfigureImage(display,resource_info,windows,*image);
8226  break;
8227  }
8228  case EdgeDetectCommand:
8229  {
8230  Image
8231  *edge_image;
8232 
8233  static char
8234  radius[MaxTextExtent] = "0";
8235 
8236  /*
8237  Query user for edge factor.
8238  */
8239  (void) XDialogWidget(display,windows,"Detect Edges",
8240  "Enter the edge detect radius:",radius);
8241  if (*radius == '\0')
8242  break;
8243  /*
8244  Detect edge in image.
8245  */
8246  XSetCursorState(display,windows,MagickTrue);
8247  XCheckRefreshWindows(display,windows);
8248  flags=ParseGeometry(radius,&geometry_info);
8249  edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8250  if (edge_image != (Image *) NULL)
8251  {
8252  *image=DestroyImage(*image);
8253  *image=edge_image;
8254  }
8255  CatchException(&(*image)->exception);
8256  XSetCursorState(display,windows,MagickFalse);
8257  if (windows->image.orphan != MagickFalse)
8258  break;
8259  XConfigureImageColormap(display,resource_info,windows,*image);
8260  (void) XConfigureImage(display,resource_info,windows,*image);
8261  break;
8262  }
8263  case SpreadCommand:
8264  {
8265  Image
8266  *spread_image;
8267 
8268  static char
8269  amount[MaxTextExtent] = "2";
8270 
8271  /*
8272  Query user for spread amount.
8273  */
8274  (void) XDialogWidget(display,windows,"Spread",
8275  "Enter the displacement amount:",amount);
8276  if (*amount == '\0')
8277  break;
8278  /*
8279  Displace image pixels by a random amount.
8280  */
8281  XSetCursorState(display,windows,MagickTrue);
8282  XCheckRefreshWindows(display,windows);
8283  flags=ParseGeometry(amount,&geometry_info);
8284  spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8285  if (spread_image != (Image *) NULL)
8286  {
8287  *image=DestroyImage(*image);
8288  *image=spread_image;
8289  }
8290  CatchException(&(*image)->exception);
8291  XSetCursorState(display,windows,MagickFalse);
8292  if (windows->image.orphan != MagickFalse)
8293  break;
8294  XConfigureImageColormap(display,resource_info,windows,*image);
8295  (void) XConfigureImage(display,resource_info,windows,*image);
8296  break;
8297  }
8298  case ShadeCommand:
8299  {
8300  Image
8301  *shade_image;
8302 
8303  int
8304  status;
8305 
8306  static char
8307  geometry[MaxTextExtent] = "30x30";
8308 
8309  /*
8310  Query user for the shade geometry.
8311  */
8312  status=XDialogWidget(display,windows,"Shade",
8313  "Enter the azimuth and elevation of the light source:",geometry);
8314  if (*geometry == '\0')
8315  break;
8316  /*
8317  Shade image pixels.
8318  */
8319  XSetCursorState(display,windows,MagickTrue);
8320  XCheckRefreshWindows(display,windows);
8321  flags=ParseGeometry(geometry,&geometry_info);
8322  if ((flags & SigmaValue) == 0)
8323  geometry_info.sigma=1.0;
8324  shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8325  geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8326  if (shade_image != (Image *) NULL)
8327  {
8328  *image=DestroyImage(*image);
8329  *image=shade_image;
8330  }
8331  CatchException(&(*image)->exception);
8332  XSetCursorState(display,windows,MagickFalse);
8333  if (windows->image.orphan != MagickFalse)
8334  break;
8335  XConfigureImageColormap(display,resource_info,windows,*image);
8336  (void) XConfigureImage(display,resource_info,windows,*image);
8337  break;
8338  }
8339  case RaiseCommand:
8340  {
8341  static char
8342  bevel_width[MaxTextExtent] = "10";
8343 
8344  /*
8345  Query user for bevel width.
8346  */
8347  (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8348  if (*bevel_width == '\0')
8349  break;
8350  /*
8351  Raise an image.
8352  */
8353  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8354  XSetCursorState(display,windows,MagickTrue);
8355  XCheckRefreshWindows(display,windows);
8356  (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8357  &(*image)->exception);
8358  (void) RaiseImage(*image,&page_geometry,MagickTrue);
8359  XSetCursorState(display,windows,MagickFalse);
8360  if (windows->image.orphan != MagickFalse)
8361  break;
8362  XConfigureImageColormap(display,resource_info,windows,*image);
8363  (void) XConfigureImage(display,resource_info,windows,*image);
8364  break;
8365  }
8366  case SegmentCommand:
8367  {
8368  static char
8369  threshold[MaxTextExtent] = "1.0x1.5";
8370 
8371  /*
8372  Query user for smoothing threshold.
8373  */
8374  (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8375  threshold);
8376  if (*threshold == '\0')
8377  break;
8378  /*
8379  Segment an image.
8380  */
8381  XSetCursorState(display,windows,MagickTrue);
8382  XCheckRefreshWindows(display,windows);
8383  flags=ParseGeometry(threshold,&geometry_info);
8384  if ((flags & SigmaValue) == 0)
8385  geometry_info.sigma=1.0;
8386  (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8387  geometry_info.sigma);
8388  XSetCursorState(display,windows,MagickFalse);
8389  if (windows->image.orphan != MagickFalse)
8390  break;
8391  XConfigureImageColormap(display,resource_info,windows,*image);
8392  (void) XConfigureImage(display,resource_info,windows,*image);
8393  break;
8394  }
8395  case SepiaToneCommand:
8396  {
8397  double
8398  threshold;
8399 
8400  Image
8401  *sepia_image;
8402 
8403  static char
8404  factor[MaxTextExtent] = "80%";
8405 
8406  /*
8407  Query user for sepia-tone factor.
8408  */
8409  (void) XDialogWidget(display,windows,"Sepia Tone",
8410  "Enter the sepia tone factor (0 - 99.9%):",factor);
8411  if (*factor == '\0')
8412  break;
8413  /*
8414  Sepia tone image pixels.
8415  */
8416  XSetCursorState(display,windows,MagickTrue);
8417  XCheckRefreshWindows(display,windows);
8418  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8419  sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8420  if (sepia_image != (Image *) NULL)
8421  {
8422  *image=DestroyImage(*image);
8423  *image=sepia_image;
8424  }
8425  CatchException(&(*image)->exception);
8426  XSetCursorState(display,windows,MagickFalse);
8427  if (windows->image.orphan != MagickFalse)
8428  break;
8429  XConfigureImageColormap(display,resource_info,windows,*image);
8430  (void) XConfigureImage(display,resource_info,windows,*image);
8431  break;
8432  }
8433  case SolarizeCommand:
8434  {
8435  double
8436  threshold;
8437 
8438  static char
8439  factor[MaxTextExtent] = "60%";
8440 
8441  /*
8442  Query user for solarize factor.
8443  */
8444  (void) XDialogWidget(display,windows,"Solarize",
8445  "Enter the solarize factor (0 - 99.9%):",factor);
8446  if (*factor == '\0')
8447  break;
8448  /*
8449  Solarize image pixels.
8450  */
8451  XSetCursorState(display,windows,MagickTrue);
8452  XCheckRefreshWindows(display,windows);
8453  threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8454  (void) SolarizeImage(*image,threshold);
8455  XSetCursorState(display,windows,MagickFalse);
8456  if (windows->image.orphan != MagickFalse)
8457  break;
8458  XConfigureImageColormap(display,resource_info,windows,*image);
8459  (void) XConfigureImage(display,resource_info,windows,*image);
8460  break;
8461  }
8462  case SwirlCommand:
8463  {
8464  Image
8465  *swirl_image;
8466 
8467  static char
8468  degrees[MaxTextExtent] = "60";
8469 
8470  /*
8471  Query user for swirl angle.
8472  */
8473  (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8474  degrees);
8475  if (*degrees == '\0')
8476  break;
8477  /*
8478  Swirl image pixels about the center.
8479  */
8480  XSetCursorState(display,windows,MagickTrue);
8481  XCheckRefreshWindows(display,windows);
8482  flags=ParseGeometry(degrees,&geometry_info);
8483  swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8484  if (swirl_image != (Image *) NULL)
8485  {
8486  *image=DestroyImage(*image);
8487  *image=swirl_image;
8488  }
8489  CatchException(&(*image)->exception);
8490  XSetCursorState(display,windows,MagickFalse);
8491  if (windows->image.orphan != MagickFalse)
8492  break;
8493  XConfigureImageColormap(display,resource_info,windows,*image);
8494  (void) XConfigureImage(display,resource_info,windows,*image);
8495  break;
8496  }
8497  case ImplodeCommand:
8498  {
8499  Image
8500  *implode_image;
8501 
8502  static char
8503  factor[MaxTextExtent] = "0.3";
8504 
8505  /*
8506  Query user for implode factor.
8507  */
8508  (void) XDialogWidget(display,windows,"Implode",
8509  "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8510  if (*factor == '\0')
8511  break;
8512  /*
8513  Implode image pixels about the center.
8514  */
8515  XSetCursorState(display,windows,MagickTrue);
8516  XCheckRefreshWindows(display,windows);
8517  flags=ParseGeometry(factor,&geometry_info);
8518  implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8519  if (implode_image != (Image *) NULL)
8520  {
8521  *image=DestroyImage(*image);
8522  *image=implode_image;
8523  }
8524  CatchException(&(*image)->exception);
8525  XSetCursorState(display,windows,MagickFalse);
8526  if (windows->image.orphan != MagickFalse)
8527  break;
8528  XConfigureImageColormap(display,resource_info,windows,*image);
8529  (void) XConfigureImage(display,resource_info,windows,*image);
8530  break;
8531  }
8532  case VignetteCommand:
8533  {
8534  Image
8535  *vignette_image;
8536 
8537  static char
8538  geometry[MaxTextExtent] = "0x20";
8539 
8540  /*
8541  Query user for the vignette geometry.
8542  */
8543  (void) XDialogWidget(display,windows,"Vignette",
8544  "Enter the radius, sigma, and x and y offsets:",geometry);
8545  if (*geometry == '\0')
8546  break;
8547  /*
8548  Soften the edges of the image in vignette style
8549  */
8550  XSetCursorState(display,windows,MagickTrue);
8551  XCheckRefreshWindows(display,windows);
8552  flags=ParseGeometry(geometry,&geometry_info);
8553  if ((flags & SigmaValue) == 0)
8554  geometry_info.sigma=1.0;
8555  if ((flags & XiValue) == 0)
8556  geometry_info.xi=0.1*(*image)->columns;
8557  if ((flags & PsiValue) == 0)
8558  geometry_info.psi=0.1*(*image)->rows;
8559  vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8560  (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8561  0.5),&(*image)->exception);
8562  if (vignette_image != (Image *) NULL)
8563  {
8564  *image=DestroyImage(*image);
8565  *image=vignette_image;
8566  }
8567  CatchException(&(*image)->exception);
8568  XSetCursorState(display,windows,MagickFalse);
8569  if (windows->image.orphan != MagickFalse)
8570  break;
8571  XConfigureImageColormap(display,resource_info,windows,*image);
8572  (void) XConfigureImage(display,resource_info,windows,*image);
8573  break;
8574  }
8575  case WaveCommand:
8576  {
8577  Image
8578  *wave_image;
8579 
8580  static char
8581  geometry[MaxTextExtent] = "25x150";
8582 
8583  /*
8584  Query user for the wave geometry.
8585  */
8586  (void) XDialogWidget(display,windows,"Wave",
8587  "Enter the amplitude and length of the wave:",geometry);
8588  if (*geometry == '\0')
8589  break;
8590  /*
8591  Alter an image along a sine wave.
8592  */
8593  XSetCursorState(display,windows,MagickTrue);
8594  XCheckRefreshWindows(display,windows);
8595  flags=ParseGeometry(geometry,&geometry_info);
8596  if ((flags & SigmaValue) == 0)
8597  geometry_info.sigma=1.0;
8598  wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8599  &(*image)->exception);
8600  if (wave_image != (Image *) NULL)
8601  {
8602  *image=DestroyImage(*image);
8603  *image=wave_image;
8604  }
8605  CatchException(&(*image)->exception);
8606  XSetCursorState(display,windows,MagickFalse);
8607  if (windows->image.orphan != MagickFalse)
8608  break;
8609  XConfigureImageColormap(display,resource_info,windows,*image);
8610  (void) XConfigureImage(display,resource_info,windows,*image);
8611  break;
8612  }
8613  case OilPaintCommand:
8614  {
8615  Image
8616  *paint_image;
8617 
8618  static char
8619  radius[MaxTextExtent] = "0";
8620 
8621  /*
8622  Query user for circular neighborhood radius.
8623  */
8624  (void) XDialogWidget(display,windows,"Oil Paint",
8625  "Enter the mask radius:",radius);
8626  if (*radius == '\0')
8627  break;
8628  /*
8629  OilPaint image scanlines.
8630  */
8631  XSetCursorState(display,windows,MagickTrue);
8632  XCheckRefreshWindows(display,windows);
8633  flags=ParseGeometry(radius,&geometry_info);
8634  paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8635  if (paint_image != (Image *) NULL)
8636  {
8637  *image=DestroyImage(*image);
8638  *image=paint_image;
8639  }
8640  CatchException(&(*image)->exception);
8641  XSetCursorState(display,windows,MagickFalse);
8642  if (windows->image.orphan != MagickFalse)
8643  break;
8644  XConfigureImageColormap(display,resource_info,windows,*image);
8645  (void) XConfigureImage(display,resource_info,windows,*image);
8646  break;
8647  }
8648  case CharcoalDrawCommand:
8649  {
8650  Image
8651  *charcoal_image;
8652 
8653  static char
8654  radius[MaxTextExtent] = "0x1";
8655 
8656  /*
8657  Query user for charcoal radius.
8658  */
8659  (void) XDialogWidget(display,windows,"Charcoal Draw",
8660  "Enter the charcoal radius and sigma:",radius);
8661  if (*radius == '\0')
8662  break;
8663  /*
8664  Charcoal the image.
8665  */
8666  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8667  XSetCursorState(display,windows,MagickTrue);
8668  XCheckRefreshWindows(display,windows);
8669  flags=ParseGeometry(radius,&geometry_info);
8670  if ((flags & SigmaValue) == 0)
8671  geometry_info.sigma=geometry_info.rho;
8672  charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8673  &(*image)->exception);
8674  if (charcoal_image != (Image *) NULL)
8675  {
8676  *image=DestroyImage(*image);
8677  *image=charcoal_image;
8678  }
8679  CatchException(&(*image)->exception);
8680  XSetCursorState(display,windows,MagickFalse);
8681  if (windows->image.orphan != MagickFalse)
8682  break;
8683  XConfigureImageColormap(display,resource_info,windows,*image);
8684  (void) XConfigureImage(display,resource_info,windows,*image);
8685  break;
8686  }
8687  case AnnotateCommand:
8688  {
8689  /*
8690  Annotate the image with text.
8691  */
8692  status=XAnnotateEditImage(display,resource_info,windows,*image);
8693  if (status == MagickFalse)
8694  {
8695  XNoticeWidget(display,windows,"Unable to annotate X image",
8696  (*image)->filename);
8697  break;
8698  }
8699  break;
8700  }
8701  case DrawCommand:
8702  {
8703  /*
8704  Draw image.
8705  */
8706  status=XDrawEditImage(display,resource_info,windows,image);
8707  if (status == MagickFalse)
8708  {
8709  XNoticeWidget(display,windows,"Unable to draw on the X image",
8710  (*image)->filename);
8711  break;
8712  }
8713  break;
8714  }
8715  case ColorCommand:
8716  {
8717  /*
8718  Color edit.
8719  */
8720  status=XColorEditImage(display,resource_info,windows,image);
8721  if (status == MagickFalse)
8722  {
8723  XNoticeWidget(display,windows,"Unable to pixel edit X image",
8724  (*image)->filename);
8725  break;
8726  }
8727  break;
8728  }
8729  case MatteCommand:
8730  {
8731  /*
8732  Matte edit.
8733  */
8734  status=XMatteEditImage(display,resource_info,windows,image);
8735  if (status == MagickFalse)
8736  {
8737  XNoticeWidget(display,windows,"Unable to matte edit X image",
8738  (*image)->filename);
8739  break;
8740  }
8741  break;
8742  }
8743  case CompositeCommand:
8744  {
8745  /*
8746  Composite image.
8747  */
8748  status=XCompositeImage(display,resource_info,windows,*image);
8749  if (status == MagickFalse)
8750  {
8751  XNoticeWidget(display,windows,"Unable to composite X image",
8752  (*image)->filename);
8753  break;
8754  }
8755  break;
8756  }
8757  case AddBorderCommand:
8758  {
8759  Image
8760  *border_image;
8761 
8762  static char
8763  geometry[MaxTextExtent] = "6x6";
8764 
8765  /*
8766  Query user for border color and geometry.
8767  */
8768  XColorBrowserWidget(display,windows,"Select",color);
8769  if (*color == '\0')
8770  break;
8771  (void) XDialogWidget(display,windows,"Add Border",
8772  "Enter border geometry:",geometry);
8773  if (*geometry == '\0')
8774  break;
8775  /*
8776  Add a border to the image.
8777  */
8778  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8779  XSetCursorState(display,windows,MagickTrue);
8780  XCheckRefreshWindows(display,windows);
8781  (void) QueryColorDatabase(color,&(*image)->border_color,
8782  &(*image)->exception);
8783  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8784  &(*image)->exception);
8785  border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8786  if (border_image != (Image *) NULL)
8787  {
8788  *image=DestroyImage(*image);
8789  *image=border_image;
8790  }
8791  CatchException(&(*image)->exception);
8792  XSetCursorState(display,windows,MagickFalse);
8793  if (windows->image.orphan != MagickFalse)
8794  break;
8795  windows->image.window_changes.width=(int) (*image)->columns;
8796  windows->image.window_changes.height=(int) (*image)->rows;
8797  XConfigureImageColormap(display,resource_info,windows,*image);
8798  (void) XConfigureImage(display,resource_info,windows,*image);
8799  break;
8800  }
8801  case AddFrameCommand:
8802  {
8803  FrameInfo
8804  frame_info;
8805 
8806  Image
8807  *frame_image;
8808 
8809  static char
8810  geometry[MaxTextExtent] = "6x6";
8811 
8812  /*
8813  Query user for frame color and geometry.
8814  */
8815  XColorBrowserWidget(display,windows,"Select",color);
8816  if (*color == '\0')
8817  break;
8818  (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8819  geometry);
8820  if (*geometry == '\0')
8821  break;
8822  /*
8823  Surround image with an ornamental border.
8824  */
8825  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8826  XSetCursorState(display,windows,MagickTrue);
8827  XCheckRefreshWindows(display,windows);
8828  (void) QueryColorDatabase(color,&(*image)->matte_color,
8829  &(*image)->exception);
8830  (void) ParsePageGeometry(*image,geometry,&page_geometry,
8831  &(*image)->exception);
8832  frame_info.width=page_geometry.width;
8833  frame_info.height=page_geometry.height;
8834  frame_info.outer_bevel=page_geometry.x;
8835  frame_info.inner_bevel=page_geometry.y;
8836  frame_info.x=(ssize_t) frame_info.width;
8837  frame_info.y=(ssize_t) frame_info.height;
8838  frame_info.width=(*image)->columns+2*frame_info.width;
8839  frame_info.height=(*image)->rows+2*frame_info.height;
8840  frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8841  if (frame_image != (Image *) NULL)
8842  {
8843  *image=DestroyImage(*image);
8844  *image=frame_image;
8845  }
8846  CatchException(&(*image)->exception);
8847  XSetCursorState(display,windows,MagickFalse);
8848  if (windows->image.orphan != MagickFalse)
8849  break;
8850  windows->image.window_changes.width=(int) (*image)->columns;
8851  windows->image.window_changes.height=(int) (*image)->rows;
8852  XConfigureImageColormap(display,resource_info,windows,*image);
8853  (void) XConfigureImage(display,resource_info,windows,*image);
8854  break;
8855  }
8856  case CommentCommand:
8857  {
8858  const char
8859  *value;
8860 
8861  FILE
8862  *file;
8863 
8864  int
8865  unique_file;
8866 
8867  /*
8868  Edit image comment.
8869  */
8870  unique_file=AcquireUniqueFileResource(image_info->filename);
8871  if (unique_file == -1)
8872  {
8873  XNoticeWidget(display,windows,"Unable to edit image comment",
8874  image_info->filename);
8875  break;
8876  }
8877  value=GetImageProperty(*image,"comment");
8878  if (value == (char *) NULL)
8879  unique_file=close_utf8(unique_file)-1;
8880  else
8881  {
8882  const char
8883  *p;
8884 
8885  file=fdopen(unique_file,"w");
8886  if (file == (FILE *) NULL)
8887  {
8888  XNoticeWidget(display,windows,"Unable to edit image comment",
8889  image_info->filename);
8890  break;
8891  }
8892  for (p=value; *p != '\0'; p++)
8893  (void) fputc((int) *p,file);
8894  (void) fputc('\n',file);
8895  (void) fclose(file);
8896  }
8897  XSetCursorState(display,windows,MagickTrue);
8898  XCheckRefreshWindows(display,windows);
8899  status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8900  &(*image)->exception);
8901  if (status == MagickFalse)
8902  XNoticeWidget(display,windows,"Unable to edit image comment",
8903  (char *) NULL);
8904  else
8905  {
8906  char
8907  *comment;
8908 
8909  comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8910  if (comment != (char *) NULL)
8911  {
8912  (void) SetImageProperty(*image,"comment",comment);
8913  (*image)->taint=MagickTrue;
8914  }
8915  }
8916  (void) RelinquishUniqueFileResource(image_info->filename);
8917  XSetCursorState(display,windows,MagickFalse);
8918  break;
8919  }
8920  case LaunchCommand:
8921  {
8922  /*
8923  Launch program.
8924  */
8925  XSetCursorState(display,windows,MagickTrue);
8926  XCheckRefreshWindows(display,windows);
8927  (void) AcquireUniqueFilename(filename);
8928  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8929  filename);
8930  status=WriteImage(image_info,*image);
8931  if (status == MagickFalse)
8932  XNoticeWidget(display,windows,"Unable to launch image editor",
8933  (char *) NULL);
8934  else
8935  {
8936  nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8937  CatchException(&(*image)->exception);
8938  XClientMessage(display,windows->image.id,windows->im_protocols,
8939  windows->im_next_image,CurrentTime);
8940  }
8941  (void) RelinquishUniqueFileResource(filename);
8942  XSetCursorState(display,windows,MagickFalse);
8943  break;
8944  }
8945  case RegionOfInterestCommand:
8946  {
8947  /*
8948  Apply an image processing technique to a region of interest.
8949  */
8950  (void) XROIImage(display,resource_info,windows,image);
8951  break;
8952  }
8953  case InfoCommand:
8954  break;
8955  case ZoomCommand:
8956  {
8957  /*
8958  Zoom image.
8959  */
8960  if (windows->magnify.mapped != MagickFalse)
8961  (void) XRaiseWindow(display,windows->magnify.id);
8962  else
8963  {
8964  /*
8965  Make magnify image.
8966  */
8967  XSetCursorState(display,windows,MagickTrue);
8968  (void) XMapRaised(display,windows->magnify.id);
8969  XSetCursorState(display,windows,MagickFalse);
8970  }
8971  break;
8972  }
8973  case ShowPreviewCommand:
8974  {
8975  char
8976  **previews;
8977 
8978  Image
8979  *preview_image;
8980 
8981  static char
8982  preview_type[MaxTextExtent] = "Gamma";
8983 
8984  /*
8985  Select preview type from menu.
8986  */
8987  previews=GetCommandOptions(MagickPreviewOptions);
8988  if (previews == (char **) NULL)
8989  break;
8990  XListBrowserWidget(display,windows,&windows->widget,
8991  (const char **) previews,"Preview",
8992  "Select an enhancement, effect, or F/X:",preview_type);
8993  previews=DestroyStringList(previews);
8994  if (*preview_type == '\0')
8995  break;
8996  /*
8997  Show image preview.
8998  */
8999  XSetCursorState(display,windows,MagickTrue);
9000  XCheckRefreshWindows(display,windows);
9001  image_info->preview_type=(PreviewType)
9002  ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9003  image_info->group=(ssize_t) windows->image.id;
9004  (void) DeleteImageProperty(*image,"label");
9005  (void) SetImageProperty(*image,"label","Preview");
9006  (void) AcquireUniqueFilename(filename);
9007  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9008  filename);
9009  status=WriteImage(image_info,*image);
9010  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9011  preview_image=ReadImage(image_info,&(*image)->exception);
9012  (void) RelinquishUniqueFileResource(filename);
9013  if (preview_image == (Image *) NULL)
9014  break;
9015  (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9016  filename);
9017  status=WriteImage(image_info,preview_image);
9018  preview_image=DestroyImage(preview_image);
9019  if (status == MagickFalse)
9020  XNoticeWidget(display,windows,"Unable to show image preview",
9021  (*image)->filename);
9022  XDelay(display,1500);
9023  XSetCursorState(display,windows,MagickFalse);
9024  break;
9025  }
9026  case ShowHistogramCommand:
9027  {
9028  Image
9029  *histogram_image;
9030 
9031  /*
9032  Show image histogram.
9033  */
9034  XSetCursorState(display,windows,MagickTrue);
9035  XCheckRefreshWindows(display,windows);
9036  image_info->group=(ssize_t) windows->image.id;
9037  (void) DeleteImageProperty(*image,"label");
9038  (void) SetImageProperty(*image,"label","Histogram");
9039  (void) AcquireUniqueFilename(filename);
9040  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9041  filename);
9042  status=WriteImage(image_info,*image);
9043  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9044  histogram_image=ReadImage(image_info,&(*image)->exception);
9045  (void) RelinquishUniqueFileResource(filename);
9046  if (histogram_image == (Image *) NULL)
9047  break;
9048  (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9049  "show:%s",filename);
9050  status=WriteImage(image_info,histogram_image);
9051  histogram_image=DestroyImage(histogram_image);
9052  if (status == MagickFalse)
9053  XNoticeWidget(display,windows,"Unable to show histogram",
9054  (*image)->filename);
9055  XDelay(display,1500);
9056  XSetCursorState(display,windows,MagickFalse);
9057  break;
9058  }
9059  case ShowMatteCommand:
9060  {
9061  Image
9062  *matte_image;
9063 
9064  if ((*image)->matte == MagickFalse)
9065  {
9066  XNoticeWidget(display,windows,
9067  "Image does not have any matte information",(*image)->filename);
9068  break;
9069  }
9070  /*
9071  Show image matte.
9072  */
9073  XSetCursorState(display,windows,MagickTrue);
9074  XCheckRefreshWindows(display,windows);
9075  image_info->group=(ssize_t) windows->image.id;
9076  (void) DeleteImageProperty(*image,"label");
9077  (void) SetImageProperty(*image,"label","Matte");
9078  (void) AcquireUniqueFilename(filename);
9079  (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9080  filename);
9081  status=WriteImage(image_info,*image);
9082  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9083  matte_image=ReadImage(image_info,&(*image)->exception);
9084  (void) RelinquishUniqueFileResource(filename);
9085  if (matte_image == (Image *) NULL)
9086  break;
9087  (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9088  filename);
9089  status=WriteImage(image_info,matte_image);
9090  matte_image=DestroyImage(matte_image);
9091  if (status == MagickFalse)
9092  XNoticeWidget(display,windows,"Unable to show matte",
9093  (*image)->filename);
9094  XDelay(display,1500);
9095  XSetCursorState(display,windows,MagickFalse);
9096  break;
9097  }
9098  case BackgroundCommand:
9099  {
9100  /*
9101  Background image.
9102  */
9103  status=XBackgroundImage(display,resource_info,windows,image);
9104  if (status == MagickFalse)
9105  break;
9106  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9107  if (nexus != (Image *) NULL)
9108  XClientMessage(display,windows->image.id,windows->im_protocols,
9109  windows->im_next_image,CurrentTime);
9110  break;
9111  }
9112  case SlideShowCommand:
9113  {
9114  static char
9115  delay[MaxTextExtent] = "5";
9116 
9117  /*
9118  Display next image after pausing.
9119  */
9120  (void) XDialogWidget(display,windows,"Slide Show",
9121  "Pause how many 1/100ths of a second between images:",delay);
9122  if (*delay == '\0')
9123  break;
9124  resource_info->delay=StringToUnsignedLong(delay);
9125  XClientMessage(display,windows->image.id,windows->im_protocols,
9126  windows->im_next_image,CurrentTime);
9127  break;
9128  }
9129  case PreferencesCommand:
9130  {
9131  /*
9132  Set user preferences.
9133  */
9134  status=XPreferencesWidget(display,resource_info,windows);
9135  if (status == MagickFalse)
9136  break;
9137  nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9138  if (nexus != (Image *) NULL)
9139  XClientMessage(display,windows->image.id,windows->im_protocols,
9140  windows->im_next_image,CurrentTime);
9141  break;
9142  }
9143  case HelpCommand:
9144  {
9145  /*
9146  User requested help.
9147  */
9148  XTextViewHelp(display,resource_info,windows,MagickFalse,
9149  "Help Viewer - Display",DisplayHelp);
9150  break;
9151  }
9152  case BrowseDocumentationCommand:
9153  {
9154  Atom
9155  mozilla_atom;
9156 
9157  Window
9158  mozilla_window,
9159  root_window;
9160 
9161  /*
9162  Browse the ImageMagick documentation.
9163  */
9164  root_window=XRootWindow(display,XDefaultScreen(display));
9165  mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9166  mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9167  if (mozilla_window != (Window) NULL)
9168  {
9169  char
9170  command[MaxTextExtent];
9171 
9172  /*
9173  Display documentation using Netscape remote control.
9174  */
9175  (void) FormatLocaleString(command,MaxTextExtent,
9176  "openurl(%s,new-tab)",MagickAuthoritativeURL);
9177  mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9178  (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9179  8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9180  XSetCursorState(display,windows,MagickFalse);
9181  break;
9182  }
9183  XSetCursorState(display,windows,MagickTrue);
9184  XCheckRefreshWindows(display,windows);
9185  status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9186  &(*image)->exception);
9187  if (status == MagickFalse)
9188  XNoticeWidget(display,windows,"Unable to browse documentation",
9189  (char *) NULL);
9190  XDelay(display,1500);
9191  XSetCursorState(display,windows,MagickFalse);
9192  break;
9193  }
9194  case VersionCommand:
9195  {
9196  XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9197  GetMagickCopyright());
9198  break;
9199  }
9200  case SaveToUndoBufferCommand:
9201  break;
9202  default:
9203  {
9204  (void) XBell(display,0);
9205  break;
9206  }
9207  }
9208  image_info=DestroyImageInfo(image_info);
9209  return(nexus);
9210 }
9211 
9212 /*
9213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9214 % %
9215 % %
9216 % %
9217 + X M a g n i f y I m a g e %
9218 % %
9219 % %
9220 % %
9221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9222 %
9223 % XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9224 % The magnified portion is displayed in a separate window.
9225 %
9226 % The format of the XMagnifyImage method is:
9227 %
9228 % void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9229 %
9230 % A description of each parameter follows:
9231 %
9232 % o display: Specifies a connection to an X server; returned from
9233 % XOpenDisplay.
9234 %
9235 % o windows: Specifies a pointer to a XWindows structure.
9236 %
9237 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
9238 % the entire image is refreshed.
9239 %
9240 */
9241 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9242 {
9243  char
9244  text[MaxTextExtent];
9245 
9246  int
9247  x,
9248  y;
9249 
9250  size_t
9251  state;
9252 
9253  /*
9254  Update magnified image until the mouse button is released.
9255  */
9256  (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9257  state=DefaultState;
9258  x=event->xbutton.x;
9259  y=event->xbutton.y;
9260  windows->magnify.x=(int) windows->image.x+x;
9261  windows->magnify.y=(int) windows->image.y+y;
9262  do
9263  {
9264  /*
9265  Map and unmap Info widget as text cursor crosses its boundaries.
9266  */
9267  if (windows->info.mapped != MagickFalse)
9268  {
9269  if ((x < (int) (windows->info.x+windows->info.width)) &&
9270  (y < (int) (windows->info.y+windows->info.height)))
9271  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9272  }
9273  else
9274  if ((x > (int) (windows->info.x+windows->info.width)) ||
9275  (y > (int) (windows->info.y+windows->info.height)))
9276  (void) XMapWindow(display,windows->info.id);
9277  if (windows->info.mapped != MagickFalse)
9278  {
9279  /*
9280  Display pointer position.
9281  */
9282  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9283  windows->magnify.x,windows->magnify.y);
9284  XInfoWidget(display,windows,text);
9285  }
9286  /*
9287  Wait for next event.
9288  */
9289  XScreenEvent(display,windows,event);
9290  switch (event->type)
9291  {
9292  case ButtonPress:
9293  break;
9294  case ButtonRelease:
9295  {
9296  /*
9297  User has finished magnifying image.
9298  */
9299  x=event->xbutton.x;
9300  y=event->xbutton.y;
9301  state|=ExitState;
9302  break;
9303  }
9304  case Expose:
9305  break;
9306  case MotionNotify:
9307  {
9308  x=event->xmotion.x;
9309  y=event->xmotion.y;
9310  break;
9311  }
9312  default:
9313  break;
9314  }
9315  /*
9316  Check boundary conditions.
9317  */
9318  if (x < 0)
9319  x=0;
9320  else
9321  if (x >= (int) windows->image.width)
9322  x=(int) windows->image.width-1;
9323  if (y < 0)
9324  y=0;
9325  else
9326  if (y >= (int) windows->image.height)
9327  y=(int) windows->image.height-1;
9328  } while ((state & ExitState) == 0);
9329  /*
9330  Display magnified image.
9331  */
9332  XSetCursorState(display,windows,MagickFalse);
9333 }
9334 
9335 /*
9336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9337 % %
9338 % %
9339 % %
9340 + X M a g n i f y W i n d o w C o m m a n d %
9341 % %
9342 % %
9343 % %
9344 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9345 %
9346 % XMagnifyWindowCommand() moves the image within an Magnify window by one
9347 % pixel as specified by the key symbol.
9348 %
9349 % The format of the XMagnifyWindowCommand method is:
9350 %
9351 % void XMagnifyWindowCommand(Display *display,XWindows *windows,
9352 % const MagickStatusType state,const KeySym key_symbol)
9353 %
9354 % A description of each parameter follows:
9355 %
9356 % o display: Specifies a connection to an X server; returned from
9357 % XOpenDisplay.
9358 %
9359 % o windows: Specifies a pointer to a XWindows structure.
9360 %
9361 % o state: key mask.
9362 %
9363 % o key_symbol: Specifies a KeySym which indicates which side of the image
9364 % to trim.
9365 %
9366 */
9367 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9368  const MagickStatusType state,const KeySym key_symbol)
9369 {
9370  unsigned int
9371  quantum;
9372 
9373  /*
9374  User specified a magnify factor or position.
9375  */
9376  quantum=1;
9377  if ((state & Mod1Mask) != 0)
9378  quantum=10;
9379  switch ((int) key_symbol)
9380  {
9381  case QuitCommand:
9382  {
9383  (void) XWithdrawWindow(display,windows->magnify.id,
9384  windows->magnify.screen);
9385  break;
9386  }
9387  case XK_Home:
9388  case XK_KP_Home:
9389  {
9390  windows->magnify.x=(int) windows->image.width/2;
9391  windows->magnify.y=(int) windows->image.height/2;
9392  break;
9393  }
9394  case XK_Left:
9395  case XK_KP_Left:
9396  {
9397  if (windows->magnify.x > 0)
9398  windows->magnify.x-=quantum;
9399  break;
9400  }
9401  case XK_Up:
9402  case XK_KP_Up:
9403  {
9404  if (windows->magnify.y > 0)
9405  windows->magnify.y-=quantum;
9406  break;
9407  }
9408  case XK_Right:
9409  case XK_KP_Right:
9410  {
9411  if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9412  windows->magnify.x+=quantum;
9413  break;
9414  }
9415  case XK_Down:
9416  case XK_KP_Down:
9417  {
9418  if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9419  windows->magnify.y+=quantum;
9420  break;
9421  }
9422  case XK_0:
9423  case XK_1:
9424  case XK_2:
9425  case XK_3:
9426  case XK_4:
9427  case XK_5:
9428  case XK_6:
9429  case XK_7:
9430  case XK_8:
9431  case XK_9:
9432  {
9433  windows->magnify.data=(key_symbol-XK_0);
9434  break;
9435  }
9436  case XK_KP_0:
9437  case XK_KP_1:
9438  case XK_KP_2:
9439  case XK_KP_3:
9440  case XK_KP_4:
9441  case XK_KP_5:
9442  case XK_KP_6:
9443  case XK_KP_7:
9444  case XK_KP_8:
9445  case XK_KP_9:
9446  {
9447  windows->magnify.data=(key_symbol-XK_KP_0);
9448  break;
9449  }
9450  default:
9451  break;
9452  }
9453  XMakeMagnifyImage(display,windows);
9454 }
9455 
9456 /*
9457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9458 % %
9459 % %
9460 % %
9461 + X M a k e P a n I m a g e %
9462 % %
9463 % %
9464 % %
9465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9466 %
9467 % XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9468 % icon window.
9469 %
9470 % The format of the XMakePanImage method is:
9471 %
9472 % void XMakePanImage(Display *display,XResourceInfo *resource_info,
9473 % XWindows *windows,Image *image)
9474 %
9475 % A description of each parameter follows:
9476 %
9477 % o display: Specifies a connection to an X server; returned from
9478 % XOpenDisplay.
9479 %
9480 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9481 %
9482 % o windows: Specifies a pointer to a XWindows structure.
9483 %
9484 % o image: the image.
9485 %
9486 */
9487 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9488  XWindows *windows,Image *image)
9489 {
9490  MagickStatusType
9491  status;
9492 
9493  /*
9494  Create and display image for panning icon.
9495  */
9496  XSetCursorState(display,windows,MagickTrue);
9497  XCheckRefreshWindows(display,windows);
9498  windows->pan.x=(int) windows->image.x;
9499  windows->pan.y=(int) windows->image.y;
9500  status=XMakeImage(display,resource_info,&windows->pan,image,
9501  windows->pan.width,windows->pan.height);
9502  if (status == MagickFalse)
9503  ThrowXWindowFatalException(XServerFatalError,image->exception.reason,
9504  image->exception.description);
9505  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9506  windows->pan.pixmap);
9507  (void) XClearWindow(display,windows->pan.id);
9508  XDrawPanRectangle(display,windows);
9509  XSetCursorState(display,windows,MagickFalse);
9510 }
9511 
9512 /*
9513 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9514 % %
9515 % %
9516 % %
9517 + X M a t t a E d i t I m a g e %
9518 % %
9519 % %
9520 % %
9521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9522 %
9523 % XMatteEditImage() allows the user to interactively change the Matte channel
9524 % of an image. If the image is PseudoClass it is promoted to DirectClass
9525 % before the matte information is stored.
9526 %
9527 % The format of the XMatteEditImage method is:
9528 %
9529 % MagickBooleanType XMatteEditImage(Display *display,
9530 % XResourceInfo *resource_info,XWindows *windows,Image **image)
9531 %
9532 % A description of each parameter follows:
9533 %
9534 % o display: Specifies a connection to an X server; returned from
9535 % XOpenDisplay.
9536 %
9537 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9538 %
9539 % o windows: Specifies a pointer to a XWindows structure.
9540 %
9541 % o image: the image; returned from ReadImage.
9542 %
9543 */
9544 static MagickBooleanType XMatteEditImage(Display *display,
9545  XResourceInfo *resource_info,XWindows *windows,Image **image)
9546 {
9547  const char
9548  *const MatteEditMenu[] =
9549  {
9550  "Method",
9551  "Border Color",
9552  "Fuzz",
9553  "Matte Value",
9554  "Undo",
9555  "Help",
9556  "Dismiss",
9557  (char *) NULL
9558  };
9559 
9560  static char
9561  matte[MaxTextExtent] = "0";
9562 
9563  static const ModeType
9564  MatteEditCommands[] =
9565  {
9566  MatteEditMethod,
9567  MatteEditBorderCommand,
9568  MatteEditFuzzCommand,
9569  MatteEditValueCommand,
9570  MatteEditUndoCommand,
9571  MatteEditHelpCommand,
9572  MatteEditDismissCommand
9573  };
9574 
9575  static PaintMethod
9576  method = PointMethod;
9577 
9578  static XColor
9579  border_color = { 0, 0, 0, 0, 0, 0 };
9580 
9581  char
9582  command[MaxTextExtent],
9583  text[MaxTextExtent] = "";
9584 
9585  Cursor
9586  cursor;
9587 
9588  int
9589  entry,
9590  id,
9591  x,
9592  x_offset,
9593  y,
9594  y_offset;
9595 
9596  int
9597  i;
9598 
9599  PixelPacket
9600  *q;
9601 
9602  unsigned int
9603  height,
9604  width;
9605 
9606  size_t
9607  state;
9608 
9609  XEvent
9610  event;
9611 
9612  /*
9613  Map Command widget.
9614  */
9615  (void) CloneString(&windows->command.name,"Matte Edit");
9616  windows->command.data=4;
9617  (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9618  (void) XMapRaised(display,windows->command.id);
9619  XClientMessage(display,windows->image.id,windows->im_protocols,
9620  windows->im_update_widget,CurrentTime);
9621  /*
9622  Make cursor.
9623  */
9624  cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9625  resource_info->background_color,resource_info->foreground_color);
9626  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9627  /*
9628  Track pointer until button 1 is pressed.
9629  */
9630  XQueryPosition(display,windows->image.id,&x,&y);
9631  (void) XSelectInput(display,windows->image.id,
9632  windows->image.attributes.event_mask | PointerMotionMask);
9633  state=DefaultState;
9634  do
9635  {
9636  if (windows->info.mapped != MagickFalse)
9637  {
9638  /*
9639  Display pointer position.
9640  */
9641  (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9642  x+windows->image.x,y+windows->image.y);
9643  XInfoWidget(display,windows,text);
9644  }
9645  /*
9646  Wait for next event.
9647  */
9648  XScreenEvent(display,windows,&event);
9649  if (event.xany.window == windows->command.id)
9650  {
9651  /*
9652  Select a command from the Command widget.
9653  */
9654  id=XCommandWidget(display,windows,MatteEditMenu,&event);
9655  if (id < 0)
9656  {
9657  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9658  continue;
9659  }
9660  switch (MatteEditCommands[id])
9661  {
9662  case MatteEditMethod:
9663  {
9664  char
9665  **methods;
9666 
9667  /*
9668  Select a method from the pop-up menu.
9669  */
9670  methods=GetCommandOptions(MagickMethodOptions);
9671  if (methods == (char **) NULL)
9672  break;
9673  entry=XMenuWidget(display,windows,MatteEditMenu[id],
9674  (const char **) methods,command);
9675  if (entry >= 0)
9676  method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9677  MagickFalse,methods[entry]);
9678  methods=DestroyStringList(methods);
9679  break;
9680  }
9681  case MatteEditBorderCommand:
9682  {
9683  const char
9684  *ColorMenu[MaxNumberPens];
9685 
9686  int
9687  pen_number;
9688 
9689  /*
9690  Initialize menu selections.
9691  */
9692  for (i=0; i < (int) (MaxNumberPens-2); i++)
9693  ColorMenu[i]=resource_info->pen_colors[i];
9694  ColorMenu[MaxNumberPens-2]="Browser...";
9695  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9696  /*
9697  Select a pen color from the pop-up menu.
9698  */
9699  pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9700  (const char **) ColorMenu,command);
9701  if (pen_number < 0)
9702  break;
9703  if (pen_number == (MaxNumberPens-2))
9704  {
9705  static char
9706  color_name[MaxTextExtent] = "gray";
9707 
9708  /*
9709  Select a pen color from a dialog.
9710  */
9711  resource_info->pen_colors[pen_number]=color_name;
9712  XColorBrowserWidget(display,windows,"Select",color_name);
9713  if (*color_name == '\0')
9714  break;
9715  }
9716  /*
9717  Set border color.
9718  */
9719  (void) XParseColor(display,windows->map_info->colormap,
9720  resource_info->pen_colors[pen_number],&border_color);
9721  break;
9722  }
9723  case MatteEditFuzzCommand:
9724  {
9725  const char
9726  *const FuzzMenu[] =
9727  {
9728  "0%",
9729  "2%",
9730  "5%",
9731  "10%",
9732  "15%",
9733  "Dialog...",
9734  (char *) NULL,
9735  };
9736 
9737  static char
9738  fuzz[MaxTextExtent];
9739 
9740  /*
9741  Select a command from the pop-up menu.
9742  */
9743  entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9744  command);
9745  if (entry < 0)
9746  break;
9747  if (entry != 5)
9748  {
9749  (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9750  QuantumRange+1.0);
9751  break;
9752  }
9753  (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9754  (void) XDialogWidget(display,windows,"Ok",
9755  "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9756  if (*fuzz == '\0')
9757  break;
9758  (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9759  (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9760  1.0);
9761  break;
9762  }
9763  case MatteEditValueCommand:
9764  {
9765  const char
9766  *const MatteMenu[] =
9767  {
9768  "Opaque",
9769  "Transparent",
9770  "Dialog...",
9771  (char *) NULL,
9772  };
9773 
9774  static char
9775  message[MaxTextExtent];
9776 
9777  /*
9778  Select a command from the pop-up menu.
9779  */
9780  entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9781  command);
9782  if (entry < 0)
9783  break;
9784  if (entry != 2)
9785  {
9786  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9787  (double) OpaqueOpacity);
9788  if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9789  (void) FormatLocaleString(matte,MaxTextExtent,"%g",
9790  (double) TransparentOpacity);
9791  break;
9792  }
9793  (void) FormatLocaleString(message,MaxTextExtent,
9794  "Enter matte value (0 - " "%g" "):",(double) QuantumRange);
9795  (void) XDialogWidget(display,windows,"Matte",message,matte);
9796  if (*matte == '\0')
9797  break;
9798  break;
9799  }
9800  case MatteEditUndoCommand:
9801  {
9802  (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9803  image);
9804  break;
9805  }
9806  case MatteEditHelpCommand:
9807  {
9808  XTextViewHelp(display,resource_info,windows,MagickFalse,
9809  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9810  break;
9811  }
9812  case MatteEditDismissCommand:
9813  {
9814  /*
9815  Prematurely exit.
9816  */
9817  state|=EscapeState;
9818  state|=ExitState;
9819  break;
9820  }
9821  default:
9822  break;
9823  }
9824  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9825  continue;
9826  }
9827  switch (event.type)
9828  {
9829  case ButtonPress:
9830  {
9831  if (event.xbutton.button != Button1)
9832  break;
9833  if ((event.xbutton.window != windows->image.id) &&
9834  (event.xbutton.window != windows->magnify.id))
9835  break;
9836  /*
9837  Update matte data.
9838  */
9839  x=event.xbutton.x;
9840  y=event.xbutton.y;
9841  (void) XMagickCommand(display,resource_info,windows,
9842  SaveToUndoBufferCommand,image);
9843  state|=UpdateConfigurationState;
9844  break;
9845  }
9846  case ButtonRelease:
9847  {
9848  if (event.xbutton.button != Button1)
9849  break;
9850  if ((event.xbutton.window != windows->image.id) &&
9851  (event.xbutton.window != windows->magnify.id))
9852  break;
9853  /*
9854  Update colormap information.
9855  */
9856  x=event.xbutton.x;
9857  y=event.xbutton.y;
9858  XConfigureImageColormap(display,resource_info,windows,*image);
9859  (void) XConfigureImage(display,resource_info,windows,*image);
9860  XInfoWidget(display,windows,text);
9861  (void) XCheckDefineCursor(display,windows->image.id,cursor);
9862  state&=(~UpdateConfigurationState);
9863  break;
9864  }
9865  case Expose:
9866  break;
9867  case KeyPress:
9868  {
9869  char
9870  command[MaxTextExtent];
9871 
9872  KeySym
9873  key_symbol;
9874 
9875  if (event.xkey.window == windows->magnify.id)
9876  {
9877  Window
9878  window;
9879 
9880  window=windows->magnify.id;
9881  while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9882  }
9883  if (event.xkey.window != windows->image.id)
9884  break;
9885  /*
9886  Respond to a user key press.
9887  */
9888  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9889  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9890  switch ((int) key_symbol)
9891  {
9892  case XK_Escape:
9893  case XK_F20:
9894  {
9895  /*
9896  Prematurely exit.
9897  */
9898  state|=ExitState;
9899  break;
9900  }
9901  case XK_F1:
9902  case XK_Help:
9903  {
9904  XTextViewHelp(display,resource_info,windows,MagickFalse,
9905  "Help Viewer - Matte Edit",ImageMatteEditHelp);
9906  break;
9907  }
9908  default:
9909  {
9910  (void) XBell(display,0);
9911  break;
9912  }
9913  }
9914  break;
9915  }
9916  case MotionNotify:
9917  {
9918  /*
9919  Map and unmap Info widget as cursor crosses its boundaries.
9920  */
9921  x=event.xmotion.x;
9922  y=event.xmotion.y;
9923  if (windows->info.mapped != MagickFalse)
9924  {
9925  if ((x < (int) (windows->info.x+windows->info.width)) &&
9926  (y < (int) (windows->info.y+windows->info.height)))
9927  (void) XWithdrawWindow(display,windows->info.id,
9928  windows->info.screen);
9929  }
9930  else
9931  if ((x > (int) (windows->info.x+windows->info.width)) ||
9932  (y > (int) (windows->info.y+windows->info.height)))
9933  (void) XMapWindow(display,windows->info.id);
9934  break;
9935  }
9936  default:
9937  break;
9938  }
9939  if (event.xany.window == windows->magnify.id)
9940  {
9941  x=windows->magnify.x-windows->image.x;
9942  y=windows->magnify.y-windows->image.y;
9943  }
9944  x_offset=x;
9945  y_offset=y;
9946  if ((state & UpdateConfigurationState) != 0)
9947  {
9948  CacheView
9949  *image_view;
9950 
9952  *exception;
9953 
9954  int
9955  x,
9956  y;
9957 
9958  /*
9959  Matte edit is relative to image configuration.
9960  */
9961  (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9962  MagickTrue);
9963  XPutPixel(windows->image.ximage,x_offset,y_offset,
9964  windows->pixel_info->background_color.pixel);
9965  width=(unsigned int) (*image)->columns;
9966  height=(unsigned int) (*image)->rows;
9967  x=0;
9968  y=0;
9969  if (windows->image.crop_geometry != (char *) NULL)
9970  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9971  &width,&height);
9972  x_offset=(int)
9973  (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9974  y_offset=(int)
9975  (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9976  if ((x_offset < 0) || (y_offset < 0))
9977  continue;
9978  if ((x_offset >= (int) (*image)->columns) ||
9979  (y_offset >= (int) (*image)->rows))
9980  continue;
9981  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9982  return(MagickFalse);
9983  (*image)->matte=MagickTrue;
9984  exception=(&(*image)->exception);
9985  image_view=AcquireAuthenticCacheView(*image,exception);
9986  switch (method)
9987  {
9988  case PointMethod:
9989  default:
9990  {
9991  /*
9992  Update matte information using point algorithm.
9993  */
9994  q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9995  (ssize_t) y_offset,1,1,exception);
9996  if (q == (PixelPacket *) NULL)
9997  break;
9998  q->opacity=(Quantum) StringToLong(matte);
9999  (void) SyncCacheViewAuthenticPixels(image_view,exception);
10000  break;
10001  }
10002  case ReplaceMethod:
10003  {
10004  PixelPacket
10005  target;
10006 
10007  /*
10008  Update matte information using replace algorithm.
10009  */
10010  (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10011  (ssize_t) y_offset,&target,exception);
10012  for (y=0; y < (int) (*image)->rows; y++)
10013  {
10014  q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10015  (*image)->columns,1,&(*image)->exception);
10016  if (q == (PixelPacket *) NULL)
10017  break;
10018  for (x=0; x < (int) (*image)->columns; x++)
10019  {
10020  if (IsColorSimilar(*image,q,&target))
10021  q->opacity=(Quantum) StringToLong(matte);
10022  q++;
10023  }
10024  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10025  break;
10026  }
10027  break;
10028  }
10029  case FloodfillMethod:
10030  case FillToBorderMethod:
10031  {
10032  DrawInfo
10033  *draw_info;
10034 
10036  target;
10037 
10038  /*
10039  Update matte information using floodfill algorithm.
10040  */
10041  (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10042  (ssize_t) y_offset,&target,exception);
10043  if (method == FillToBorderMethod)
10044  {
10045  target.red=(MagickRealType)
10046  ScaleShortToQuantum(border_color.red);
10047  target.green=(MagickRealType)
10048  ScaleShortToQuantum(border_color.green);
10049  target.blue=(MagickRealType)
10050  ScaleShortToQuantum(border_color.blue);
10051  }
10052  draw_info=CloneDrawInfo(resource_info->image_info,
10053  (DrawInfo *) NULL);
10054  draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte,
10055  (char **) NULL));
10056  (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10057  (ssize_t) x_offset,(ssize_t) y_offset,
10058  method == FloodfillMethod ? MagickFalse : MagickTrue);
10059  draw_info=DestroyDrawInfo(draw_info);
10060  break;
10061  }
10062  case ResetMethod:
10063  {
10064  /*
10065  Update matte information using reset algorithm.
10066  */
10067  if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10068  return(MagickFalse);
10069  for (y=0; y < (int) (*image)->rows; y++)
10070  {
10071  q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10072  (*image)->columns,1,exception);
10073  if (q == (PixelPacket *) NULL)
10074  break;
10075  for (x=0; x < (int) (*image)->columns; x++)
10076  {
10077  q->opacity=(Quantum) StringToLong(matte);
10078  q++;
10079  }
10080  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10081  break;
10082  }
10083  if (StringToLong(matte) == OpaqueOpacity)
10084  (*image)->matte=MagickFalse;
10085  break;
10086  }
10087  }
10088  image_view=DestroyCacheView(image_view);
10089  state&=(~UpdateConfigurationState);
10090  }
10091  } while ((state & ExitState) == 0);
10092  (void) XSelectInput(display,windows->image.id,
10093  windows->image.attributes.event_mask);
10094  XSetCursorState(display,windows,MagickFalse);
10095  (void) XFreeCursor(display,cursor);
10096  return(MagickTrue);
10097 }
10098 
10099 /*
10100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10101 % %
10102 % %
10103 % %
10104 + X O p e n I m a g e %
10105 % %
10106 % %
10107 % %
10108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10109 %
10110 % XOpenImage() loads an image from a file.
10111 %
10112 % The format of the XOpenImage method is:
10113 %
10114 % Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10115 % XWindows *windows,const unsigned int command)
10116 %
10117 % A description of each parameter follows:
10118 %
10119 % o display: Specifies a connection to an X server; returned from
10120 % XOpenDisplay.
10121 %
10122 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10123 %
10124 % o windows: Specifies a pointer to a XWindows structure.
10125 %
10126 % o command: A value other than zero indicates that the file is selected
10127 % from the command line argument list.
10128 %
10129 */
10130 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10131  XWindows *windows,const MagickBooleanType command)
10132 {
10133  const MagickInfo
10134  *magick_info;
10135 
10137  *exception;
10138 
10139  Image
10140  *nexus;
10141 
10142  ImageInfo
10143  *image_info;
10144 
10145  static char
10146  filename[MaxTextExtent] = "\0";
10147 
10148  /*
10149  Request file name from user.
10150  */
10151  if (command == MagickFalse)
10152  XFileBrowserWidget(display,windows,"Open",filename);
10153  else
10154  {
10155  char
10156  **filelist,
10157  **files;
10158 
10159  int
10160  count,
10161  status;
10162 
10163  int
10164  i,
10165  j;
10166 
10167  /*
10168  Select next image from the command line.
10169  */
10170  status=XGetCommand(display,windows->image.id,&files,&count);
10171  if (status == 0)
10172  ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10173  filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10174  if (filelist == (char **) NULL)
10175  {
10176  (void) XFreeStringList(files);
10177  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
10178  "...");
10179  return((Image *) NULL);
10180  }
10181  j=0;
10182  for (i=1; i < count; i++)
10183  if (*files[i] != '-')
10184  filelist[j++]=files[i];
10185  filelist[j]=(char *) NULL;
10186  XListBrowserWidget(display,windows,&windows->widget,
10187  (const char **) filelist,"Load","Select Image to Load:",filename);
10188  filelist=(char **) RelinquishMagickMemory(filelist);
10189  (void) XFreeStringList(files);
10190  }
10191  if (*filename == '\0')
10192  return((Image *) NULL);
10193  image_info=CloneImageInfo(resource_info->image_info);
10194  (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10195  (void *) NULL);
10196  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10197  exception=AcquireExceptionInfo();
10198  (void) SetImageInfo(image_info,0,exception);
10199  if (LocaleCompare(image_info->magick,"X") == 0)
10200  {
10201  char
10202  seconds[MaxTextExtent];
10203 
10204  /*
10205  User may want to delay the X server screen grab.
10206  */
10207  (void) CopyMagickString(seconds,"0",MaxTextExtent);
10208  (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10209  seconds);
10210  if (*seconds == '\0')
10211  return((Image *) NULL);
10212  XDelay(display,(size_t) (1000*StringToLong(seconds)));
10213  }
10214  magick_info=GetMagickInfo(image_info->magick,exception);
10215  if ((magick_info != (const MagickInfo *) NULL) &&
10216  (magick_info->raw != MagickFalse))
10217  {
10218  char
10219  geometry[MaxTextExtent];
10220 
10221  /*
10222  Request image size from the user.
10223  */
10224  (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10225  if (image_info->size != (char *) NULL)
10226  (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10227  (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10228  geometry);
10229  (void) CloneString(&image_info->size,geometry);
10230  }
10231  /*
10232  Load the image.
10233  */
10234  XSetCursorState(display,windows,MagickTrue);
10235  XCheckRefreshWindows(display,windows);
10236  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10237  nexus=ReadImage(image_info,exception);
10238  CatchException(exception);
10239  XSetCursorState(display,windows,MagickFalse);
10240  if (nexus != (Image *) NULL)
10241  XClientMessage(display,windows->image.id,windows->im_protocols,
10242  windows->im_next_image,CurrentTime);
10243  else
10244  {
10245  char
10246  *text,
10247  **textlist;
10248 
10249  /*
10250  Unknown image format.
10251  */
10252  text=FileToString(filename,~0UL,exception);
10253  if (text == (char *) NULL)
10254  return((Image *) NULL);
10255  textlist=StringToList(text);
10256  if (textlist != (char **) NULL)
10257  {
10258  char
10259  title[MaxTextExtent];
10260 
10261  int
10262  i;
10263 
10264  (void) FormatLocaleString(title,MaxTextExtent,
10265  "Unknown format: %s",filename);
10266  XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10267  (const char **) textlist);
10268  for (i=0; textlist[i] != (char *) NULL; i++)
10269  textlist[i]=DestroyString(textlist[i]);
10270  textlist=(char **) RelinquishMagickMemory(textlist);
10271  }
10272  text=DestroyString(text);
10273  }
10274  exception=DestroyExceptionInfo(exception);
10275  image_info=DestroyImageInfo(image_info);
10276  return(nexus);
10277 }
10278 
10279 /*
10280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10281 % %
10282 % %
10283 % %
10284 + X P a n I m a g e %
10285 % %
10286 % %
10287 % %
10288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10289 %
10290 % XPanImage() pans the image until the mouse button is released.
10291 %
10292 % The format of the XPanImage method is:
10293 %
10294 % void XPanImage(Display *display,XWindows *windows,XEvent *event)
10295 %
10296 % A description of each parameter follows:
10297 %
10298 % o display: Specifies a connection to an X server; returned from
10299 % XOpenDisplay.
10300 %
10301 % o windows: Specifies a pointer to a XWindows structure.
10302 %
10303 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
10304 % the entire image is refreshed.
10305 %
10306 */
10307 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10308 {
10309  char
10310  text[MaxTextExtent];
10311 
10312  Cursor
10313  cursor;
10314 
10315  MagickRealType
10316  x_factor,
10317  y_factor;
10318 
10320  pan_info;
10321 
10322  size_t
10323  state;
10324 
10325  /*
10326  Define cursor.
10327  */
10328  if ((windows->image.ximage->width > (int) windows->image.width) &&
10329  (windows->image.ximage->height > (int) windows->image.height))
10330  cursor=XCreateFontCursor(display,XC_fleur);
10331  else
10332  if (windows->image.ximage->width > (int) windows->image.width)
10333  cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10334  else
10335  if (windows->image.ximage->height > (int) windows->image.height)
10336  cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10337  else
10338  cursor=XCreateFontCursor(display,XC_arrow);
10339  (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10340  /*
10341  Pan image as pointer moves until the mouse button is released.
10342  */
10343  x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10344  y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10345  pan_info.width=windows->pan.width*windows->image.width/
10346  windows->image.ximage->width;
10347  pan_info.height=windows->pan.height*windows->image.height/
10348  windows->image.ximage->height;
10349  pan_info.x=0;
10350  pan_info.y=0;
10351  state=UpdateConfigurationState;
10352  do
10353  {
10354  switch (event->type)
10355  {
10356  case ButtonPress:
10357  {
10358  /*
10359  User choose an initial pan location.
10360  */
10361  pan_info.x=(ssize_t) event->xbutton.x;
10362  pan_info.y=(ssize_t) event->xbutton.y;
10363  state|=UpdateConfigurationState;
10364  break;
10365  }
10366  case ButtonRelease:
10367  {
10368  /*
10369  User has finished panning the image.
10370  */
10371  pan_info.x=(ssize_t) event->xbutton.x;
10372  pan_info.y=(ssize_t) event->xbutton.y;
10373  state|=UpdateConfigurationState | ExitState;
10374  break;
10375  }
10376  case MotionNotify:
10377  {
10378  pan_info.x=(ssize_t) event->xmotion.x;
10379  pan_info.y=(ssize_t) event->xmotion.y;
10380  state|=UpdateConfigurationState;
10381  }
10382  default:
10383  break;
10384  }
10385  if ((state & UpdateConfigurationState) != 0)
10386  {
10387  /*
10388  Check boundary conditions.
10389  */
10390  if (pan_info.x < (ssize_t) (pan_info.width/2))
10391  pan_info.x=0;
10392  else
10393  pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10394  if (pan_info.x < 0)
10395  pan_info.x=0;
10396  else
10397  if ((int) (pan_info.x+windows->image.width) >
10398  windows->image.ximage->width)
10399  pan_info.x=(ssize_t)
10400  (windows->image.ximage->width-windows->image.width);
10401  if (pan_info.y < (ssize_t) (pan_info.height/2))
10402  pan_info.y=0;
10403  else
10404  pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10405  if (pan_info.y < 0)
10406  pan_info.y=0;
10407  else
10408  if ((int) (pan_info.y+windows->image.height) >
10409  windows->image.ximage->height)
10410  pan_info.y=(ssize_t)
10411  (windows->image.ximage->height-windows->image.height);
10412  if ((windows->image.x != (int) pan_info.x) ||
10413  (windows->image.y != (int) pan_info.y))
10414  {
10415  /*
10416  Display image pan offset.
10417  */
10418  windows->image.x=(int) pan_info.x;
10419  windows->image.y=(int) pan_info.y;
10420  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10421  windows->image.width,windows->image.height,windows->image.x,
10422  windows->image.y);
10423  XInfoWidget(display,windows,text);
10424  /*
10425  Refresh Image window.
10426  */
10427  XDrawPanRectangle(display,windows);
10428  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10429  }
10430  state&=(~UpdateConfigurationState);
10431  }
10432  /*
10433  Wait for next event.
10434  */
10435  if ((state & ExitState) == 0)
10436  XScreenEvent(display,windows,event);
10437  } while ((state & ExitState) == 0);
10438  /*
10439  Restore cursor.
10440  */
10441  (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10442  (void) XFreeCursor(display,cursor);
10443  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10444 }
10445 
10446 /*
10447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10448 % %
10449 % %
10450 % %
10451 + X P a s t e I m a g e %
10452 % %
10453 % %
10454 % %
10455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10456 %
10457 % XPasteImage() pastes an image previously saved with XCropImage in the X
10458 % window image at a location the user chooses with the pointer.
10459 %
10460 % The format of the XPasteImage method is:
10461 %
10462 % MagickBooleanType XPasteImage(Display *display,
10463 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10464 %
10465 % A description of each parameter follows:
10466 %
10467 % o display: Specifies a connection to an X server; returned from
10468 % XOpenDisplay.
10469 %
10470 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10471 %
10472 % o windows: Specifies a pointer to a XWindows structure.
10473 %
10474 % o image: the image; returned from ReadImage.
10475 %
10476 */
10477 static MagickBooleanType XPasteImage(Display *display,
10478  XResourceInfo *resource_info,XWindows *windows,Image *image)
10479 {
10480  const char
10481  *const PasteMenu[] =
10482  {
10483  "Operator",
10484  "Help",
10485  "Dismiss",
10486  (char *) NULL
10487  };
10488 
10489  static const ModeType
10490  PasteCommands[] =
10491  {
10492  PasteOperatorsCommand,
10493  PasteHelpCommand,
10494  PasteDismissCommand
10495  };
10496 
10497  static CompositeOperator
10498  compose = CopyCompositeOp;
10499 
10500  char
10501  text[MaxTextExtent];
10502 
10503  Cursor
10504  cursor;
10505 
10506  Image
10507  *paste_image;
10508 
10509  int
10510  entry,
10511  id,
10512  x,
10513  y;
10514 
10515  MagickRealType
10516  scale_factor;
10517 
10519  highlight_info,
10520  paste_info;
10521 
10522  unsigned int
10523  height,
10524  width;
10525 
10526  size_t
10527  state;
10528 
10529  XEvent
10530  event;
10531 
10532  /*
10533  Copy image.
10534  */
10535  if (resource_info->copy_image == (Image *) NULL)
10536  return(MagickFalse);
10537  paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10538  &image->exception);
10539  if (paste_image == (Image *) NULL)
10540  return(MagickFalse);
10541  /*
10542  Map Command widget.
10543  */
10544  (void) CloneString(&windows->command.name,"Paste");
10545  windows->command.data=1;
10546  (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10547  (void) XMapRaised(display,windows->command.id);
10548  XClientMessage(display,windows->image.id,windows->im_protocols,
10549  windows->im_update_widget,CurrentTime);
10550  /*
10551  Track pointer until button 1 is pressed.
10552  */
10553  XSetCursorState(display,windows,MagickFalse);
10554  XQueryPosition(display,windows->image.id,&x,&y);
10555  (void) XSelectInput(display,windows->image.id,
10556  windows->image.attributes.event_mask | PointerMotionMask);
10557  paste_info.x=(ssize_t) windows->image.x+x;
10558  paste_info.y=(ssize_t) windows->image.y+y;
10559  paste_info.width=0;
10560  paste_info.height=0;
10561  cursor=XCreateFontCursor(display,XC_ul_angle);
10562  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10563  state=DefaultState;
10564  do
10565  {
10566  if (windows->info.mapped != MagickFalse)
10567  {
10568  /*
10569  Display pointer position.
10570  */
10571  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10572  (long) paste_info.x,(long) paste_info.y);
10573  XInfoWidget(display,windows,text);
10574  }
10575  highlight_info=paste_info;
10576  highlight_info.x=paste_info.x-windows->image.x;
10577  highlight_info.y=paste_info.y-windows->image.y;
10578  XHighlightRectangle(display,windows->image.id,
10579  windows->image.highlight_context,&highlight_info);
10580  /*
10581  Wait for next event.
10582  */
10583  XScreenEvent(display,windows,&event);
10584  XHighlightRectangle(display,windows->image.id,
10585  windows->image.highlight_context,&highlight_info);
10586  if (event.xany.window == windows->command.id)
10587  {
10588  /*
10589  Select a command from the Command widget.
10590  */
10591  id=XCommandWidget(display,windows,PasteMenu,&event);
10592  if (id < 0)
10593  continue;
10594  switch (PasteCommands[id])
10595  {
10596  case PasteOperatorsCommand:
10597  {
10598  char
10599  command[MaxTextExtent],
10600  **operators;
10601 
10602  /*
10603  Select a command from the pop-up menu.
10604  */
10605  operators=GetCommandOptions(MagickComposeOptions);
10606  if (operators == (char **) NULL)
10607  break;
10608  entry=XMenuWidget(display,windows,PasteMenu[id],
10609  (const char **) operators,command);
10610  if (entry >= 0)
10611  compose=(CompositeOperator) ParseCommandOption(
10612  MagickComposeOptions,MagickFalse,operators[entry]);
10613  operators=DestroyStringList(operators);
10614  break;
10615  }
10616  case PasteHelpCommand:
10617  {
10618  XTextViewHelp(display,resource_info,windows,MagickFalse,
10619  "Help Viewer - Image Composite",ImagePasteHelp);
10620  break;
10621  }
10622  case PasteDismissCommand:
10623  {
10624  /*
10625  Prematurely exit.
10626  */
10627  state|=EscapeState;
10628  state|=ExitState;
10629  break;
10630  }
10631  default:
10632  break;
10633  }
10634  continue;
10635  }
10636  switch (event.type)
10637  {
10638  case ButtonPress:
10639  {
10640  if (resource_info->debug != MagickFalse)
10641  (void) LogMagickEvent(X11Event,GetMagickModule(),
10642  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10643  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10644  if (event.xbutton.button != Button1)
10645  break;
10646  if (event.xbutton.window != windows->image.id)
10647  break;
10648  /*
10649  Paste rectangle is relative to image configuration.
10650  */
10651  width=(unsigned int) image->columns;
10652  height=(unsigned int) image->rows;
10653  x=0;
10654  y=0;
10655  if (windows->image.crop_geometry != (char *) NULL)
10656  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10657  &width,&height);
10658  scale_factor=(MagickRealType) windows->image.ximage->width/width;
10659  paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10660  scale_factor=(MagickRealType) windows->image.ximage->height/height;
10661  paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10662  (void) XCheckDefineCursor(display,windows->image.id,cursor);
10663  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10664  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10665  break;
10666  }
10667  case ButtonRelease:
10668  {
10669  if (resource_info->debug != MagickFalse)
10670  (void) LogMagickEvent(X11Event,GetMagickModule(),
10671  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10672  event.xbutton.button,event.xbutton.x,event.xbutton.y);
10673  if (event.xbutton.button != Button1)
10674  break;
10675  if (event.xbutton.window != windows->image.id)
10676  break;
10677  if ((paste_info.width != 0) && (paste_info.height != 0))
10678  {
10679  /*
10680  User has selected the location of the paste image.
10681  */
10682  paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10683  paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10684  state|=ExitState;
10685  }
10686  break;
10687  }
10688  case Expose:
10689  break;
10690  case KeyPress:
10691  {
10692  char
10693  command[MaxTextExtent];
10694 
10695  KeySym
10696  key_symbol;
10697 
10698  int
10699  length;
10700 
10701  if (event.xkey.window != windows->image.id)
10702  break;
10703  /*
10704  Respond to a user key press.
10705  */
10706  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10707  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10708  *(command+length)='\0';
10709  if (resource_info->debug != MagickFalse)
10710  (void) LogMagickEvent(X11Event,GetMagickModule(),
10711  "Key press: 0x%lx (%s)",(long) key_symbol,command);
10712  switch ((int) key_symbol)
10713  {
10714  case XK_Escape:
10715  case XK_F20:
10716  {
10717  /*
10718  Prematurely exit.
10719  */
10720  paste_image=DestroyImage(paste_image);
10721  state|=EscapeState;
10722  state|=ExitState;
10723  break;
10724  }
10725  case XK_F1:
10726  case XK_Help:
10727  {
10728  (void) XSetFunction(display,windows->image.highlight_context,
10729  GXcopy);
10730  XTextViewHelp(display,resource_info,windows,MagickFalse,
10731  "Help Viewer - Image Composite",ImagePasteHelp);
10732  (void) XSetFunction(display,windows->image.highlight_context,
10733  GXinvert);
10734  break;
10735  }
10736  default:
10737  {
10738  (void) XBell(display,0);
10739  break;
10740  }
10741  }
10742  break;
10743  }
10744  case MotionNotify:
10745  {
10746  /*
10747  Map and unmap Info widget as text cursor crosses its boundaries.
10748  */
10749  x=event.xmotion.x;
10750  y=event.xmotion.y;
10751  if (windows->info.mapped != MagickFalse)
10752  {
10753  if ((x < (int) (windows->info.x+windows->info.width)) &&
10754  (y < (int) (windows->info.y+windows->info.height)))
10755  (void) XWithdrawWindow(display,windows->info.id,
10756  windows->info.screen);
10757  }
10758  else
10759  if ((x > (int) (windows->info.x+windows->info.width)) ||
10760  (y > (int) (windows->info.y+windows->info.height)))
10761  (void) XMapWindow(display,windows->info.id);
10762  paste_info.x=(ssize_t) windows->image.x+x;
10763  paste_info.y=(ssize_t) windows->image.y+y;
10764  break;
10765  }
10766  default:
10767  {
10768  if (resource_info->debug != MagickFalse)
10769  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10770  event.type);
10771  break;
10772  }
10773  }
10774  } while ((state & ExitState) == 0);
10775  (void) XSelectInput(display,windows->image.id,
10776  windows->image.attributes.event_mask);
10777  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10778  XSetCursorState(display,windows,MagickFalse);
10779  (void) XFreeCursor(display,cursor);
10780  if ((state & EscapeState) != 0)
10781  return(MagickTrue);
10782  /*
10783  Image pasting is relative to image configuration.
10784  */
10785  XSetCursorState(display,windows,MagickTrue);
10786  XCheckRefreshWindows(display,windows);
10787  width=(unsigned int) image->columns;
10788  height=(unsigned int) image->rows;
10789  x=0;
10790  y=0;
10791  if (windows->image.crop_geometry != (char *) NULL)
10792  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10793  scale_factor=(MagickRealType) width/windows->image.ximage->width;
10794  paste_info.x+=x;
10795  paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10796  paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10797  scale_factor=(MagickRealType) height/windows->image.ximage->height;
10798  paste_info.y+=y;
10799  paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10800  paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10801  /*
10802  Paste image with X Image window.
10803  */
10804  (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10805  paste_image=DestroyImage(paste_image);
10806  XSetCursorState(display,windows,MagickFalse);
10807  /*
10808  Update image colormap.
10809  */
10810  XConfigureImageColormap(display,resource_info,windows,image);
10811  (void) XConfigureImage(display,resource_info,windows,image);
10812  return(MagickTrue);
10813 }
10814 
10815 /*
10816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10817 % %
10818 % %
10819 % %
10820 + X P r i n t I m a g e %
10821 % %
10822 % %
10823 % %
10824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10825 %
10826 % XPrintImage() prints an image to a Postscript printer.
10827 %
10828 % The format of the XPrintImage method is:
10829 %
10830 % MagickBooleanType XPrintImage(Display *display,
10831 % XResourceInfo *resource_info,XWindows *windows,Image *image)
10832 %
10833 % A description of each parameter follows:
10834 %
10835 % o display: Specifies a connection to an X server; returned from
10836 % XOpenDisplay.
10837 %
10838 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10839 %
10840 % o windows: Specifies a pointer to a XWindows structure.
10841 %
10842 % o image: the image.
10843 %
10844 */
10845 static MagickBooleanType XPrintImage(Display *display,
10846  XResourceInfo *resource_info,XWindows *windows,Image *image)
10847 {
10848  char
10849  filename[MaxTextExtent],
10850  geometry[MaxTextExtent];
10851 
10852  const char
10853  *const PageSizes[] =
10854  {
10855  "Letter",
10856  "Tabloid",
10857  "Ledger",
10858  "Legal",
10859  "Statement",
10860  "Executive",
10861  "A3",
10862  "A4",
10863  "A5",
10864  "B4",
10865  "B5",
10866  "Folio",
10867  "Quarto",
10868  "10x14",
10869  (char *) NULL
10870  };
10871 
10872  Image
10873  *print_image;
10874 
10875  ImageInfo
10876  *image_info;
10877 
10878  MagickStatusType
10879  status;
10880 
10881  /*
10882  Request Postscript page geometry from user.
10883  */
10884  image_info=CloneImageInfo(resource_info->image_info);
10885  (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10886  if (image_info->page != (char *) NULL)
10887  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10888  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10889  "Select Postscript Page Geometry:",geometry);
10890  if (*geometry == '\0')
10891  return(MagickTrue);
10892  image_info->page=GetPageGeometry(geometry);
10893  /*
10894  Apply image transforms.
10895  */
10896  XSetCursorState(display,windows,MagickTrue);
10897  XCheckRefreshWindows(display,windows);
10898  print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10899  if (print_image == (Image *) NULL)
10900  return(MagickFalse);
10901  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10902  windows->image.ximage->width,windows->image.ximage->height);
10903  (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10904  /*
10905  Print image.
10906  */
10907  (void) AcquireUniqueFilename(filename);
10908  (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10909  filename);
10910  status=WriteImage(image_info,print_image);
10911  (void) RelinquishUniqueFileResource(filename);
10912  print_image=DestroyImage(print_image);
10913  image_info=DestroyImageInfo(image_info);
10914  XSetCursorState(display,windows,MagickFalse);
10915  return(status != 0 ? MagickTrue : MagickFalse);
10916 }
10917 
10918 /*
10919 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10920 % %
10921 % %
10922 % %
10923 + X R O I I m a g e %
10924 % %
10925 % %
10926 % %
10927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10928 %
10929 % XROIImage() applies an image processing technique to a region of interest.
10930 %
10931 % The format of the XROIImage method is:
10932 %
10933 % MagickBooleanType XROIImage(Display *display,
10934 % XResourceInfo *resource_info,XWindows *windows,Image **image)
10935 %
10936 % A description of each parameter follows:
10937 %
10938 % o display: Specifies a connection to an X server; returned from
10939 % XOpenDisplay.
10940 %
10941 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10942 %
10943 % o windows: Specifies a pointer to a XWindows structure.
10944 %
10945 % o image: the image; returned from ReadImage.
10946 %
10947 */
10948 static MagickBooleanType XROIImage(Display *display,
10949  XResourceInfo *resource_info,XWindows *windows,Image **image)
10950 {
10951 #define ApplyMenus 7
10952 
10953  const char
10954  *const ROIMenu[] =
10955  {
10956  "Help",
10957  "Dismiss",
10958  (char *) NULL
10959  },
10960  *const ApplyMenu[] =
10961  {
10962  "File",
10963  "Edit",
10964  "Transform",
10965  "Enhance",
10966  "Effects",
10967  "F/X",
10968  "Miscellany",
10969  "Help",
10970  "Dismiss",
10971  (char *) NULL
10972  },
10973  *const FileMenu[] =
10974  {
10975  "Save...",
10976  "Print...",
10977  (char *) NULL
10978  },
10979  *const EditMenu[] =
10980  {
10981  "Undo",
10982  "Redo",
10983  (char *) NULL
10984  },
10985  *const TransformMenu[] =
10986  {
10987  "Flop",
10988  "Flip",
10989  "Rotate Right",
10990  "Rotate Left",
10991  (char *) NULL
10992  },
10993  *const EnhanceMenu[] =
10994  {
10995  "Hue...",
10996  "Saturation...",
10997  "Brightness...",
10998  "Gamma...",
10999  "Spiff",
11000  "Dull",
11001  "Contrast Stretch...",
11002  "Sigmoidal Contrast...",
11003  "Normalize",
11004  "Equalize",
11005  "Negate",
11006  "Grayscale",
11007  "Map...",
11008  "Quantize...",
11009  (char *) NULL
11010  },
11011  *const EffectsMenu[] =
11012  {
11013  "Despeckle",
11014  "Emboss",
11015  "Reduce Noise",
11016  "Add Noise",
11017  "Sharpen...",
11018  "Blur...",
11019  "Threshold...",
11020  "Edge Detect...",
11021  "Spread...",
11022  "Shade...",
11023  "Raise...",
11024  "Segment...",
11025  (char *) NULL
11026  },
11027  *const FXMenu[] =
11028  {
11029  "Solarize...",
11030  "Sepia Tone...",
11031  "Swirl...",
11032  "Implode...",
11033  "Vignette...",
11034  "Wave...",
11035  "Oil Paint...",
11036  "Charcoal Draw...",
11037  (char *) NULL
11038  },
11039  *const MiscellanyMenu[] =
11040  {
11041  "Image Info",
11042  "Zoom Image",
11043  "Show Preview...",
11044  "Show Histogram",
11045  "Show Matte",
11046  (char *) NULL
11047  };
11048 
11049  const char
11050  *const *Menus[ApplyMenus] =
11051  {
11052  FileMenu,
11053  EditMenu,
11054  TransformMenu,
11055  EnhanceMenu,
11056  EffectsMenu,
11057  FXMenu,
11058  MiscellanyMenu
11059  };
11060 
11061  static const DisplayCommand
11062  ApplyCommands[] =
11063  {
11064  NullCommand,
11065  NullCommand,
11066  NullCommand,
11067  NullCommand,
11068  NullCommand,
11069  NullCommand,
11070  NullCommand,
11071  HelpCommand,
11072  QuitCommand
11073  },
11074  FileCommands[] =
11075  {
11076  SaveCommand,
11077  PrintCommand
11078  },
11079  EditCommands[] =
11080  {
11081  UndoCommand,
11082  RedoCommand
11083  },
11084  TransformCommands[] =
11085  {
11086  FlopCommand,
11087  FlipCommand,
11088  RotateRightCommand,
11089  RotateLeftCommand
11090  },
11091  EnhanceCommands[] =
11092  {
11093  HueCommand,
11094  SaturationCommand,
11095  BrightnessCommand,
11096  GammaCommand,
11097  SpiffCommand,
11098  DullCommand,
11099  ContrastStretchCommand,
11100  SigmoidalContrastCommand,
11101  NormalizeCommand,
11102  EqualizeCommand,
11103  NegateCommand,
11104  GrayscaleCommand,
11105  MapCommand,
11106  QuantizeCommand
11107  },
11108  EffectsCommands[] =
11109  {
11110  DespeckleCommand,
11111  EmbossCommand,
11112  ReduceNoiseCommand,
11113  AddNoiseCommand,
11114  SharpenCommand,
11115  BlurCommand,
11116  ThresholdCommand,
11117  EdgeDetectCommand,
11118  SpreadCommand,
11119  ShadeCommand,
11120  RaiseCommand,
11121  SegmentCommand
11122  },
11123  FXCommands[] =
11124  {
11125  SolarizeCommand,
11126  SepiaToneCommand,
11127  SwirlCommand,
11128  ImplodeCommand,
11129  VignetteCommand,
11130  WaveCommand,
11131  OilPaintCommand,
11132  CharcoalDrawCommand
11133  },
11134  MiscellanyCommands[] =
11135  {
11136  InfoCommand,
11137  ZoomCommand,
11138  ShowPreviewCommand,
11139  ShowHistogramCommand,
11140  ShowMatteCommand
11141  },
11142  ROICommands[] =
11143  {
11144  ROIHelpCommand,
11145  ROIDismissCommand
11146  };
11147 
11148  static const DisplayCommand
11149  *Commands[ApplyMenus] =
11150  {
11151  FileCommands,
11152  EditCommands,
11153  TransformCommands,
11154  EnhanceCommands,
11155  EffectsCommands,
11156  FXCommands,
11157  MiscellanyCommands
11158  };
11159 
11160  char
11161  command[MaxTextExtent],
11162  text[MaxTextExtent];
11163 
11164  DisplayCommand
11165  display_command;
11166 
11167  Cursor
11168  cursor;
11169 
11170  Image
11171  *roi_image;
11172 
11173  int
11174  entry,
11175  id,
11176  x,
11177  y;
11178 
11179  MagickRealType
11180  scale_factor;
11181 
11182  MagickProgressMonitor
11183  progress_monitor;
11184 
11186  crop_info,
11187  highlight_info,
11188  roi_info;
11189 
11190  unsigned int
11191  height,
11192  width;
11193 
11194  size_t
11195  state;
11196 
11197  XEvent
11198  event;
11199 
11200  /*
11201  Map Command widget.
11202  */
11203  (void) CloneString(&windows->command.name,"ROI");
11204  windows->command.data=0;
11205  (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11206  (void) XMapRaised(display,windows->command.id);
11207  XClientMessage(display,windows->image.id,windows->im_protocols,
11208  windows->im_update_widget,CurrentTime);
11209  /*
11210  Track pointer until button 1 is pressed.
11211  */
11212  XQueryPosition(display,windows->image.id,&x,&y);
11213  (void) XSelectInput(display,windows->image.id,
11214  windows->image.attributes.event_mask | PointerMotionMask);
11215  crop_info.width=0;
11216  crop_info.height=0;
11217  crop_info.x=0;
11218  crop_info.y=0;
11219  roi_info.x=(ssize_t) windows->image.x+x;
11220  roi_info.y=(ssize_t) windows->image.y+y;
11221  roi_info.width=0;
11222  roi_info.height=0;
11223  cursor=XCreateFontCursor(display,XC_fleur);
11224  state=DefaultState;
11225  do
11226  {
11227  if (windows->info.mapped != MagickFalse)
11228  {
11229  /*
11230  Display pointer position.
11231  */
11232  (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11233  (long) roi_info.x,(long) roi_info.y);
11234  XInfoWidget(display,windows,text);
11235  }
11236  /*
11237  Wait for next event.
11238  */
11239  XScreenEvent(display,windows,&event);
11240  if (event.xany.window == windows->command.id)
11241  {
11242  /*
11243  Select a command from the Command widget.
11244  */
11245  id=XCommandWidget(display,windows,ROIMenu,&event);
11246  if (id < 0)
11247  continue;
11248  switch (ROICommands[id])
11249  {
11250  case ROIHelpCommand:
11251  {
11252  XTextViewHelp(display,resource_info,windows,MagickFalse,
11253  "Help Viewer - Region of Interest",ImageROIHelp);
11254  break;
11255  }
11256  case ROIDismissCommand:
11257  {
11258  /*
11259  Prematurely exit.
11260  */
11261  state|=EscapeState;
11262  state|=ExitState;
11263  break;
11264  }
11265  default:
11266  break;
11267  }
11268  continue;
11269  }
11270  switch (event.type)
11271  {
11272  case ButtonPress:
11273  {
11274  if (event.xbutton.button != Button1)
11275  break;
11276  if (event.xbutton.window != windows->image.id)
11277  break;
11278  /*
11279  Note first corner of region of interest rectangle-- exit loop.
11280  */
11281  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11282  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11283  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11284  state|=ExitState;
11285  break;
11286  }
11287  case ButtonRelease:
11288  break;
11289  case Expose:
11290  break;
11291  case KeyPress:
11292  {
11293  KeySym
11294  key_symbol;
11295 
11296  if (event.xkey.window != windows->image.id)
11297  break;
11298  /*
11299  Respond to a user key press.
11300  */
11301  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11302  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11303  switch ((int) key_symbol)
11304  {
11305  case XK_Escape:
11306  case XK_F20:
11307  {
11308  /*
11309  Prematurely exit.
11310  */
11311  state|=EscapeState;
11312  state|=ExitState;
11313  break;
11314  }
11315  case XK_F1:
11316  case XK_Help:
11317  {
11318  XTextViewHelp(display,resource_info,windows,MagickFalse,
11319  "Help Viewer - Region of Interest",ImageROIHelp);
11320  break;
11321  }
11322  default:
11323  {
11324  (void) XBell(display,0);
11325  break;
11326  }
11327  }
11328  break;
11329  }
11330  case MotionNotify:
11331  {
11332  /*
11333  Map and unmap Info widget as text cursor crosses its boundaries.
11334  */
11335  x=event.xmotion.x;
11336  y=event.xmotion.y;
11337  if (windows->info.mapped != MagickFalse)
11338  {
11339  if ((x < (int) (windows->info.x+windows->info.width)) &&
11340  (y < (int) (windows->info.y+windows->info.height)))
11341  (void) XWithdrawWindow(display,windows->info.id,
11342  windows->info.screen);
11343  }
11344  else
11345  if ((x > (int) (windows->info.x+windows->info.width)) ||
11346  (y > (int) (windows->info.y+windows->info.height)))
11347  (void) XMapWindow(display,windows->info.id);
11348  roi_info.x=(ssize_t) windows->image.x+x;
11349  roi_info.y=(ssize_t) windows->image.y+y;
11350  break;
11351  }
11352  default:
11353  break;
11354  }
11355  } while ((state & ExitState) == 0);
11356  (void) XSelectInput(display,windows->image.id,
11357  windows->image.attributes.event_mask);
11358  if ((state & EscapeState) != 0)
11359  {
11360  /*
11361  User want to exit without region of interest.
11362  */
11363  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11364  (void) XFreeCursor(display,cursor);
11365  return(MagickTrue);
11366  }
11367  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11368  do
11369  {
11370  /*
11371  Size rectangle as pointer moves until the mouse button is released.
11372  */
11373  x=(int) roi_info.x;
11374  y=(int) roi_info.y;
11375  roi_info.width=0;
11376  roi_info.height=0;
11377  state=DefaultState;
11378  do
11379  {
11380  highlight_info=roi_info;
11381  highlight_info.x=roi_info.x-windows->image.x;
11382  highlight_info.y=roi_info.y-windows->image.y;
11383  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11384  {
11385  /*
11386  Display info and draw region of interest rectangle.
11387  */
11388  if (windows->info.mapped == MagickFalse)
11389  (void) XMapWindow(display,windows->info.id);
11390  (void) FormatLocaleString(text,MaxTextExtent,
11391  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11392  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11393  XInfoWidget(display,windows,text);
11394  XHighlightRectangle(display,windows->image.id,
11395  windows->image.highlight_context,&highlight_info);
11396  }
11397  else
11398  if (windows->info.mapped != MagickFalse)
11399  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11400  /*
11401  Wait for next event.
11402  */
11403  XScreenEvent(display,windows,&event);
11404  if ((highlight_info.width > 3) && (highlight_info.height > 3))
11405  XHighlightRectangle(display,windows->image.id,
11406  windows->image.highlight_context,&highlight_info);
11407  switch (event.type)
11408  {
11409  case ButtonPress:
11410  {
11411  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11412  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11413  break;
11414  }
11415  case ButtonRelease:
11416  {
11417  /*
11418  User has committed to region of interest rectangle.
11419  */
11420  roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11421  roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11422  XSetCursorState(display,windows,MagickFalse);
11423  state|=ExitState;
11424  if (LocaleCompare(windows->command.name,"Apply") == 0)
11425  break;
11426  (void) CloneString(&windows->command.name,"Apply");
11427  windows->command.data=ApplyMenus;
11428  (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11429  break;
11430  }
11431  case Expose:
11432  break;
11433  case MotionNotify:
11434  {
11435  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11436  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11437  }
11438  default:
11439  break;
11440  }
11441  if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11442  ((state & ExitState) != 0))
11443  {
11444  /*
11445  Check boundary conditions.
11446  */
11447  if (roi_info.x < 0)
11448  roi_info.x=0;
11449  else
11450  if (roi_info.x > (ssize_t) windows->image.ximage->width)
11451  roi_info.x=(ssize_t) windows->image.ximage->width;
11452  if ((int) roi_info.x < x)
11453  roi_info.width=(unsigned int) (x-roi_info.x);
11454  else
11455  {
11456  roi_info.width=(unsigned int) (roi_info.x-x);
11457  roi_info.x=(ssize_t) x;
11458  }
11459  if (roi_info.y < 0)
11460  roi_info.y=0;
11461  else
11462  if (roi_info.y > (ssize_t) windows->image.ximage->height)
11463  roi_info.y=(ssize_t) windows->image.ximage->height;
11464  if ((int) roi_info.y < y)
11465  roi_info.height=(unsigned int) (y-roi_info.y);
11466  else
11467  {
11468  roi_info.height=(unsigned int) (roi_info.y-y);
11469  roi_info.y=(ssize_t) y;
11470  }
11471  }
11472  } while ((state & ExitState) == 0);
11473  /*
11474  Wait for user to grab a corner of the rectangle or press return.
11475  */
11476  state=DefaultState;
11477  display_command=NullCommand;
11478  (void) XMapWindow(display,windows->info.id);
11479  do
11480  {
11481  if (windows->info.mapped != MagickFalse)
11482  {
11483  /*
11484  Display pointer position.
11485  */
11486  (void) FormatLocaleString(text,MaxTextExtent,
11487  " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11488  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11489  XInfoWidget(display,windows,text);
11490  }
11491  highlight_info=roi_info;
11492  highlight_info.x=roi_info.x-windows->image.x;
11493  highlight_info.y=roi_info.y-windows->image.y;
11494  if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11495  {
11496  state|=EscapeState;
11497  state|=ExitState;
11498  break;
11499  }
11500  if ((state & UpdateRegionState) != 0)
11501  {
11502  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11503  switch (display_command)
11504  {
11505  case UndoCommand:
11506  case RedoCommand:
11507  {
11508  (void) XMagickCommand(display,resource_info,windows,
11509  display_command,image);
11510  break;
11511  }
11512  default:
11513  {
11514  /*
11515  Region of interest is relative to image configuration.
11516  */
11517  progress_monitor=SetImageProgressMonitor(*image,
11518  (MagickProgressMonitor) NULL,(*image)->client_data);
11519  crop_info=roi_info;
11520  width=(unsigned int) (*image)->columns;
11521  height=(unsigned int) (*image)->rows;
11522  x=0;
11523  y=0;
11524  if (windows->image.crop_geometry != (char *) NULL)
11525  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11526  &width,&height);
11527  scale_factor=(MagickRealType) width/windows->image.ximage->width;
11528  crop_info.x+=x;
11529  crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11530  crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11531  scale_factor=(MagickRealType)
11532  height/windows->image.ximage->height;
11533  crop_info.y+=y;
11534  crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11535  crop_info.height=(unsigned int)
11536  (scale_factor*crop_info.height+0.5);
11537  roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11538  (void) SetImageProgressMonitor(*image,progress_monitor,
11539  (*image)->client_data);
11540  if (roi_image == (Image *) NULL)
11541  continue;
11542  /*
11543  Apply image processing technique to the region of interest.
11544  */
11545  windows->image.orphan=MagickTrue;
11546  (void) XMagickCommand(display,resource_info,windows,
11547  display_command,&roi_image);
11548  progress_monitor=SetImageProgressMonitor(*image,
11549  (MagickProgressMonitor) NULL,(*image)->client_data);
11550  (void) XMagickCommand(display,resource_info,windows,
11551  SaveToUndoBufferCommand,image);
11552  windows->image.orphan=MagickFalse;
11553  (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11554  crop_info.x,crop_info.y);
11555  roi_image=DestroyImage(roi_image);
11556  (void) SetImageProgressMonitor(*image,progress_monitor,
11557  (*image)->client_data);
11558  break;
11559  }
11560  }
11561  if (display_command != InfoCommand)
11562  {
11563  XConfigureImageColormap(display,resource_info,windows,*image);
11564  (void) XConfigureImage(display,resource_info,windows,*image);
11565  }
11566  XCheckRefreshWindows(display,windows);
11567  XInfoWidget(display,windows,text);
11568  (void) XSetFunction(display,windows->image.highlight_context,
11569  GXinvert);
11570  state&=(~UpdateRegionState);
11571  }
11572  XHighlightRectangle(display,windows->image.id,
11573  windows->image.highlight_context,&highlight_info);
11574  XScreenEvent(display,windows,&event);
11575  if (event.xany.window == windows->command.id)
11576  {
11577  /*
11578  Select a command from the Command widget.
11579  */
11580  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11581  display_command=NullCommand;
11582  id=XCommandWidget(display,windows,ApplyMenu,&event);
11583  if (id >= 0)
11584  {
11585  (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11586  display_command=ApplyCommands[id];
11587  if (id < ApplyMenus)
11588  {
11589  /*
11590  Select a command from a pop-up menu.
11591  */
11592  entry=XMenuWidget(display,windows,ApplyMenu[id],
11593  (const char **) Menus[id],command);
11594  if (entry >= 0)
11595  {
11596  (void) CopyMagickString(command,Menus[id][entry],
11597  MaxTextExtent);
11598  display_command=Commands[id][entry];
11599  }
11600  }
11601  }
11602  (void) XSetFunction(display,windows->image.highlight_context,
11603  GXinvert);
11604  XHighlightRectangle(display,windows->image.id,
11605  windows->image.highlight_context,&highlight_info);
11606  if (display_command == HelpCommand)
11607  {
11608  (void) XSetFunction(display,windows->image.highlight_context,
11609  GXcopy);
11610  XTextViewHelp(display,resource_info,windows,MagickFalse,
11611  "Help Viewer - Region of Interest",ImageROIHelp);
11612  (void) XSetFunction(display,windows->image.highlight_context,
11613  GXinvert);
11614  continue;
11615  }
11616  if (display_command == QuitCommand)
11617  {
11618  /*
11619  exit.
11620  */
11621  state|=EscapeState;
11622  state|=ExitState;
11623  continue;
11624  }
11625  if (display_command != NullCommand)
11626  state|=UpdateRegionState;
11627  continue;
11628  }
11629  XHighlightRectangle(display,windows->image.id,
11630  windows->image.highlight_context,&highlight_info);
11631  switch (event.type)
11632  {
11633  case ButtonPress:
11634  {
11635  x=windows->image.x;
11636  y=windows->image.y;
11637  if (event.xbutton.button != Button1)
11638  break;
11639  if (event.xbutton.window != windows->image.id)
11640  break;
11641  x=windows->image.x+event.xbutton.x;
11642  y=windows->image.y+event.xbutton.y;
11643  if ((x < (int) (roi_info.x+RoiDelta)) &&
11644  (x > (int) (roi_info.x-RoiDelta)) &&
11645  (y < (int) (roi_info.y+RoiDelta)) &&
11646  (y > (int) (roi_info.y-RoiDelta)))
11647  {
11648  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11649  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11650  state|=UpdateConfigurationState;
11651  break;
11652  }
11653  if ((x < (int) (roi_info.x+RoiDelta)) &&
11654  (x > (int) (roi_info.x-RoiDelta)) &&
11655  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11656  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11657  {
11658  roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11659  state|=UpdateConfigurationState;
11660  break;
11661  }
11662  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11663  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11664  (y < (int) (roi_info.y+RoiDelta)) &&
11665  (y > (int) (roi_info.y-RoiDelta)))
11666  {
11667  roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11668  state|=UpdateConfigurationState;
11669  break;
11670  }
11671  if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11672  (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11673  (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11674  (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11675  {
11676  state|=UpdateConfigurationState;
11677  break;
11678  }
11679  magick_fallthrough;
11680  }
11681  case ButtonRelease:
11682  {
11683  if (event.xbutton.window == windows->pan.id)
11684  if ((highlight_info.x != crop_info.x-windows->image.x) ||
11685  (highlight_info.y != crop_info.y-windows->image.y))
11686  XHighlightRectangle(display,windows->image.id,
11687  windows->image.highlight_context,&highlight_info);
11688  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11689  event.xbutton.time);
11690  break;
11691  }
11692  case Expose:
11693  {
11694  if (event.xexpose.window == windows->image.id)
11695  if (event.xexpose.count == 0)
11696  {
11697  event.xexpose.x=(int) highlight_info.x;
11698  event.xexpose.y=(int) highlight_info.y;
11699  event.xexpose.width=(int) highlight_info.width;
11700  event.xexpose.height=(int) highlight_info.height;
11701  XRefreshWindow(display,&windows->image,&event);
11702  }
11703  if (event.xexpose.window == windows->info.id)
11704  if (event.xexpose.count == 0)
11705  XInfoWidget(display,windows,text);
11706  break;
11707  }
11708  case KeyPress:
11709  {
11710  KeySym
11711  key_symbol;
11712 
11713  if (event.xkey.window != windows->image.id)
11714  break;
11715  /*
11716  Respond to a user key press.
11717  */
11718  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11719  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11720  switch ((int) key_symbol)
11721  {
11722  case XK_Shift_L:
11723  case XK_Shift_R:
11724  break;
11725  case XK_Escape:
11726  case XK_F20:
11727  {
11728  state|=EscapeState;
11729  magick_fallthrough;
11730  }
11731  case XK_Return:
11732  {
11733  state|=ExitState;
11734  break;
11735  }
11736  case XK_Home:
11737  case XK_KP_Home:
11738  {
11739  roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11740  roi_info.y=(ssize_t) (windows->image.height/2L-
11741  roi_info.height/2L);
11742  break;
11743  }
11744  case XK_Left:
11745  case XK_KP_Left:
11746  {
11747  roi_info.x--;
11748  break;
11749  }
11750  case XK_Up:
11751  case XK_KP_Up:
11752  case XK_Next:
11753  {
11754  roi_info.y--;
11755  break;
11756  }
11757  case XK_Right:
11758  case XK_KP_Right:
11759  {
11760  roi_info.x++;
11761  break;
11762  }
11763  case XK_Prior:
11764  case XK_Down:
11765  case XK_KP_Down:
11766  {
11767  roi_info.y++;
11768  break;
11769  }
11770  case XK_F1:
11771  case XK_Help:
11772  {
11773  (void) XSetFunction(display,windows->image.highlight_context,
11774  GXcopy);
11775  XTextViewHelp(display,resource_info,windows,MagickFalse,
11776  "Help Viewer - Region of Interest",ImageROIHelp);
11777  (void) XSetFunction(display,windows->image.highlight_context,
11778  GXinvert);
11779  break;
11780  }
11781  default:
11782  {
11783  display_command=XImageWindowCommand(display,resource_info,windows,
11784  event.xkey.state,key_symbol,image);
11785  if (display_command != NullCommand)
11786  state|=UpdateRegionState;
11787  break;
11788  }
11789  }
11790  (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11791  event.xkey.time);
11792  break;
11793  }
11794  case KeyRelease:
11795  break;
11796  case MotionNotify:
11797  {
11798  if (event.xbutton.window != windows->image.id)
11799  break;
11800  /*
11801  Map and unmap Info widget as text cursor crosses its boundaries.
11802  */
11803  x=event.xmotion.x;
11804  y=event.xmotion.y;
11805  if (windows->info.mapped != MagickFalse)
11806  {
11807  if ((x < (int) (windows->info.x+windows->info.width)) &&
11808  (y < (int) (windows->info.y+windows->info.height)))
11809  (void) XWithdrawWindow(display,windows->info.id,
11810  windows->info.screen);
11811  }
11812  else
11813  if ((x > (int) (windows->info.x+windows->info.width)) ||
11814  (y > (int) (windows->info.y+windows->info.height)))
11815  (void) XMapWindow(display,windows->info.id);
11816  roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11817  roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11818  break;
11819  }
11820  case SelectionRequest:
11821  {
11822  XSelectionEvent
11823  notify;
11824 
11825  XSelectionRequestEvent
11826  *request;
11827 
11828  /*
11829  Set primary selection.
11830  */
11831  (void) FormatLocaleString(text,MaxTextExtent,
11832  "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11833  roi_info.height,(double) roi_info.x,(double) roi_info.y);
11834  request=(&(event.xselectionrequest));
11835  (void) XChangeProperty(request->display,request->requestor,
11836  request->property,request->target,8,PropModeReplace,
11837  (unsigned char *) text,(int) strlen(text));
11838  notify.type=SelectionNotify;
11839  notify.display=request->display;
11840  notify.requestor=request->requestor;
11841  notify.selection=request->selection;
11842  notify.target=request->target;
11843  notify.time=request->time;
11844  if (request->property == None)
11845  notify.property=request->target;
11846  else
11847  notify.property=request->property;
11848  (void) XSendEvent(request->display,request->requestor,False,0,
11849  (XEvent *) &notify);
11850  }
11851  default:
11852  break;
11853  }
11854  if ((state & UpdateConfigurationState) != 0)
11855  {
11856  (void) XPutBackEvent(display,&event);
11857  (void) XCheckDefineCursor(display,windows->image.id,cursor);
11858  break;
11859  }
11860  } while ((state & ExitState) == 0);
11861  } while ((state & ExitState) == 0);
11862  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11863  XSetCursorState(display,windows,MagickFalse);
11864  if ((state & EscapeState) != 0)
11865  return(MagickTrue);
11866  return(MagickTrue);
11867 }
11868 
11869 /*
11870 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11871 % %
11872 % %
11873 % %
11874 + X R o t a t e I m a g e %
11875 % %
11876 % %
11877 % %
11878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11879 %
11880 % XRotateImage() rotates the X image. If the degrees parameter if zero, the
11881 % rotation angle is computed from the slope of a line drawn by the user.
11882 %
11883 % The format of the XRotateImage method is:
11884 %
11885 % MagickBooleanType XRotateImage(Display *display,
11886 % XResourceInfo *resource_info,XWindows *windows,double degrees,
11887 % Image **image)
11888 %
11889 % A description of each parameter follows:
11890 %
11891 % o display: Specifies a connection to an X server; returned from
11892 % XOpenDisplay.
11893 %
11894 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11895 %
11896 % o windows: Specifies a pointer to a XWindows structure.
11897 %
11898 % o degrees: Specifies the number of degrees to rotate the image.
11899 %
11900 % o image: the image.
11901 %
11902 */
11903 static MagickBooleanType XRotateImage(Display *display,
11904  XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11905 {
11906  const char
11907  *const RotateMenu[] =
11908  {
11909  "Pixel Color",
11910  "Direction",
11911  "Help",
11912  "Dismiss",
11913  (char *) NULL
11914  };
11915 
11916  static ModeType
11917  direction = HorizontalRotateCommand;
11918 
11919  static const ModeType
11920  DirectionCommands[] =
11921  {
11922  HorizontalRotateCommand,
11923  VerticalRotateCommand
11924  },
11925  RotateCommands[] =
11926  {
11927  RotateColorCommand,
11928  RotateDirectionCommand,
11929  RotateHelpCommand,
11930  RotateDismissCommand
11931  };
11932 
11933  static unsigned int
11934  pen_id = 0;
11935 
11936  char
11937  command[MaxTextExtent],
11938  text[MaxTextExtent];
11939 
11940  Image
11941  *rotate_image;
11942 
11943  int
11944  id,
11945  x,
11946  y;
11947 
11948  MagickRealType
11949  normalized_degrees;
11950 
11951  int
11952  i;
11953 
11954  unsigned int
11955  height,
11956  rotations,
11957  width;
11958 
11959  if (degrees == 0.0)
11960  {
11961  unsigned int
11962  distance;
11963 
11964  size_t
11965  state;
11966 
11967  XEvent
11968  event;
11969 
11970  XSegment
11971  rotate_info;
11972 
11973  /*
11974  Map Command widget.
11975  */
11976  (void) CloneString(&windows->command.name,"Rotate");
11977  windows->command.data=2;
11978  (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11979  (void) XMapRaised(display,windows->command.id);
11980  XClientMessage(display,windows->image.id,windows->im_protocols,
11981  windows->im_update_widget,CurrentTime);
11982  /*
11983  Wait for first button press.
11984  */
11985  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11986  XQueryPosition(display,windows->image.id,&x,&y);
11987  rotate_info.x1=x;
11988  rotate_info.y1=y;
11989  rotate_info.x2=x;
11990  rotate_info.y2=y;
11991  state=DefaultState;
11992  do
11993  {
11994  XHighlightLine(display,windows->image.id,
11995  windows->image.highlight_context,&rotate_info);
11996  /*
11997  Wait for next event.
11998  */
11999  XScreenEvent(display,windows,&event);
12000  XHighlightLine(display,windows->image.id,
12001  windows->image.highlight_context,&rotate_info);
12002  if (event.xany.window == windows->command.id)
12003  {
12004  /*
12005  Select a command from the Command widget.
12006  */
12007  id=XCommandWidget(display,windows,RotateMenu,&event);
12008  if (id < 0)
12009  continue;
12010  (void) XSetFunction(display,windows->image.highlight_context,
12011  GXcopy);
12012  switch (RotateCommands[id])
12013  {
12014  case RotateColorCommand:
12015  {
12016  const char
12017  *ColorMenu[MaxNumberPens];
12018 
12019  int
12020  pen_number;
12021 
12022  XColor
12023  color;
12024 
12025  /*
12026  Initialize menu selections.
12027  */
12028  for (i=0; i < (int) (MaxNumberPens-2); i++)
12029  ColorMenu[i]=resource_info->pen_colors[i];
12030  ColorMenu[MaxNumberPens-2]="Browser...";
12031  ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12032  /*
12033  Select a pen color from the pop-up menu.
12034  */
12035  pen_number=XMenuWidget(display,windows,RotateMenu[id],
12036  (const char **) ColorMenu,command);
12037  if (pen_number < 0)
12038  break;
12039  if (pen_number == (MaxNumberPens-2))
12040  {
12041  static char
12042  color_name[MaxTextExtent] = "gray";
12043 
12044  /*
12045  Select a pen color from a dialog.
12046  */
12047  resource_info->pen_colors[pen_number]=color_name;
12048  XColorBrowserWidget(display,windows,"Select",color_name);
12049  if (*color_name == '\0')
12050  break;
12051  }
12052  /*
12053  Set pen color.
12054  */
12055  (void) XParseColor(display,windows->map_info->colormap,
12056  resource_info->pen_colors[pen_number],&color);
12057  XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12058  (unsigned int) MaxColors,&color);
12059  windows->pixel_info->pen_colors[pen_number]=color;
12060  pen_id=(unsigned int) pen_number;
12061  break;
12062  }
12063  case RotateDirectionCommand:
12064  {
12065  const char
12066  *const Directions[] =
12067  {
12068  "horizontal",
12069  "vertical",
12070  (char *) NULL,
12071  };
12072 
12073  /*
12074  Select a command from the pop-up menu.
12075  */
12076  id=XMenuWidget(display,windows,RotateMenu[id],
12077  Directions,command);
12078  if (id >= 0)
12079  direction=DirectionCommands[id];
12080  break;
12081  }
12082  case RotateHelpCommand:
12083  {
12084  XTextViewHelp(display,resource_info,windows,MagickFalse,
12085  "Help Viewer - Image Rotation",ImageRotateHelp);
12086  break;
12087  }
12088  case RotateDismissCommand:
12089  {
12090  /*
12091  Prematurely exit.
12092  */
12093  state|=EscapeState;
12094  state|=ExitState;
12095  break;
12096  }
12097  default:
12098  break;
12099  }
12100  (void) XSetFunction(display,windows->image.highlight_context,
12101  GXinvert);
12102  continue;
12103  }
12104  switch (event.type)
12105  {
12106  case ButtonPress:
12107  {
12108  if (event.xbutton.button != Button1)
12109  break;
12110  if (event.xbutton.window != windows->image.id)
12111  break;
12112  /*
12113  exit loop.
12114  */
12115  (void) XSetFunction(display,windows->image.highlight_context,
12116  GXcopy);
12117  rotate_info.x1=event.xbutton.x;
12118  rotate_info.y1=event.xbutton.y;
12119  state|=ExitState;
12120  break;
12121  }
12122  case ButtonRelease:
12123  break;
12124  case Expose:
12125  break;
12126  case KeyPress:
12127  {
12128  char
12129  command[MaxTextExtent];
12130 
12131  KeySym
12132  key_symbol;
12133 
12134  if (event.xkey.window != windows->image.id)
12135  break;
12136  /*
12137  Respond to a user key press.
12138  */
12139  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12140  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12141  switch ((int) key_symbol)
12142  {
12143  case XK_Escape:
12144  case XK_F20:
12145  {
12146  /*
12147  Prematurely exit.
12148  */
12149  state|=EscapeState;
12150  state|=ExitState;
12151  break;
12152  }
12153  case XK_F1:
12154  case XK_Help:
12155  {
12156  (void) XSetFunction(display,windows->image.highlight_context,
12157  GXcopy);
12158  XTextViewHelp(display,resource_info,windows,MagickFalse,
12159  "Help Viewer - Image Rotation",ImageRotateHelp);
12160  (void) XSetFunction(display,windows->image.highlight_context,
12161  GXinvert);
12162  break;
12163  }
12164  default:
12165  {
12166  (void) XBell(display,0);
12167  break;
12168  }
12169  }
12170  break;
12171  }
12172  case MotionNotify:
12173  {
12174  rotate_info.x1=event.xmotion.x;
12175  rotate_info.y1=event.xmotion.y;
12176  }
12177  }
12178  rotate_info.x2=rotate_info.x1;
12179  rotate_info.y2=rotate_info.y1;
12180  if (direction == HorizontalRotateCommand)
12181  rotate_info.x2+=32;
12182  else
12183  rotate_info.y2-=32;
12184  } while ((state & ExitState) == 0);
12185  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12186  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12187  if ((state & EscapeState) != 0)
12188  return(MagickTrue);
12189  /*
12190  Draw line as pointer moves until the mouse button is released.
12191  */
12192  distance=0;
12193  (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12194  state=DefaultState;
12195  do
12196  {
12197  if (distance > 9)
12198  {
12199  /*
12200  Display info and draw rotation line.
12201  */
12202  if (windows->info.mapped == MagickFalse)
12203  (void) XMapWindow(display,windows->info.id);
12204  (void) FormatLocaleString(text,MaxTextExtent," %g",
12205  direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12206  XInfoWidget(display,windows,text);
12207  XHighlightLine(display,windows->image.id,
12208  windows->image.highlight_context,&rotate_info);
12209  }
12210  else
12211  if (windows->info.mapped != MagickFalse)
12212  (void) XWithdrawWindow(display,windows->info.id,
12213  windows->info.screen);
12214  /*
12215  Wait for next event.
12216  */
12217  XScreenEvent(display,windows,&event);
12218  if (distance > 9)
12219  XHighlightLine(display,windows->image.id,
12220  windows->image.highlight_context,&rotate_info);
12221  switch (event.type)
12222  {
12223  case ButtonPress:
12224  break;
12225  case ButtonRelease:
12226  {
12227  /*
12228  User has committed to rotation line.
12229  */
12230  rotate_info.x2=event.xbutton.x;
12231  rotate_info.y2=event.xbutton.y;
12232  state|=ExitState;
12233  break;
12234  }
12235  case Expose:
12236  break;
12237  case MotionNotify:
12238  {
12239  rotate_info.x2=event.xmotion.x;
12240  rotate_info.y2=event.xmotion.y;
12241  }
12242  default:
12243  break;
12244  }
12245  /*
12246  Check boundary conditions.
12247  */
12248  if (rotate_info.x2 < 0)
12249  rotate_info.x2=0;
12250  else
12251  if (rotate_info.x2 > (int) windows->image.width)
12252  rotate_info.x2=(short) windows->image.width;
12253  if (rotate_info.y2 < 0)
12254  rotate_info.y2=0;
12255  else
12256  if (rotate_info.y2 > (int) windows->image.height)
12257  rotate_info.y2=(short) windows->image.height;
12258  /*
12259  Compute rotation angle from the slope of the line.
12260  */
12261  degrees=0.0;
12262  distance=(unsigned int)
12263  ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12264  ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12265  if (distance > 9)
12266  degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12267  rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12268  } while ((state & ExitState) == 0);
12269  (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12270  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12271  if (distance <= 9)
12272  return(MagickTrue);
12273  }
12274  if (direction == VerticalRotateCommand)
12275  degrees-=90.0;
12276  if (degrees == 0.0)
12277  return(MagickTrue);
12278  /*
12279  Rotate image.
12280  */
12281  normalized_degrees=degrees;
12282  while (normalized_degrees < -45.0)
12283  normalized_degrees+=360.0;
12284  for (rotations=0; normalized_degrees > 45.0; rotations++)
12285  normalized_degrees-=90.0;
12286  if (normalized_degrees != 0.0)
12287  (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12288  XSetCursorState(display,windows,MagickTrue);
12289  XCheckRefreshWindows(display,windows);
12290  (*image)->background_color.red=ScaleShortToQuantum(
12291  windows->pixel_info->pen_colors[pen_id].red);
12292  (*image)->background_color.green=ScaleShortToQuantum(
12293  windows->pixel_info->pen_colors[pen_id].green);
12294  (*image)->background_color.blue=ScaleShortToQuantum(
12295  windows->pixel_info->pen_colors[pen_id].blue);
12296  rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12297  XSetCursorState(display,windows,MagickFalse);
12298  if (rotate_image == (Image *) NULL)
12299  return(MagickFalse);
12300  *image=DestroyImage(*image);
12301  *image=rotate_image;
12302  if (windows->image.crop_geometry != (char *) NULL)
12303  {
12304  /*
12305  Rotate crop geometry.
12306  */
12307  width=(unsigned int) (*image)->columns;
12308  height=(unsigned int) (*image)->rows;
12309  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12310  switch (rotations % 4)
12311  {
12312  default:
12313  case 0:
12314  break;
12315  case 1:
12316  {
12317  /*
12318  Rotate 90 degrees.
12319  */
12320  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12321  "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12322  (int) height-y,x);
12323  break;
12324  }
12325  case 2:
12326  {
12327  /*
12328  Rotate 180 degrees.
12329  */
12330  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12331  "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12332  break;
12333  }
12334  case 3:
12335  {
12336  /*
12337  Rotate 270 degrees.
12338  */
12339  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12340  "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12341  break;
12342  }
12343  }
12344  }
12345  if (windows->image.orphan != MagickFalse)
12346  return(MagickTrue);
12347  if (normalized_degrees != 0.0)
12348  {
12349  /*
12350  Update image colormap.
12351  */
12352  windows->image.window_changes.width=(int) (*image)->columns;
12353  windows->image.window_changes.height=(int) (*image)->rows;
12354  if (windows->image.crop_geometry != (char *) NULL)
12355  {
12356  /*
12357  Obtain dimensions of image from crop geometry.
12358  */
12359  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12360  &width,&height);
12361  windows->image.window_changes.width=(int) width;
12362  windows->image.window_changes.height=(int) height;
12363  }
12364  XConfigureImageColormap(display,resource_info,windows,*image);
12365  }
12366  else
12367  if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12368  {
12369  windows->image.window_changes.width=windows->image.ximage->height;
12370  windows->image.window_changes.height=windows->image.ximage->width;
12371  }
12372  /*
12373  Update image configuration.
12374  */
12375  (void) XConfigureImage(display,resource_info,windows,*image);
12376  return(MagickTrue);
12377 }
12378 
12379 /*
12380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12381 % %
12382 % %
12383 % %
12384 + X S a v e I m a g e %
12385 % %
12386 % %
12387 % %
12388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12389 %
12390 % XSaveImage() saves an image to a file.
12391 %
12392 % The format of the XSaveImage method is:
12393 %
12394 % MagickBooleanType XSaveImage(Display *display,
12395 % XResourceInfo *resource_info,XWindows *windows,Image *image)
12396 %
12397 % A description of each parameter follows:
12398 %
12399 % o display: Specifies a connection to an X server; returned from
12400 % XOpenDisplay.
12401 %
12402 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12403 %
12404 % o windows: Specifies a pointer to a XWindows structure.
12405 %
12406 % o image: the image.
12407 %
12408 */
12409 static MagickBooleanType XSaveImage(Display *display,
12410  XResourceInfo *resource_info,XWindows *windows,Image *image)
12411 {
12412  char
12413  filename[MaxTextExtent],
12414  geometry[MaxTextExtent];
12415 
12416  Image
12417  *save_image;
12418 
12419  ImageInfo
12420  *image_info;
12421 
12422  MagickStatusType
12423  status;
12424 
12425  /*
12426  Request file name from user.
12427  */
12428  if (resource_info->write_filename != (char *) NULL)
12429  (void) CopyMagickString(filename,resource_info->write_filename,
12430  MaxTextExtent);
12431  else
12432  {
12433  char
12434  path[MaxTextExtent];
12435 
12436  int
12437  status;
12438 
12439  GetPathComponent(image->filename,HeadPath,path);
12440  GetPathComponent(image->filename,TailPath,filename);
12441  if (*path != '\0')
12442  {
12443  status=chdir(path);
12444  if (status == -1)
12445  (void) ThrowMagickException(&image->exception,GetMagickModule(),
12446  FileOpenError,"UnableToOpenFile","%s",path);
12447  }
12448  }
12449  XFileBrowserWidget(display,windows,"Save",filename);
12450  if (*filename == '\0')
12451  return(MagickTrue);
12452  if (IsPathAccessible(filename) != MagickFalse)
12453  {
12454  int
12455  status;
12456 
12457  /*
12458  File exists-- seek user's permission before overwriting.
12459  */
12460  status=XConfirmWidget(display,windows,"Overwrite",filename);
12461  if (status <= 0)
12462  return(MagickTrue);
12463  }
12464  image_info=CloneImageInfo(resource_info->image_info);
12465  (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12466  (void) SetImageInfo(image_info,1,&image->exception);
12467  if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12468  (LocaleCompare(image_info->magick,"JPG") == 0))
12469  {
12470  char
12471  quality[MaxTextExtent];
12472 
12473  int
12474  status;
12475 
12476  /*
12477  Request JPEG quality from user.
12478  */
12479  (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12480  image->quality);
12481  status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12482  quality);
12483  if (*quality == '\0')
12484  return(MagickTrue);
12485  image->quality=StringToUnsignedLong(quality);
12486  image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12487  }
12488  if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12489  (LocaleCompare(image_info->magick,"PDF") == 0) ||
12490  (LocaleCompare(image_info->magick,"PS") == 0) ||
12491  (LocaleCompare(image_info->magick,"PS2") == 0))
12492  {
12493  char
12494  geometry[MaxTextExtent];
12495 
12496  const char
12497  *const PageSizes[] =
12498  {
12499  "Letter",
12500  "Tabloid",
12501  "Ledger",
12502  "Legal",
12503  "Statement",
12504  "Executive",
12505  "A3",
12506  "A4",
12507  "A5",
12508  "B4",
12509  "B5",
12510  "Folio",
12511  "Quarto",
12512  "10x14",
12513  (char *) NULL
12514  };
12515 
12516  /*
12517  Request page geometry from user.
12518  */
12519  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12520  if (LocaleCompare(image_info->magick,"PDF") == 0)
12521  (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12522  if (image_info->page != (char *) NULL)
12523  (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12524  XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12525  "Select page geometry:",geometry);
12526  if (*geometry != '\0')
12527  image_info->page=GetPageGeometry(geometry);
12528  }
12529  /*
12530  Apply image transforms.
12531  */
12532  XSetCursorState(display,windows,MagickTrue);
12533  XCheckRefreshWindows(display,windows);
12534  save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12535  if (save_image == (Image *) NULL)
12536  return(MagickFalse);
12537  (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12538  windows->image.ximage->width,windows->image.ximage->height);
12539  (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12540  /*
12541  Write image.
12542  */
12543  (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12544  status=WriteImage(image_info,save_image);
12545  if (status != MagickFalse)
12546  image->taint=MagickFalse;
12547  save_image=DestroyImage(save_image);
12548  image_info=DestroyImageInfo(image_info);
12549  XSetCursorState(display,windows,MagickFalse);
12550  return(status != 0 ? MagickTrue : MagickFalse);
12551 }
12552 
12553 /*
12554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12555 % %
12556 % %
12557 % %
12558 + X S c r e e n E v e n t %
12559 % %
12560 % %
12561 % %
12562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12563 %
12564 % XScreenEvent() handles global events associated with the Pan and Magnify
12565 % windows.
12566 %
12567 % The format of the XScreenEvent function is:
12568 %
12569 % void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12570 %
12571 % A description of each parameter follows:
12572 %
12573 % o display: Specifies a pointer to the Display structure; returned from
12574 % XOpenDisplay.
12575 %
12576 % o windows: Specifies a pointer to a XWindows structure.
12577 %
12578 % o event: Specifies a pointer to a X11 XEvent structure.
12579 %
12580 %
12581 */
12582 
12583 #if defined(__cplusplus) || defined(c_plusplus)
12584 extern "C" {
12585 #endif
12586 
12587 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12588 {
12589  XWindows
12590  *windows;
12591 
12592  magick_unreferenced(display);
12593 
12594  windows=(XWindows *) data;
12595  if ((event->type == ClientMessage) &&
12596  (event->xclient.window == windows->image.id))
12597  return(MagickFalse);
12598  return(MagickTrue);
12599 }
12600 
12601 #if defined(__cplusplus) || defined(c_plusplus)
12602 }
12603 #endif
12604 
12605 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12606 {
12607  int
12608  x,
12609  y;
12610 
12611  (void) XIfEvent(display,event,XPredicate,(char *) windows);
12612  if (event->xany.window == windows->command.id)
12613  return;
12614  switch (event->type)
12615  {
12616  case ButtonPress:
12617  case ButtonRelease:
12618  {
12619  if ((event->xbutton.button == Button3) &&
12620  (event->xbutton.state & Mod1Mask))
12621  {
12622  /*
12623  Convert Alt-Button3 to Button2.
12624  */
12625  event->xbutton.button=Button2;
12626  event->xbutton.state&=(~Mod1Mask);
12627  }
12628  if (event->xbutton.window == windows->backdrop.id)
12629  {
12630  (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12631  event->xbutton.time);
12632  break;
12633  }
12634  if (event->xbutton.window == windows->pan.id)
12635  {
12636  XPanImage(display,windows,event);
12637  break;
12638  }
12639  if (event->xbutton.window == windows->image.id)
12640  if (event->xbutton.button == Button2)
12641  {
12642  /*
12643  Update magnified image.
12644  */
12645  x=event->xbutton.x;
12646  y=event->xbutton.y;
12647  if (x < 0)
12648  x=0;
12649  else
12650  if (x >= (int) windows->image.width)
12651  x=(int) (windows->image.width-1);
12652  windows->magnify.x=(int) windows->image.x+x;
12653  if (y < 0)
12654  y=0;
12655  else
12656  if (y >= (int) windows->image.height)
12657  y=(int) (windows->image.height-1);
12658  windows->magnify.y=windows->image.y+y;
12659  if (windows->magnify.mapped == MagickFalse)
12660  (void) XMapRaised(display,windows->magnify.id);
12661  XMakeMagnifyImage(display,windows);
12662  if (event->type == ButtonRelease)
12663  (void) XWithdrawWindow(display,windows->info.id,
12664  windows->info.screen);
12665  break;
12666  }
12667  break;
12668  }
12669  case ClientMessage:
12670  {
12671  /*
12672  If client window delete message, exit.
12673  */
12674  if (event->xclient.message_type != windows->wm_protocols)
12675  break;
12676  if (*event->xclient.data.l != (long) windows->wm_delete_window)
12677  break;
12678  if (event->xclient.window == windows->magnify.id)
12679  {
12680  (void) XWithdrawWindow(display,windows->magnify.id,
12681  windows->magnify.screen);
12682  break;
12683  }
12684  break;
12685  }
12686  case ConfigureNotify:
12687  {
12688  if (event->xconfigure.window == windows->magnify.id)
12689  {
12690  unsigned int
12691  magnify;
12692 
12693  /*
12694  Magnify window has a new configuration.
12695  */
12696  windows->magnify.width=(unsigned int) event->xconfigure.width;
12697  windows->magnify.height=(unsigned int) event->xconfigure.height;
12698  if (windows->magnify.mapped == MagickFalse)
12699  break;
12700  magnify=1;
12701  while ((int) magnify <= event->xconfigure.width)
12702  magnify<<=1;
12703  while ((int) magnify <= event->xconfigure.height)
12704  magnify<<=1;
12705  magnify>>=1;
12706  if (((int) magnify != event->xconfigure.width) ||
12707  ((int) magnify != event->xconfigure.height))
12708  {
12709  XWindowChanges
12710  window_changes;
12711 
12712  window_changes.width=(int) magnify;
12713  window_changes.height=(int) magnify;
12714  (void) XReconfigureWMWindow(display,windows->magnify.id,
12715  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12716  &window_changes);
12717  break;
12718  }
12719  XMakeMagnifyImage(display,windows);
12720  break;
12721  }
12722  break;
12723  }
12724  case Expose:
12725  {
12726  if (event->xexpose.window == windows->image.id)
12727  {
12728  XRefreshWindow(display,&windows->image,event);
12729  break;
12730  }
12731  if (event->xexpose.window == windows->pan.id)
12732  if (event->xexpose.count == 0)
12733  {
12734  XDrawPanRectangle(display,windows);
12735  break;
12736  }
12737  if (event->xexpose.window == windows->magnify.id)
12738  if (event->xexpose.count == 0)
12739  {
12740  XMakeMagnifyImage(display,windows);
12741  break;
12742  }
12743  break;
12744  }
12745  case KeyPress:
12746  {
12747  char
12748  command[MaxTextExtent];
12749 
12750  KeySym
12751  key_symbol;
12752 
12753  if (event->xkey.window != windows->magnify.id)
12754  break;
12755  /*
12756  Respond to a user key press.
12757  */
12758  (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12759  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12760  XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12761  break;
12762  }
12763  case MapNotify:
12764  {
12765  if (event->xmap.window == windows->magnify.id)
12766  {
12767  windows->magnify.mapped=MagickTrue;
12768  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12769  break;
12770  }
12771  if (event->xmap.window == windows->info.id)
12772  {
12773  windows->info.mapped=MagickTrue;
12774  break;
12775  }
12776  break;
12777  }
12778  case MotionNotify:
12779  {
12780  while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12781  if (event->xmotion.window == windows->image.id)
12782  if (windows->magnify.mapped != MagickFalse)
12783  {
12784  /*
12785  Update magnified image.
12786  */
12787  x=event->xmotion.x;
12788  y=event->xmotion.y;
12789  if (x < 0)
12790  x=0;
12791  else
12792  if (x >= (int) windows->image.width)
12793  x=(int) (windows->image.width-1);
12794  windows->magnify.x=(int) windows->image.x+x;
12795  if (y < 0)
12796  y=0;
12797  else
12798  if (y >= (int) windows->image.height)
12799  y=(int) (windows->image.height-1);
12800  windows->magnify.y=windows->image.y+y;
12801  XMakeMagnifyImage(display,windows);
12802  }
12803  break;
12804  }
12805  case UnmapNotify:
12806  {
12807  if (event->xunmap.window == windows->magnify.id)
12808  {
12809  windows->magnify.mapped=MagickFalse;
12810  break;
12811  }
12812  if (event->xunmap.window == windows->info.id)
12813  {
12814  windows->info.mapped=MagickFalse;
12815  break;
12816  }
12817  break;
12818  }
12819  default:
12820  break;
12821  }
12822 }
12823 
12824 /*
12825 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12826 % %
12827 % %
12828 % %
12829 + X S e t C r o p G e o m e t r y %
12830 % %
12831 % %
12832 % %
12833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12834 %
12835 % XSetCropGeometry() accepts a cropping geometry relative to the Image window
12836 % and translates it to a cropping geometry relative to the image.
12837 %
12838 % The format of the XSetCropGeometry method is:
12839 %
12840 % void XSetCropGeometry(Display *display,XWindows *windows,
12841 % RectangleInfo *crop_info,Image *image)
12842 %
12843 % A description of each parameter follows:
12844 %
12845 % o display: Specifies a connection to an X server; returned from
12846 % XOpenDisplay.
12847 %
12848 % o windows: Specifies a pointer to a XWindows structure.
12849 %
12850 % o crop_info: A pointer to a RectangleInfo that defines a region of the
12851 % Image window to crop.
12852 %
12853 % o image: the image.
12854 %
12855 */
12856 static void XSetCropGeometry(Display *display,XWindows *windows,
12857  RectangleInfo *crop_info,Image *image)
12858 {
12859  char
12860  text[MaxTextExtent];
12861 
12862  int
12863  x,
12864  y;
12865 
12866  MagickRealType
12867  scale_factor;
12868 
12869  unsigned int
12870  height,
12871  width;
12872 
12873  if (windows->info.mapped != MagickFalse)
12874  {
12875  /*
12876  Display info on cropping rectangle.
12877  */
12878  (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12879  (double) crop_info->width,(double) crop_info->height,(double)
12880  crop_info->x,(double) crop_info->y);
12881  XInfoWidget(display,windows,text);
12882  }
12883  /*
12884  Cropping geometry is relative to any previous crop geometry.
12885  */
12886  x=0;
12887  y=0;
12888  width=(unsigned int) image->columns;
12889  height=(unsigned int) image->rows;
12890  if (windows->image.crop_geometry != (char *) NULL)
12891  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12892  else
12893  windows->image.crop_geometry=AcquireString((char *) NULL);
12894  /*
12895  Define the crop geometry string from the cropping rectangle.
12896  */
12897  scale_factor=(MagickRealType) width/windows->image.ximage->width;
12898  if (crop_info->x > 0)
12899  x+=(int) (scale_factor*crop_info->x+0.5);
12900  width=(unsigned int) (scale_factor*crop_info->width+0.5);
12901  if (width == 0)
12902  width=1;
12903  scale_factor=(MagickRealType) height/windows->image.ximage->height;
12904  if (crop_info->y > 0)
12905  y+=(int) (scale_factor*crop_info->y+0.5);
12906  height=(unsigned int) (scale_factor*crop_info->height+0.5);
12907  if (height == 0)
12908  height=1;
12909  (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12910  "%ux%u%+d%+d",width,height,x,y);
12911 }
12912 
12913 /*
12914 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12915 % %
12916 % %
12917 % %
12918 + X T i l e I m a g e %
12919 % %
12920 % %
12921 % %
12922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12923 %
12924 % XTileImage() loads or deletes a selected tile from a visual image directory.
12925 % The load or delete command is chosen from a menu.
12926 %
12927 % The format of the XTileImage method is:
12928 %
12929 % Image *XTileImage(Display *display,XResourceInfo *resource_info,
12930 % XWindows *windows,Image *image,XEvent *event)
12931 %
12932 % A description of each parameter follows:
12933 %
12934 % o tile_image: XTileImage reads or deletes the tile image
12935 % and returns it. A null image is returned if an error occurs.
12936 %
12937 % o display: Specifies a connection to an X server; returned from
12938 % XOpenDisplay.
12939 %
12940 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12941 %
12942 % o windows: Specifies a pointer to a XWindows structure.
12943 %
12944 % o image: the image; returned from ReadImage.
12945 %
12946 % o event: Specifies a pointer to a XEvent structure. If it is NULL,
12947 % the entire image is refreshed.
12948 %
12949 */
12950 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12951  XWindows *windows,Image *image,XEvent *event)
12952 {
12953  const char
12954  *const VerbMenu[] =
12955  {
12956  "Load",
12957  "Next",
12958  "Former",
12959  "Delete",
12960  "Update",
12961  (char *) NULL,
12962  };
12963 
12964  static const ModeType
12965  TileCommands[] =
12966  {
12967  TileLoadCommand,
12968  TileNextCommand,
12969  TileFormerCommand,
12970  TileDeleteCommand,
12971  TileUpdateCommand
12972  };
12973 
12974  char
12975  command[MaxTextExtent],
12976  filename[MaxTextExtent];
12977 
12978  Image
12979  *tile_image;
12980 
12981  int
12982  id,
12983  status,
12984  tile,
12985  x,
12986  y;
12987 
12988  MagickRealType
12989  scale_factor;
12990 
12991  char
12992  *p,
12993  *q;
12994 
12995  int
12996  i;
12997 
12998  unsigned int
12999  height,
13000  width;
13001 
13002  /*
13003  Tile image is relative to montage image configuration.
13004  */
13005  x=0;
13006  y=0;
13007  width=(unsigned int) image->columns;
13008  height=(unsigned int) image->rows;
13009  if (windows->image.crop_geometry != (char *) NULL)
13010  (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13011  scale_factor=(MagickRealType) width/windows->image.ximage->width;
13012  event->xbutton.x+=windows->image.x;
13013  event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13014  scale_factor=(MagickRealType) height/windows->image.ximage->height;
13015  event->xbutton.y+=windows->image.y;
13016  event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13017  /*
13018  Determine size and location of each tile in the visual image directory.
13019  */
13020  width=(unsigned int) image->columns;
13021  height=(unsigned int) image->rows;
13022  x=0;
13023  y=0;
13024  (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13025  tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13026  (event->xbutton.x-x)/width;
13027  if (tile < 0)
13028  {
13029  /*
13030  Button press is outside any tile.
13031  */
13032  (void) XBell(display,0);
13033  return((Image *) NULL);
13034  }
13035  /*
13036  Determine file name from the tile directory.
13037  */
13038  p=image->directory;
13039  for (i=tile; (i != 0) && (*p != '\0'); )
13040  {
13041  if (*p == '\xff')
13042  i--;
13043  p++;
13044  }
13045  if (*p == '\0')
13046  {
13047  /*
13048  Button press is outside any tile.
13049  */
13050  (void) XBell(display,0);
13051  return((Image *) NULL);
13052  }
13053  /*
13054  Select a command from the pop-up menu.
13055  */
13056  id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13057  if (id < 0)
13058  return((Image *) NULL);
13059  q=p;
13060  while ((*q != '\xff') && (*q != '\0'))
13061  q++;
13062  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13063  /*
13064  Perform command for the selected tile.
13065  */
13066  XSetCursorState(display,windows,MagickTrue);
13067  XCheckRefreshWindows(display,windows);
13068  tile_image=NewImageList();
13069  switch (TileCommands[id])
13070  {
13071  case TileLoadCommand:
13072  {
13073  /*
13074  Load tile image.
13075  */
13076  XCheckRefreshWindows(display,windows);
13077  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13078  MaxTextExtent);
13079  (void) CopyMagickString(resource_info->image_info->filename,filename,
13080  MaxTextExtent);
13081  tile_image=ReadImage(resource_info->image_info,&image->exception);
13082  CatchException(&image->exception);
13083  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13084  break;
13085  }
13086  case TileNextCommand:
13087  {
13088  /*
13089  Display next image.
13090  */
13091  XClientMessage(display,windows->image.id,windows->im_protocols,
13092  windows->im_next_image,CurrentTime);
13093  break;
13094  }
13095  case TileFormerCommand:
13096  {
13097  /*
13098  Display former image.
13099  */
13100  XClientMessage(display,windows->image.id,windows->im_protocols,
13101  windows->im_former_image,CurrentTime);
13102  break;
13103  }
13104  case TileDeleteCommand:
13105  {
13106  /*
13107  Delete tile image.
13108  */
13109  if (IsPathAccessible(filename) == MagickFalse)
13110  {
13111  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13112  break;
13113  }
13114  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13115  if (status <= 0)
13116  break;
13117  status=ShredFile(filename);
13118  status|=remove_utf8(filename);
13119  if (status != MagickFalse)
13120  {
13121  XNoticeWidget(display,windows,"Unable to delete image file:",
13122  filename);
13123  break;
13124  }
13125  magick_fallthrough;
13126  }
13127  case TileUpdateCommand:
13128  {
13130  *exception;
13131 
13132  int
13133  x_offset,
13134  y_offset;
13135 
13136  PixelPacket
13137  pixel;
13138 
13139  int
13140  j;
13141 
13142  PixelPacket
13143  *s;
13144 
13145  /*
13146  Ensure all the images exist.
13147  */
13148  tile=0;
13149  for (p=image->directory; *p != '\0'; p++)
13150  {
13151  CacheView
13152  *image_view;
13153 
13154  q=p;
13155  while ((*q != '\xff') && (*q != '\0'))
13156  q++;
13157  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13158  p=q;
13159  if (IsPathAccessible(filename) != MagickFalse)
13160  {
13161  tile++;
13162  continue;
13163  }
13164  /*
13165  Overwrite tile with background color.
13166  */
13167  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13168  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13169  exception=(&image->exception);
13170  image_view=AcquireAuthenticCacheView(image,exception);
13171  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13172  for (i=0; i < (int) height; i++)
13173  {
13174  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13175  y_offset+i,width,1,exception);
13176  if (s == (PixelPacket *) NULL)
13177  break;
13178  for (j=0; j < (int) width; j++)
13179  *s++=pixel;
13180  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13181  break;
13182  }
13183  image_view=DestroyCacheView(image_view);
13184  tile++;
13185  }
13186  windows->image.window_changes.width=(int) image->columns;
13187  windows->image.window_changes.height=(int) image->rows;
13188  XConfigureImageColormap(display,resource_info,windows,image);
13189  (void) XConfigureImage(display,resource_info,windows,image);
13190  break;
13191  }
13192  default:
13193  break;
13194  }
13195  XSetCursorState(display,windows,MagickFalse);
13196  return(tile_image);
13197 }
13198 
13199 /*
13200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13201 % %
13202 % %
13203 % %
13204 + X T r a n s l a t e I m a g e %
13205 % %
13206 % %
13207 % %
13208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13209 %
13210 % XTranslateImage() translates the image within an Image window by one pixel
13211 % as specified by the key symbol. If the image has a `montage string the
13212 % translation is respect to the width and height contained within the string.
13213 %
13214 % The format of the XTranslateImage method is:
13215 %
13216 % void XTranslateImage(Display *display,XWindows *windows,
13217 % Image *image,const KeySym key_symbol)
13218 %
13219 % A description of each parameter follows:
13220 %
13221 % o display: Specifies a connection to an X server; returned from
13222 % XOpenDisplay.
13223 %
13224 % o windows: Specifies a pointer to a XWindows structure.
13225 %
13226 % o image: the image.
13227 %
13228 % o key_symbol: Specifies a KeySym which indicates which side of the image
13229 % to trim.
13230 %
13231 */
13232 static void XTranslateImage(Display *display,XWindows *windows,
13233  Image *image,const KeySym key_symbol)
13234 {
13235  char
13236  text[MaxTextExtent];
13237 
13238  int
13239  x,
13240  y;
13241 
13242  unsigned int
13243  x_offset,
13244  y_offset;
13245 
13246  /*
13247  User specified a pan position offset.
13248  */
13249  x_offset=windows->image.width;
13250  y_offset=windows->image.height;
13251  if (image->montage != (char *) NULL)
13252  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13253  switch ((int) key_symbol)
13254  {
13255  case XK_Home:
13256  case XK_KP_Home:
13257  {
13258  windows->image.x=(int) windows->image.width/2;
13259  windows->image.y=(int) windows->image.height/2;
13260  break;
13261  }
13262  case XK_Left:
13263  case XK_KP_Left:
13264  {
13265  windows->image.x-=x_offset;
13266  break;
13267  }
13268  case XK_Next:
13269  case XK_Up:
13270  case XK_KP_Up:
13271  {
13272  windows->image.y-=y_offset;
13273  break;
13274  }
13275  case XK_Right:
13276  case XK_KP_Right:
13277  {
13278  windows->image.x+=x_offset;
13279  break;
13280  }
13281  case XK_Prior:
13282  case XK_Down:
13283  case XK_KP_Down:
13284  {
13285  windows->image.y+=y_offset;
13286  break;
13287  }
13288  default:
13289  return;
13290  }
13291  /*
13292  Check boundary conditions.
13293  */
13294  if (windows->image.x < 0)
13295  windows->image.x=0;
13296  else
13297  if ((int) (windows->image.x+windows->image.width) >
13298  windows->image.ximage->width)
13299  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13300  if (windows->image.y < 0)
13301  windows->image.y=0;
13302  else
13303  if ((int) (windows->image.y+windows->image.height) >
13304  windows->image.ximage->height)
13305  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13306  /*
13307  Refresh Image window.
13308  */
13309  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13310  windows->image.width,windows->image.height,windows->image.x,
13311  windows->image.y);
13312  XInfoWidget(display,windows,text);
13313  XCheckRefreshWindows(display,windows);
13314  XDrawPanRectangle(display,windows);
13315  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13316  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13317 }
13318 
13319 /*
13320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13321 % %
13322 % %
13323 % %
13324 + X T r i m I m a g e %
13325 % %
13326 % %
13327 % %
13328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13329 %
13330 % XTrimImage() trims the edges from the Image window.
13331 %
13332 % The format of the XTrimImage method is:
13333 %
13334 % MagickBooleanType XTrimImage(Display *display,
13335 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13336 %
13337 % A description of each parameter follows:
13338 %
13339 % o display: Specifies a connection to an X server; returned from
13340 % XOpenDisplay.
13341 %
13342 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13343 %
13344 % o windows: Specifies a pointer to a XWindows structure.
13345 %
13346 % o image: the image.
13347 %
13348 */
13349 static MagickBooleanType XTrimImage(Display *display,
13350  XResourceInfo *resource_info,XWindows *windows,Image *image)
13351 {
13353  trim_info;
13354 
13355  int
13356  x,
13357  y;
13358 
13359  size_t
13360  background,
13361  pixel;
13362 
13363  /*
13364  Trim edges from image.
13365  */
13366  XSetCursorState(display,windows,MagickTrue);
13367  XCheckRefreshWindows(display,windows);
13368  /*
13369  Crop the left edge.
13370  */
13371  background=XGetPixel(windows->image.ximage,0,0);
13372  trim_info.width=(size_t) windows->image.ximage->width;
13373  for (x=0; x < windows->image.ximage->width; x++)
13374  {
13375  for (y=0; y < windows->image.ximage->height; y++)
13376  {
13377  pixel=XGetPixel(windows->image.ximage,x,y);
13378  if (pixel != background)
13379  break;
13380  }
13381  if (y < windows->image.ximage->height)
13382  break;
13383  }
13384  trim_info.x=(ssize_t) x;
13385  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13386  {
13387  XSetCursorState(display,windows,MagickFalse);
13388  return(MagickFalse);
13389  }
13390  /*
13391  Crop the right edge.
13392  */
13393  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13394  for (x=windows->image.ximage->width-1; x != 0; x--)
13395  {
13396  for (y=0; y < windows->image.ximage->height; y++)
13397  {
13398  pixel=XGetPixel(windows->image.ximage,x,y);
13399  if (pixel != background)
13400  break;
13401  }
13402  if (y < windows->image.ximage->height)
13403  break;
13404  }
13405  trim_info.width=(size_t) (x-trim_info.x+1);
13406  /*
13407  Crop the top edge.
13408  */
13409  background=XGetPixel(windows->image.ximage,0,0);
13410  trim_info.height=(size_t) windows->image.ximage->height;
13411  for (y=0; y < windows->image.ximage->height; y++)
13412  {
13413  for (x=0; x < windows->image.ximage->width; x++)
13414  {
13415  pixel=XGetPixel(windows->image.ximage,x,y);
13416  if (pixel != background)
13417  break;
13418  }
13419  if (x < windows->image.ximage->width)
13420  break;
13421  }
13422  trim_info.y=(ssize_t) y;
13423  /*
13424  Crop the bottom edge.
13425  */
13426  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13427  for (y=windows->image.ximage->height-1; y != 0; y--)
13428  {
13429  for (x=0; x < windows->image.ximage->width; x++)
13430  {
13431  pixel=XGetPixel(windows->image.ximage,x,y);
13432  if (pixel != background)
13433  break;
13434  }
13435  if (x < windows->image.ximage->width)
13436  break;
13437  }
13438  trim_info.height=(size_t) y-trim_info.y+1;
13439  if (((unsigned int) trim_info.width != windows->image.width) ||
13440  ((unsigned int) trim_info.height != windows->image.height))
13441  {
13442  /*
13443  Reconfigure Image window as defined by the trimming rectangle.
13444  */
13445  XSetCropGeometry(display,windows,&trim_info,image);
13446  windows->image.window_changes.width=(int) trim_info.width;
13447  windows->image.window_changes.height=(int) trim_info.height;
13448  (void) XConfigureImage(display,resource_info,windows,image);
13449  }
13450  XSetCursorState(display,windows,MagickFalse);
13451  return(MagickTrue);
13452 }
13453 
13454 /*
13455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13456 % %
13457 % %
13458 % %
13459 + X V i s u a l D i r e c t o r y I m a g e %
13460 % %
13461 % %
13462 % %
13463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13464 %
13465 % XVisualDirectoryImage() creates a Visual Image Directory.
13466 %
13467 % The format of the XVisualDirectoryImage method is:
13468 %
13469 % Image *XVisualDirectoryImage(Display *display,
13470 % XResourceInfo *resource_info,XWindows *windows)
13471 %
13472 % A description of each parameter follows:
13473 %
13474 % o nexus: Method XVisualDirectoryImage returns a visual image
13475 % directory if it can be created successfully. Otherwise a null image
13476 % is returned.
13477 %
13478 % o display: Specifies a connection to an X server; returned from
13479 % XOpenDisplay.
13480 %
13481 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13482 %
13483 % o windows: Specifies a pointer to a XWindows structure.
13484 %
13485 */
13486 static Image *XVisualDirectoryImage(Display *display,
13487  XResourceInfo *resource_info,XWindows *windows)
13488 {
13489 #define TileImageTag "Scale/Image"
13490 #define XClientName "montage"
13491 
13492  char
13493  **filelist;
13494 
13496  *exception;
13497 
13498  Image
13499  *images,
13500  *montage_image,
13501  *next_image,
13502  *thumbnail_image;
13503 
13504  ImageInfo
13505  *read_info;
13506 
13507  int
13508  number_files;
13509 
13510  MagickBooleanType
13511  backdrop;
13512 
13513  MagickStatusType
13514  status;
13515 
13516  MontageInfo
13517  *montage_info;
13518 
13520  geometry;
13521 
13522  int
13523  i;
13524 
13525  static char
13526  filename[MaxTextExtent] = "\0",
13527  filenames[MaxTextExtent] = "*";
13528 
13529  XResourceInfo
13530  background_resources;
13531 
13532  /*
13533  Request file name from user.
13534  */
13535  XFileBrowserWidget(display,windows,"Directory",filenames);
13536  if (*filenames == '\0')
13537  return((Image *) NULL);
13538  /*
13539  Expand the filenames.
13540  */
13541  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13542  if (filelist == (char **) NULL)
13543  {
13544  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13545  filenames);
13546  return((Image *) NULL);
13547  }
13548  number_files=1;
13549  filelist[0]=filenames;
13550  status=ExpandFilenames(&number_files,&filelist);
13551  if ((status == MagickFalse) || (number_files == 0))
13552  {
13553  if (number_files == 0)
13554  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13555  else
13556  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13557  filenames);
13558  return((Image *) NULL);
13559  }
13560  /*
13561  Set image background resources.
13562  */
13563  background_resources=(*resource_info);
13564  background_resources.window_id=AcquireString("");
13565  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13566  "0x%lx",windows->image.id);
13567  background_resources.backdrop=MagickTrue;
13568  /*
13569  Read each image and convert them to a tile.
13570  */
13571  backdrop=(windows->visual_info->klass == TrueColor) ||
13572  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13573  read_info=CloneImageInfo(resource_info->image_info);
13574  (void) SetImageOption(read_info,"jpeg:size","120x120");
13575  (void) CloneString(&read_info->size,DefaultTileGeometry);
13576  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13577  (void *) NULL);
13578  images=NewImageList();
13579  exception=AcquireExceptionInfo();
13580  XSetCursorState(display,windows,MagickTrue);
13581  XCheckRefreshWindows(display,windows);
13582  for (i=0; i < (int) number_files; i++)
13583  {
13584  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13585  filelist[i]=DestroyString(filelist[i]);
13586  *read_info->magick='\0';
13587  next_image=ReadImage(read_info,exception);
13588  CatchException(exception);
13589  if (next_image != (Image *) NULL)
13590  {
13591  (void) DeleteImageProperty(next_image,"label");
13592  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13593  read_info,next_image,DefaultTileLabel));
13594  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13595  exception);
13596  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13597  geometry.height,exception);
13598  if (thumbnail_image != (Image *) NULL)
13599  {
13600  next_image=DestroyImage(next_image);
13601  next_image=thumbnail_image;
13602  }
13603  if (backdrop)
13604  {
13605  (void) XDisplayBackgroundImage(display,&background_resources,
13606  next_image);
13607  XSetCursorState(display,windows,MagickTrue);
13608  }
13609  AppendImageToList(&images,next_image);
13610  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13611  {
13612  MagickBooleanType
13613  proceed;
13614 
13615  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13616  (MagickSizeType) number_files);
13617  if (proceed == MagickFalse)
13618  break;
13619  }
13620  }
13621  }
13622  exception=DestroyExceptionInfo(exception);
13623  filelist=(char **) RelinquishMagickMemory(filelist);
13624  if (images == (Image *) NULL)
13625  {
13626  read_info=DestroyImageInfo(read_info);
13627  XSetCursorState(display,windows,MagickFalse);
13628  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13629  return((Image *) NULL);
13630  }
13631  /*
13632  Create the Visual Image Directory.
13633  */
13634  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13635  montage_info->pointsize=10;
13636  if (resource_info->font != (char *) NULL)
13637  (void) CloneString(&montage_info->font,resource_info->font);
13638  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13639  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13640  images),&images->exception);
13641  images=DestroyImageList(images);
13642  montage_info=DestroyMontageInfo(montage_info);
13643  read_info=DestroyImageInfo(read_info);
13644  XSetCursorState(display,windows,MagickFalse);
13645  if (montage_image == (Image *) NULL)
13646  return(montage_image);
13647  XClientMessage(display,windows->image.id,windows->im_protocols,
13648  windows->im_next_image,CurrentTime);
13649  return(montage_image);
13650 }
13651 
13652 /*
13653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13654 % %
13655 % %
13656 % %
13657 % X D i s p l a y B a c k g r o u n d I m a g e %
13658 % %
13659 % %
13660 % %
13661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13662 %
13663 % XDisplayBackgroundImage() displays an image in the background of a window.
13664 %
13665 % The format of the XDisplayBackgroundImage method is:
13666 %
13667 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13668 % XResourceInfo *resource_info,Image *image)
13669 %
13670 % A description of each parameter follows:
13671 %
13672 % o display: Specifies a connection to an X server; returned from
13673 % XOpenDisplay.
13674 %
13675 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13676 %
13677 % o image: the image.
13678 %
13679 */
13680 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13681  XResourceInfo *resource_info,Image *image)
13682 {
13683  char
13684  geometry[MaxTextExtent],
13685  visual_type[MaxTextExtent];
13686 
13687  int
13688  height,
13689  status,
13690  width;
13691 
13693  geometry_info;
13694 
13695  static XPixelInfo
13696  pixel;
13697 
13698  static XStandardColormap
13699  *map_info;
13700 
13701  static XVisualInfo
13702  *visual_info = (XVisualInfo *) NULL;
13703 
13704  static XWindowInfo
13705  window_info;
13706 
13707  size_t
13708  delay;
13709 
13710  Window
13711  root_window;
13712 
13713  XGCValues
13714  context_values;
13715 
13716  XResourceInfo
13717  resources;
13718 
13719  XWindowAttributes
13720  window_attributes;
13721 
13722  /*
13723  Determine target window.
13724  */
13725  assert(image != (Image *) NULL);
13726  assert(image->signature == MagickCoreSignature);
13727  if (IsEventLogging() != MagickFalse)
13728  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13729  resources=(*resource_info);
13730  window_info.id=(Window) NULL;
13731  root_window=XRootWindow(display,XDefaultScreen(display));
13732  if (LocaleCompare(resources.window_id,"root") == 0)
13733  window_info.id=root_window;
13734  else
13735  {
13736  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13737  window_info.id=XWindowByID(display,root_window,
13738  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13739  if (window_info.id == (Window) NULL)
13740  window_info.id=XWindowByName(display,root_window,resources.window_id);
13741  }
13742  if (window_info.id == (Window) NULL)
13743  {
13744  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13745  resources.window_id);
13746  }
13747  /*
13748  Determine window visual id.
13749  */
13750  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13751  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13752  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13753  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13754  if (status != 0)
13755  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13756  XVisualIDFromVisual(window_attributes.visual));
13757  if (visual_info == (XVisualInfo *) NULL)
13758  {
13759  /*
13760  Allocate standard colormap.
13761  */
13762  map_info=XAllocStandardColormap();
13763  if (map_info == (XStandardColormap *) NULL)
13764  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13765  image->filename);
13766  map_info->colormap=(Colormap) NULL;
13767  pixel.pixels=(unsigned long *) NULL;
13768  /*
13769  Initialize visual info.
13770  */
13771  resources.map_type=(char *) NULL;
13772  resources.visual_type=visual_type;
13773  visual_info=XBestVisualInfo(display,map_info,&resources);
13774  if (visual_info == (XVisualInfo *) NULL)
13775  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13776  resources.visual_type);
13777  /*
13778  Initialize window info.
13779  */
13780  window_info.ximage=(XImage *) NULL;
13781  window_info.matte_image=(XImage *) NULL;
13782  window_info.pixmap=(Pixmap) NULL;
13783  window_info.matte_pixmap=(Pixmap) NULL;
13784  }
13785  /*
13786  Free previous root colors.
13787  */
13788  if (window_info.id == root_window)
13789  (void) XDestroyWindowColors(display,root_window);
13790  /*
13791  Initialize Standard Colormap.
13792  */
13793  resources.colormap=SharedColormap;
13794  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13795  /*
13796  Graphic context superclass.
13797  */
13798  context_values.background=pixel.foreground_color.pixel;
13799  context_values.foreground=pixel.background_color.pixel;
13800  pixel.annotate_context=XCreateGC(display,window_info.id,
13801  (size_t) (GCBackground | GCForeground),&context_values);
13802  if (pixel.annotate_context == (GC) NULL)
13803  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13804  image->filename);
13805  /*
13806  Initialize Image window attributes.
13807  */
13808  window_info.name=AcquireString("\0");
13809  window_info.icon_name=AcquireString("\0");
13810  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13811  &resources,&window_info);
13812  /*
13813  Create the X image.
13814  */
13815  window_info.width=(unsigned int) image->columns;
13816  window_info.height=(unsigned int) image->rows;
13817  if ((image->columns != window_info.width) ||
13818  (image->rows != window_info.height))
13819  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13820  image->filename);
13821  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13822  window_attributes.width,window_attributes.height);
13823  geometry_info.width=window_info.width;
13824  geometry_info.height=window_info.height;
13825  geometry_info.x=(ssize_t) window_info.x;
13826  geometry_info.y=(ssize_t) window_info.y;
13827  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13828  &geometry_info.width,&geometry_info.height);
13829  window_info.width=(unsigned int) geometry_info.width;
13830  window_info.height=(unsigned int) geometry_info.height;
13831  window_info.x=(int) geometry_info.x;
13832  window_info.y=(int) geometry_info.y;
13833  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13834  window_info.height);
13835  if (status == MagickFalse)
13836  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13837  image->filename);
13838  window_info.x=0;
13839  window_info.y=0;
13840  if (resource_info->debug != MagickFalse)
13841  {
13842  (void) LogMagickEvent(X11Event,GetMagickModule(),
13843  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13844  (double) image->columns,(double) image->rows);
13845  if (image->colors != 0)
13846  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13847  image->colors);
13848  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13849  }
13850  /*
13851  Adjust image dimensions as specified by backdrop or geometry options.
13852  */
13853  width=(int) window_info.width;
13854  height=(int) window_info.height;
13855  if (resources.backdrop != MagickFalse)
13856  {
13857  /*
13858  Center image on window.
13859  */
13860  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13861  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13862  width=window_attributes.width;
13863  height=window_attributes.height;
13864  }
13865  if ((resources.image_geometry != (char *) NULL) &&
13866  (*resources.image_geometry != '\0'))
13867  {
13868  char
13869  default_geometry[MaxTextExtent];
13870 
13871  int
13872  flags,
13873  gravity;
13874 
13875  XSizeHints
13876  *size_hints;
13877 
13878  /*
13879  User specified geometry.
13880  */
13881  size_hints=XAllocSizeHints();
13882  if (size_hints == (XSizeHints *) NULL)
13883  ThrowXWindowFatalException(ResourceLimitFatalError,
13884  "MemoryAllocationFailed",image->filename);
13885  size_hints->flags=0L;
13886  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13887  width,height);
13888  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13889  default_geometry,window_info.border_width,size_hints,&window_info.x,
13890  &window_info.y,&width,&height,&gravity);
13891  if (flags & (XValue | YValue))
13892  {
13893  width=window_attributes.width;
13894  height=window_attributes.height;
13895  }
13896  (void) XFree((void *) size_hints);
13897  }
13898  /*
13899  Create the X pixmap.
13900  */
13901  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13902  (unsigned int) height,window_info.depth);
13903  if (window_info.pixmap == (Pixmap) NULL)
13904  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13905  image->filename);
13906  /*
13907  Display pixmap on the window.
13908  */
13909  if (((unsigned int) width > window_info.width) ||
13910  ((unsigned int) height > window_info.height))
13911  (void) XFillRectangle(display,window_info.pixmap,
13912  window_info.annotate_context,0,0,(unsigned int) width,
13913  (unsigned int) height);
13914  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13915  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13916  window_info.width,(unsigned int) window_info.height);
13917  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13918  (void) XClearWindow(display,window_info.id);
13919  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13920  XDelay(display,delay == 0UL ? 10UL : delay);
13921  (void) XSync(display,MagickFalse);
13922  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13923 }
13924 
13925 /*
13926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13927 % %
13928 % %
13929 % %
13930 + X D i s p l a y I m a g e %
13931 % %
13932 % %
13933 % %
13934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13935 %
13936 % XDisplayImage() displays an image via X11. A new image is created and
13937 % returned if the user interactively transforms the displayed image.
13938 %
13939 % The format of the XDisplayImage method is:
13940 %
13941 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13942 % char **argv,int argc,Image **image,size_t *state)
13943 %
13944 % A description of each parameter follows:
13945 %
13946 % o nexus: Method XDisplayImage returns an image when the
13947 % user chooses 'Open Image' from the command menu or picks a tile
13948 % from the image directory. Otherwise a null image is returned.
13949 %
13950 % o display: Specifies a connection to an X server; returned from
13951 % XOpenDisplay.
13952 %
13953 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13954 %
13955 % o argv: Specifies the application's argument list.
13956 %
13957 % o argc: Specifies the number of arguments.
13958 %
13959 % o image: Specifies an address to an address of an Image structure;
13960 %
13961 */
13962 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13963  char **argv,int argc,Image **image,size_t *state)
13964 {
13965 #define MagnifySize 256 /* must be a power of 2 */
13966 #define MagickMenus 10
13967 #define MagickTitle "Commands"
13968 
13969  const char
13970  *const CommandMenu[] =
13971  {
13972  "File",
13973  "Edit",
13974  "View",
13975  "Transform",
13976  "Enhance",
13977  "Effects",
13978  "F/X",
13979  "Image Edit",
13980  "Miscellany",
13981  "Help",
13982  (char *) NULL
13983  },
13984  *const FileMenu[] =
13985  {
13986  "Open...",
13987  "Next",
13988  "Former",
13989  "Select...",
13990  "Save...",
13991  "Print...",
13992  "Delete...",
13993  "New...",
13994  "Visual Directory...",
13995  "Quit",
13996  (char *) NULL
13997  },
13998  *const EditMenu[] =
13999  {
14000  "Undo",
14001  "Redo",
14002  "Cut",
14003  "Copy",
14004  "Paste",
14005  (char *) NULL
14006  },
14007  *const ViewMenu[] =
14008  {
14009  "Half Size",
14010  "Original Size",
14011  "Double Size",
14012  "Resize...",
14013  "Apply",
14014  "Refresh",
14015  "Restore",
14016  (char *) NULL
14017  },
14018  *const TransformMenu[] =
14019  {
14020  "Crop",
14021  "Chop",
14022  "Flop",
14023  "Flip",
14024  "Rotate Right",
14025  "Rotate Left",
14026  "Rotate...",
14027  "Shear...",
14028  "Roll...",
14029  "Trim Edges",
14030  (char *) NULL
14031  },
14032  *const EnhanceMenu[] =
14033  {
14034  "Hue...",
14035  "Saturation...",
14036  "Brightness...",
14037  "Gamma...",
14038  "Spiff",
14039  "Dull",
14040  "Contrast Stretch...",
14041  "Sigmoidal Contrast...",
14042  "Normalize",
14043  "Equalize",
14044  "Negate",
14045  "Grayscale",
14046  "Map...",
14047  "Quantize...",
14048  (char *) NULL
14049  },
14050  *const EffectsMenu[] =
14051  {
14052  "Despeckle",
14053  "Emboss",
14054  "Reduce Noise",
14055  "Add Noise...",
14056  "Sharpen...",
14057  "Blur...",
14058  "Threshold...",
14059  "Edge Detect...",
14060  "Spread...",
14061  "Shade...",
14062  "Raise...",
14063  "Segment...",
14064  (char *) NULL
14065  },
14066  *const FXMenu[] =
14067  {
14068  "Solarize...",
14069  "Sepia Tone...",
14070  "Swirl...",
14071  "Implode...",
14072  "Vignette...",
14073  "Wave...",
14074  "Oil Paint...",
14075  "Charcoal Draw...",
14076  (char *) NULL
14077  },
14078  *const ImageEditMenu[] =
14079  {
14080  "Annotate...",
14081  "Draw...",
14082  "Color...",
14083  "Matte...",
14084  "Composite...",
14085  "Add Border...",
14086  "Add Frame...",
14087  "Comment...",
14088  "Launch...",
14089  "Region of Interest...",
14090  (char *) NULL
14091  },
14092  *const MiscellanyMenu[] =
14093  {
14094  "Image Info",
14095  "Zoom Image",
14096  "Show Preview...",
14097  "Show Histogram",
14098  "Show Matte",
14099  "Background...",
14100  "Slide Show...",
14101  "Preferences...",
14102  (char *) NULL
14103  },
14104  *const HelpMenu[] =
14105  {
14106  "Overview",
14107  "Browse Documentation",
14108  "About Display",
14109  (char *) NULL
14110  },
14111  *const ShortCutsMenu[] =
14112  {
14113  "Next",
14114  "Former",
14115  "Open...",
14116  "Save...",
14117  "Print...",
14118  "Undo",
14119  "Restore",
14120  "Image Info",
14121  "Quit",
14122  (char *) NULL
14123  },
14124  *const VirtualMenu[] =
14125  {
14126  "Image Info",
14127  "Print",
14128  "Next",
14129  "Quit",
14130  (char *) NULL
14131  };
14132 
14133  const char
14134  *const *Menus[MagickMenus] =
14135  {
14136  FileMenu,
14137  EditMenu,
14138  ViewMenu,
14139  TransformMenu,
14140  EnhanceMenu,
14141  EffectsMenu,
14142  FXMenu,
14143  ImageEditMenu,
14144  MiscellanyMenu,
14145  HelpMenu
14146  };
14147 
14148  static DisplayCommand
14149  CommandMenus[] =
14150  {
14151  NullCommand,
14152  NullCommand,
14153  NullCommand,
14154  NullCommand,
14155  NullCommand,
14156  NullCommand,
14157  NullCommand,
14158  NullCommand,
14159  NullCommand,
14160  NullCommand,
14161  },
14162  FileCommands[] =
14163  {
14164  OpenCommand,
14165  NextCommand,
14166  FormerCommand,
14167  SelectCommand,
14168  SaveCommand,
14169  PrintCommand,
14170  DeleteCommand,
14171  NewCommand,
14172  VisualDirectoryCommand,
14173  QuitCommand
14174  },
14175  EditCommands[] =
14176  {
14177  UndoCommand,
14178  RedoCommand,
14179  CutCommand,
14180  CopyCommand,
14181  PasteCommand
14182  },
14183  ViewCommands[] =
14184  {
14185  HalfSizeCommand,
14186  OriginalSizeCommand,
14187  DoubleSizeCommand,
14188  ResizeCommand,
14189  ApplyCommand,
14190  RefreshCommand,
14191  RestoreCommand
14192  },
14193  TransformCommands[] =
14194  {
14195  CropCommand,
14196  ChopCommand,
14197  FlopCommand,
14198  FlipCommand,
14199  RotateRightCommand,
14200  RotateLeftCommand,
14201  RotateCommand,
14202  ShearCommand,
14203  RollCommand,
14204  TrimCommand
14205  },
14206  EnhanceCommands[] =
14207  {
14208  HueCommand,
14209  SaturationCommand,
14210  BrightnessCommand,
14211  GammaCommand,
14212  SpiffCommand,
14213  DullCommand,
14214  ContrastStretchCommand,
14215  SigmoidalContrastCommand,
14216  NormalizeCommand,
14217  EqualizeCommand,
14218  NegateCommand,
14219  GrayscaleCommand,
14220  MapCommand,
14221  QuantizeCommand
14222  },
14223  EffectsCommands[] =
14224  {
14225  DespeckleCommand,
14226  EmbossCommand,
14227  ReduceNoiseCommand,
14228  AddNoiseCommand,
14229  SharpenCommand,
14230  BlurCommand,
14231  ThresholdCommand,
14232  EdgeDetectCommand,
14233  SpreadCommand,
14234  ShadeCommand,
14235  RaiseCommand,
14236  SegmentCommand
14237  },
14238  FXCommands[] =
14239  {
14240  SolarizeCommand,
14241  SepiaToneCommand,
14242  SwirlCommand,
14243  ImplodeCommand,
14244  VignetteCommand,
14245  WaveCommand,
14246  OilPaintCommand,
14247  CharcoalDrawCommand
14248  },
14249  ImageEditCommands[] =
14250  {
14251  AnnotateCommand,
14252  DrawCommand,
14253  ColorCommand,
14254  MatteCommand,
14255  CompositeCommand,
14256  AddBorderCommand,
14257  AddFrameCommand,
14258  CommentCommand,
14259  LaunchCommand,
14260  RegionOfInterestCommand
14261  },
14262  MiscellanyCommands[] =
14263  {
14264  InfoCommand,
14265  ZoomCommand,
14266  ShowPreviewCommand,
14267  ShowHistogramCommand,
14268  ShowMatteCommand,
14269  BackgroundCommand,
14270  SlideShowCommand,
14271  PreferencesCommand
14272  },
14273  HelpCommands[] =
14274  {
14275  HelpCommand,
14276  BrowseDocumentationCommand,
14277  VersionCommand
14278  },
14279  ShortCutsCommands[] =
14280  {
14281  NextCommand,
14282  FormerCommand,
14283  OpenCommand,
14284  SaveCommand,
14285  PrintCommand,
14286  UndoCommand,
14287  RestoreCommand,
14288  InfoCommand,
14289  QuitCommand
14290  },
14291  VirtualCommands[] =
14292  {
14293  InfoCommand,
14294  PrintCommand,
14295  NextCommand,
14296  QuitCommand
14297  };
14298 
14299  static DisplayCommand
14300  *Commands[MagickMenus] =
14301  {
14302  FileCommands,
14303  EditCommands,
14304  ViewCommands,
14305  TransformCommands,
14306  EnhanceCommands,
14307  EffectsCommands,
14308  FXCommands,
14309  ImageEditCommands,
14310  MiscellanyCommands,
14311  HelpCommands
14312  };
14313 
14314  char
14315  command[MaxTextExtent],
14316  *directory,
14317  geometry[MaxTextExtent],
14318  resource_name[MaxTextExtent];
14319 
14320  DisplayCommand
14321  display_command;
14322 
14323  Image
14324  *display_image,
14325  *nexus;
14326 
14327  int
14328  entry,
14329  id;
14330 
14331  KeySym
14332  key_symbol;
14333 
14334  MagickStatusType
14335  context_mask,
14336  status;
14337 
14339  geometry_info;
14340 
14341  int
14342  i;
14343 
14344  static char
14345  working_directory[MaxTextExtent];
14346 
14347  static XPoint
14348  vid_info;
14349 
14350  static XWindowInfo
14351  *magick_windows[MaxXWindows];
14352 
14353  static unsigned int
14354  number_windows;
14355 
14356  struct stat
14357  attributes;
14358 
14359  time_t
14360  timer,
14361  timestamp,
14362  update_time;
14363 
14364  unsigned int
14365  height,
14366  width;
14367 
14368  size_t
14369  delay;
14370 
14371  WarningHandler
14372  warning_handler;
14373 
14374  Window
14375  root_window;
14376 
14377  XClassHint
14378  *class_hints;
14379 
14380  XEvent
14381  event;
14382 
14383  XFontStruct
14384  *font_info;
14385 
14386  XGCValues
14387  context_values;
14388 
14389  XPixelInfo
14390  *icon_pixel,
14391  *pixel;
14392 
14393  XResourceInfo
14394  *icon_resources;
14395 
14396  XStandardColormap
14397  *icon_map,
14398  *map_info;
14399 
14400  XVisualInfo
14401  *icon_visual,
14402  *visual_info;
14403 
14404  XWindowChanges
14405  window_changes;
14406 
14407  XWindows
14408  *windows;
14409 
14410  XWMHints
14411  *manager_hints;
14412 
14413  assert(image != (Image **) NULL);
14414  assert((*image)->signature == MagickCoreSignature);
14415  if (IsEventLogging() != MagickFalse)
14416  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14417  display_image=(*image);
14418  warning_handler=(WarningHandler) NULL;
14419  windows=XSetWindows((XWindows *) ~0);
14420  if (windows != (XWindows *) NULL)
14421  {
14422  int
14423  status;
14424 
14425  if (*working_directory == '\0')
14426  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14427  status=chdir(working_directory);
14428  if (status == -1)
14429  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14430  FileOpenError,"UnableToOpenFile","%s",working_directory);
14431  warning_handler=resource_info->display_warnings ?
14432  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14433  warning_handler=resource_info->display_warnings ?
14434  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14435  }
14436  else
14437  {
14438  /*
14439  Allocate windows structure.
14440  */
14441  resource_info->colors=display_image->colors;
14442  windows=XSetWindows(XInitializeWindows(display,resource_info));
14443  if (windows == (XWindows *) NULL)
14444  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14445  (*image)->filename);
14446  /*
14447  Initialize window id's.
14448  */
14449  number_windows=0;
14450  magick_windows[number_windows++]=(&windows->icon);
14451  magick_windows[number_windows++]=(&windows->backdrop);
14452  magick_windows[number_windows++]=(&windows->image);
14453  magick_windows[number_windows++]=(&windows->info);
14454  magick_windows[number_windows++]=(&windows->command);
14455  magick_windows[number_windows++]=(&windows->widget);
14456  magick_windows[number_windows++]=(&windows->popup);
14457  magick_windows[number_windows++]=(&windows->magnify);
14458  magick_windows[number_windows++]=(&windows->pan);
14459  for (i=0; i < (int) number_windows; i++)
14460  magick_windows[i]->id=(Window) NULL;
14461  vid_info.x=0;
14462  vid_info.y=0;
14463  }
14464  /*
14465  Initialize font info.
14466  */
14467  if (windows->font_info != (XFontStruct *) NULL)
14468  (void) XFreeFont(display,windows->font_info);
14469  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14470  if (windows->font_info == (XFontStruct *) NULL)
14471  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14472  resource_info->font);
14473  /*
14474  Initialize Standard Colormap.
14475  */
14476  map_info=windows->map_info;
14477  icon_map=windows->icon_map;
14478  visual_info=windows->visual_info;
14479  icon_visual=windows->icon_visual;
14480  pixel=windows->pixel_info;
14481  icon_pixel=windows->icon_pixel;
14482  font_info=windows->font_info;
14483  icon_resources=windows->icon_resources;
14484  class_hints=windows->class_hints;
14485  manager_hints=windows->manager_hints;
14486  root_window=XRootWindow(display,visual_info->screen);
14487  nexus=NewImageList();
14488  if (resource_info->debug != MagickFalse)
14489  {
14490  (void) LogMagickEvent(X11Event,GetMagickModule(),
14491  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14492  (double) display_image->scene,(double) display_image->columns,
14493  (double) display_image->rows);
14494  if (display_image->colors != 0)
14495  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14496  display_image->colors);
14497  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14498  display_image->magick);
14499  }
14500  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14501  map_info,pixel);
14502  display_image->taint=MagickFalse;
14503  /*
14504  Initialize graphic context.
14505  */
14506  windows->context.id=(Window) NULL;
14507  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14508  resource_info,&windows->context);
14509  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14510  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14511  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14512  class_hints->res_class[0]);
14513  manager_hints->flags=InputHint | StateHint;
14514  manager_hints->input=MagickFalse;
14515  manager_hints->initial_state=WithdrawnState;
14516  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14517  &windows->context);
14518  if (resource_info->debug != MagickFalse)
14519  (void) LogMagickEvent(X11Event,GetMagickModule(),
14520  "Window id: 0x%lx (context)",windows->context.id);
14521  context_values.background=pixel->background_color.pixel;
14522  context_values.font=font_info->fid;
14523  context_values.foreground=pixel->foreground_color.pixel;
14524  context_values.graphics_exposures=MagickFalse;
14525  context_mask=(MagickStatusType)
14526  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14527  if (pixel->annotate_context != (GC) NULL)
14528  (void) XFreeGC(display,pixel->annotate_context);
14529  pixel->annotate_context=XCreateGC(display,windows->context.id,
14530  context_mask,&context_values);
14531  if (pixel->annotate_context == (GC) NULL)
14532  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14533  display_image->filename);
14534  context_values.background=pixel->depth_color.pixel;
14535  if (pixel->widget_context != (GC) NULL)
14536  (void) XFreeGC(display,pixel->widget_context);
14537  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14538  &context_values);
14539  if (pixel->widget_context == (GC) NULL)
14540  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14541  display_image->filename);
14542  context_values.background=pixel->foreground_color.pixel;
14543  context_values.foreground=pixel->background_color.pixel;
14544  context_values.plane_mask=context_values.background ^
14545  context_values.foreground;
14546  if (pixel->highlight_context != (GC) NULL)
14547  (void) XFreeGC(display,pixel->highlight_context);
14548  pixel->highlight_context=XCreateGC(display,windows->context.id,
14549  (size_t) (context_mask | GCPlaneMask),&context_values);
14550  if (pixel->highlight_context == (GC) NULL)
14551  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14552  display_image->filename);
14553  (void) XDestroyWindow(display,windows->context.id);
14554  /*
14555  Initialize icon window.
14556  */
14557  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14558  icon_resources,&windows->icon);
14559  windows->icon.geometry=resource_info->icon_geometry;
14560  XBestIconSize(display,&windows->icon,display_image);
14561  windows->icon.attributes.colormap=XDefaultColormap(display,
14562  icon_visual->screen);
14563  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14564  manager_hints->flags=InputHint | StateHint;
14565  manager_hints->input=MagickFalse;
14566  manager_hints->initial_state=IconicState;
14567  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14568  &windows->icon);
14569  if (resource_info->debug != MagickFalse)
14570  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14571  windows->icon.id);
14572  /*
14573  Initialize graphic context for icon window.
14574  */
14575  if (icon_pixel->annotate_context != (GC) NULL)
14576  (void) XFreeGC(display,icon_pixel->annotate_context);
14577  context_values.background=icon_pixel->background_color.pixel;
14578  context_values.foreground=icon_pixel->foreground_color.pixel;
14579  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14580  (size_t) (GCBackground | GCForeground),&context_values);
14581  if (icon_pixel->annotate_context == (GC) NULL)
14582  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14583  display_image->filename);
14584  windows->icon.annotate_context=icon_pixel->annotate_context;
14585  /*
14586  Initialize Image window.
14587  */
14588  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14589  &windows->image);
14590  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14591  if (resource_info->use_shared_memory == MagickFalse)
14592  windows->image.shared_memory=MagickFalse;
14593  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14594  {
14595  char
14596  *title;
14597 
14598  title=InterpretImageProperties(resource_info->image_info,display_image,
14599  resource_info->title);
14600  (void) CloneString(&windows->image.name,title);
14601  (void) CloneString(&windows->image.icon_name,title);
14602  title=DestroyString(title);
14603  }
14604  else
14605  {
14606  char
14607  filename[MaxTextExtent],
14608  window_name[MaxTextExtent];
14609 
14610  /*
14611  Window name is the base of the filename.
14612  */
14613  GetPathComponent(display_image->magick_filename,TailPath,filename);
14614  if (display_image->scene == 0)
14615  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14616  MagickPackageName,filename);
14617  else
14618  (void) FormatLocaleString(window_name,MaxTextExtent,
14619  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14620  (double) display_image->scene,(double) GetImageListLength(
14621  display_image));
14622  (void) CloneString(&windows->image.name,window_name);
14623  (void) CloneString(&windows->image.icon_name,filename);
14624  }
14625  if (resource_info->immutable)
14626  windows->image.immutable=MagickTrue;
14627  windows->image.use_pixmap=resource_info->use_pixmap;
14628  windows->image.geometry=resource_info->image_geometry;
14629  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14630  XDisplayWidth(display,visual_info->screen),
14631  XDisplayHeight(display,visual_info->screen));
14632  geometry_info.width=display_image->columns;
14633  geometry_info.height=display_image->rows;
14634  geometry_info.x=0;
14635  geometry_info.y=0;
14636  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14637  &geometry_info.width,&geometry_info.height);
14638  windows->image.width=(unsigned int) geometry_info.width;
14639  windows->image.height=(unsigned int) geometry_info.height;
14640  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14641  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14642  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14643  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14644  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14645  resource_info,&windows->backdrop);
14646  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14647  {
14648  /*
14649  Initialize backdrop window.
14650  */
14651  windows->backdrop.x=0;
14652  windows->backdrop.y=0;
14653  (void) CloneString(&windows->backdrop.name,"Backdrop");
14654  windows->backdrop.flags=(size_t) (USSize | USPosition);
14655  windows->backdrop.width=(unsigned int)
14656  XDisplayWidth(display,visual_info->screen);
14657  windows->backdrop.height=(unsigned int)
14658  XDisplayHeight(display,visual_info->screen);
14659  windows->backdrop.border_width=0;
14660  windows->backdrop.immutable=MagickTrue;
14661  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14662  ButtonReleaseMask;
14663  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14664  StructureNotifyMask;
14665  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14666  manager_hints->icon_window=windows->icon.id;
14667  manager_hints->input=MagickTrue;
14668  manager_hints->initial_state=resource_info->iconic ? IconicState :
14669  NormalState;
14670  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14671  &windows->backdrop);
14672  if (resource_info->debug != MagickFalse)
14673  (void) LogMagickEvent(X11Event,GetMagickModule(),
14674  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14675  (void) XMapWindow(display,windows->backdrop.id);
14676  (void) XClearWindow(display,windows->backdrop.id);
14677  if (windows->image.id != (Window) NULL)
14678  {
14679  (void) XDestroyWindow(display,windows->image.id);
14680  windows->image.id=(Window) NULL;
14681  }
14682  /*
14683  Position image in the center the backdrop.
14684  */
14685  windows->image.flags|=USPosition;
14686  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14687  (windows->image.width/2);
14688  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14689  (windows->image.height/2);
14690  }
14691  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14692  manager_hints->icon_window=windows->icon.id;
14693  manager_hints->input=MagickTrue;
14694  manager_hints->initial_state=resource_info->iconic ? IconicState :
14695  NormalState;
14696  if (windows->group_leader.id != (Window) NULL)
14697  {
14698  /*
14699  Follow the leader.
14700  */
14701  manager_hints->flags|=WindowGroupHint;
14702  manager_hints->window_group=windows->group_leader.id;
14703  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14704  if (resource_info->debug != MagickFalse)
14705  (void) LogMagickEvent(X11Event,GetMagickModule(),
14706  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14707  }
14708  XMakeWindow(display,
14709  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14710  argv,argc,class_hints,manager_hints,&windows->image);
14711  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14712  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14713  if (windows->group_leader.id != (Window) NULL)
14714  (void) XSetTransientForHint(display,windows->image.id,
14715  windows->group_leader.id);
14716  if (resource_info->debug != MagickFalse)
14717  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14718  windows->image.id);
14719  /*
14720  Initialize Info widget.
14721  */
14722  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14723  &windows->info);
14724  (void) CloneString(&windows->info.name,"Info");
14725  (void) CloneString(&windows->info.icon_name,"Info");
14726  windows->info.border_width=1;
14727  windows->info.x=2;
14728  windows->info.y=2;
14729  windows->info.flags|=PPosition;
14730  windows->info.attributes.win_gravity=UnmapGravity;
14731  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14732  StructureNotifyMask;
14733  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14734  manager_hints->input=MagickFalse;
14735  manager_hints->initial_state=NormalState;
14736  manager_hints->window_group=windows->image.id;
14737  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14738  &windows->info);
14739  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14740  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14741  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14742  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14743  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14744  if (windows->image.mapped != MagickFalse)
14745  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14746  if (resource_info->debug != MagickFalse)
14747  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14748  windows->info.id);
14749  /*
14750  Initialize Command widget.
14751  */
14752  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14753  resource_info,&windows->command);
14754  windows->command.data=MagickMenus;
14755  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14756  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14757  resource_info->client_name);
14758  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14759  resource_name,"geometry",(char *) NULL);
14760  (void) CloneString(&windows->command.name,MagickTitle);
14761  windows->command.border_width=0;
14762  windows->command.flags|=PPosition;
14763  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14764  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14765  OwnerGrabButtonMask | StructureNotifyMask;
14766  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14767  manager_hints->input=MagickTrue;
14768  manager_hints->initial_state=NormalState;
14769  manager_hints->window_group=windows->image.id;
14770  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14771  &windows->command);
14772  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14773  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14774  HighlightHeight);
14775  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14776  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14777  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14778  if (windows->command.mapped != MagickFalse)
14779  (void) XMapRaised(display,windows->command.id);
14780  if (resource_info->debug != MagickFalse)
14781  (void) LogMagickEvent(X11Event,GetMagickModule(),
14782  "Window id: 0x%lx (command)",windows->command.id);
14783  /*
14784  Initialize Widget window.
14785  */
14786  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14787  resource_info,&windows->widget);
14788  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14789  resource_info->client_name);
14790  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14791  resource_name,"geometry",(char *) NULL);
14792  windows->widget.border_width=0;
14793  windows->widget.flags|=PPosition;
14794  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14795  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14796  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14797  StructureNotifyMask;
14798  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14799  manager_hints->input=MagickTrue;
14800  manager_hints->initial_state=NormalState;
14801  manager_hints->window_group=windows->image.id;
14802  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14803  &windows->widget);
14804  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14805  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14806  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14807  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14808  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14809  if (resource_info->debug != MagickFalse)
14810  (void) LogMagickEvent(X11Event,GetMagickModule(),
14811  "Window id: 0x%lx (widget)",windows->widget.id);
14812  /*
14813  Initialize popup window.
14814  */
14815  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14816  resource_info,&windows->popup);
14817  windows->popup.border_width=0;
14818  windows->popup.flags|=PPosition;
14819  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14820  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14821  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14822  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14823  manager_hints->input=MagickTrue;
14824  manager_hints->initial_state=NormalState;
14825  manager_hints->window_group=windows->image.id;
14826  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14827  &windows->popup);
14828  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14829  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14830  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14831  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14832  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14833  if (resource_info->debug != MagickFalse)
14834  (void) LogMagickEvent(X11Event,GetMagickModule(),
14835  "Window id: 0x%lx (pop up)",windows->popup.id);
14836  /*
14837  Initialize Magnify window and cursor.
14838  */
14839  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14840  resource_info,&windows->magnify);
14841  if (resource_info->use_shared_memory == MagickFalse)
14842  windows->magnify.shared_memory=MagickFalse;
14843  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14844  resource_info->client_name);
14845  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14846  resource_name,"geometry",(char *) NULL);
14847  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14848  resource_info->magnify);
14849  if (windows->magnify.cursor != (Cursor) NULL)
14850  (void) XFreeCursor(display,windows->magnify.cursor);
14851  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14852  map_info->colormap,resource_info->background_color,
14853  resource_info->foreground_color);
14854  if (windows->magnify.cursor == (Cursor) NULL)
14855  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14856  display_image->filename);
14857  windows->magnify.width=MagnifySize;
14858  windows->magnify.height=MagnifySize;
14859  windows->magnify.flags|=PPosition;
14860  windows->magnify.min_width=MagnifySize;
14861  windows->magnify.min_height=MagnifySize;
14862  windows->magnify.width_inc=MagnifySize;
14863  windows->magnify.height_inc=MagnifySize;
14864  windows->magnify.data=resource_info->magnify;
14865  windows->magnify.attributes.cursor=windows->magnify.cursor;
14866  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14867  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14868  StructureNotifyMask;
14869  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14870  manager_hints->input=MagickTrue;
14871  manager_hints->initial_state=NormalState;
14872  manager_hints->window_group=windows->image.id;
14873  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14874  &windows->magnify);
14875  if (resource_info->debug != MagickFalse)
14876  (void) LogMagickEvent(X11Event,GetMagickModule(),
14877  "Window id: 0x%lx (magnify)",windows->magnify.id);
14878  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14879  /*
14880  Initialize panning window.
14881  */
14882  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14883  resource_info,&windows->pan);
14884  (void) CloneString(&windows->pan.name,"Pan Icon");
14885  windows->pan.width=windows->icon.width;
14886  windows->pan.height=windows->icon.height;
14887  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14888  resource_info->client_name);
14889  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14890  resource_name,"geometry",(char *) NULL);
14891  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14892  &windows->pan.width,&windows->pan.height);
14893  windows->pan.flags|=PPosition;
14894  windows->pan.immutable=MagickTrue;
14895  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14896  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14897  StructureNotifyMask;
14898  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14899  manager_hints->input=MagickFalse;
14900  manager_hints->initial_state=NormalState;
14901  manager_hints->window_group=windows->image.id;
14902  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14903  &windows->pan);
14904  if (resource_info->debug != MagickFalse)
14905  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14906  windows->pan.id);
14907  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14908  if (windows->info.mapped != MagickFalse)
14909  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14910  if ((windows->image.mapped == MagickFalse) ||
14911  (windows->backdrop.id != (Window) NULL))
14912  (void) XMapWindow(display,windows->image.id);
14913  /*
14914  Set our progress monitor and warning handlers.
14915  */
14916  if (warning_handler == (WarningHandler) NULL)
14917  {
14918  warning_handler=resource_info->display_warnings ?
14919  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14920  warning_handler=resource_info->display_warnings ?
14921  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14922  }
14923  /*
14924  Initialize Image and Magnify X images.
14925  */
14926  windows->image.x=0;
14927  windows->image.y=0;
14928  windows->magnify.shape=MagickFalse;
14929  width=(unsigned int) display_image->columns;
14930  height=(unsigned int) display_image->rows;
14931  if ((display_image->columns != width) || (display_image->rows != height))
14932  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14933  display_image->filename);
14934  status=XMakeImage(display,resource_info,&windows->image,display_image,
14935  width,height);
14936  if (status == MagickFalse)
14937  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14938  display_image->filename);
14939  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14940  windows->magnify.width,windows->magnify.height);
14941  if (status == MagickFalse)
14942  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14943  display_image->filename);
14944  if (windows->magnify.mapped != MagickFalse)
14945  (void) XMapRaised(display,windows->magnify.id);
14946  if (windows->pan.mapped != MagickFalse)
14947  (void) XMapRaised(display,windows->pan.id);
14948  windows->image.window_changes.width=(int) display_image->columns;
14949  windows->image.window_changes.height=(int) display_image->rows;
14950  (void) XConfigureImage(display,resource_info,windows,display_image);
14951  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14952  (void) XSync(display,MagickFalse);
14953  /*
14954  Respond to events.
14955  */
14956  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14957  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14958  update_time=0;
14959  if (resource_info->update != MagickFalse)
14960  {
14961  MagickBooleanType
14962  status;
14963 
14964  /*
14965  Determine when file data was last modified.
14966  */
14967  status=GetPathAttributes(display_image->filename,&attributes);
14968  if (status != MagickFalse)
14969  update_time=attributes.st_mtime;
14970  }
14971  *state&=(~FormerImageState);
14972  *state&=(~MontageImageState);
14973  *state&=(~NextImageState);
14974  do
14975  {
14976  /*
14977  Handle a window event.
14978  */
14979  if (windows->image.mapped != MagickFalse)
14980  if ((display_image->delay != 0) || (resource_info->update != 0))
14981  {
14982  if (timer < GetMagickTime())
14983  {
14984  if (resource_info->update == MagickFalse)
14985  *state|=NextImageState | ExitState;
14986  else
14987  {
14988  MagickBooleanType
14989  status;
14990 
14991  /*
14992  Determine if image file was modified.
14993  */
14994  status=GetPathAttributes(display_image->filename,&attributes);
14995  if (status != MagickFalse)
14996  if (update_time != attributes.st_mtime)
14997  {
14998  /*
14999  Redisplay image.
15000  */
15001  (void) FormatLocaleString(
15002  resource_info->image_info->filename,MaxTextExtent,
15003  "%s:%s",display_image->magick,
15004  display_image->filename);
15005  nexus=ReadImage(resource_info->image_info,
15006  &display_image->exception);
15007  if (nexus != (Image *) NULL)
15008  *state|=NextImageState | ExitState;
15009  }
15010  delay=display_image->delay/MagickMax(
15011  display_image->ticks_per_second,1L);
15012  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15013  }
15014  }
15015  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15016  {
15017  /*
15018  Do not block if delay > 0.
15019  */
15020  XDelay(display,SuspendTime << 2);
15021  continue;
15022  }
15023  }
15024  timestamp=GetMagickTime();
15025  (void) XNextEvent(display,&event);
15026  if (windows->image.stasis == MagickFalse)
15027  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15028  MagickTrue : MagickFalse;
15029  if (windows->magnify.stasis == MagickFalse)
15030  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15031  MagickTrue : MagickFalse;
15032  if (event.xany.window == windows->command.id)
15033  {
15034  /*
15035  Select a command from the Command widget.
15036  */
15037  id=XCommandWidget(display,windows,CommandMenu,&event);
15038  if (id < 0)
15039  continue;
15040  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15041  display_command=CommandMenus[id];
15042  if (id < MagickMenus)
15043  {
15044  /*
15045  Select a command from a pop-up menu.
15046  */
15047  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15048  command);
15049  if (entry < 0)
15050  continue;
15051  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15052  display_command=Commands[id][entry];
15053  }
15054  if (display_command != NullCommand)
15055  nexus=XMagickCommand(display,resource_info,windows,display_command,
15056  &display_image);
15057  continue;
15058  }
15059  switch (event.type)
15060  {
15061  case ButtonPress:
15062  {
15063  if (resource_info->debug != MagickFalse)
15064  (void) LogMagickEvent(X11Event,GetMagickModule(),
15065  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15066  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15067  if ((event.xbutton.button == Button3) &&
15068  (event.xbutton.state & Mod1Mask))
15069  {
15070  /*
15071  Convert Alt-Button3 to Button2.
15072  */
15073  event.xbutton.button=Button2;
15074  event.xbutton.state&=(~Mod1Mask);
15075  }
15076  if (event.xbutton.window == windows->backdrop.id)
15077  {
15078  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15079  event.xbutton.time);
15080  break;
15081  }
15082  if (event.xbutton.window == windows->image.id)
15083  {
15084  switch (event.xbutton.button)
15085  {
15086  case Button1:
15087  {
15088  if (resource_info->immutable)
15089  {
15090  /*
15091  Select a command from the Virtual menu.
15092  */
15093  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15094  command);
15095  if (entry >= 0)
15096  nexus=XMagickCommand(display,resource_info,windows,
15097  VirtualCommands[entry],&display_image);
15098  break;
15099  }
15100  /*
15101  Map/unmap Command widget.
15102  */
15103  if (windows->command.mapped != MagickFalse)
15104  (void) XWithdrawWindow(display,windows->command.id,
15105  windows->command.screen);
15106  else
15107  {
15108  (void) XCommandWidget(display,windows,CommandMenu,
15109  (XEvent *) NULL);
15110  (void) XMapRaised(display,windows->command.id);
15111  }
15112  break;
15113  }
15114  case Button2:
15115  {
15116  /*
15117  User pressed the image magnify button.
15118  */
15119  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15120  &display_image);
15121  XMagnifyImage(display,windows,&event);
15122  break;
15123  }
15124  case Button3:
15125  {
15126  if (resource_info->immutable)
15127  {
15128  /*
15129  Select a command from the Virtual menu.
15130  */
15131  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15132  command);
15133  if (entry >= 0)
15134  nexus=XMagickCommand(display,resource_info,windows,
15135  VirtualCommands[entry],&display_image);
15136  break;
15137  }
15138  if (display_image->montage != (char *) NULL)
15139  {
15140  /*
15141  Open or delete a tile from a visual image directory.
15142  */
15143  nexus=XTileImage(display,resource_info,windows,
15144  display_image,&event);
15145  if (nexus != (Image *) NULL)
15146  *state|=MontageImageState | NextImageState | ExitState;
15147  vid_info.x=(short int) windows->image.x;
15148  vid_info.y=(short int) windows->image.y;
15149  break;
15150  }
15151  /*
15152  Select a command from the Short Cuts menu.
15153  */
15154  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15155  command);
15156  if (entry >= 0)
15157  nexus=XMagickCommand(display,resource_info,windows,
15158  ShortCutsCommands[entry],&display_image);
15159  break;
15160  }
15161  case Button4:
15162  {
15163  /*
15164  Wheel up.
15165  */
15166  XTranslateImage(display,windows,*image,XK_Up);
15167  break;
15168  }
15169  case Button5:
15170  {
15171  /*
15172  Wheel down.
15173  */
15174  XTranslateImage(display,windows,*image,XK_Down);
15175  break;
15176  }
15177  default:
15178  break;
15179  }
15180  break;
15181  }
15182  if (event.xbutton.window == windows->magnify.id)
15183  {
15184  const char
15185  *const MagnifyMenu[] =
15186  {
15187  "2",
15188  "4",
15189  "5",
15190  "6",
15191  "7",
15192  "8",
15193  "9",
15194  "3",
15195  (char *) NULL,
15196  };
15197 
15198  int
15199  factor;
15200 
15201  static KeySym
15202  MagnifyCommands[] =
15203  {
15204  XK_2,
15205  XK_4,
15206  XK_5,
15207  XK_6,
15208  XK_7,
15209  XK_8,
15210  XK_9,
15211  XK_3
15212  };
15213 
15214  /*
15215  Select a magnify factor from the pop-up menu.
15216  */
15217  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15218  if (factor >= 0)
15219  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15220  break;
15221  }
15222  if (event.xbutton.window == windows->pan.id)
15223  {
15224  switch (event.xbutton.button)
15225  {
15226  case Button4:
15227  {
15228  /*
15229  Wheel up.
15230  */
15231  XTranslateImage(display,windows,*image,XK_Up);
15232  break;
15233  }
15234  case Button5:
15235  {
15236  /*
15237  Wheel down.
15238  */
15239  XTranslateImage(display,windows,*image,XK_Down);
15240  break;
15241  }
15242  default:
15243  {
15244  XPanImage(display,windows,&event);
15245  break;
15246  }
15247  }
15248  break;
15249  }
15250  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15251  1L);
15252  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15253  break;
15254  }
15255  case ButtonRelease:
15256  {
15257  if (resource_info->debug != MagickFalse)
15258  (void) LogMagickEvent(X11Event,GetMagickModule(),
15259  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15260  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15261  break;
15262  }
15263  case ClientMessage:
15264  {
15265  if (resource_info->debug != MagickFalse)
15266  (void) LogMagickEvent(X11Event,GetMagickModule(),
15267  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15268  event.xclient.message_type,event.xclient.format,(unsigned long)
15269  event.xclient.data.l[0]);
15270  if (event.xclient.message_type == windows->im_protocols)
15271  {
15272  if (*event.xclient.data.l == (long) windows->im_update_widget)
15273  {
15274  (void) CloneString(&windows->command.name,MagickTitle);
15275  windows->command.data=MagickMenus;
15276  (void) XCommandWidget(display,windows,CommandMenu,
15277  (XEvent *) NULL);
15278  break;
15279  }
15280  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15281  {
15282  /*
15283  Update graphic context and window colormap.
15284  */
15285  for (i=0; i < (int) number_windows; i++)
15286  {
15287  if (magick_windows[i]->id == windows->icon.id)
15288  continue;
15289  context_values.background=pixel->background_color.pixel;
15290  context_values.foreground=pixel->foreground_color.pixel;
15291  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15292  context_mask,&context_values);
15293  (void) XChangeGC(display,magick_windows[i]->widget_context,
15294  context_mask,&context_values);
15295  context_values.background=pixel->foreground_color.pixel;
15296  context_values.foreground=pixel->background_color.pixel;
15297  context_values.plane_mask=context_values.background ^
15298  context_values.foreground;
15299  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15300  (size_t) (context_mask | GCPlaneMask),
15301  &context_values);
15302  magick_windows[i]->attributes.background_pixel=
15303  pixel->background_color.pixel;
15304  magick_windows[i]->attributes.border_pixel=
15305  pixel->border_color.pixel;
15306  magick_windows[i]->attributes.colormap=map_info->colormap;
15307  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15308  (unsigned long) magick_windows[i]->mask,
15309  &magick_windows[i]->attributes);
15310  }
15311  if (windows->pan.mapped != MagickFalse)
15312  {
15313  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15314  windows->pan.pixmap);
15315  (void) XClearWindow(display,windows->pan.id);
15316  XDrawPanRectangle(display,windows);
15317  }
15318  if (windows->backdrop.id != (Window) NULL)
15319  (void) XInstallColormap(display,map_info->colormap);
15320  break;
15321  }
15322  if (*event.xclient.data.l == (long) windows->im_former_image)
15323  {
15324  *state|=FormerImageState | ExitState;
15325  break;
15326  }
15327  if (*event.xclient.data.l == (long) windows->im_next_image)
15328  {
15329  *state|=NextImageState | ExitState;
15330  break;
15331  }
15332  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15333  {
15334  *state|=RetainColorsState;
15335  break;
15336  }
15337  if (*event.xclient.data.l == (long) windows->im_exit)
15338  {
15339  *state|=ExitState;
15340  break;
15341  }
15342  break;
15343  }
15344  if (event.xclient.message_type == windows->dnd_protocols)
15345  {
15346  Atom
15347  selection,
15348  type;
15349 
15350  int
15351  format,
15352  status;
15353 
15354  unsigned char
15355  *data;
15356 
15357  unsigned long
15358  after,
15359  length;
15360 
15361  /*
15362  Display image named by the Drag-and-Drop selection.
15363  */
15364  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15365  break;
15366  selection=XInternAtom(display,"DndSelection",MagickFalse);
15367  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15368  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15369  &length,&after,&data);
15370  if ((status != Success) || (length == 0))
15371  break;
15372  if (*event.xclient.data.l == 2)
15373  {
15374  /*
15375  Offix DND.
15376  */
15377  (void) CopyMagickString(resource_info->image_info->filename,
15378  (char *) data,MaxTextExtent);
15379  }
15380  else
15381  {
15382  /*
15383  XDND.
15384  */
15385  if (strncmp((char *) data, "file:", 5) != 0)
15386  {
15387  (void) XFree((void *) data);
15388  break;
15389  }
15390  (void) CopyMagickString(resource_info->image_info->filename,
15391  ((char *) data)+5,MaxTextExtent);
15392  }
15393  nexus=ReadImage(resource_info->image_info,
15394  &display_image->exception);
15395  CatchException(&display_image->exception);
15396  if (nexus != (Image *) NULL)
15397  *state|=NextImageState | ExitState;
15398  (void) XFree((void *) data);
15399  break;
15400  }
15401  /*
15402  If client window delete message, exit.
15403  */
15404  if (event.xclient.message_type != windows->wm_protocols)
15405  break;
15406  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15407  break;
15408  (void) XWithdrawWindow(display,event.xclient.window,
15409  visual_info->screen);
15410  if (event.xclient.window == windows->image.id)
15411  {
15412  *state|=ExitState;
15413  break;
15414  }
15415  if (event.xclient.window == windows->pan.id)
15416  {
15417  /*
15418  Restore original image size when pan window is deleted.
15419  */
15420  windows->image.window_changes.width=windows->image.ximage->width;
15421  windows->image.window_changes.height=windows->image.ximage->height;
15422  (void) XConfigureImage(display,resource_info,windows,
15423  display_image);
15424  }
15425  break;
15426  }
15427  case ConfigureNotify:
15428  {
15429  if (resource_info->debug != MagickFalse)
15430  (void) LogMagickEvent(X11Event,GetMagickModule(),
15431  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15432  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15433  event.xconfigure.y,event.xconfigure.send_event);
15434  if (event.xconfigure.window == windows->image.id)
15435  {
15436  /*
15437  Image window has a new configuration.
15438  */
15439  if (event.xconfigure.send_event != 0)
15440  {
15441  XWindowChanges
15442  window_changes;
15443 
15444  /*
15445  Position the transient windows relative of the Image window.
15446  */
15447  if (windows->command.geometry == (char *) NULL)
15448  if (windows->command.mapped == MagickFalse)
15449  {
15450  windows->command.x=event.xconfigure.x-
15451  windows->command.width-25;
15452  windows->command.y=event.xconfigure.y;
15453  XConstrainWindowPosition(display,&windows->command);
15454  window_changes.x=windows->command.x;
15455  window_changes.y=windows->command.y;
15456  (void) XReconfigureWMWindow(display,windows->command.id,
15457  windows->command.screen,(unsigned int) (CWX | CWY),
15458  &window_changes);
15459  }
15460  if (windows->widget.geometry == (char *) NULL)
15461  if (windows->widget.mapped == MagickFalse)
15462  {
15463  windows->widget.x=event.xconfigure.x+
15464  event.xconfigure.width/10;
15465  windows->widget.y=event.xconfigure.y+
15466  event.xconfigure.height/10;
15467  XConstrainWindowPosition(display,&windows->widget);
15468  window_changes.x=windows->widget.x;
15469  window_changes.y=windows->widget.y;
15470  (void) XReconfigureWMWindow(display,windows->widget.id,
15471  windows->widget.screen,(unsigned int) (CWX | CWY),
15472  &window_changes);
15473  }
15474  if (windows->magnify.geometry == (char *) NULL)
15475  if (windows->magnify.mapped == MagickFalse)
15476  {
15477  windows->magnify.x=event.xconfigure.x+
15478  event.xconfigure.width+25;
15479  windows->magnify.y=event.xconfigure.y;
15480  XConstrainWindowPosition(display,&windows->magnify);
15481  window_changes.x=windows->magnify.x;
15482  window_changes.y=windows->magnify.y;
15483  (void) XReconfigureWMWindow(display,windows->magnify.id,
15484  windows->magnify.screen,(unsigned int) (CWX | CWY),
15485  &window_changes);
15486  }
15487  if (windows->pan.geometry == (char *) NULL)
15488  if (windows->pan.mapped == MagickFalse)
15489  {
15490  windows->pan.x=event.xconfigure.x+
15491  event.xconfigure.width+25;
15492  windows->pan.y=event.xconfigure.y+
15493  windows->magnify.height+50;
15494  XConstrainWindowPosition(display,&windows->pan);
15495  window_changes.x=windows->pan.x;
15496  window_changes.y=windows->pan.y;
15497  (void) XReconfigureWMWindow(display,windows->pan.id,
15498  windows->pan.screen,(unsigned int) (CWX | CWY),
15499  &window_changes);
15500  }
15501  }
15502  if ((event.xconfigure.width == (int) windows->image.width) &&
15503  (event.xconfigure.height == (int) windows->image.height))
15504  break;
15505  windows->image.width=(unsigned int) event.xconfigure.width;
15506  windows->image.height=(unsigned int) event.xconfigure.height;
15507  windows->image.x=0;
15508  windows->image.y=0;
15509  if (display_image->montage != (char *) NULL)
15510  {
15511  windows->image.x=vid_info.x;
15512  windows->image.y=vid_info.y;
15513  }
15514  if ((windows->image.mapped != MagickFalse) &&
15515  (windows->image.stasis != MagickFalse))
15516  {
15517  /*
15518  Update image window configuration.
15519  */
15520  windows->image.window_changes.width=event.xconfigure.width;
15521  windows->image.window_changes.height=event.xconfigure.height;
15522  (void) XConfigureImage(display,resource_info,windows,
15523  display_image);
15524  }
15525  /*
15526  Update pan window configuration.
15527  */
15528  if ((event.xconfigure.width < windows->image.ximage->width) ||
15529  (event.xconfigure.height < windows->image.ximage->height))
15530  {
15531  (void) XMapRaised(display,windows->pan.id);
15532  XDrawPanRectangle(display,windows);
15533  }
15534  else
15535  if (windows->pan.mapped != MagickFalse)
15536  (void) XWithdrawWindow(display,windows->pan.id,
15537  windows->pan.screen);
15538  break;
15539  }
15540  if (event.xconfigure.window == windows->magnify.id)
15541  {
15542  unsigned int
15543  magnify;
15544 
15545  /*
15546  Magnify window has a new configuration.
15547  */
15548  windows->magnify.width=(unsigned int) event.xconfigure.width;
15549  windows->magnify.height=(unsigned int) event.xconfigure.height;
15550  if (windows->magnify.mapped == MagickFalse)
15551  break;
15552  magnify=1;
15553  while ((int) magnify <= event.xconfigure.width)
15554  magnify<<=1;
15555  while ((int) magnify <= event.xconfigure.height)
15556  magnify<<=1;
15557  magnify>>=1;
15558  if (((int) magnify != event.xconfigure.width) ||
15559  ((int) magnify != event.xconfigure.height))
15560  {
15561  window_changes.width=(int) magnify;
15562  window_changes.height=(int) magnify;
15563  (void) XReconfigureWMWindow(display,windows->magnify.id,
15564  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15565  &window_changes);
15566  break;
15567  }
15568  if ((windows->magnify.mapped != MagickFalse) &&
15569  (windows->magnify.stasis != MagickFalse))
15570  {
15571  status=XMakeImage(display,resource_info,&windows->magnify,
15572  display_image,windows->magnify.width,windows->magnify.height);
15573  XMakeMagnifyImage(display,windows);
15574  }
15575  break;
15576  }
15577  if ((windows->magnify.mapped != MagickFalse) &&
15578  (event.xconfigure.window == windows->pan.id))
15579  {
15580  /*
15581  Pan icon window has a new configuration.
15582  */
15583  if (event.xconfigure.send_event != 0)
15584  {
15585  windows->pan.x=event.xconfigure.x;
15586  windows->pan.y=event.xconfigure.y;
15587  }
15588  windows->pan.width=(unsigned int) event.xconfigure.width;
15589  windows->pan.height=(unsigned int) event.xconfigure.height;
15590  break;
15591  }
15592  if (event.xconfigure.window == windows->icon.id)
15593  {
15594  /*
15595  Icon window has a new configuration.
15596  */
15597  windows->icon.width=(unsigned int) event.xconfigure.width;
15598  windows->icon.height=(unsigned int) event.xconfigure.height;
15599  break;
15600  }
15601  break;
15602  }
15603  case DestroyNotify:
15604  {
15605  /*
15606  Group leader has exited.
15607  */
15608  if (resource_info->debug != MagickFalse)
15609  (void) LogMagickEvent(X11Event,GetMagickModule(),
15610  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15611  if (event.xdestroywindow.window == windows->group_leader.id)
15612  {
15613  *state|=ExitState;
15614  break;
15615  }
15616  break;
15617  }
15618  case EnterNotify:
15619  {
15620  /*
15621  Selectively install colormap.
15622  */
15623  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15624  if (event.xcrossing.mode != NotifyUngrab)
15625  XInstallColormap(display,map_info->colormap);
15626  break;
15627  }
15628  case Expose:
15629  {
15630  if (resource_info->debug != MagickFalse)
15631  (void) LogMagickEvent(X11Event,GetMagickModule(),
15632  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15633  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15634  event.xexpose.y);
15635  /*
15636  Refresh windows that are now exposed.
15637  */
15638  if ((event.xexpose.window == windows->image.id) &&
15639  (windows->image.mapped != MagickFalse))
15640  {
15641  XRefreshWindow(display,&windows->image,&event);
15642  delay=display_image->delay/MagickMax(
15643  display_image->ticks_per_second,1L);
15644  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15645  break;
15646  }
15647  if ((event.xexpose.window == windows->magnify.id) &&
15648  (windows->magnify.mapped != MagickFalse))
15649  {
15650  XMakeMagnifyImage(display,windows);
15651  break;
15652  }
15653  if (event.xexpose.window == windows->pan.id)
15654  {
15655  XDrawPanRectangle(display,windows);
15656  break;
15657  }
15658  if (event.xexpose.window == windows->icon.id)
15659  {
15660  XRefreshWindow(display,&windows->icon,&event);
15661  break;
15662  }
15663  break;
15664  }
15665  case KeyPress:
15666  {
15667  int
15668  length;
15669 
15670  /*
15671  Respond to a user key press.
15672  */
15673  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15674  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15675  *(command+length)='\0';
15676  if (resource_info->debug != MagickFalse)
15677  (void) LogMagickEvent(X11Event,GetMagickModule(),
15678  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15679  key_symbol,command);
15680  if (event.xkey.window == windows->image.id)
15681  {
15682  display_command=XImageWindowCommand(display,resource_info,windows,
15683  event.xkey.state,key_symbol,&display_image);
15684  if (display_command != NullCommand)
15685  nexus=XMagickCommand(display,resource_info,windows,display_command,
15686  &display_image);
15687  }
15688  if (event.xkey.window == windows->magnify.id)
15689  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15690  if (event.xkey.window == windows->pan.id)
15691  {
15692  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15693  (void) XWithdrawWindow(display,windows->pan.id,
15694  windows->pan.screen);
15695  else
15696  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15697  XTextViewHelp(display,resource_info,windows,MagickFalse,
15698  "Help Viewer - Image Pan",ImagePanHelp);
15699  else
15700  XTranslateImage(display,windows,*image,key_symbol);
15701  }
15702  delay=display_image->delay/MagickMax(
15703  display_image->ticks_per_second,1L);
15704  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15705  break;
15706  }
15707  case KeyRelease:
15708  {
15709  /*
15710  Respond to a user key release.
15711  */
15712  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15713  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15714  if (resource_info->debug != MagickFalse)
15715  (void) LogMagickEvent(X11Event,GetMagickModule(),
15716  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15717  break;
15718  }
15719  case LeaveNotify:
15720  {
15721  /*
15722  Selectively uninstall colormap.
15723  */
15724  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15725  if (event.xcrossing.mode != NotifyUngrab)
15726  XUninstallColormap(display,map_info->colormap);
15727  break;
15728  }
15729  case MapNotify:
15730  {
15731  if (resource_info->debug != MagickFalse)
15732  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15733  event.xmap.window);
15734  if (event.xmap.window == windows->backdrop.id)
15735  {
15736  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15737  CurrentTime);
15738  windows->backdrop.mapped=MagickTrue;
15739  break;
15740  }
15741  if (event.xmap.window == windows->image.id)
15742  {
15743  if (windows->backdrop.id != (Window) NULL)
15744  (void) XInstallColormap(display,map_info->colormap);
15745  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15746  {
15747  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15748  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15749  }
15750  if (((int) windows->image.width < windows->image.ximage->width) ||
15751  ((int) windows->image.height < windows->image.ximage->height))
15752  (void) XMapRaised(display,windows->pan.id);
15753  windows->image.mapped=MagickTrue;
15754  break;
15755  }
15756  if (event.xmap.window == windows->magnify.id)
15757  {
15758  XMakeMagnifyImage(display,windows);
15759  windows->magnify.mapped=MagickTrue;
15760  (void) XWithdrawWindow(display,windows->info.id,
15761  windows->info.screen);
15762  break;
15763  }
15764  if (event.xmap.window == windows->pan.id)
15765  {
15766  XMakePanImage(display,resource_info,windows,display_image);
15767  windows->pan.mapped=MagickTrue;
15768  break;
15769  }
15770  if (event.xmap.window == windows->info.id)
15771  {
15772  windows->info.mapped=MagickTrue;
15773  break;
15774  }
15775  if (event.xmap.window == windows->icon.id)
15776  {
15777  MagickBooleanType
15778  taint;
15779 
15780  /*
15781  Create an icon image.
15782  */
15783  taint=display_image->taint;
15784  XMakeStandardColormap(display,icon_visual,icon_resources,
15785  display_image,icon_map,icon_pixel);
15786  (void) XMakeImage(display,icon_resources,&windows->icon,
15787  display_image,windows->icon.width,windows->icon.height);
15788  display_image->taint=taint;
15789  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15790  windows->icon.pixmap);
15791  (void) XClearWindow(display,windows->icon.id);
15792  (void) XWithdrawWindow(display,windows->info.id,
15793  windows->info.screen);
15794  windows->icon.mapped=MagickTrue;
15795  break;
15796  }
15797  if (event.xmap.window == windows->command.id)
15798  {
15799  windows->command.mapped=MagickTrue;
15800  break;
15801  }
15802  if (event.xmap.window == windows->popup.id)
15803  {
15804  windows->popup.mapped=MagickTrue;
15805  break;
15806  }
15807  if (event.xmap.window == windows->widget.id)
15808  {
15809  windows->widget.mapped=MagickTrue;
15810  break;
15811  }
15812  break;
15813  }
15814  case MappingNotify:
15815  {
15816  (void) XRefreshKeyboardMapping(&event.xmapping);
15817  break;
15818  }
15819  case NoExpose:
15820  break;
15821  case PropertyNotify:
15822  {
15823  Atom
15824  type;
15825 
15826  int
15827  format,
15828  status;
15829 
15830  unsigned char
15831  *data;
15832 
15833  unsigned long
15834  after,
15835  length;
15836 
15837  if (resource_info->debug != MagickFalse)
15838  (void) LogMagickEvent(X11Event,GetMagickModule(),
15839  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15840  event.xproperty.atom,event.xproperty.state);
15841  if (event.xproperty.atom != windows->im_remote_command)
15842  break;
15843  /*
15844  Display image named by the remote command protocol.
15845  */
15846  status=XGetWindowProperty(display,event.xproperty.window,
15847  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15848  AnyPropertyType,&type,&format,&length,&after,&data);
15849  if ((status != Success) || (length == 0))
15850  break;
15851  if (LocaleCompare((char *) data,"-quit") == 0)
15852  {
15853  XClientMessage(display,windows->image.id,windows->im_protocols,
15854  windows->im_exit,CurrentTime);
15855  (void) XFree((void *) data);
15856  break;
15857  }
15858  (void) CopyMagickString(resource_info->image_info->filename,
15859  (char *) data,MaxTextExtent);
15860  (void) XFree((void *) data);
15861  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15862  CatchException(&display_image->exception);
15863  if (nexus != (Image *) NULL)
15864  *state|=NextImageState | ExitState;
15865  break;
15866  }
15867  case ReparentNotify:
15868  {
15869  if (resource_info->debug != MagickFalse)
15870  (void) LogMagickEvent(X11Event,GetMagickModule(),
15871  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15872  event.xreparent.window);
15873  break;
15874  }
15875  case UnmapNotify:
15876  {
15877  if (resource_info->debug != MagickFalse)
15878  (void) LogMagickEvent(X11Event,GetMagickModule(),
15879  "Unmap Notify: 0x%lx",event.xunmap.window);
15880  if (event.xunmap.window == windows->backdrop.id)
15881  {
15882  windows->backdrop.mapped=MagickFalse;
15883  break;
15884  }
15885  if (event.xunmap.window == windows->image.id)
15886  {
15887  windows->image.mapped=MagickFalse;
15888  break;
15889  }
15890  if (event.xunmap.window == windows->magnify.id)
15891  {
15892  windows->magnify.mapped=MagickFalse;
15893  break;
15894  }
15895  if (event.xunmap.window == windows->pan.id)
15896  {
15897  windows->pan.mapped=MagickFalse;
15898  break;
15899  }
15900  if (event.xunmap.window == windows->info.id)
15901  {
15902  windows->info.mapped=MagickFalse;
15903  break;
15904  }
15905  if (event.xunmap.window == windows->icon.id)
15906  {
15907  if (map_info->colormap == icon_map->colormap)
15908  XConfigureImageColormap(display,resource_info,windows,
15909  display_image);
15910  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15911  icon_pixel);
15912  windows->icon.mapped=MagickFalse;
15913  break;
15914  }
15915  if (event.xunmap.window == windows->command.id)
15916  {
15917  windows->command.mapped=MagickFalse;
15918  break;
15919  }
15920  if (event.xunmap.window == windows->popup.id)
15921  {
15922  if (windows->backdrop.id != (Window) NULL)
15923  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15924  CurrentTime);
15925  windows->popup.mapped=MagickFalse;
15926  break;
15927  }
15928  if (event.xunmap.window == windows->widget.id)
15929  {
15930  if (windows->backdrop.id != (Window) NULL)
15931  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15932  CurrentTime);
15933  windows->widget.mapped=MagickFalse;
15934  break;
15935  }
15936  break;
15937  }
15938  default:
15939  {
15940  if (resource_info->debug != MagickFalse)
15941  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15942  event.type);
15943  break;
15944  }
15945  }
15946  } while (!(*state & ExitState));
15947  if ((*state & ExitState) == 0)
15948  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15949  &display_image);
15950  else
15951  if (resource_info->confirm_edit != MagickFalse)
15952  {
15953  /*
15954  Query user if image has changed.
15955  */
15956  if ((resource_info->immutable == MagickFalse) &&
15957  (display_image->taint != MagickFalse))
15958  {
15959  int
15960  status;
15961 
15962  status=XConfirmWidget(display,windows,"Your image changed.",
15963  "Do you want to save it");
15964  if (status == 0)
15965  *state&=(~ExitState);
15966  else
15967  if (status > 0)
15968  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15969  &display_image);
15970  }
15971  }
15972  if ((windows->visual_info->klass == GrayScale) ||
15973  (windows->visual_info->klass == PseudoColor) ||
15974  (windows->visual_info->klass == DirectColor))
15975  {
15976  /*
15977  Withdraw pan and Magnify window.
15978  */
15979  if (windows->info.mapped != MagickFalse)
15980  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15981  if (windows->magnify.mapped != MagickFalse)
15982  (void) XWithdrawWindow(display,windows->magnify.id,
15983  windows->magnify.screen);
15984  if (windows->command.mapped != MagickFalse)
15985  (void) XWithdrawWindow(display,windows->command.id,
15986  windows->command.screen);
15987  }
15988  if (windows->pan.mapped != MagickFalse)
15989  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15990  if (resource_info->backdrop == MagickFalse)
15991  if (windows->backdrop.mapped)
15992  {
15993  (void) XWithdrawWindow(display,windows->backdrop.id,
15994  windows->backdrop.screen);
15995  (void) XDestroyWindow(display,windows->backdrop.id);
15996  windows->backdrop.id=(Window) NULL;
15997  (void) XWithdrawWindow(display,windows->image.id,
15998  windows->image.screen);
15999  (void) XDestroyWindow(display,windows->image.id);
16000  windows->image.id=(Window) NULL;
16001  }
16002  XSetCursorState(display,windows,MagickTrue);
16003  XCheckRefreshWindows(display,windows);
16004  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16005  *state&=(~ExitState);
16006  if (*state & ExitState)
16007  {
16008  /*
16009  Free Standard Colormap.
16010  */
16011  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16012  if (resource_info->map_type == (char *) NULL)
16013  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16014  /*
16015  Free X resources.
16016  */
16017  if (resource_info->copy_image != (Image *) NULL)
16018  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16019  DestroyXResources();
16020  }
16021  (void) XSync(display,MagickFalse);
16022  /*
16023  Restore our progress monitor and warning handlers.
16024  */
16025  (void) SetErrorHandler(warning_handler);
16026  (void) SetWarningHandler(warning_handler);
16027  /*
16028  Change to home directory.
16029  */
16030  directory=getcwd(working_directory,MaxTextExtent);
16031  (void) directory;
16032  {
16033  int
16034  status;
16035 
16036  if (*resource_info->home_directory == '\0')
16037  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16038  status=chdir(resource_info->home_directory);
16039  if (status == -1)
16040  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16041  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16042  }
16043  *image=display_image;
16044  return(nexus);
16045 }
16046 #else
16047 
16048 /*
16049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16050 % %
16051 % %
16052 % %
16053 + D i s p l a y I m a g e s %
16054 % %
16055 % %
16056 % %
16057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16058 %
16059 % DisplayImages() displays an image sequence to any X window screen. It
16060 % returns a value other than 0 if successful. Check the exception member
16061 % of image to determine the reason for any failure.
16062 %
16063 % The format of the DisplayImages method is:
16064 %
16065 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16066 % Image *images)
16067 %
16068 % A description of each parameter follows:
16069 %
16070 % o image_info: the image info.
16071 %
16072 % o image: the image.
16073 %
16074 */
16075 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16076  Image *image)
16077 {
16078  assert(image_info != (const ImageInfo *) NULL);
16079  assert(image_info->signature == MagickCoreSignature);
16080  assert(image != (Image *) NULL);
16081  assert(image->signature == MagickCoreSignature);
16082  if (IsEventLogging() != MagickFalse)
16083  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16084  (void) image_info;
16085  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16086  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16087  image->filename);
16088  return(MagickFalse);
16089 }
16090 
16091 /*
16092 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16093 % %
16094 % %
16095 % %
16096 + R e m o t e D i s p l a y C o m m a n d %
16097 % %
16098 % %
16099 % %
16100 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16101 %
16102 % RemoteDisplayCommand() encourages a remote display program to display the
16103 % specified image filename.
16104 %
16105 % The format of the RemoteDisplayCommand method is:
16106 %
16107 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16108 % const char *window,const char *filename,ExceptionInfo *exception)
16109 %
16110 % A description of each parameter follows:
16111 %
16112 % o image_info: the image info.
16113 %
16114 % o window: Specifies the name or id of an X window.
16115 %
16116 % o filename: the name of the image filename to display.
16117 %
16118 % o exception: return any errors or warnings in this structure.
16119 %
16120 */
16121 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16122  const char *window,const char *filename,ExceptionInfo *exception)
16123 {
16124  assert(image_info != (const ImageInfo *) NULL);
16125  assert(image_info->signature == MagickCoreSignature);
16126  assert(filename != (char *) NULL);
16127  if (IsEventLogging() != MagickFalse)
16128  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16129  (void) window;
16130  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16131  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16132  return(MagickFalse);
16133 }
16134 #endif
Definition: image.h:133