MagickCore  6.9.13-46
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  ((size_t) (q-p) < sizeof(filename)))
13062  q++;
13063  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13064  /*
13065  Perform command for the selected tile.
13066  */
13067  XSetCursorState(display,windows,MagickTrue);
13068  XCheckRefreshWindows(display,windows);
13069  tile_image=NewImageList();
13070  switch (TileCommands[id])
13071  {
13072  case TileLoadCommand:
13073  {
13074  /*
13075  Load tile image.
13076  */
13077  XCheckRefreshWindows(display,windows);
13078  (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13079  MaxTextExtent);
13080  (void) CopyMagickString(resource_info->image_info->filename,filename,
13081  MaxTextExtent);
13082  tile_image=ReadImage(resource_info->image_info,&image->exception);
13083  CatchException(&image->exception);
13084  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13085  break;
13086  }
13087  case TileNextCommand:
13088  {
13089  /*
13090  Display next image.
13091  */
13092  XClientMessage(display,windows->image.id,windows->im_protocols,
13093  windows->im_next_image,CurrentTime);
13094  break;
13095  }
13096  case TileFormerCommand:
13097  {
13098  /*
13099  Display former image.
13100  */
13101  XClientMessage(display,windows->image.id,windows->im_protocols,
13102  windows->im_former_image,CurrentTime);
13103  break;
13104  }
13105  case TileDeleteCommand:
13106  {
13107  /*
13108  Delete tile image.
13109  */
13110  if (IsPathAccessible(filename) == MagickFalse)
13111  {
13112  XNoticeWidget(display,windows,"Image file does not exist:",filename);
13113  break;
13114  }
13115  status=XConfirmWidget(display,windows,"Really delete tile",filename);
13116  if (status <= 0)
13117  break;
13118  status=ShredFile(filename);
13119  status|=remove_utf8(filename);
13120  if (status != MagickFalse)
13121  {
13122  XNoticeWidget(display,windows,"Unable to delete image file:",
13123  filename);
13124  break;
13125  }
13126  magick_fallthrough;
13127  }
13128  case TileUpdateCommand:
13129  {
13131  *exception;
13132 
13133  int
13134  x_offset,
13135  y_offset;
13136 
13137  PixelPacket
13138  pixel;
13139 
13140  int
13141  j;
13142 
13143  PixelPacket
13144  *s;
13145 
13146  /*
13147  Ensure all the images exist.
13148  */
13149  tile=0;
13150  for (p=image->directory; *p != '\0'; p++)
13151  {
13152  CacheView
13153  *image_view;
13154 
13155  q=p;
13156  while ((*q != '\xff') && (*q != '\0') &&
13157  ((size_t) (q-p) < sizeof(filename)))
13158  q++;
13159  (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13160  p=q;
13161  if (IsPathAccessible(filename) != MagickFalse)
13162  {
13163  tile++;
13164  continue;
13165  }
13166  /*
13167  Overwrite tile with background color.
13168  */
13169  x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13170  y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13171  exception=(&image->exception);
13172  image_view=AcquireAuthenticCacheView(image,exception);
13173  (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13174  for (i=0; i < (int) height; i++)
13175  {
13176  s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13177  y_offset+i,width,1,exception);
13178  if (s == (PixelPacket *) NULL)
13179  break;
13180  for (j=0; j < (int) width; j++)
13181  *s++=pixel;
13182  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13183  break;
13184  }
13185  image_view=DestroyCacheView(image_view);
13186  tile++;
13187  }
13188  windows->image.window_changes.width=(int) image->columns;
13189  windows->image.window_changes.height=(int) image->rows;
13190  XConfigureImageColormap(display,resource_info,windows,image);
13191  (void) XConfigureImage(display,resource_info,windows,image);
13192  break;
13193  }
13194  default:
13195  break;
13196  }
13197  XSetCursorState(display,windows,MagickFalse);
13198  return(tile_image);
13199 }
13200 
13201 /*
13202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13203 % %
13204 % %
13205 % %
13206 + X T r a n s l a t e I m a g e %
13207 % %
13208 % %
13209 % %
13210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13211 %
13212 % XTranslateImage() translates the image within an Image window by one pixel
13213 % as specified by the key symbol. If the image has a `montage string the
13214 % translation is respect to the width and height contained within the string.
13215 %
13216 % The format of the XTranslateImage method is:
13217 %
13218 % void XTranslateImage(Display *display,XWindows *windows,
13219 % Image *image,const KeySym key_symbol)
13220 %
13221 % A description of each parameter follows:
13222 %
13223 % o display: Specifies a connection to an X server; returned from
13224 % XOpenDisplay.
13225 %
13226 % o windows: Specifies a pointer to a XWindows structure.
13227 %
13228 % o image: the image.
13229 %
13230 % o key_symbol: Specifies a KeySym which indicates which side of the image
13231 % to trim.
13232 %
13233 */
13234 static void XTranslateImage(Display *display,XWindows *windows,
13235  Image *image,const KeySym key_symbol)
13236 {
13237  char
13238  text[MaxTextExtent];
13239 
13240  int
13241  x,
13242  y;
13243 
13244  unsigned int
13245  x_offset,
13246  y_offset;
13247 
13248  /*
13249  User specified a pan position offset.
13250  */
13251  x_offset=windows->image.width;
13252  y_offset=windows->image.height;
13253  if (image->montage != (char *) NULL)
13254  (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13255  switch ((int) key_symbol)
13256  {
13257  case XK_Home:
13258  case XK_KP_Home:
13259  {
13260  windows->image.x=(int) windows->image.width/2;
13261  windows->image.y=(int) windows->image.height/2;
13262  break;
13263  }
13264  case XK_Left:
13265  case XK_KP_Left:
13266  {
13267  windows->image.x-=x_offset;
13268  break;
13269  }
13270  case XK_Next:
13271  case XK_Up:
13272  case XK_KP_Up:
13273  {
13274  windows->image.y-=y_offset;
13275  break;
13276  }
13277  case XK_Right:
13278  case XK_KP_Right:
13279  {
13280  windows->image.x+=x_offset;
13281  break;
13282  }
13283  case XK_Prior:
13284  case XK_Down:
13285  case XK_KP_Down:
13286  {
13287  windows->image.y+=y_offset;
13288  break;
13289  }
13290  default:
13291  return;
13292  }
13293  /*
13294  Check boundary conditions.
13295  */
13296  if (windows->image.x < 0)
13297  windows->image.x=0;
13298  else
13299  if ((int) (windows->image.x+windows->image.width) >
13300  windows->image.ximage->width)
13301  windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13302  if (windows->image.y < 0)
13303  windows->image.y=0;
13304  else
13305  if ((int) (windows->image.y+windows->image.height) >
13306  windows->image.ximage->height)
13307  windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13308  /*
13309  Refresh Image window.
13310  */
13311  (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13312  windows->image.width,windows->image.height,windows->image.x,
13313  windows->image.y);
13314  XInfoWidget(display,windows,text);
13315  XCheckRefreshWindows(display,windows);
13316  XDrawPanRectangle(display,windows);
13317  XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13318  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13319 }
13320 
13321 /*
13322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13323 % %
13324 % %
13325 % %
13326 + X T r i m I m a g e %
13327 % %
13328 % %
13329 % %
13330 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13331 %
13332 % XTrimImage() trims the edges from the Image window.
13333 %
13334 % The format of the XTrimImage method is:
13335 %
13336 % MagickBooleanType XTrimImage(Display *display,
13337 % XResourceInfo *resource_info,XWindows *windows,Image *image)
13338 %
13339 % A description of each parameter follows:
13340 %
13341 % o display: Specifies a connection to an X server; returned from
13342 % XOpenDisplay.
13343 %
13344 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13345 %
13346 % o windows: Specifies a pointer to a XWindows structure.
13347 %
13348 % o image: the image.
13349 %
13350 */
13351 static MagickBooleanType XTrimImage(Display *display,
13352  XResourceInfo *resource_info,XWindows *windows,Image *image)
13353 {
13355  trim_info;
13356 
13357  int
13358  x,
13359  y;
13360 
13361  size_t
13362  background,
13363  pixel;
13364 
13365  /*
13366  Trim edges from image.
13367  */
13368  XSetCursorState(display,windows,MagickTrue);
13369  XCheckRefreshWindows(display,windows);
13370  /*
13371  Crop the left edge.
13372  */
13373  background=XGetPixel(windows->image.ximage,0,0);
13374  trim_info.width=(size_t) windows->image.ximage->width;
13375  for (x=0; x < windows->image.ximage->width; x++)
13376  {
13377  for (y=0; y < windows->image.ximage->height; y++)
13378  {
13379  pixel=XGetPixel(windows->image.ximage,x,y);
13380  if (pixel != background)
13381  break;
13382  }
13383  if (y < windows->image.ximage->height)
13384  break;
13385  }
13386  trim_info.x=(ssize_t) x;
13387  if (trim_info.x == (ssize_t) windows->image.ximage->width)
13388  {
13389  XSetCursorState(display,windows,MagickFalse);
13390  return(MagickFalse);
13391  }
13392  /*
13393  Crop the right edge.
13394  */
13395  background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13396  for (x=windows->image.ximage->width-1; x != 0; x--)
13397  {
13398  for (y=0; y < windows->image.ximage->height; y++)
13399  {
13400  pixel=XGetPixel(windows->image.ximage,x,y);
13401  if (pixel != background)
13402  break;
13403  }
13404  if (y < windows->image.ximage->height)
13405  break;
13406  }
13407  trim_info.width=(size_t) (x-trim_info.x+1);
13408  /*
13409  Crop the top edge.
13410  */
13411  background=XGetPixel(windows->image.ximage,0,0);
13412  trim_info.height=(size_t) windows->image.ximage->height;
13413  for (y=0; y < windows->image.ximage->height; y++)
13414  {
13415  for (x=0; x < windows->image.ximage->width; x++)
13416  {
13417  pixel=XGetPixel(windows->image.ximage,x,y);
13418  if (pixel != background)
13419  break;
13420  }
13421  if (x < windows->image.ximage->width)
13422  break;
13423  }
13424  trim_info.y=(ssize_t) y;
13425  /*
13426  Crop the bottom edge.
13427  */
13428  background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13429  for (y=windows->image.ximage->height-1; y != 0; y--)
13430  {
13431  for (x=0; x < windows->image.ximage->width; x++)
13432  {
13433  pixel=XGetPixel(windows->image.ximage,x,y);
13434  if (pixel != background)
13435  break;
13436  }
13437  if (x < windows->image.ximage->width)
13438  break;
13439  }
13440  trim_info.height=(size_t) y-trim_info.y+1;
13441  if (((unsigned int) trim_info.width != windows->image.width) ||
13442  ((unsigned int) trim_info.height != windows->image.height))
13443  {
13444  /*
13445  Reconfigure Image window as defined by the trimming rectangle.
13446  */
13447  XSetCropGeometry(display,windows,&trim_info,image);
13448  windows->image.window_changes.width=(int) trim_info.width;
13449  windows->image.window_changes.height=(int) trim_info.height;
13450  (void) XConfigureImage(display,resource_info,windows,image);
13451  }
13452  XSetCursorState(display,windows,MagickFalse);
13453  return(MagickTrue);
13454 }
13455 
13456 /*
13457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13458 % %
13459 % %
13460 % %
13461 + X V i s u a l D i r e c t o r y I m a g e %
13462 % %
13463 % %
13464 % %
13465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13466 %
13467 % XVisualDirectoryImage() creates a Visual Image Directory.
13468 %
13469 % The format of the XVisualDirectoryImage method is:
13470 %
13471 % Image *XVisualDirectoryImage(Display *display,
13472 % XResourceInfo *resource_info,XWindows *windows)
13473 %
13474 % A description of each parameter follows:
13475 %
13476 % o nexus: Method XVisualDirectoryImage returns a visual image
13477 % directory if it can be created successfully. Otherwise a null image
13478 % is returned.
13479 %
13480 % o display: Specifies a connection to an X server; returned from
13481 % XOpenDisplay.
13482 %
13483 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13484 %
13485 % o windows: Specifies a pointer to a XWindows structure.
13486 %
13487 */
13488 static Image *XVisualDirectoryImage(Display *display,
13489  XResourceInfo *resource_info,XWindows *windows)
13490 {
13491 #define TileImageTag "Scale/Image"
13492 #define XClientName "montage"
13493 
13494  char
13495  **filelist;
13496 
13498  *exception;
13499 
13500  Image
13501  *images,
13502  *montage_image,
13503  *next_image,
13504  *thumbnail_image;
13505 
13506  ImageInfo
13507  *read_info;
13508 
13509  int
13510  number_files;
13511 
13512  MagickBooleanType
13513  backdrop;
13514 
13515  MagickStatusType
13516  status;
13517 
13518  MontageInfo
13519  *montage_info;
13520 
13522  geometry;
13523 
13524  int
13525  i;
13526 
13527  static char
13528  filename[MaxTextExtent] = "\0",
13529  filenames[MaxTextExtent] = "*";
13530 
13531  XResourceInfo
13532  background_resources;
13533 
13534  /*
13535  Request file name from user.
13536  */
13537  XFileBrowserWidget(display,windows,"Directory",filenames);
13538  if (*filenames == '\0')
13539  return((Image *) NULL);
13540  /*
13541  Expand the filenames.
13542  */
13543  filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13544  if (filelist == (char **) NULL)
13545  {
13546  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13547  filenames);
13548  return((Image *) NULL);
13549  }
13550  number_files=1;
13551  filelist[0]=filenames;
13552  status=ExpandFilenames(&number_files,&filelist);
13553  if ((status == MagickFalse) || (number_files == 0))
13554  {
13555  if (number_files == 0)
13556  ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13557  else
13558  ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13559  filenames);
13560  return((Image *) NULL);
13561  }
13562  /*
13563  Set image background resources.
13564  */
13565  background_resources=(*resource_info);
13566  background_resources.window_id=AcquireString("");
13567  (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13568  "0x%lx",windows->image.id);
13569  background_resources.backdrop=MagickTrue;
13570  /*
13571  Read each image and convert them to a tile.
13572  */
13573  backdrop=(windows->visual_info->klass == TrueColor) ||
13574  (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13575  read_info=CloneImageInfo(resource_info->image_info);
13576  (void) SetImageOption(read_info,"jpeg:size","120x120");
13577  (void) CloneString(&read_info->size,DefaultTileGeometry);
13578  (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13579  (void *) NULL);
13580  images=NewImageList();
13581  exception=AcquireExceptionInfo();
13582  XSetCursorState(display,windows,MagickTrue);
13583  XCheckRefreshWindows(display,windows);
13584  for (i=0; i < (int) number_files; i++)
13585  {
13586  (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13587  filelist[i]=DestroyString(filelist[i]);
13588  *read_info->magick='\0';
13589  next_image=ReadImage(read_info,exception);
13590  CatchException(exception);
13591  if (next_image != (Image *) NULL)
13592  {
13593  (void) DeleteImageProperty(next_image,"label");
13594  (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13595  read_info,next_image,DefaultTileLabel));
13596  (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13597  exception);
13598  thumbnail_image=ThumbnailImage(next_image,geometry.width,
13599  geometry.height,exception);
13600  if (thumbnail_image != (Image *) NULL)
13601  {
13602  next_image=DestroyImage(next_image);
13603  next_image=thumbnail_image;
13604  }
13605  if (backdrop)
13606  {
13607  (void) XDisplayBackgroundImage(display,&background_resources,
13608  next_image);
13609  XSetCursorState(display,windows,MagickTrue);
13610  }
13611  AppendImageToList(&images,next_image);
13612  if (images->progress_monitor != (MagickProgressMonitor) NULL)
13613  {
13614  MagickBooleanType
13615  proceed;
13616 
13617  proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13618  (MagickSizeType) number_files);
13619  if (proceed == MagickFalse)
13620  break;
13621  }
13622  }
13623  }
13624  exception=DestroyExceptionInfo(exception);
13625  filelist=(char **) RelinquishMagickMemory(filelist);
13626  if (images == (Image *) NULL)
13627  {
13628  read_info=DestroyImageInfo(read_info);
13629  XSetCursorState(display,windows,MagickFalse);
13630  ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13631  return((Image *) NULL);
13632  }
13633  /*
13634  Create the Visual Image Directory.
13635  */
13636  montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13637  montage_info->pointsize=10;
13638  if (resource_info->font != (char *) NULL)
13639  (void) CloneString(&montage_info->font,resource_info->font);
13640  (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13641  montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13642  images),&images->exception);
13643  images=DestroyImageList(images);
13644  montage_info=DestroyMontageInfo(montage_info);
13645  read_info=DestroyImageInfo(read_info);
13646  XSetCursorState(display,windows,MagickFalse);
13647  if (montage_image == (Image *) NULL)
13648  return(montage_image);
13649  XClientMessage(display,windows->image.id,windows->im_protocols,
13650  windows->im_next_image,CurrentTime);
13651  return(montage_image);
13652 }
13653 
13654 /*
13655 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13656 % %
13657 % %
13658 % %
13659 % X D i s p l a y B a c k g r o u n d I m a g e %
13660 % %
13661 % %
13662 % %
13663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13664 %
13665 % XDisplayBackgroundImage() displays an image in the background of a window.
13666 %
13667 % The format of the XDisplayBackgroundImage method is:
13668 %
13669 % MagickBooleanType XDisplayBackgroundImage(Display *display,
13670 % XResourceInfo *resource_info,Image *image)
13671 %
13672 % A description of each parameter follows:
13673 %
13674 % o display: Specifies a connection to an X server; returned from
13675 % XOpenDisplay.
13676 %
13677 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13678 %
13679 % o image: the image.
13680 %
13681 */
13682 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13683  XResourceInfo *resource_info,Image *image)
13684 {
13685  char
13686  geometry[MaxTextExtent],
13687  visual_type[MaxTextExtent];
13688 
13689  int
13690  height,
13691  status,
13692  width;
13693 
13695  geometry_info;
13696 
13697  static XPixelInfo
13698  pixel;
13699 
13700  static XStandardColormap
13701  *map_info;
13702 
13703  static XVisualInfo
13704  *visual_info = (XVisualInfo *) NULL;
13705 
13706  static XWindowInfo
13707  window_info;
13708 
13709  size_t
13710  delay;
13711 
13712  Window
13713  root_window;
13714 
13715  XGCValues
13716  context_values;
13717 
13718  XResourceInfo
13719  resources;
13720 
13721  XWindowAttributes
13722  window_attributes;
13723 
13724  /*
13725  Determine target window.
13726  */
13727  assert(image != (Image *) NULL);
13728  assert(image->signature == MagickCoreSignature);
13729  if (IsEventLogging() != MagickFalse)
13730  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13731  resources=(*resource_info);
13732  window_info.id=(Window) NULL;
13733  root_window=XRootWindow(display,XDefaultScreen(display));
13734  if (LocaleCompare(resources.window_id,"root") == 0)
13735  window_info.id=root_window;
13736  else
13737  {
13738  if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13739  window_info.id=XWindowByID(display,root_window,
13740  (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13741  if (window_info.id == (Window) NULL)
13742  window_info.id=XWindowByName(display,root_window,resources.window_id);
13743  }
13744  if (window_info.id == (Window) NULL)
13745  {
13746  ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13747  resources.window_id);
13748  }
13749  /*
13750  Determine window visual id.
13751  */
13752  window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13753  window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13754  (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13755  status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13756  if (status != 0)
13757  (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13758  XVisualIDFromVisual(window_attributes.visual));
13759  if (visual_info == (XVisualInfo *) NULL)
13760  {
13761  /*
13762  Allocate standard colormap.
13763  */
13764  map_info=XAllocStandardColormap();
13765  if (map_info == (XStandardColormap *) NULL)
13766  ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13767  image->filename);
13768  map_info->colormap=(Colormap) NULL;
13769  pixel.pixels=(unsigned long *) NULL;
13770  /*
13771  Initialize visual info.
13772  */
13773  resources.map_type=(char *) NULL;
13774  resources.visual_type=visual_type;
13775  visual_info=XBestVisualInfo(display,map_info,&resources);
13776  if (visual_info == (XVisualInfo *) NULL)
13777  ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13778  resources.visual_type);
13779  /*
13780  Initialize window info.
13781  */
13782  window_info.ximage=(XImage *) NULL;
13783  window_info.matte_image=(XImage *) NULL;
13784  window_info.pixmap=(Pixmap) NULL;
13785  window_info.matte_pixmap=(Pixmap) NULL;
13786  }
13787  /*
13788  Free previous root colors.
13789  */
13790  if (window_info.id == root_window)
13791  (void) XDestroyWindowColors(display,root_window);
13792  /*
13793  Initialize Standard Colormap.
13794  */
13795  resources.colormap=SharedColormap;
13796  XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13797  /*
13798  Graphic context superclass.
13799  */
13800  context_values.background=pixel.foreground_color.pixel;
13801  context_values.foreground=pixel.background_color.pixel;
13802  pixel.annotate_context=XCreateGC(display,window_info.id,
13803  (size_t) (GCBackground | GCForeground),&context_values);
13804  if (pixel.annotate_context == (GC) NULL)
13805  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13806  image->filename);
13807  /*
13808  Initialize Image window attributes.
13809  */
13810  window_info.name=AcquireString("\0");
13811  window_info.icon_name=AcquireString("\0");
13812  XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13813  &resources,&window_info);
13814  /*
13815  Create the X image.
13816  */
13817  window_info.width=(unsigned int) image->columns;
13818  window_info.height=(unsigned int) image->rows;
13819  if ((image->columns != window_info.width) ||
13820  (image->rows != window_info.height))
13821  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13822  image->filename);
13823  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13824  window_attributes.width,window_attributes.height);
13825  geometry_info.width=window_info.width;
13826  geometry_info.height=window_info.height;
13827  geometry_info.x=(ssize_t) window_info.x;
13828  geometry_info.y=(ssize_t) window_info.y;
13829  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13830  &geometry_info.width,&geometry_info.height);
13831  window_info.width=(unsigned int) geometry_info.width;
13832  window_info.height=(unsigned int) geometry_info.height;
13833  window_info.x=(int) geometry_info.x;
13834  window_info.y=(int) geometry_info.y;
13835  status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13836  window_info.height);
13837  if (status == MagickFalse)
13838  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13839  image->filename);
13840  window_info.x=0;
13841  window_info.y=0;
13842  if (resource_info->debug != MagickFalse)
13843  {
13844  (void) LogMagickEvent(X11Event,GetMagickModule(),
13845  "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13846  (double) image->columns,(double) image->rows);
13847  if (image->colors != 0)
13848  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13849  image->colors);
13850  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13851  }
13852  /*
13853  Adjust image dimensions as specified by backdrop or geometry options.
13854  */
13855  width=(int) window_info.width;
13856  height=(int) window_info.height;
13857  if (resources.backdrop != MagickFalse)
13858  {
13859  /*
13860  Center image on window.
13861  */
13862  window_info.x=(window_attributes.width/2)-(window_info.ximage->width/2);
13863  window_info.y=(window_attributes.height/2)-(window_info.ximage->height/2);
13864  width=window_attributes.width;
13865  height=window_attributes.height;
13866  }
13867  if ((resources.image_geometry != (char *) NULL) &&
13868  (*resources.image_geometry != '\0'))
13869  {
13870  char
13871  default_geometry[MaxTextExtent];
13872 
13873  int
13874  flags,
13875  gravity;
13876 
13877  XSizeHints
13878  *size_hints;
13879 
13880  /*
13881  User specified geometry.
13882  */
13883  size_hints=XAllocSizeHints();
13884  if (size_hints == (XSizeHints *) NULL)
13885  ThrowXWindowFatalException(ResourceLimitFatalError,
13886  "MemoryAllocationFailed",image->filename);
13887  size_hints->flags=0L;
13888  (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13889  width,height);
13890  flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13891  default_geometry,window_info.border_width,size_hints,&window_info.x,
13892  &window_info.y,&width,&height,&gravity);
13893  if (flags & (XValue | YValue))
13894  {
13895  width=window_attributes.width;
13896  height=window_attributes.height;
13897  }
13898  (void) XFree((void *) size_hints);
13899  }
13900  /*
13901  Create the X pixmap.
13902  */
13903  window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13904  (unsigned int) height,window_info.depth);
13905  if (window_info.pixmap == (Pixmap) NULL)
13906  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13907  image->filename);
13908  /*
13909  Display pixmap on the window.
13910  */
13911  if (((unsigned int) width > window_info.width) ||
13912  ((unsigned int) height > window_info.height))
13913  (void) XFillRectangle(display,window_info.pixmap,
13914  window_info.annotate_context,0,0,(unsigned int) width,
13915  (unsigned int) height);
13916  (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13917  window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13918  window_info.width,(unsigned int) window_info.height);
13919  (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13920  (void) XClearWindow(display,window_info.id);
13921  delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13922  XDelay(display,delay == 0UL ? 10UL : delay);
13923  (void) XSync(display,MagickFalse);
13924  return(window_info.id == root_window ? MagickTrue : MagickFalse);
13925 }
13926 
13927 /*
13928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13929 % %
13930 % %
13931 % %
13932 + X D i s p l a y I m a g e %
13933 % %
13934 % %
13935 % %
13936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13937 %
13938 % XDisplayImage() displays an image via X11. A new image is created and
13939 % returned if the user interactively transforms the displayed image.
13940 %
13941 % The format of the XDisplayImage method is:
13942 %
13943 % Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13944 % char **argv,int argc,Image **image,size_t *state)
13945 %
13946 % A description of each parameter follows:
13947 %
13948 % o nexus: Method XDisplayImage returns an image when the
13949 % user chooses 'Open Image' from the command menu or picks a tile
13950 % from the image directory. Otherwise a null image is returned.
13951 %
13952 % o display: Specifies a connection to an X server; returned from
13953 % XOpenDisplay.
13954 %
13955 % o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13956 %
13957 % o argv: Specifies the application's argument list.
13958 %
13959 % o argc: Specifies the number of arguments.
13960 %
13961 % o image: Specifies an address to an address of an Image structure;
13962 %
13963 */
13964 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13965  char **argv,int argc,Image **image,size_t *state)
13966 {
13967 #define MagnifySize 256 /* must be a power of 2 */
13968 #define MagickMenus 10
13969 #define MagickTitle "Commands"
13970 
13971  const char
13972  *const CommandMenu[] =
13973  {
13974  "File",
13975  "Edit",
13976  "View",
13977  "Transform",
13978  "Enhance",
13979  "Effects",
13980  "F/X",
13981  "Image Edit",
13982  "Miscellany",
13983  "Help",
13984  (char *) NULL
13985  },
13986  *const FileMenu[] =
13987  {
13988  "Open...",
13989  "Next",
13990  "Former",
13991  "Select...",
13992  "Save...",
13993  "Print...",
13994  "Delete...",
13995  "New...",
13996  "Visual Directory...",
13997  "Quit",
13998  (char *) NULL
13999  },
14000  *const EditMenu[] =
14001  {
14002  "Undo",
14003  "Redo",
14004  "Cut",
14005  "Copy",
14006  "Paste",
14007  (char *) NULL
14008  },
14009  *const ViewMenu[] =
14010  {
14011  "Half Size",
14012  "Original Size",
14013  "Double Size",
14014  "Resize...",
14015  "Apply",
14016  "Refresh",
14017  "Restore",
14018  (char *) NULL
14019  },
14020  *const TransformMenu[] =
14021  {
14022  "Crop",
14023  "Chop",
14024  "Flop",
14025  "Flip",
14026  "Rotate Right",
14027  "Rotate Left",
14028  "Rotate...",
14029  "Shear...",
14030  "Roll...",
14031  "Trim Edges",
14032  (char *) NULL
14033  },
14034  *const EnhanceMenu[] =
14035  {
14036  "Hue...",
14037  "Saturation...",
14038  "Brightness...",
14039  "Gamma...",
14040  "Spiff",
14041  "Dull",
14042  "Contrast Stretch...",
14043  "Sigmoidal Contrast...",
14044  "Normalize",
14045  "Equalize",
14046  "Negate",
14047  "Grayscale",
14048  "Map...",
14049  "Quantize...",
14050  (char *) NULL
14051  },
14052  *const EffectsMenu[] =
14053  {
14054  "Despeckle",
14055  "Emboss",
14056  "Reduce Noise",
14057  "Add Noise...",
14058  "Sharpen...",
14059  "Blur...",
14060  "Threshold...",
14061  "Edge Detect...",
14062  "Spread...",
14063  "Shade...",
14064  "Raise...",
14065  "Segment...",
14066  (char *) NULL
14067  },
14068  *const FXMenu[] =
14069  {
14070  "Solarize...",
14071  "Sepia Tone...",
14072  "Swirl...",
14073  "Implode...",
14074  "Vignette...",
14075  "Wave...",
14076  "Oil Paint...",
14077  "Charcoal Draw...",
14078  (char *) NULL
14079  },
14080  *const ImageEditMenu[] =
14081  {
14082  "Annotate...",
14083  "Draw...",
14084  "Color...",
14085  "Matte...",
14086  "Composite...",
14087  "Add Border...",
14088  "Add Frame...",
14089  "Comment...",
14090  "Launch...",
14091  "Region of Interest...",
14092  (char *) NULL
14093  },
14094  *const MiscellanyMenu[] =
14095  {
14096  "Image Info",
14097  "Zoom Image",
14098  "Show Preview...",
14099  "Show Histogram",
14100  "Show Matte",
14101  "Background...",
14102  "Slide Show...",
14103  "Preferences...",
14104  (char *) NULL
14105  },
14106  *const HelpMenu[] =
14107  {
14108  "Overview",
14109  "Browse Documentation",
14110  "About Display",
14111  (char *) NULL
14112  },
14113  *const ShortCutsMenu[] =
14114  {
14115  "Next",
14116  "Former",
14117  "Open...",
14118  "Save...",
14119  "Print...",
14120  "Undo",
14121  "Restore",
14122  "Image Info",
14123  "Quit",
14124  (char *) NULL
14125  },
14126  *const VirtualMenu[] =
14127  {
14128  "Image Info",
14129  "Print",
14130  "Next",
14131  "Quit",
14132  (char *) NULL
14133  };
14134 
14135  const char
14136  *const *Menus[MagickMenus] =
14137  {
14138  FileMenu,
14139  EditMenu,
14140  ViewMenu,
14141  TransformMenu,
14142  EnhanceMenu,
14143  EffectsMenu,
14144  FXMenu,
14145  ImageEditMenu,
14146  MiscellanyMenu,
14147  HelpMenu
14148  };
14149 
14150  static DisplayCommand
14151  CommandMenus[] =
14152  {
14153  NullCommand,
14154  NullCommand,
14155  NullCommand,
14156  NullCommand,
14157  NullCommand,
14158  NullCommand,
14159  NullCommand,
14160  NullCommand,
14161  NullCommand,
14162  NullCommand,
14163  },
14164  FileCommands[] =
14165  {
14166  OpenCommand,
14167  NextCommand,
14168  FormerCommand,
14169  SelectCommand,
14170  SaveCommand,
14171  PrintCommand,
14172  DeleteCommand,
14173  NewCommand,
14174  VisualDirectoryCommand,
14175  QuitCommand
14176  },
14177  EditCommands[] =
14178  {
14179  UndoCommand,
14180  RedoCommand,
14181  CutCommand,
14182  CopyCommand,
14183  PasteCommand
14184  },
14185  ViewCommands[] =
14186  {
14187  HalfSizeCommand,
14188  OriginalSizeCommand,
14189  DoubleSizeCommand,
14190  ResizeCommand,
14191  ApplyCommand,
14192  RefreshCommand,
14193  RestoreCommand
14194  },
14195  TransformCommands[] =
14196  {
14197  CropCommand,
14198  ChopCommand,
14199  FlopCommand,
14200  FlipCommand,
14201  RotateRightCommand,
14202  RotateLeftCommand,
14203  RotateCommand,
14204  ShearCommand,
14205  RollCommand,
14206  TrimCommand
14207  },
14208  EnhanceCommands[] =
14209  {
14210  HueCommand,
14211  SaturationCommand,
14212  BrightnessCommand,
14213  GammaCommand,
14214  SpiffCommand,
14215  DullCommand,
14216  ContrastStretchCommand,
14217  SigmoidalContrastCommand,
14218  NormalizeCommand,
14219  EqualizeCommand,
14220  NegateCommand,
14221  GrayscaleCommand,
14222  MapCommand,
14223  QuantizeCommand
14224  },
14225  EffectsCommands[] =
14226  {
14227  DespeckleCommand,
14228  EmbossCommand,
14229  ReduceNoiseCommand,
14230  AddNoiseCommand,
14231  SharpenCommand,
14232  BlurCommand,
14233  ThresholdCommand,
14234  EdgeDetectCommand,
14235  SpreadCommand,
14236  ShadeCommand,
14237  RaiseCommand,
14238  SegmentCommand
14239  },
14240  FXCommands[] =
14241  {
14242  SolarizeCommand,
14243  SepiaToneCommand,
14244  SwirlCommand,
14245  ImplodeCommand,
14246  VignetteCommand,
14247  WaveCommand,
14248  OilPaintCommand,
14249  CharcoalDrawCommand
14250  },
14251  ImageEditCommands[] =
14252  {
14253  AnnotateCommand,
14254  DrawCommand,
14255  ColorCommand,
14256  MatteCommand,
14257  CompositeCommand,
14258  AddBorderCommand,
14259  AddFrameCommand,
14260  CommentCommand,
14261  LaunchCommand,
14262  RegionOfInterestCommand
14263  },
14264  MiscellanyCommands[] =
14265  {
14266  InfoCommand,
14267  ZoomCommand,
14268  ShowPreviewCommand,
14269  ShowHistogramCommand,
14270  ShowMatteCommand,
14271  BackgroundCommand,
14272  SlideShowCommand,
14273  PreferencesCommand
14274  },
14275  HelpCommands[] =
14276  {
14277  HelpCommand,
14278  BrowseDocumentationCommand,
14279  VersionCommand
14280  },
14281  ShortCutsCommands[] =
14282  {
14283  NextCommand,
14284  FormerCommand,
14285  OpenCommand,
14286  SaveCommand,
14287  PrintCommand,
14288  UndoCommand,
14289  RestoreCommand,
14290  InfoCommand,
14291  QuitCommand
14292  },
14293  VirtualCommands[] =
14294  {
14295  InfoCommand,
14296  PrintCommand,
14297  NextCommand,
14298  QuitCommand
14299  };
14300 
14301  static DisplayCommand
14302  *Commands[MagickMenus] =
14303  {
14304  FileCommands,
14305  EditCommands,
14306  ViewCommands,
14307  TransformCommands,
14308  EnhanceCommands,
14309  EffectsCommands,
14310  FXCommands,
14311  ImageEditCommands,
14312  MiscellanyCommands,
14313  HelpCommands
14314  };
14315 
14316  char
14317  command[MaxTextExtent],
14318  *directory,
14319  geometry[MaxTextExtent],
14320  resource_name[MaxTextExtent];
14321 
14322  DisplayCommand
14323  display_command;
14324 
14325  Image
14326  *display_image,
14327  *nexus;
14328 
14329  int
14330  entry,
14331  id;
14332 
14333  KeySym
14334  key_symbol;
14335 
14336  MagickStatusType
14337  context_mask,
14338  status;
14339 
14341  geometry_info;
14342 
14343  int
14344  i;
14345 
14346  static char
14347  working_directory[MaxTextExtent];
14348 
14349  static XPoint
14350  vid_info;
14351 
14352  static XWindowInfo
14353  *magick_windows[MaxXWindows];
14354 
14355  static unsigned int
14356  number_windows;
14357 
14358  struct stat
14359  attributes;
14360 
14361  time_t
14362  timer,
14363  timestamp,
14364  update_time;
14365 
14366  unsigned int
14367  height,
14368  width;
14369 
14370  size_t
14371  delay;
14372 
14373  WarningHandler
14374  warning_handler;
14375 
14376  Window
14377  root_window;
14378 
14379  XClassHint
14380  *class_hints;
14381 
14382  XEvent
14383  event;
14384 
14385  XFontStruct
14386  *font_info;
14387 
14388  XGCValues
14389  context_values;
14390 
14391  XPixelInfo
14392  *icon_pixel,
14393  *pixel;
14394 
14395  XResourceInfo
14396  *icon_resources;
14397 
14398  XStandardColormap
14399  *icon_map,
14400  *map_info;
14401 
14402  XVisualInfo
14403  *icon_visual,
14404  *visual_info;
14405 
14406  XWindowChanges
14407  window_changes;
14408 
14409  XWindows
14410  *windows;
14411 
14412  XWMHints
14413  *manager_hints;
14414 
14415  assert(image != (Image **) NULL);
14416  assert((*image)->signature == MagickCoreSignature);
14417  if (IsEventLogging() != MagickFalse)
14418  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14419  display_image=(*image);
14420  warning_handler=(WarningHandler) NULL;
14421  windows=XSetWindows((XWindows *) ~0);
14422  if (windows != (XWindows *) NULL)
14423  {
14424  int
14425  status;
14426 
14427  if (*working_directory == '\0')
14428  (void) CopyMagickString(working_directory,".",MaxTextExtent);
14429  status=chdir(working_directory);
14430  if (status == -1)
14431  (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14432  FileOpenError,"UnableToOpenFile","%s",working_directory);
14433  warning_handler=resource_info->display_warnings ?
14434  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14435  warning_handler=resource_info->display_warnings ?
14436  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14437  }
14438  else
14439  {
14440  /*
14441  Allocate windows structure.
14442  */
14443  resource_info->colors=display_image->colors;
14444  windows=XSetWindows(XInitializeWindows(display,resource_info));
14445  if (windows == (XWindows *) NULL)
14446  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14447  (*image)->filename);
14448  /*
14449  Initialize window id's.
14450  */
14451  number_windows=0;
14452  magick_windows[number_windows++]=(&windows->icon);
14453  magick_windows[number_windows++]=(&windows->backdrop);
14454  magick_windows[number_windows++]=(&windows->image);
14455  magick_windows[number_windows++]=(&windows->info);
14456  magick_windows[number_windows++]=(&windows->command);
14457  magick_windows[number_windows++]=(&windows->widget);
14458  magick_windows[number_windows++]=(&windows->popup);
14459  magick_windows[number_windows++]=(&windows->magnify);
14460  magick_windows[number_windows++]=(&windows->pan);
14461  for (i=0; i < (int) number_windows; i++)
14462  magick_windows[i]->id=(Window) NULL;
14463  vid_info.x=0;
14464  vid_info.y=0;
14465  }
14466  /*
14467  Initialize font info.
14468  */
14469  if (windows->font_info != (XFontStruct *) NULL)
14470  (void) XFreeFont(display,windows->font_info);
14471  windows->font_info=XBestFont(display,resource_info,MagickFalse);
14472  if (windows->font_info == (XFontStruct *) NULL)
14473  ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14474  resource_info->font);
14475  /*
14476  Initialize Standard Colormap.
14477  */
14478  map_info=windows->map_info;
14479  icon_map=windows->icon_map;
14480  visual_info=windows->visual_info;
14481  icon_visual=windows->icon_visual;
14482  pixel=windows->pixel_info;
14483  icon_pixel=windows->icon_pixel;
14484  font_info=windows->font_info;
14485  icon_resources=windows->icon_resources;
14486  class_hints=windows->class_hints;
14487  manager_hints=windows->manager_hints;
14488  root_window=XRootWindow(display,visual_info->screen);
14489  nexus=NewImageList();
14490  if (resource_info->debug != MagickFalse)
14491  {
14492  (void) LogMagickEvent(X11Event,GetMagickModule(),
14493  "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14494  (double) display_image->scene,(double) display_image->columns,
14495  (double) display_image->rows);
14496  if (display_image->colors != 0)
14497  (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14498  display_image->colors);
14499  (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14500  display_image->magick);
14501  }
14502  XMakeStandardColormap(display,visual_info,resource_info,display_image,
14503  map_info,pixel);
14504  display_image->taint=MagickFalse;
14505  /*
14506  Initialize graphic context.
14507  */
14508  windows->context.id=(Window) NULL;
14509  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14510  resource_info,&windows->context);
14511  (void) CloneString(&class_hints->res_name,resource_info->client_name);
14512  (void) CloneString(&class_hints->res_class,resource_info->client_name);
14513  class_hints->res_class[0]=(char) LocaleToUppercase((int)
14514  class_hints->res_class[0]);
14515  manager_hints->flags=InputHint | StateHint;
14516  manager_hints->input=MagickFalse;
14517  manager_hints->initial_state=WithdrawnState;
14518  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14519  &windows->context);
14520  if (resource_info->debug != MagickFalse)
14521  (void) LogMagickEvent(X11Event,GetMagickModule(),
14522  "Window id: 0x%lx (context)",windows->context.id);
14523  context_values.background=pixel->background_color.pixel;
14524  context_values.font=font_info->fid;
14525  context_values.foreground=pixel->foreground_color.pixel;
14526  context_values.graphics_exposures=MagickFalse;
14527  context_mask=(MagickStatusType)
14528  (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14529  if (pixel->annotate_context != (GC) NULL)
14530  (void) XFreeGC(display,pixel->annotate_context);
14531  pixel->annotate_context=XCreateGC(display,windows->context.id,
14532  context_mask,&context_values);
14533  if (pixel->annotate_context == (GC) NULL)
14534  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14535  display_image->filename);
14536  context_values.background=pixel->depth_color.pixel;
14537  if (pixel->widget_context != (GC) NULL)
14538  (void) XFreeGC(display,pixel->widget_context);
14539  pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14540  &context_values);
14541  if (pixel->widget_context == (GC) NULL)
14542  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14543  display_image->filename);
14544  context_values.background=pixel->foreground_color.pixel;
14545  context_values.foreground=pixel->background_color.pixel;
14546  context_values.plane_mask=context_values.background ^
14547  context_values.foreground;
14548  if (pixel->highlight_context != (GC) NULL)
14549  (void) XFreeGC(display,pixel->highlight_context);
14550  pixel->highlight_context=XCreateGC(display,windows->context.id,
14551  (size_t) (context_mask | GCPlaneMask),&context_values);
14552  if (pixel->highlight_context == (GC) NULL)
14553  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14554  display_image->filename);
14555  (void) XDestroyWindow(display,windows->context.id);
14556  /*
14557  Initialize icon window.
14558  */
14559  XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14560  icon_resources,&windows->icon);
14561  windows->icon.geometry=resource_info->icon_geometry;
14562  XBestIconSize(display,&windows->icon,display_image);
14563  windows->icon.attributes.colormap=XDefaultColormap(display,
14564  icon_visual->screen);
14565  windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14566  manager_hints->flags=InputHint | StateHint;
14567  manager_hints->input=MagickFalse;
14568  manager_hints->initial_state=IconicState;
14569  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14570  &windows->icon);
14571  if (resource_info->debug != MagickFalse)
14572  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14573  windows->icon.id);
14574  /*
14575  Initialize graphic context for icon window.
14576  */
14577  if (icon_pixel->annotate_context != (GC) NULL)
14578  (void) XFreeGC(display,icon_pixel->annotate_context);
14579  context_values.background=icon_pixel->background_color.pixel;
14580  context_values.foreground=icon_pixel->foreground_color.pixel;
14581  icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14582  (size_t) (GCBackground | GCForeground),&context_values);
14583  if (icon_pixel->annotate_context == (GC) NULL)
14584  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14585  display_image->filename);
14586  windows->icon.annotate_context=icon_pixel->annotate_context;
14587  /*
14588  Initialize Image window.
14589  */
14590  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14591  &windows->image);
14592  windows->image.shape=MagickTrue; /* non-rectangular shape hint */
14593  if (resource_info->use_shared_memory == MagickFalse)
14594  windows->image.shared_memory=MagickFalse;
14595  if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14596  {
14597  char
14598  *title;
14599 
14600  title=InterpretImageProperties(resource_info->image_info,display_image,
14601  resource_info->title);
14602  (void) CloneString(&windows->image.name,title);
14603  (void) CloneString(&windows->image.icon_name,title);
14604  title=DestroyString(title);
14605  }
14606  else
14607  {
14608  char
14609  filename[MaxTextExtent],
14610  window_name[MaxTextExtent];
14611 
14612  /*
14613  Window name is the base of the filename.
14614  */
14615  GetPathComponent(display_image->magick_filename,TailPath,filename);
14616  if (display_image->scene == 0)
14617  (void) FormatLocaleString(window_name,MaxTextExtent,"%s: %s",
14618  MagickPackageName,filename);
14619  else
14620  (void) FormatLocaleString(window_name,MaxTextExtent,
14621  "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14622  (double) display_image->scene,(double) GetImageListLength(
14623  display_image));
14624  (void) CloneString(&windows->image.name,window_name);
14625  (void) CloneString(&windows->image.icon_name,filename);
14626  }
14627  if (resource_info->immutable)
14628  windows->image.immutable=MagickTrue;
14629  windows->image.use_pixmap=resource_info->use_pixmap;
14630  windows->image.geometry=resource_info->image_geometry;
14631  (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14632  XDisplayWidth(display,visual_info->screen),
14633  XDisplayHeight(display,visual_info->screen));
14634  geometry_info.width=display_image->columns;
14635  geometry_info.height=display_image->rows;
14636  geometry_info.x=0;
14637  geometry_info.y=0;
14638  (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14639  &geometry_info.width,&geometry_info.height);
14640  windows->image.width=(unsigned int) geometry_info.width;
14641  windows->image.height=(unsigned int) geometry_info.height;
14642  windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14643  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14644  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14645  PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14646  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14647  resource_info,&windows->backdrop);
14648  if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14649  {
14650  /*
14651  Initialize backdrop window.
14652  */
14653  windows->backdrop.x=0;
14654  windows->backdrop.y=0;
14655  (void) CloneString(&windows->backdrop.name,"Backdrop");
14656  windows->backdrop.flags=(size_t) (USSize | USPosition);
14657  windows->backdrop.width=(unsigned int)
14658  XDisplayWidth(display,visual_info->screen);
14659  windows->backdrop.height=(unsigned int)
14660  XDisplayHeight(display,visual_info->screen);
14661  windows->backdrop.border_width=0;
14662  windows->backdrop.immutable=MagickTrue;
14663  windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14664  ButtonReleaseMask;
14665  windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14666  StructureNotifyMask;
14667  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14668  manager_hints->icon_window=windows->icon.id;
14669  manager_hints->input=MagickTrue;
14670  manager_hints->initial_state=resource_info->iconic ? IconicState :
14671  NormalState;
14672  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14673  &windows->backdrop);
14674  if (resource_info->debug != MagickFalse)
14675  (void) LogMagickEvent(X11Event,GetMagickModule(),
14676  "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14677  (void) XMapWindow(display,windows->backdrop.id);
14678  (void) XClearWindow(display,windows->backdrop.id);
14679  if (windows->image.id != (Window) NULL)
14680  {
14681  (void) XDestroyWindow(display,windows->image.id);
14682  windows->image.id=(Window) NULL;
14683  }
14684  /*
14685  Position image in the center the backdrop.
14686  */
14687  windows->image.flags|=USPosition;
14688  windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14689  (windows->image.width/2);
14690  windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14691  (windows->image.height/2);
14692  }
14693  manager_hints->flags=IconWindowHint | InputHint | StateHint;
14694  manager_hints->icon_window=windows->icon.id;
14695  manager_hints->input=MagickTrue;
14696  manager_hints->initial_state=resource_info->iconic ? IconicState :
14697  NormalState;
14698  if (windows->group_leader.id != (Window) NULL)
14699  {
14700  /*
14701  Follow the leader.
14702  */
14703  manager_hints->flags|=WindowGroupHint;
14704  manager_hints->window_group=windows->group_leader.id;
14705  (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14706  if (resource_info->debug != MagickFalse)
14707  (void) LogMagickEvent(X11Event,GetMagickModule(),
14708  "Window id: 0x%lx (group leader)",windows->group_leader.id);
14709  }
14710  XMakeWindow(display,
14711  (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14712  argv,argc,class_hints,manager_hints,&windows->image);
14713  (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14714  XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14715  if (windows->group_leader.id != (Window) NULL)
14716  (void) XSetTransientForHint(display,windows->image.id,
14717  windows->group_leader.id);
14718  if (resource_info->debug != MagickFalse)
14719  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14720  windows->image.id);
14721  /*
14722  Initialize Info widget.
14723  */
14724  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14725  &windows->info);
14726  (void) CloneString(&windows->info.name,"Info");
14727  (void) CloneString(&windows->info.icon_name,"Info");
14728  windows->info.border_width=1;
14729  windows->info.x=2;
14730  windows->info.y=2;
14731  windows->info.flags|=PPosition;
14732  windows->info.attributes.win_gravity=UnmapGravity;
14733  windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14734  StructureNotifyMask;
14735  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14736  manager_hints->input=MagickFalse;
14737  manager_hints->initial_state=NormalState;
14738  manager_hints->window_group=windows->image.id;
14739  XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14740  &windows->info);
14741  windows->info.highlight_stipple=XCreateBitmapFromData(display,
14742  windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14743  windows->info.shadow_stipple=XCreateBitmapFromData(display,
14744  windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14745  (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14746  if (windows->image.mapped != MagickFalse)
14747  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14748  if (resource_info->debug != MagickFalse)
14749  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14750  windows->info.id);
14751  /*
14752  Initialize Command widget.
14753  */
14754  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14755  resource_info,&windows->command);
14756  windows->command.data=MagickMenus;
14757  (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14758  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14759  resource_info->client_name);
14760  windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14761  resource_name,"geometry",(char *) NULL);
14762  (void) CloneString(&windows->command.name,MagickTitle);
14763  windows->command.border_width=0;
14764  windows->command.flags|=PPosition;
14765  windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14766  ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14767  OwnerGrabButtonMask | StructureNotifyMask;
14768  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14769  manager_hints->input=MagickTrue;
14770  manager_hints->initial_state=NormalState;
14771  manager_hints->window_group=windows->image.id;
14772  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14773  &windows->command);
14774  windows->command.highlight_stipple=XCreateBitmapFromData(display,
14775  windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14776  HighlightHeight);
14777  windows->command.shadow_stipple=XCreateBitmapFromData(display,
14778  windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14779  (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14780  if (windows->command.mapped != MagickFalse)
14781  (void) XMapRaised(display,windows->command.id);
14782  if (resource_info->debug != MagickFalse)
14783  (void) LogMagickEvent(X11Event,GetMagickModule(),
14784  "Window id: 0x%lx (command)",windows->command.id);
14785  /*
14786  Initialize Widget window.
14787  */
14788  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14789  resource_info,&windows->widget);
14790  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14791  resource_info->client_name);
14792  windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14793  resource_name,"geometry",(char *) NULL);
14794  windows->widget.border_width=0;
14795  windows->widget.flags|=PPosition;
14796  windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14797  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14798  KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14799  StructureNotifyMask;
14800  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14801  manager_hints->input=MagickTrue;
14802  manager_hints->initial_state=NormalState;
14803  manager_hints->window_group=windows->image.id;
14804  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14805  &windows->widget);
14806  windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14807  windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14808  windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14809  windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14810  (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14811  if (resource_info->debug != MagickFalse)
14812  (void) LogMagickEvent(X11Event,GetMagickModule(),
14813  "Window id: 0x%lx (widget)",windows->widget.id);
14814  /*
14815  Initialize popup window.
14816  */
14817  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14818  resource_info,&windows->popup);
14819  windows->popup.border_width=0;
14820  windows->popup.flags|=PPosition;
14821  windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14822  ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14823  KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14824  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14825  manager_hints->input=MagickTrue;
14826  manager_hints->initial_state=NormalState;
14827  manager_hints->window_group=windows->image.id;
14828  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14829  &windows->popup);
14830  windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14831  windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14832  windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14833  windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14834  (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14835  if (resource_info->debug != MagickFalse)
14836  (void) LogMagickEvent(X11Event,GetMagickModule(),
14837  "Window id: 0x%lx (pop up)",windows->popup.id);
14838  /*
14839  Initialize Magnify window and cursor.
14840  */
14841  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14842  resource_info,&windows->magnify);
14843  if (resource_info->use_shared_memory == MagickFalse)
14844  windows->magnify.shared_memory=MagickFalse;
14845  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14846  resource_info->client_name);
14847  windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14848  resource_name,"geometry",(char *) NULL);
14849  (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14850  resource_info->magnify);
14851  if (windows->magnify.cursor != (Cursor) NULL)
14852  (void) XFreeCursor(display,windows->magnify.cursor);
14853  windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14854  map_info->colormap,resource_info->background_color,
14855  resource_info->foreground_color);
14856  if (windows->magnify.cursor == (Cursor) NULL)
14857  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14858  display_image->filename);
14859  windows->magnify.width=MagnifySize;
14860  windows->magnify.height=MagnifySize;
14861  windows->magnify.flags|=PPosition;
14862  windows->magnify.min_width=MagnifySize;
14863  windows->magnify.min_height=MagnifySize;
14864  windows->magnify.width_inc=MagnifySize;
14865  windows->magnify.height_inc=MagnifySize;
14866  windows->magnify.data=resource_info->magnify;
14867  windows->magnify.attributes.cursor=windows->magnify.cursor;
14868  windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14869  ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14870  StructureNotifyMask;
14871  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14872  manager_hints->input=MagickTrue;
14873  manager_hints->initial_state=NormalState;
14874  manager_hints->window_group=windows->image.id;
14875  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14876  &windows->magnify);
14877  if (resource_info->debug != MagickFalse)
14878  (void) LogMagickEvent(X11Event,GetMagickModule(),
14879  "Window id: 0x%lx (magnify)",windows->magnify.id);
14880  (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14881  /*
14882  Initialize panning window.
14883  */
14884  XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14885  resource_info,&windows->pan);
14886  (void) CloneString(&windows->pan.name,"Pan Icon");
14887  windows->pan.width=windows->icon.width;
14888  windows->pan.height=windows->icon.height;
14889  (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14890  resource_info->client_name);
14891  windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14892  resource_name,"geometry",(char *) NULL);
14893  (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14894  &windows->pan.width,&windows->pan.height);
14895  windows->pan.flags|=PPosition;
14896  windows->pan.immutable=MagickTrue;
14897  windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14898  ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14899  StructureNotifyMask;
14900  manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14901  manager_hints->input=MagickFalse;
14902  manager_hints->initial_state=NormalState;
14903  manager_hints->window_group=windows->image.id;
14904  XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14905  &windows->pan);
14906  if (resource_info->debug != MagickFalse)
14907  (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14908  windows->pan.id);
14909  (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14910  if (windows->info.mapped != MagickFalse)
14911  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14912  if ((windows->image.mapped == MagickFalse) ||
14913  (windows->backdrop.id != (Window) NULL))
14914  (void) XMapWindow(display,windows->image.id);
14915  /*
14916  Set our progress monitor and warning handlers.
14917  */
14918  if (warning_handler == (WarningHandler) NULL)
14919  {
14920  warning_handler=resource_info->display_warnings ?
14921  SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14922  warning_handler=resource_info->display_warnings ?
14923  SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14924  }
14925  /*
14926  Initialize Image and Magnify X images.
14927  */
14928  windows->image.x=0;
14929  windows->image.y=0;
14930  windows->magnify.shape=MagickFalse;
14931  width=(unsigned int) display_image->columns;
14932  height=(unsigned int) display_image->rows;
14933  if ((display_image->columns != width) || (display_image->rows != height))
14934  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14935  display_image->filename);
14936  status=XMakeImage(display,resource_info,&windows->image,display_image,
14937  width,height);
14938  if (status == MagickFalse)
14939  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14940  display_image->filename);
14941  status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14942  windows->magnify.width,windows->magnify.height);
14943  if (status == MagickFalse)
14944  ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14945  display_image->filename);
14946  if (windows->magnify.mapped != MagickFalse)
14947  (void) XMapRaised(display,windows->magnify.id);
14948  if (windows->pan.mapped != MagickFalse)
14949  (void) XMapRaised(display,windows->pan.id);
14950  windows->image.window_changes.width=(int) display_image->columns;
14951  windows->image.window_changes.height=(int) display_image->rows;
14952  (void) XConfigureImage(display,resource_info,windows,display_image);
14953  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14954  (void) XSync(display,MagickFalse);
14955  /*
14956  Respond to events.
14957  */
14958  delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14959  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
14960  update_time=0;
14961  if (resource_info->update != MagickFalse)
14962  {
14963  MagickBooleanType
14964  status;
14965 
14966  /*
14967  Determine when file data was last modified.
14968  */
14969  status=GetPathAttributes(display_image->filename,&attributes);
14970  if (status != MagickFalse)
14971  update_time=attributes.st_mtime;
14972  }
14973  *state&=(~FormerImageState);
14974  *state&=(~MontageImageState);
14975  *state&=(~NextImageState);
14976  do
14977  {
14978  /*
14979  Handle a window event.
14980  */
14981  if (windows->image.mapped != MagickFalse)
14982  if ((display_image->delay != 0) || (resource_info->update != 0))
14983  {
14984  if (timer < GetMagickTime())
14985  {
14986  if (resource_info->update == MagickFalse)
14987  *state|=NextImageState | ExitState;
14988  else
14989  {
14990  MagickBooleanType
14991  status;
14992 
14993  /*
14994  Determine if image file was modified.
14995  */
14996  status=GetPathAttributes(display_image->filename,&attributes);
14997  if (status != MagickFalse)
14998  if (update_time != attributes.st_mtime)
14999  {
15000  /*
15001  Redisplay image.
15002  */
15003  (void) FormatLocaleString(
15004  resource_info->image_info->filename,MaxTextExtent,
15005  "%s:%s",display_image->magick,
15006  display_image->filename);
15007  nexus=ReadImage(resource_info->image_info,
15008  &display_image->exception);
15009  if (nexus != (Image *) NULL)
15010  *state|=NextImageState | ExitState;
15011  }
15012  delay=display_image->delay/MagickMax(
15013  display_image->ticks_per_second,1L);
15014  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15015  }
15016  }
15017  if (XEventsQueued(display,QueuedAfterFlush) == 0)
15018  {
15019  /*
15020  Do not block if delay > 0.
15021  */
15022  XDelay(display,SuspendTime << 2);
15023  continue;
15024  }
15025  }
15026  timestamp=GetMagickTime();
15027  (void) XNextEvent(display,&event);
15028  if (windows->image.stasis == MagickFalse)
15029  windows->image.stasis=(GetMagickTime()-timestamp) > 0 ?
15030  MagickTrue : MagickFalse;
15031  if (windows->magnify.stasis == MagickFalse)
15032  windows->magnify.stasis=(GetMagickTime()-timestamp) > 0 ?
15033  MagickTrue : MagickFalse;
15034  if (event.xany.window == windows->command.id)
15035  {
15036  /*
15037  Select a command from the Command widget.
15038  */
15039  id=XCommandWidget(display,windows,CommandMenu,&event);
15040  if (id < 0)
15041  continue;
15042  (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15043  display_command=CommandMenus[id];
15044  if (id < MagickMenus)
15045  {
15046  /*
15047  Select a command from a pop-up menu.
15048  */
15049  entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15050  command);
15051  if (entry < 0)
15052  continue;
15053  (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15054  display_command=Commands[id][entry];
15055  }
15056  if (display_command != NullCommand)
15057  nexus=XMagickCommand(display,resource_info,windows,display_command,
15058  &display_image);
15059  continue;
15060  }
15061  switch (event.type)
15062  {
15063  case ButtonPress:
15064  {
15065  if (resource_info->debug != MagickFalse)
15066  (void) LogMagickEvent(X11Event,GetMagickModule(),
15067  "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15068  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15069  if ((event.xbutton.button == Button3) &&
15070  (event.xbutton.state & Mod1Mask))
15071  {
15072  /*
15073  Convert Alt-Button3 to Button2.
15074  */
15075  event.xbutton.button=Button2;
15076  event.xbutton.state&=(~Mod1Mask);
15077  }
15078  if (event.xbutton.window == windows->backdrop.id)
15079  {
15080  (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15081  event.xbutton.time);
15082  break;
15083  }
15084  if (event.xbutton.window == windows->image.id)
15085  {
15086  switch (event.xbutton.button)
15087  {
15088  case Button1:
15089  {
15090  if (resource_info->immutable)
15091  {
15092  /*
15093  Select a command from the Virtual menu.
15094  */
15095  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15096  command);
15097  if (entry >= 0)
15098  nexus=XMagickCommand(display,resource_info,windows,
15099  VirtualCommands[entry],&display_image);
15100  break;
15101  }
15102  /*
15103  Map/unmap Command widget.
15104  */
15105  if (windows->command.mapped != MagickFalse)
15106  (void) XWithdrawWindow(display,windows->command.id,
15107  windows->command.screen);
15108  else
15109  {
15110  (void) XCommandWidget(display,windows,CommandMenu,
15111  (XEvent *) NULL);
15112  (void) XMapRaised(display,windows->command.id);
15113  }
15114  break;
15115  }
15116  case Button2:
15117  {
15118  /*
15119  User pressed the image magnify button.
15120  */
15121  (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15122  &display_image);
15123  XMagnifyImage(display,windows,&event);
15124  break;
15125  }
15126  case Button3:
15127  {
15128  if (resource_info->immutable)
15129  {
15130  /*
15131  Select a command from the Virtual menu.
15132  */
15133  entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15134  command);
15135  if (entry >= 0)
15136  nexus=XMagickCommand(display,resource_info,windows,
15137  VirtualCommands[entry],&display_image);
15138  break;
15139  }
15140  if (display_image->montage != (char *) NULL)
15141  {
15142  /*
15143  Open or delete a tile from a visual image directory.
15144  */
15145  nexus=XTileImage(display,resource_info,windows,
15146  display_image,&event);
15147  if (nexus != (Image *) NULL)
15148  *state|=MontageImageState | NextImageState | ExitState;
15149  vid_info.x=(short int) windows->image.x;
15150  vid_info.y=(short int) windows->image.y;
15151  break;
15152  }
15153  /*
15154  Select a command from the Short Cuts menu.
15155  */
15156  entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15157  command);
15158  if (entry >= 0)
15159  nexus=XMagickCommand(display,resource_info,windows,
15160  ShortCutsCommands[entry],&display_image);
15161  break;
15162  }
15163  case Button4:
15164  {
15165  /*
15166  Wheel up.
15167  */
15168  XTranslateImage(display,windows,*image,XK_Up);
15169  break;
15170  }
15171  case Button5:
15172  {
15173  /*
15174  Wheel down.
15175  */
15176  XTranslateImage(display,windows,*image,XK_Down);
15177  break;
15178  }
15179  default:
15180  break;
15181  }
15182  break;
15183  }
15184  if (event.xbutton.window == windows->magnify.id)
15185  {
15186  const char
15187  *const MagnifyMenu[] =
15188  {
15189  "2",
15190  "4",
15191  "5",
15192  "6",
15193  "7",
15194  "8",
15195  "9",
15196  "3",
15197  (char *) NULL,
15198  };
15199 
15200  int
15201  factor;
15202 
15203  static KeySym
15204  MagnifyCommands[] =
15205  {
15206  XK_2,
15207  XK_4,
15208  XK_5,
15209  XK_6,
15210  XK_7,
15211  XK_8,
15212  XK_9,
15213  XK_3
15214  };
15215 
15216  /*
15217  Select a magnify factor from the pop-up menu.
15218  */
15219  factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15220  if (factor >= 0)
15221  XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15222  break;
15223  }
15224  if (event.xbutton.window == windows->pan.id)
15225  {
15226  switch (event.xbutton.button)
15227  {
15228  case Button4:
15229  {
15230  /*
15231  Wheel up.
15232  */
15233  XTranslateImage(display,windows,*image,XK_Up);
15234  break;
15235  }
15236  case Button5:
15237  {
15238  /*
15239  Wheel down.
15240  */
15241  XTranslateImage(display,windows,*image,XK_Down);
15242  break;
15243  }
15244  default:
15245  {
15246  XPanImage(display,windows,&event);
15247  break;
15248  }
15249  }
15250  break;
15251  }
15252  delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15253  1L);
15254  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15255  break;
15256  }
15257  case ButtonRelease:
15258  {
15259  if (resource_info->debug != MagickFalse)
15260  (void) LogMagickEvent(X11Event,GetMagickModule(),
15261  "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15262  event.xbutton.button,event.xbutton.x,event.xbutton.y);
15263  break;
15264  }
15265  case ClientMessage:
15266  {
15267  if (resource_info->debug != MagickFalse)
15268  (void) LogMagickEvent(X11Event,GetMagickModule(),
15269  "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15270  event.xclient.message_type,event.xclient.format,(unsigned long)
15271  event.xclient.data.l[0]);
15272  if (event.xclient.message_type == windows->im_protocols)
15273  {
15274  if (*event.xclient.data.l == (long) windows->im_update_widget)
15275  {
15276  (void) CloneString(&windows->command.name,MagickTitle);
15277  windows->command.data=MagickMenus;
15278  (void) XCommandWidget(display,windows,CommandMenu,
15279  (XEvent *) NULL);
15280  break;
15281  }
15282  if (*event.xclient.data.l == (long) windows->im_update_colormap)
15283  {
15284  /*
15285  Update graphic context and window colormap.
15286  */
15287  for (i=0; i < (int) number_windows; i++)
15288  {
15289  if (magick_windows[i]->id == windows->icon.id)
15290  continue;
15291  context_values.background=pixel->background_color.pixel;
15292  context_values.foreground=pixel->foreground_color.pixel;
15293  (void) XChangeGC(display,magick_windows[i]->annotate_context,
15294  context_mask,&context_values);
15295  (void) XChangeGC(display,magick_windows[i]->widget_context,
15296  context_mask,&context_values);
15297  context_values.background=pixel->foreground_color.pixel;
15298  context_values.foreground=pixel->background_color.pixel;
15299  context_values.plane_mask=context_values.background ^
15300  context_values.foreground;
15301  (void) XChangeGC(display,magick_windows[i]->highlight_context,
15302  (size_t) (context_mask | GCPlaneMask),
15303  &context_values);
15304  magick_windows[i]->attributes.background_pixel=
15305  pixel->background_color.pixel;
15306  magick_windows[i]->attributes.border_pixel=
15307  pixel->border_color.pixel;
15308  magick_windows[i]->attributes.colormap=map_info->colormap;
15309  (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15310  (unsigned long) magick_windows[i]->mask,
15311  &magick_windows[i]->attributes);
15312  }
15313  if (windows->pan.mapped != MagickFalse)
15314  {
15315  (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15316  windows->pan.pixmap);
15317  (void) XClearWindow(display,windows->pan.id);
15318  XDrawPanRectangle(display,windows);
15319  }
15320  if (windows->backdrop.id != (Window) NULL)
15321  (void) XInstallColormap(display,map_info->colormap);
15322  break;
15323  }
15324  if (*event.xclient.data.l == (long) windows->im_former_image)
15325  {
15326  *state|=FormerImageState | ExitState;
15327  break;
15328  }
15329  if (*event.xclient.data.l == (long) windows->im_next_image)
15330  {
15331  *state|=NextImageState | ExitState;
15332  break;
15333  }
15334  if (*event.xclient.data.l == (long) windows->im_retain_colors)
15335  {
15336  *state|=RetainColorsState;
15337  break;
15338  }
15339  if (*event.xclient.data.l == (long) windows->im_exit)
15340  {
15341  *state|=ExitState;
15342  break;
15343  }
15344  break;
15345  }
15346  if (event.xclient.message_type == windows->dnd_protocols)
15347  {
15348  Atom
15349  selection,
15350  type;
15351 
15352  int
15353  format,
15354  status;
15355 
15356  unsigned char
15357  *data;
15358 
15359  unsigned long
15360  after,
15361  length;
15362 
15363  /*
15364  Display image named by the Drag-and-Drop selection.
15365  */
15366  if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15367  break;
15368  selection=XInternAtom(display,"DndSelection",MagickFalse);
15369  status=XGetWindowProperty(display,root_window,selection,0L,(long)
15370  MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15371  &length,&after,&data);
15372  if ((status != Success) || (length == 0))
15373  break;
15374  if (*event.xclient.data.l == 2)
15375  {
15376  /*
15377  Offix DND.
15378  */
15379  (void) CopyMagickString(resource_info->image_info->filename,
15380  (char *) data,MaxTextExtent);
15381  }
15382  else
15383  {
15384  /*
15385  XDND.
15386  */
15387  if (strncmp((char *) data, "file:", 5) != 0)
15388  {
15389  (void) XFree((void *) data);
15390  break;
15391  }
15392  (void) CopyMagickString(resource_info->image_info->filename,
15393  ((char *) data)+5,MaxTextExtent);
15394  }
15395  nexus=ReadImage(resource_info->image_info,
15396  &display_image->exception);
15397  CatchException(&display_image->exception);
15398  if (nexus != (Image *) NULL)
15399  *state|=NextImageState | ExitState;
15400  (void) XFree((void *) data);
15401  break;
15402  }
15403  /*
15404  If client window delete message, exit.
15405  */
15406  if (event.xclient.message_type != windows->wm_protocols)
15407  break;
15408  if (*event.xclient.data.l != (long) windows->wm_delete_window)
15409  break;
15410  (void) XWithdrawWindow(display,event.xclient.window,
15411  visual_info->screen);
15412  if (event.xclient.window == windows->image.id)
15413  {
15414  *state|=ExitState;
15415  break;
15416  }
15417  if (event.xclient.window == windows->pan.id)
15418  {
15419  /*
15420  Restore original image size when pan window is deleted.
15421  */
15422  windows->image.window_changes.width=windows->image.ximage->width;
15423  windows->image.window_changes.height=windows->image.ximage->height;
15424  (void) XConfigureImage(display,resource_info,windows,
15425  display_image);
15426  }
15427  break;
15428  }
15429  case ConfigureNotify:
15430  {
15431  if (resource_info->debug != MagickFalse)
15432  (void) LogMagickEvent(X11Event,GetMagickModule(),
15433  "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15434  event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15435  event.xconfigure.y,event.xconfigure.send_event);
15436  if (event.xconfigure.window == windows->image.id)
15437  {
15438  /*
15439  Image window has a new configuration.
15440  */
15441  if (event.xconfigure.send_event != 0)
15442  {
15443  XWindowChanges
15444  window_changes;
15445 
15446  /*
15447  Position the transient windows relative of the Image window.
15448  */
15449  if (windows->command.geometry == (char *) NULL)
15450  if (windows->command.mapped == MagickFalse)
15451  {
15452  windows->command.x=event.xconfigure.x-
15453  windows->command.width-25;
15454  windows->command.y=event.xconfigure.y;
15455  XConstrainWindowPosition(display,&windows->command);
15456  window_changes.x=windows->command.x;
15457  window_changes.y=windows->command.y;
15458  (void) XReconfigureWMWindow(display,windows->command.id,
15459  windows->command.screen,(unsigned int) (CWX | CWY),
15460  &window_changes);
15461  }
15462  if (windows->widget.geometry == (char *) NULL)
15463  if (windows->widget.mapped == MagickFalse)
15464  {
15465  windows->widget.x=event.xconfigure.x+
15466  event.xconfigure.width/10;
15467  windows->widget.y=event.xconfigure.y+
15468  event.xconfigure.height/10;
15469  XConstrainWindowPosition(display,&windows->widget);
15470  window_changes.x=windows->widget.x;
15471  window_changes.y=windows->widget.y;
15472  (void) XReconfigureWMWindow(display,windows->widget.id,
15473  windows->widget.screen,(unsigned int) (CWX | CWY),
15474  &window_changes);
15475  }
15476  if (windows->magnify.geometry == (char *) NULL)
15477  if (windows->magnify.mapped == MagickFalse)
15478  {
15479  windows->magnify.x=event.xconfigure.x+
15480  event.xconfigure.width+25;
15481  windows->magnify.y=event.xconfigure.y;
15482  XConstrainWindowPosition(display,&windows->magnify);
15483  window_changes.x=windows->magnify.x;
15484  window_changes.y=windows->magnify.y;
15485  (void) XReconfigureWMWindow(display,windows->magnify.id,
15486  windows->magnify.screen,(unsigned int) (CWX | CWY),
15487  &window_changes);
15488  }
15489  if (windows->pan.geometry == (char *) NULL)
15490  if (windows->pan.mapped == MagickFalse)
15491  {
15492  windows->pan.x=event.xconfigure.x+
15493  event.xconfigure.width+25;
15494  windows->pan.y=event.xconfigure.y+
15495  windows->magnify.height+50;
15496  XConstrainWindowPosition(display,&windows->pan);
15497  window_changes.x=windows->pan.x;
15498  window_changes.y=windows->pan.y;
15499  (void) XReconfigureWMWindow(display,windows->pan.id,
15500  windows->pan.screen,(unsigned int) (CWX | CWY),
15501  &window_changes);
15502  }
15503  }
15504  if ((event.xconfigure.width == (int) windows->image.width) &&
15505  (event.xconfigure.height == (int) windows->image.height))
15506  break;
15507  windows->image.width=(unsigned int) event.xconfigure.width;
15508  windows->image.height=(unsigned int) event.xconfigure.height;
15509  windows->image.x=0;
15510  windows->image.y=0;
15511  if (display_image->montage != (char *) NULL)
15512  {
15513  windows->image.x=vid_info.x;
15514  windows->image.y=vid_info.y;
15515  }
15516  if ((windows->image.mapped != MagickFalse) &&
15517  (windows->image.stasis != MagickFalse))
15518  {
15519  /*
15520  Update image window configuration.
15521  */
15522  windows->image.window_changes.width=event.xconfigure.width;
15523  windows->image.window_changes.height=event.xconfigure.height;
15524  (void) XConfigureImage(display,resource_info,windows,
15525  display_image);
15526  }
15527  /*
15528  Update pan window configuration.
15529  */
15530  if ((event.xconfigure.width < windows->image.ximage->width) ||
15531  (event.xconfigure.height < windows->image.ximage->height))
15532  {
15533  (void) XMapRaised(display,windows->pan.id);
15534  XDrawPanRectangle(display,windows);
15535  }
15536  else
15537  if (windows->pan.mapped != MagickFalse)
15538  (void) XWithdrawWindow(display,windows->pan.id,
15539  windows->pan.screen);
15540  break;
15541  }
15542  if (event.xconfigure.window == windows->magnify.id)
15543  {
15544  unsigned int
15545  magnify;
15546 
15547  /*
15548  Magnify window has a new configuration.
15549  */
15550  windows->magnify.width=(unsigned int) event.xconfigure.width;
15551  windows->magnify.height=(unsigned int) event.xconfigure.height;
15552  if (windows->magnify.mapped == MagickFalse)
15553  break;
15554  magnify=1;
15555  while ((int) magnify <= event.xconfigure.width)
15556  magnify<<=1;
15557  while ((int) magnify <= event.xconfigure.height)
15558  magnify<<=1;
15559  magnify>>=1;
15560  if (((int) magnify != event.xconfigure.width) ||
15561  ((int) magnify != event.xconfigure.height))
15562  {
15563  window_changes.width=(int) magnify;
15564  window_changes.height=(int) magnify;
15565  (void) XReconfigureWMWindow(display,windows->magnify.id,
15566  windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15567  &window_changes);
15568  break;
15569  }
15570  if ((windows->magnify.mapped != MagickFalse) &&
15571  (windows->magnify.stasis != MagickFalse))
15572  {
15573  status=XMakeImage(display,resource_info,&windows->magnify,
15574  display_image,windows->magnify.width,windows->magnify.height);
15575  XMakeMagnifyImage(display,windows);
15576  }
15577  break;
15578  }
15579  if ((windows->magnify.mapped != MagickFalse) &&
15580  (event.xconfigure.window == windows->pan.id))
15581  {
15582  /*
15583  Pan icon window has a new configuration.
15584  */
15585  if (event.xconfigure.send_event != 0)
15586  {
15587  windows->pan.x=event.xconfigure.x;
15588  windows->pan.y=event.xconfigure.y;
15589  }
15590  windows->pan.width=(unsigned int) event.xconfigure.width;
15591  windows->pan.height=(unsigned int) event.xconfigure.height;
15592  break;
15593  }
15594  if (event.xconfigure.window == windows->icon.id)
15595  {
15596  /*
15597  Icon window has a new configuration.
15598  */
15599  windows->icon.width=(unsigned int) event.xconfigure.width;
15600  windows->icon.height=(unsigned int) event.xconfigure.height;
15601  break;
15602  }
15603  break;
15604  }
15605  case DestroyNotify:
15606  {
15607  /*
15608  Group leader has exited.
15609  */
15610  if (resource_info->debug != MagickFalse)
15611  (void) LogMagickEvent(X11Event,GetMagickModule(),
15612  "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15613  if (event.xdestroywindow.window == windows->group_leader.id)
15614  {
15615  *state|=ExitState;
15616  break;
15617  }
15618  break;
15619  }
15620  case EnterNotify:
15621  {
15622  /*
15623  Selectively install colormap.
15624  */
15625  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15626  if (event.xcrossing.mode != NotifyUngrab)
15627  XInstallColormap(display,map_info->colormap);
15628  break;
15629  }
15630  case Expose:
15631  {
15632  if (resource_info->debug != MagickFalse)
15633  (void) LogMagickEvent(X11Event,GetMagickModule(),
15634  "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15635  event.xexpose.width,event.xexpose.height,event.xexpose.x,
15636  event.xexpose.y);
15637  /*
15638  Refresh windows that are now exposed.
15639  */
15640  if ((event.xexpose.window == windows->image.id) &&
15641  (windows->image.mapped != MagickFalse))
15642  {
15643  XRefreshWindow(display,&windows->image,&event);
15644  delay=display_image->delay/MagickMax(
15645  display_image->ticks_per_second,1L);
15646  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15647  break;
15648  }
15649  if ((event.xexpose.window == windows->magnify.id) &&
15650  (windows->magnify.mapped != MagickFalse))
15651  {
15652  XMakeMagnifyImage(display,windows);
15653  break;
15654  }
15655  if (event.xexpose.window == windows->pan.id)
15656  {
15657  XDrawPanRectangle(display,windows);
15658  break;
15659  }
15660  if (event.xexpose.window == windows->icon.id)
15661  {
15662  XRefreshWindow(display,&windows->icon,&event);
15663  break;
15664  }
15665  break;
15666  }
15667  case KeyPress:
15668  {
15669  int
15670  length;
15671 
15672  /*
15673  Respond to a user key press.
15674  */
15675  length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15676  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15677  *(command+length)='\0';
15678  if (resource_info->debug != MagickFalse)
15679  (void) LogMagickEvent(X11Event,GetMagickModule(),
15680  "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15681  key_symbol,command);
15682  if (event.xkey.window == windows->image.id)
15683  {
15684  display_command=XImageWindowCommand(display,resource_info,windows,
15685  event.xkey.state,key_symbol,&display_image);
15686  if (display_command != NullCommand)
15687  nexus=XMagickCommand(display,resource_info,windows,display_command,
15688  &display_image);
15689  }
15690  if (event.xkey.window == windows->magnify.id)
15691  XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15692  if (event.xkey.window == windows->pan.id)
15693  {
15694  if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15695  (void) XWithdrawWindow(display,windows->pan.id,
15696  windows->pan.screen);
15697  else
15698  if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15699  XTextViewHelp(display,resource_info,windows,MagickFalse,
15700  "Help Viewer - Image Pan",ImagePanHelp);
15701  else
15702  XTranslateImage(display,windows,*image,key_symbol);
15703  }
15704  delay=display_image->delay/MagickMax(
15705  display_image->ticks_per_second,1L);
15706  timer=GetMagickTime()+(delay == 0 ? 1 : delay)+1;
15707  break;
15708  }
15709  case KeyRelease:
15710  {
15711  /*
15712  Respond to a user key release.
15713  */
15714  (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15715  sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15716  if (resource_info->debug != MagickFalse)
15717  (void) LogMagickEvent(X11Event,GetMagickModule(),
15718  "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15719  break;
15720  }
15721  case LeaveNotify:
15722  {
15723  /*
15724  Selectively uninstall colormap.
15725  */
15726  if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15727  if (event.xcrossing.mode != NotifyUngrab)
15728  XUninstallColormap(display,map_info->colormap);
15729  break;
15730  }
15731  case MapNotify:
15732  {
15733  if (resource_info->debug != MagickFalse)
15734  (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15735  event.xmap.window);
15736  if (event.xmap.window == windows->backdrop.id)
15737  {
15738  (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15739  CurrentTime);
15740  windows->backdrop.mapped=MagickTrue;
15741  break;
15742  }
15743  if (event.xmap.window == windows->image.id)
15744  {
15745  if (windows->backdrop.id != (Window) NULL)
15746  (void) XInstallColormap(display,map_info->colormap);
15747  if (LocaleCompare(display_image->magick,"LOGO") == 0)
15748  {
15749  if (LocaleCompare(display_image->filename,"LOGO") == 0)
15750  nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15751  }
15752  if (((int) windows->image.width < windows->image.ximage->width) ||
15753  ((int) windows->image.height < windows->image.ximage->height))
15754  (void) XMapRaised(display,windows->pan.id);
15755  windows->image.mapped=MagickTrue;
15756  break;
15757  }
15758  if (event.xmap.window == windows->magnify.id)
15759  {
15760  XMakeMagnifyImage(display,windows);
15761  windows->magnify.mapped=MagickTrue;
15762  (void) XWithdrawWindow(display,windows->info.id,
15763  windows->info.screen);
15764  break;
15765  }
15766  if (event.xmap.window == windows->pan.id)
15767  {
15768  XMakePanImage(display,resource_info,windows,display_image);
15769  windows->pan.mapped=MagickTrue;
15770  break;
15771  }
15772  if (event.xmap.window == windows->info.id)
15773  {
15774  windows->info.mapped=MagickTrue;
15775  break;
15776  }
15777  if (event.xmap.window == windows->icon.id)
15778  {
15779  MagickBooleanType
15780  taint;
15781 
15782  /*
15783  Create an icon image.
15784  */
15785  taint=display_image->taint;
15786  XMakeStandardColormap(display,icon_visual,icon_resources,
15787  display_image,icon_map,icon_pixel);
15788  (void) XMakeImage(display,icon_resources,&windows->icon,
15789  display_image,windows->icon.width,windows->icon.height);
15790  display_image->taint=taint;
15791  (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15792  windows->icon.pixmap);
15793  (void) XClearWindow(display,windows->icon.id);
15794  (void) XWithdrawWindow(display,windows->info.id,
15795  windows->info.screen);
15796  windows->icon.mapped=MagickTrue;
15797  break;
15798  }
15799  if (event.xmap.window == windows->command.id)
15800  {
15801  windows->command.mapped=MagickTrue;
15802  break;
15803  }
15804  if (event.xmap.window == windows->popup.id)
15805  {
15806  windows->popup.mapped=MagickTrue;
15807  break;
15808  }
15809  if (event.xmap.window == windows->widget.id)
15810  {
15811  windows->widget.mapped=MagickTrue;
15812  break;
15813  }
15814  break;
15815  }
15816  case MappingNotify:
15817  {
15818  (void) XRefreshKeyboardMapping(&event.xmapping);
15819  break;
15820  }
15821  case NoExpose:
15822  break;
15823  case PropertyNotify:
15824  {
15825  Atom
15826  type;
15827 
15828  int
15829  format,
15830  status;
15831 
15832  unsigned char
15833  *data;
15834 
15835  unsigned long
15836  after,
15837  length;
15838 
15839  if (resource_info->debug != MagickFalse)
15840  (void) LogMagickEvent(X11Event,GetMagickModule(),
15841  "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15842  event.xproperty.atom,event.xproperty.state);
15843  if (event.xproperty.atom != windows->im_remote_command)
15844  break;
15845  /*
15846  Display image named by the remote command protocol.
15847  */
15848  status=XGetWindowProperty(display,event.xproperty.window,
15849  event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15850  AnyPropertyType,&type,&format,&length,&after,&data);
15851  if ((status != Success) || (length == 0))
15852  break;
15853  if (LocaleCompare((char *) data,"-quit") == 0)
15854  {
15855  XClientMessage(display,windows->image.id,windows->im_protocols,
15856  windows->im_exit,CurrentTime);
15857  (void) XFree((void *) data);
15858  break;
15859  }
15860  (void) CopyMagickString(resource_info->image_info->filename,
15861  (char *) data,MaxTextExtent);
15862  (void) XFree((void *) data);
15863  nexus=ReadImage(resource_info->image_info,&display_image->exception);
15864  CatchException(&display_image->exception);
15865  if (nexus != (Image *) NULL)
15866  *state|=NextImageState | ExitState;
15867  break;
15868  }
15869  case ReparentNotify:
15870  {
15871  if (resource_info->debug != MagickFalse)
15872  (void) LogMagickEvent(X11Event,GetMagickModule(),
15873  "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15874  event.xreparent.window);
15875  break;
15876  }
15877  case UnmapNotify:
15878  {
15879  if (resource_info->debug != MagickFalse)
15880  (void) LogMagickEvent(X11Event,GetMagickModule(),
15881  "Unmap Notify: 0x%lx",event.xunmap.window);
15882  if (event.xunmap.window == windows->backdrop.id)
15883  {
15884  windows->backdrop.mapped=MagickFalse;
15885  break;
15886  }
15887  if (event.xunmap.window == windows->image.id)
15888  {
15889  windows->image.mapped=MagickFalse;
15890  break;
15891  }
15892  if (event.xunmap.window == windows->magnify.id)
15893  {
15894  windows->magnify.mapped=MagickFalse;
15895  break;
15896  }
15897  if (event.xunmap.window == windows->pan.id)
15898  {
15899  windows->pan.mapped=MagickFalse;
15900  break;
15901  }
15902  if (event.xunmap.window == windows->info.id)
15903  {
15904  windows->info.mapped=MagickFalse;
15905  break;
15906  }
15907  if (event.xunmap.window == windows->icon.id)
15908  {
15909  if (map_info->colormap == icon_map->colormap)
15910  XConfigureImageColormap(display,resource_info,windows,
15911  display_image);
15912  (void) XFreeStandardColormap(display,icon_visual,icon_map,
15913  icon_pixel);
15914  windows->icon.mapped=MagickFalse;
15915  break;
15916  }
15917  if (event.xunmap.window == windows->command.id)
15918  {
15919  windows->command.mapped=MagickFalse;
15920  break;
15921  }
15922  if (event.xunmap.window == windows->popup.id)
15923  {
15924  if (windows->backdrop.id != (Window) NULL)
15925  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15926  CurrentTime);
15927  windows->popup.mapped=MagickFalse;
15928  break;
15929  }
15930  if (event.xunmap.window == windows->widget.id)
15931  {
15932  if (windows->backdrop.id != (Window) NULL)
15933  (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15934  CurrentTime);
15935  windows->widget.mapped=MagickFalse;
15936  break;
15937  }
15938  break;
15939  }
15940  default:
15941  {
15942  if (resource_info->debug != MagickFalse)
15943  (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15944  event.type);
15945  break;
15946  }
15947  }
15948  } while (!(*state & ExitState));
15949  if ((*state & ExitState) == 0)
15950  (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15951  &display_image);
15952  else
15953  if (resource_info->confirm_edit != MagickFalse)
15954  {
15955  /*
15956  Query user if image has changed.
15957  */
15958  if ((resource_info->immutable == MagickFalse) &&
15959  (display_image->taint != MagickFalse))
15960  {
15961  int
15962  status;
15963 
15964  status=XConfirmWidget(display,windows,"Your image changed.",
15965  "Do you want to save it");
15966  if (status == 0)
15967  *state&=(~ExitState);
15968  else
15969  if (status > 0)
15970  (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15971  &display_image);
15972  }
15973  }
15974  if ((windows->visual_info->klass == GrayScale) ||
15975  (windows->visual_info->klass == PseudoColor) ||
15976  (windows->visual_info->klass == DirectColor))
15977  {
15978  /*
15979  Withdraw pan and Magnify window.
15980  */
15981  if (windows->info.mapped != MagickFalse)
15982  (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15983  if (windows->magnify.mapped != MagickFalse)
15984  (void) XWithdrawWindow(display,windows->magnify.id,
15985  windows->magnify.screen);
15986  if (windows->command.mapped != MagickFalse)
15987  (void) XWithdrawWindow(display,windows->command.id,
15988  windows->command.screen);
15989  }
15990  if (windows->pan.mapped != MagickFalse)
15991  (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15992  if (resource_info->backdrop == MagickFalse)
15993  if (windows->backdrop.mapped)
15994  {
15995  (void) XWithdrawWindow(display,windows->backdrop.id,
15996  windows->backdrop.screen);
15997  (void) XDestroyWindow(display,windows->backdrop.id);
15998  windows->backdrop.id=(Window) NULL;
15999  (void) XWithdrawWindow(display,windows->image.id,
16000  windows->image.screen);
16001  (void) XDestroyWindow(display,windows->image.id);
16002  windows->image.id=(Window) NULL;
16003  }
16004  XSetCursorState(display,windows,MagickTrue);
16005  XCheckRefreshWindows(display,windows);
16006  if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16007  *state&=(~ExitState);
16008  if (*state & ExitState)
16009  {
16010  /*
16011  Free Standard Colormap.
16012  */
16013  (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16014  if (resource_info->map_type == (char *) NULL)
16015  (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16016  /*
16017  Free X resources.
16018  */
16019  if (resource_info->copy_image != (Image *) NULL)
16020  resource_info->copy_image=DestroyImage(resource_info->copy_image);
16021  DestroyXResources();
16022  }
16023  (void) XSync(display,MagickFalse);
16024  /*
16025  Restore our progress monitor and warning handlers.
16026  */
16027  (void) SetErrorHandler(warning_handler);
16028  (void) SetWarningHandler(warning_handler);
16029  /*
16030  Change to home directory.
16031  */
16032  directory=getcwd(working_directory,MaxTextExtent);
16033  (void) directory;
16034  {
16035  int
16036  status;
16037 
16038  if (*resource_info->home_directory == '\0')
16039  (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16040  status=chdir(resource_info->home_directory);
16041  if (status == -1)
16042  (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16043  FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16044  }
16045  *image=display_image;
16046  return(nexus);
16047 }
16048 #else
16049 
16050 /*
16051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16052 % %
16053 % %
16054 % %
16055 + D i s p l a y I m a g e s %
16056 % %
16057 % %
16058 % %
16059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16060 %
16061 % DisplayImages() displays an image sequence to any X window screen. It
16062 % returns a value other than 0 if successful. Check the exception member
16063 % of image to determine the reason for any failure.
16064 %
16065 % The format of the DisplayImages method is:
16066 %
16067 % MagickBooleanType DisplayImages(const ImageInfo *image_info,
16068 % Image *images)
16069 %
16070 % A description of each parameter follows:
16071 %
16072 % o image_info: the image info.
16073 %
16074 % o image: the image.
16075 %
16076 */
16077 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16078  Image *image)
16079 {
16080  assert(image_info != (const ImageInfo *) NULL);
16081  assert(image_info->signature == MagickCoreSignature);
16082  assert(image != (Image *) NULL);
16083  assert(image->signature == MagickCoreSignature);
16084  if (IsEventLogging() != MagickFalse)
16085  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16086  (void) image_info;
16087  (void) ThrowMagickException(&image->exception,GetMagickModule(),
16088  MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16089  image->filename);
16090  return(MagickFalse);
16091 }
16092 
16093 /*
16094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16095 % %
16096 % %
16097 % %
16098 + R e m o t e D i s p l a y C o m m a n d %
16099 % %
16100 % %
16101 % %
16102 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16103 %
16104 % RemoteDisplayCommand() encourages a remote display program to display the
16105 % specified image filename.
16106 %
16107 % The format of the RemoteDisplayCommand method is:
16108 %
16109 % MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16110 % const char *window,const char *filename,ExceptionInfo *exception)
16111 %
16112 % A description of each parameter follows:
16113 %
16114 % o image_info: the image info.
16115 %
16116 % o window: Specifies the name or id of an X window.
16117 %
16118 % o filename: the name of the image filename to display.
16119 %
16120 % o exception: return any errors or warnings in this structure.
16121 %
16122 */
16123 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16124  const char *window,const char *filename,ExceptionInfo *exception)
16125 {
16126  assert(image_info != (const ImageInfo *) NULL);
16127  assert(image_info->signature == MagickCoreSignature);
16128  assert(filename != (char *) NULL);
16129  if (IsEventLogging() != MagickFalse)
16130  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16131  (void) window;
16132  (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16133  "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16134  return(MagickFalse);
16135 }
16136 #endif
Definition: image.h:133